hadolint 1.6.5 → 1.6.6
raw patch · 7 files changed
+45/−58 lines, 7 filesdep +containersdep −dlist
Dependencies added: containers
Dependencies removed: dlist
Files
- hadolint.cabal +4/−5
- src/Hadolint/Formatter/Checkstyle.hs +2/−2
- src/Hadolint/Formatter/Codeclimate.hs +3/−4
- src/Hadolint/Formatter/Format.hs +3/−3
- src/Hadolint/Formatter/TTY.hs +3/−5
- src/Hadolint/Rules.hs +22/−33
- test/Spec.hs +8/−6
hadolint.cabal view
@@ -1,11 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.21.2.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: b523045510203dbac76d6488afec9838250f551597ef35d8b888adcbdc39e619+-- hash: 913a53c45d2193a9fb13a8dfb47bf647d985298fc2522ce1e369ab2ed1d1a311 name: hadolint-version: 1.6.5+version: 1.6.6 synopsis: Dockerfile Linter JavaScript API description: A smarter Dockerfile linter that helps you build best practice Docker images. category: Development@@ -17,7 +17,6 @@ license-file: LICENSE build-type: Simple cabal-version: >= 1.10- extra-source-files: README.md @@ -34,7 +33,7 @@ , aeson , base >=4.8 && <5 , bytestring- , dlist+ , containers , language-docker >=5.0.0 && <6 , parsec >=3.1 , split >=0.2
src/Hadolint/Formatter/Checkstyle.hs view
@@ -9,7 +9,7 @@ import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Lazy.Char8 as B import Data.Char-import Data.DList (toList)+import Data.Foldable (toList) import Data.List (groupBy) import Data.Monoid ((<>), mconcat) import Hadolint.Formatter.Format@@ -79,7 +79,7 @@ if isOk c then [c] else "&#" ++ show (ord c) ++ ";"- isOk x = any ($x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])]+ isOk x = any (\check -> check x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])] formatResult :: Result -> Builder.Builder formatResult (Result errors checks) =
src/Hadolint/Formatter/Codeclimate.hs view
@@ -9,8 +9,8 @@ import Data.Aeson hiding (Result) import qualified Data.ByteString.Lazy as B-import Data.DList (DList) import Data.Monoid ((<>))+import Data.Sequence (Seq) import GHC.Generics import Hadolint.Formatter.Format (Result(..), formatErrorReason) import Hadolint.Rules (Metadata(..), RuleCheck(..))@@ -59,7 +59,7 @@ Issue { checkName = "DL1000" , description = formatErrorReason err- , location = LocPos (sourceName pos) Pos{..}+ , location = LocPos (sourceName pos) Pos {..} , impact = severityText ErrorC } where@@ -67,7 +67,6 @@ line = sourceLine pos column = sourceColumn pos - checkToIssue :: RuleCheck -> Issue checkToIssue RuleCheck {..} = Issue@@ -85,7 +84,7 @@ InfoC -> "info" StyleC -> "minor" -formatResult :: Result -> DList Issue+formatResult :: Result -> Seq Issue formatResult (Result errors checks) = allIssues where allIssues = errorMessages <> checkMessages
src/Hadolint/Formatter/Format.hs view
@@ -6,18 +6,18 @@ , toResult ) where -import Data.DList (DList, fromList, singleton) import Data.List (sort) import Data.Monoid (Monoid) import Data.Semigroup+import Data.Sequence (Seq, fromList, singleton) import Hadolint.Rules import ShellCheck.Interface import Text.Parsec.Error (ParseError, errorMessages, showErrorMessages) data Result = Result- { errors :: DList ParseError- , checks :: DList RuleCheck+ { errors :: Seq ParseError+ , checks :: Seq RuleCheck } deriving (Eq) instance Semigroup Result where
src/Hadolint/Formatter/TTY.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NamedFieldPuns #-}+ module Hadolint.Formatter.TTY ( printResult , formatError@@ -26,13 +27,10 @@ formatPos source line ++ code meta ++ " " ++ message meta formatPos :: Filename -> Linenumber -> String-formatPos source line =- if line >= 0- then source ++ ":" ++ show line ++ " "- else source ++ " "+formatPos source line = source ++ ":" ++ show line ++ " " printResult :: Result -> IO ()-printResult Result{errors, checks} = printErrors >> printChecks+printResult Result {errors, checks} = printErrors >> printChecks where printErrors = mapM_ putStrLn (formatErrors errors) printChecks = mapM_ putStrLn (formatChecks checks)
src/Hadolint/Rules.hs view
@@ -10,6 +10,7 @@ import Hadolint.Bash import Language.Docker.Syntax +import qualified Data.Set as Set import qualified ShellCheck.Interface import ShellCheck.Interface (Severity(..)) import qualified Text.Parsec as Parsec@@ -76,19 +77,8 @@ -> Rule instructionRuleState code severity message = mapInstructions (Metadata code severity message) -dockerfileRule ::- String -- ^ The rule code name- -> ShellCheck.Interface.Severity -- ^ The rule severity- -> String -- ^ The error message associated with the rule- -> ([Instruction] -> Bool) -- ^ The function that will check the rule- -> Rule-dockerfileRule code severity message f = rule- where- rule dockerfile =- [RuleCheck metadata (filename dockerfile) (-1) (f (map instruction dockerfile))]- metadata = Metadata code severity message- filename (inst:_) = sourcename inst- filename [] = ""+withState :: a -> b -> (a, b)+withState st res = (st, res) -- Enforce rules on a dockerfile and return failed checks analyze :: [Rule] -> Dockerfile -> [RuleCheck]@@ -225,54 +215,55 @@ check _ = True hasNoMaintainer :: Rule-hasNoMaintainer = dockerfileRule code severity message check+hasNoMaintainer = instructionRule code severity message check where code = "DL4000" severity = ErrorC message = "MAINTAINER is deprecated"- check dockerfile = not $ any isMaintainer dockerfile- isMaintainer (Maintainer _) = True- isMaintainer _ = False+ check (Maintainer _) = False+ check _ = True -- Check if a command contains a program call in the Run instruction usingProgram :: String -> [String] -> Bool usingProgram prog args = or [True | cmd:_ <- bashCommands args, cmd == prog] multipleCmds :: Rule-multipleCmds = dockerfileRule code severity message check+multipleCmds = instructionRuleState code severity message check Nothing where code = "DL4003" severity = WarningC message = "Multiple `CMD` instructions found. If you list more than one `CMD` then only the last \ \`CMD` will take effect"- check dockerfile = 1 >= length (filter (True ==) $ map isCmd dockerfile)- isCmd (Cmd _) = True- isCmd _ = False+ check Nothing line (Cmd _) = withState (Just line) True -- Remember the first CMD found+ check (Just l) _ (Cmd _) = withState (Just l) False -- Fail the rule, CMD is duplicated+ check st _ _ = withState st True multipleEntrypoints :: Rule-multipleEntrypoints = dockerfileRule code severity message check+multipleEntrypoints = instructionRuleState code severity message check Nothing where code = "DL4004" severity = ErrorC message = "Multiple `ENTRYPOINT` instructions found. If you list more than one `ENTRYPOINT` then \ \only the last `ENTRYPOINT` will take effect"- check dockerfile = 1 >= length (filter (True ==) $ map isEntrypoint dockerfile)- isEntrypoint (Entrypoint _) = True- isEntrypoint _ = False+ check Nothing line (Entrypoint _) = withState (Just line) True -- Remember the first ENTRYPOINT found+ check (Just l) _ (Entrypoint _) = withState (Just l) False -- Fail the rule, ENTRYPOINT is duplicated+ check st _ _ = withState st True wgetOrCurl :: Rule-wgetOrCurl = dockerfileRule code severity message check+wgetOrCurl = instructionRuleState code severity message check Set.empty where code = "DL4001" severity = WarningC message = "Either use Wget or Curl but not both"- check dockerfile = not $ anyCurl dockerfile && anyWget dockerfile- anyCurl = any $ usingCmd "curl"- anyWget = any $ usingCmd "wget"- usingCmd cmd (Run (Arguments args)) = cmd `elem` args- usingCmd _ _ = False+ check state _ (Run (Arguments args)) = detectDoubleUsage state args+ check state _ _ = withState state True+ detectDoubleUsage state args =+ let newArgs = extractCommands args+ newState = Set.union state newArgs+ in withState newState (Set.size newState < 2)+ extractCommands args = Set.fromList [w | w <- args, w == "curl" || w == "wget"] invalidCmd :: Rule invalidCmd = instructionRule code severity message check@@ -375,7 +366,6 @@ code = "DL3009" severity = InfoC message = "Delete the apt-get lists after installing something"- withState st res = (st, res) -- | 'check' returns a tuple (state, check_result) -- The state in this case is the FROM instruction where the current instruction we are -- inspecting is nested in.@@ -653,7 +643,6 @@ code = "DL3023" severity = ErrorC message = "COPY --from should reference a previously defined FROM alias"- withState st res = (st, res) -- | 'check' returns a tuple (state, check_result) -- The state in this case is the FROM instruction where the current instruction we are -- inspecting is nested in.
test/Spec.hs view
@@ -453,19 +453,21 @@ ruleCatchesNot copyFromAnother $ unlines dockerFile ruleCatchesNot copyEndingSlash $ unlines dockerFile -assertChecks :: Rule -> String -> ([RuleCheck] -> IO a) -> IO a-assertChecks rule s f =+assertChecks :: HasCallStack => Rule -> String -> ([RuleCheck] -> IO a) -> IO a+assertChecks rule s makeAssertions = case parseString (s ++ "\n") of Left err -> assertFailure $ show err- Right dockerFile -> f $ analyze [rule] dockerFile+ Right dockerFile -> makeAssertions $ analyze [rule] dockerFile -- Assert a failed check exists for rule-ruleCatches :: Rule -> String -> Assertion+ruleCatches :: HasCallStack => Rule -> String -> Assertion ruleCatches rule s = assertChecks rule s f where- f checks = assertEqual "No check for rule found" 1 $ length checks+ f checks = do+ assertEqual "No check for rule found" 1 $ length checks+ assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0] -ruleCatchesNot :: Rule -> String -> Assertion+ruleCatchesNot :: HasCallStack => Rule -> String -> Assertion ruleCatchesNot rule s = assertChecks rule s f where f checks = assertEqual "Found check of rule" 0 $ length checks