syncable_cli/analyzer/hadolint/rules/
dl4004.rs

1//! DL4004: Multiple ENTRYPOINT instructions
2//!
3//! Only one ENTRYPOINT instruction should be present. If multiple are present,
4//! only the last one takes effect.
5
6use crate::analyzer::hadolint::parser::instruction::Instruction;
7use crate::analyzer::hadolint::rules::{custom_rule, CustomRule, RuleState};
8use crate::analyzer::hadolint::shell::ParsedShell;
9use crate::analyzer::hadolint::types::Severity;
10
11pub fn rule() -> CustomRule<impl Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync> {
12    custom_rule(
13        "DL4004",
14        Severity::Error,
15        "Multiple `ENTRYPOINT` instructions found. If you list more than one `ENTRYPOINT` then only the last `ENTRYPOINT` will take effect",
16        |state, line, instr, _shell| {
17            match instr {
18                Instruction::From(_) => {
19                    // Reset count for each stage
20                    state.data.set_int("entrypoint_count", 0);
21                }
22                Instruction::Entrypoint(_) => {
23                    let count = state.data.get_int("entrypoint_count") + 1;
24                    state.data.set_int("entrypoint_count", count);
25
26                    if count > 1 {
27                        state.add_failure(
28                            "DL4004",
29                            Severity::Error,
30                            "Multiple `ENTRYPOINT` instructions found. If you list more than one `ENTRYPOINT` then only the last `ENTRYPOINT` will take effect",
31                            line,
32                        );
33                    }
34                }
35                _ => {}
36            }
37        },
38    )
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::analyzer::hadolint::parser::instruction::{Arguments, BaseImage};
45    use crate::analyzer::hadolint::rules::Rule;
46
47    #[test]
48    fn test_single_entrypoint() {
49        let rule = rule();
50        let mut state = RuleState::new();
51
52        let from = Instruction::From(BaseImage::new("ubuntu"));
53        let ep = Instruction::Entrypoint(Arguments::List(vec!["./entrypoint.sh".to_string()]));
54
55        rule.check(&mut state, 1, &from, None);
56        rule.check(&mut state, 2, &ep, None);
57        assert!(state.failures.is_empty());
58    }
59
60    #[test]
61    fn test_multiple_entrypoints() {
62        let rule = rule();
63        let mut state = RuleState::new();
64
65        let from = Instruction::From(BaseImage::new("ubuntu"));
66        let ep1 = Instruction::Entrypoint(Arguments::List(vec!["./script1.sh".to_string()]));
67        let ep2 = Instruction::Entrypoint(Arguments::List(vec!["./script2.sh".to_string()]));
68
69        rule.check(&mut state, 1, &from, None);
70        rule.check(&mut state, 2, &ep1, None);
71        rule.check(&mut state, 3, &ep2, None);
72        assert_eq!(state.failures.len(), 1);
73        assert_eq!(state.failures[0].code.as_str(), "DL4004");
74    }
75}