syncable_cli/analyzer/hadolint/rules/
dl3059.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 "DL3059",
13 Severity::Info,
14 "Multiple consecutive `RUN` instructions. Consider consolidation.",
15 |state, line, instr, _shell| {
16 match instr {
17 Instruction::From(_) => {
18 state.data.set_int("consecutive_runs", 0);
20 state.data.set_int("last_run_line", 0);
21 }
22 Instruction::Run(_) => {
23 let consecutive = state.data.get_int("consecutive_runs");
24 state.data.set_int("consecutive_runs", consecutive + 1);
25 state.data.set_int("last_run_line", line as i64);
26
27 if consecutive >= 1 {
29 state.add_failure(
30 "DL3059",
31 Severity::Info,
32 "Multiple consecutive `RUN` instructions. Consider consolidation.",
33 line,
34 );
35 }
36 }
37 _ => {
39 state.data.set_int("consecutive_runs", 0);
40 }
41 }
42 },
43 )
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::analyzer::hadolint::lint::{lint, LintResult};
50 use crate::analyzer::hadolint::config::HadolintConfig;
51
52 fn lint_dockerfile(content: &str) -> LintResult {
53 lint(content, &HadolintConfig::default())
54 }
55
56 #[test]
57 fn test_consecutive_runs() {
58 let result = lint_dockerfile(
59 "FROM ubuntu:20.04\nRUN apt-get update\nRUN apt-get install -y nginx"
60 );
61 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3059"));
62 }
63
64 #[test]
65 fn test_single_run() {
66 let result = lint_dockerfile(
67 "FROM ubuntu:20.04\nRUN apt-get update && apt-get install -y nginx"
68 );
69 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3059"));
70 }
71
72 #[test]
73 fn test_runs_separated_by_other() {
74 let result = lint_dockerfile(
75 "FROM ubuntu:20.04\nRUN apt-get update\nENV DEBIAN_FRONTEND=noninteractive\nRUN apt-get install -y nginx"
76 );
77 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3059"));
78 }
79
80 #[test]
81 fn test_three_consecutive_runs() {
82 let result = lint_dockerfile(
83 "FROM ubuntu:20.04\nRUN echo 1\nRUN echo 2\nRUN echo 3"
84 );
85 let count = result.failures.iter().filter(|f| f.code.as_str() == "DL3059").count();
87 assert_eq!(count, 2);
88 }
89
90 #[test]
91 fn test_different_stages() {
92 let result = lint_dockerfile(
93 "FROM ubuntu:20.04 AS stage1\nRUN echo 1\nFROM ubuntu:20.04 AS stage2\nRUN echo 2"
94 );
95 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3059"));
97 }
98}