hadolint 1.8.0 → 1.9.0
raw patch · 5 files changed
+119/−12 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.Class.ToJSON Hadolint.Formatter.Codeclimate.Issue
- Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.Class.ToJSON Hadolint.Formatter.Codeclimate.Location
- Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.Class.ToJSON Hadolint.Formatter.Codeclimate.Pos
- Hadolint.Formatter.Format: instance Data.Semigroup.Semigroup (Hadolint.Formatter.Format.Result t e)
- Hadolint.Formatter.Json: instance (Text.Megaparsec.Error.ShowToken t, GHC.Classes.Ord t, Text.Megaparsec.Error.ShowErrorComponent e) => Data.Aeson.Types.Class.ToJSON (Hadolint.Formatter.Json.JsonFormat t e)
+ Hadolint.Bash: extractTokensWith :: (Token -> Maybe Token) -> ParsedBash -> [Token]
+ Hadolint.Bash: findPipes :: ParsedBash -> [Token]
+ Hadolint.Bash: hasPipes :: ParsedBash -> Bool
+ Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.ToJSON.ToJSON Hadolint.Formatter.Codeclimate.Issue
+ Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.ToJSON.ToJSON Hadolint.Formatter.Codeclimate.Location
+ Hadolint.Formatter.Codeclimate: instance Data.Aeson.Types.ToJSON.ToJSON Hadolint.Formatter.Codeclimate.Pos
+ Hadolint.Formatter.Format: instance GHC.Base.Semigroup (Hadolint.Formatter.Format.Result t e)
+ Hadolint.Formatter.Json: instance (Text.Megaparsec.Error.ShowToken t, GHC.Classes.Ord t, Text.Megaparsec.Error.ShowErrorComponent e) => Data.Aeson.Types.ToJSON.ToJSON (Hadolint.Formatter.Json.JsonFormat t e)
+ Hadolint.Rules: usePipefail :: Rule
Files
- README.md +2/−1
- hadolint.cabal +2/−2
- src/Hadolint/Bash.hs +20/−6
- src/Hadolint/Rules.hs +27/−3
- test/Spec.hs +68/−0
README.md view
@@ -153,12 +153,13 @@ | [DL3022](https://github.com/hadolint/hadolint/wiki/DL3022) | `COPY --from` should reference a previously defined `FROM` alias | | [DL3023](https://github.com/hadolint/hadolint/wiki/DL3023) | `COPY --from` cannot reference its own `FROM` alias | | [DL3024](https://github.com/hadolint/hadolint/wiki/DL3024) | `FROM` aliases (stage names) must be unique |-| [DL3025](https://github.com/hadolint/hadolint/wiki/DL3025) | Use argumens JSON notation for CMD and ENTRYPOINT arguments |+| [DL3025](https://github.com/hadolint/hadolint/wiki/DL3025) | Use arguments JSON notation for CMD and ENTRYPOINT arguments | | [DL4000](https://github.com/hadolint/hadolint/wiki/DL4000) | MAINTAINER is deprecated. | | [DL4001](https://github.com/hadolint/hadolint/wiki/DL4001) | Either use Wget or Curl but not both. | | [DL4003](https://github.com/hadolint/hadolint/wiki/DL4003) | Multiple `CMD` instructions found. | | [DL4004](https://github.com/hadolint/hadolint/wiki/DL4004) | Multiple `ENTRYPOINT` instructions found. | | [DL4005](https://github.com/hadolint/hadolint/wiki/DL4005) | Use `SHELL` to change the default shell. |+| [DL4006](https://github.com/hadolint/hadolint/wiki/DL4006) | Set the `SHELL` option -o pipefail before `RUN` with a pipe in it | | [SC1000](https://github.com/koalaman/shellcheck/wiki/SC1000) | `$` is not used specially and should therefore be escaped. | | [SC1001](https://github.com/koalaman/shellcheck/wiki/SC1001) | This `\c` will be a regular `'c'` in this context. | | [SC1007](https://github.com/koalaman/shellcheck/wiki/SC1007) | Remove space after `=` if trying to assign a value (or for empty string, use `var='' ...`). |
hadolint.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ed40f7e723efb830715c051c8b06bf4693ace2a9f94c7a53e035fd1fc237febb+-- hash: 0723e0fb6351589066a83e427a444fd8297f43911d23ac2c3a053888a957f94b name: hadolint-version: 1.8.0+version: 1.9.0 synopsis: Dockerfile Linter JavaScript API description: A smarter Dockerfile linter that helps you build best practice Docker images. category: Development
src/Hadolint/Bash.hs view
@@ -43,17 +43,31 @@ } } -findCommands :: ParsedBash -> [Token]-findCommands (ParsedBash _ ast) =+extractTokensWith :: (Token -> Maybe Token) -> ParsedBash -> [Token]+extractTokensWith extractor (ParsedBash _ ast) = case prRoot ast of Nothing -> [] Just script -> nub . execWriter $ ShellCheck.AST.doAnalysis extract script where extract :: Token -> Writer [Token] ()- extract t =- case ShellCheck.ASTLib.getCommand t of- Nothing -> return ()- Just cmd -> tell [cmd]+ extract token =+ case extractor token of+ Nothing -> return ()+ Just t -> tell [t]++findPipes :: ParsedBash -> [Token]+findPipes = extractTokensWith pipesExtractor+ where+ pipesExtractor pipe@T_Pipe{} = Just pipe+ pipesExtractor _ = Nothing++hasPipes :: ParsedBash -> Bool+hasPipes = not . null . findPipes++findCommands :: ParsedBash -> [Token]+findCommands = extractTokensWith commandsExtractor+ where+ commandsExtractor = ShellCheck.ASTLib.getCommand allCommands :: (Token -> Bool) -> ParsedBash -> Bool allCommands check script = all check (findCommands script)
src/Hadolint/Rules.hs view
@@ -175,6 +175,7 @@ , multipleEntrypoints , useShell , useJsonArgs+ , usePipefail ] commentMetadata :: ShellCheck.Interface.Comment -> Metadata@@ -709,7 +710,30 @@ where code = "DL3025" severity = WarningC- message = "Use argumens JSON notation for CMD and ENTRYPOINT arguments"- check (Cmd (ArgumentsText _ )) = False- check (Entrypoint (ArgumentsText _ )) = False+ message = "Use arguments JSON notation for CMD and ENTRYPOINT arguments"+ check (Cmd (ArgumentsText _)) = False+ check (Entrypoint (ArgumentsText _)) = False check _ = True++usePipefail :: Rule+usePipefail = instructionRuleState code severity message check False+ where+ code = "DL4006"+ severity = WarningC+ message = "Set the SHELL option -o pipefail before RUN with a pipe in it"+ check _ _ From {} = (False, True) -- Reset the state each time we find a new FROM+ check _ _ (Shell args) = (argumentsRule hasPipefailOption args, True)+ check False _ (Run args) = (False, argumentsRule notHasPipes args)+ check st _ _ = (st, True)+ notHasPipes script = not (Bash.hasPipes script)+ hasPipefailOption script =+ not $+ null+ [ True+ | cmd <- Bash.findCommands script+ , validShell <- ["/bin/bash", "/bin/zsh", "/bin/ash", "bash", "zsh", "ash"]+ , Bash.getCommandName cmd == Just validShell+ , Bash.hasFlag "o" cmd+ , arg <- Bash.getAllArgs cmd+ , arg == "pipefail"+ ]
test/Spec.hs view
@@ -757,6 +757,74 @@ ] in ruleCatchesNot useJsonArgs $ Text.unlines dockerFile + --+ describe "Detects missing pipefail option" $ do+ it "warn on missing pipefail" $+ let dockerFile =+ [ "FROM scratch"+ , "RUN wget -O - https://some.site | wc -l > /number"+ ]+ in ruleCatches usePipefail $ Text.unlines dockerFile+ it "don't warn on commands with no pipes" $+ let dockerFile =+ [ "FROM scratch as build"+ , "RUN wget -O - https://some.site && wc -l file > /number"+ ]+ in ruleCatchesNot usePipefail $ Text.unlines dockerFile+ it "don't warn on commands with pipes and the pipefail option" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/bash\", \"-eo\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatchesNot usePipefail $ Text.unlines dockerFile+ it "don't warn on commands with pipes and the pipefail option 2" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/bash\", \"-e\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatchesNot usePipefail $ Text.unlines dockerFile+ it "don't warn on commands with pipes and the pipefail option 3" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/bash\", \"-o\", \"errexit\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatchesNot usePipefail $ Text.unlines dockerFile+ it "don't warn on commands with pipes and the pipefail zsh" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/zsh\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatchesNot usePipefail $ Text.unlines dockerFile+ it "warns when using plain sh" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/sh\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatches usePipefail $ Text.unlines dockerFile+ it "warn on missing pipefail in the next image" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/bash\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ , "FROM scratch as build2"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatches usePipefail $ Text.unlines dockerFile+ it "warn on missing pipefail if next SHELL is not using it" $+ let dockerFile =+ [ "FROM scratch as build"+ , "SHELL [\"/bin/bash\", \"-o\", \"pipefail\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ , "SHELL [\"/bin/sh\", \"-c\"]"+ , "RUN wget -O - https://some.site | wc -l file > /number"+ ]+ in ruleCatches usePipefail $ Text.unlines dockerFile+ assertChecks :: HasCallStack => Rule -> Text.Text -> ([RuleCheck] -> IO a) -> IO a assertChecks rule s makeAssertions = case parseText (s <> "\n") of