syncable_cli/analyzer/hadolint/rules/
dl3024.rs1use crate::analyzer::hadolint::parser::instruction::Instruction;
6use crate::analyzer::hadolint::rules::{custom_rule, CustomRule, RuleState};
7use crate::analyzer::hadolint::shell::ParsedShell;
8use crate::analyzer::hadolint::types::Severity;
9
10pub fn rule() -> CustomRule<impl Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync> {
11 custom_rule(
12 "DL3024",
13 Severity::Error,
14 "`FROM` aliases (stage names) must be unique.",
15 |state, line, instr, _shell| {
16 if let Instruction::From(base) = instr {
17 if let Some(alias) = &base.alias {
18 let alias_str = alias.as_str();
19 if state.data.set_contains("seen_aliases", alias_str) {
20 state.add_failure(
21 "DL3024",
22 Severity::Error,
23 format!("Duplicate `FROM` alias `{}`.", alias_str),
24 line,
25 );
26 } else {
27 state.data.insert_to_set("seen_aliases", alias_str);
28 }
29 }
30 }
31 },
32 )
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::analyzer::hadolint::lint::{lint, LintResult};
39 use crate::analyzer::hadolint::config::HadolintConfig;
40
41 fn lint_dockerfile(content: &str) -> LintResult {
42 lint(content, &HadolintConfig::default())
43 }
44
45 #[test]
46 fn test_duplicate_alias() {
47 let result = lint_dockerfile(
48 "FROM node:18 AS builder\nRUN npm ci\nFROM node:18-alpine AS builder\nRUN echo done"
49 );
50 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3024"));
51 }
52
53 #[test]
54 fn test_unique_aliases() {
55 let result = lint_dockerfile(
56 "FROM node:18 AS builder\nRUN npm ci\nFROM node:18-alpine AS runner\nRUN echo done"
57 );
58 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3024"));
59 }
60
61 #[test]
62 fn test_no_aliases() {
63 let result = lint_dockerfile(
64 "FROM node:18\nRUN npm ci\nFROM node:18-alpine\nRUN echo done"
65 );
66 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3024"));
67 }
68
69 #[test]
70 fn test_single_stage() {
71 let result = lint_dockerfile("FROM node:18 AS builder\nRUN npm ci");
72 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3024"));
73 }
74}