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