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