packages feed

hadolint 2.4.1 → 2.5.0

raw patch · 9 files changed

+180/−20 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Hadolint: SonarQube :: OutputFormat
+ Hadolint.Formatter.SonarQube: formatResult :: Result s e -> Seq (SonarQubeFormat s e)
+ Hadolint.Formatter.SonarQube: instance (Text.Megaparsec.Stream.VisualStream s, Text.Megaparsec.Stream.TraversableStream s, Text.Megaparsec.Error.ShowErrorComponent e) => Data.Aeson.Types.ToJSON.ToJSON (Hadolint.Formatter.SonarQube.SonarQubeFormat s e)
+ Hadolint.Formatter.SonarQube: printResults :: (VisualStream s, TraversableStream s, ShowErrorComponent e, Foldable f) => f (Result s e) -> IO ()
+ Hadolint.Shell: nonPosixShells :: [Text]
- Hadolint: printResults :: Foldable f => OutputFormat -> Bool -> f (Result Text DockerfileError) -> IO ()
+ Hadolint: printResults :: Foldable f => OutputFormat -> Bool -> Maybe FilePath -> f (Result Text DockerfileError) -> IO ()
- Hadolint.Formatter.Checkstyle: formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Builder
+ Hadolint.Formatter.Checkstyle: formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Maybe FilePath -> Builder
- Hadolint.Formatter.Checkstyle: printResults :: (Foldable f, VisualStream s, TraversableStream s, ShowErrorComponent e) => f (Result s e) -> IO ()
+ Hadolint.Formatter.Checkstyle: printResults :: (Foldable f, VisualStream s, TraversableStream s, ShowErrorComponent e) => f (Result s e) -> Maybe FilePath -> IO ()

Files

app/Main.hs view
@@ -59,11 +59,13 @@     isVerbose :: Bool,     format :: Hadolint.OutputFormat,     dockerfiles :: [String],-    lintingOptions :: Hadolint.LintOptions+    lintingOptions :: Hadolint.LintOptions,+    filePathInReportOption :: Maybe FilePath   }  toOutputFormat :: String -> Maybe Hadolint.OutputFormat toOutputFormat "json" = Just Hadolint.Json+toOutputFormat "sonarqube" = Just Hadolint.SonarQube toOutputFormat "tty" = Just Hadolint.TTY toOutputFormat "codeclimate" = Just Hadolint.CodeclimateJson toOutputFormat "gitlab_codeclimate" = Just Hadolint.GitlabCodeclimateJson@@ -73,6 +75,7 @@  showFormat :: Hadolint.OutputFormat -> String showFormat Hadolint.Json = "json"+showFormat Hadolint.SonarQube = "sonarqube" showFormat Hadolint.TTY = "tty" showFormat Hadolint.CodeclimateJson = "codeclimate" showFormat Hadolint.GitlabCodeclimateJson = "gitlab_codeclimate"@@ -99,6 +102,7 @@     <*> outputFormat     <*> files     <*> lintOptions+    <*> filePathInReportOption   where     version = switch (long "version" <> short 'v' <> help "Show version") @@ -206,6 +210,14 @@         <*> parseRulesConfig         <*> noFailCutoff +    filePathInReportOption =+      optional+        ( strOption+            ( long "file-path-in-report" <> metavar "FILEPATHINREPORT"+                <> help "The file path referenced in the generated report. This only applies for the 'checkstyle' format and is useful when running Hadolint with Docker to set the correct file path."+            )+        )+     labels =       Map.fromList         <$> many@@ -247,7 +259,7 @@ noFailure :: Hadolint.Result s e -> Rule.DLSeverity -> Bool noFailure (Hadolint.Result _ Seq.Empty Seq.Empty) _ = True noFailure (Hadolint.Result _ Seq.Empty fails) cutoff =-  Seq.null (Seq.filter (\f -> Rule.severity f < cutoff) fails)+  Seq.null (Seq.filter (\f -> Rule.severity f <= cutoff) fails) noFailure _ _ = False  exitProgram ::@@ -267,7 +279,8 @@   res <- Hadolint.lintIO conf files   noColorEnv <- lookupEnv "NO_COLOR"   let noColor = nocolor cmd || isJust noColorEnv-  Hadolint.printResults (format cmd) noColor res+  let filePathInReport = filePathInReportOption cmd+  Hadolint.printResults (format cmd) noColor filePathInReport res   exitProgram cmd conf res  main :: IO ()
hadolint.cabal view
@@ -3,11 +3,9 @@ -- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack------ hash: 81ce3bbf6ac259bb1aeda5e670d68ec5cbcb3bf9d955d085de125eb4e39f3fc8  name:           hadolint-version:        2.4.1+version:        2.5.0 synopsis:       Dockerfile Linter JavaScript API description:    A smarter Dockerfile linter that helps you build best practice Docker images. category:       Development@@ -39,6 +37,7 @@       Hadolint.Formatter.Codeclimate       Hadolint.Formatter.Format       Hadolint.Formatter.Json+      Hadolint.Formatter.SonarQube       Hadolint.Formatter.TTY       Hadolint.Ignore       Hadolint.Lint
src/Hadolint.hs view
@@ -16,6 +16,7 @@ import qualified Hadolint.Formatter.Codeclimate import Hadolint.Formatter.Format (Result (..)) import qualified Hadolint.Formatter.Json+import qualified Hadolint.Formatter.SonarQube import qualified Hadolint.Formatter.TTY import Hadolint.Lint import Hadolint.Process@@ -23,6 +24,7 @@  data OutputFormat   = Json+  | SonarQube   | TTY   | CodeclimateJson   | GitlabCodeclimateJson@@ -33,12 +35,13 @@ shallSkipErrorStatus :: OutputFormat -> Bool shallSkipErrorStatus format = format `elem` [CodeclimateJson, Codacy] -printResults :: Foldable f => OutputFormat -> Bool -> f (Result Text DockerfileError) -> IO ()-printResults format nocolor allResults =+printResults :: Foldable f => OutputFormat -> Bool -> Maybe FilePath -> f (Result Text DockerfileError) -> IO ()+printResults format nocolor filePathInReport allResults =   case format of     TTY -> Hadolint.Formatter.TTY.printResults allResults nocolor+    SonarQube -> Hadolint.Formatter.SonarQube.printResults allResults     Json -> Hadolint.Formatter.Json.printResults allResults-    Checkstyle -> Hadolint.Formatter.Checkstyle.printResults allResults+    Checkstyle -> Hadolint.Formatter.Checkstyle.printResults allResults filePathInReport     CodeclimateJson -> Hadolint.Formatter.Codeclimate.printResults allResults     GitlabCodeclimateJson -> Hadolint.Formatter.Codeclimate.printGitlabResults allResults     Codacy -> Hadolint.Formatter.Codacy.printResults allResults
src/Hadolint/Formatter/Checkstyle.hs view
@@ -81,8 +81,8 @@         else "&#" <> Text.pack (show (ord c)) <> ";"     isOk x = any (\check -> check x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])] -formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Builder.Builder-formatResult (Result filename errors checks) = header <> xmlBody <> footer+formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Maybe FilePath -> Builder.Builder+formatResult (Result filename errors checks) filePathInReport = header <> xmlBody <> footer   where     xmlBody = Foldl.fold (Foldl.premap toXml Foldl.mconcat) issues @@ -91,21 +91,29 @@     checkstyleChecks = fmap ruleToCheckStyle checks      isEmpty = null checks && null errors+    name = if null filePathInReport then filename else getFilePath filePathInReport     header =       if isEmpty         then ""-        else "<file " <> attr "name" (encode filename) <> ">"+        else "<file " <> attr "name" (encode name) <> ">"     footer = if isEmpty then "" else "</file>"  printResults ::   (Foldable f, VisualStream s, TraversableStream s, ShowErrorComponent e) =>-  f (Result s e) ->+  f (Result s e) -> Maybe FilePath ->   IO ()-printResults results = do+printResults results filePathInReport = do   B.putStr header   mapM_ put results   B.putStr footer   where     header = "<?xml version='1.0' encoding='UTF-8'?><checkstyle version='4.3'>"     footer = "</checkstyle>"-    put result = Builder.hPutBuilder stdout (formatResult result)+    put result = Builder.hPutBuilder stdout (formatResult result filePathInReport)++getFilePath :: Maybe FilePath -> Text.Text +getFilePath Nothing = ""+getFilePath (Just filePath) = toText [filePath]++toText :: [FilePath] -> Text.Text +toText = foldMap Text.pack
+ src/Hadolint/Formatter/SonarQube.hs view
@@ -0,0 +1,103 @@+module Hadolint.Formatter.SonarQube+  ( formatResult,+    printResults+  )+  where++import qualified Control.Foldl as Foldl+import Data.Aeson hiding (Result)+import qualified Data.ByteString.Lazy.Char8 as B+import Data.Sequence (Seq)+import qualified Data.Text as Text+import Hadolint.Formatter.Format+  ( Result (..),+    errorPosition,+    errorMessage+  )+import Hadolint.Rule+  ( CheckFailure (..),+    DLSeverity (..),+    unRuleCode+  )+import Text.Megaparsec (TraversableStream)+import Text.Megaparsec.Error+import Text.Megaparsec.Pos+  ( sourceColumn,+    sourceLine,+    sourceName,+    unPos+  )+import Text.Megaparsec.Stream (VisualStream)+++data SonarQubeFormat s e+  = SonarQubeCheck Text.Text CheckFailure+  | SonarQubeError (ParseErrorBundle s e)++instance (VisualStream s,+  TraversableStream s,+  ShowErrorComponent e) => ToJSON (SonarQubeFormat s e) where+  toJSON (SonarQubeCheck filename CheckFailure {..}) =+    object+      [ "engineId" .= Text.pack "Hadolint",+        "ruleId" .= unRuleCode code,+        "severity" .= toSeverity severity,+        "type" .= toType severity,+        "primaryLocation" .= object+          [ "message" .= message,+            "filePath" .= filename,+            "textRange" .= object+              [ "startLine" .= line,+                "endLine" .= line,+                "startColumn" .= (1 :: Int),+                "endColumn" .= (1 :: Int)+              ]+          ]+      ]+  toJSON (SonarQubeError err) =+    object+      [ "engineId" .= Text.pack "Hadolint",+        "ruleId" .= Text.pack "DL1000",+        "severity" .= Text.pack "BLOCKER",+        "type" .= Text.pack "BUG",+        "primaryLocation" .= object+          [ "message" .= errorMessage err,+            "filePath" .= Text.pack (sourceName pos),+            "textRange" .= object+              [ "startLine" .= linenumber,+                "endLine" .= linenumber,+                "startColumn" .= column,+                "endColumn" .= column+              ]+          ]+      ]+    where+      pos = errorPosition err+      linenumber = unPos $ sourceLine pos+      column = unPos $ sourceColumn pos+++formatResult :: Result s e -> Seq (SonarQubeFormat s e)+formatResult (Result filename errors checks) = allMessages+  where+    allMessages = errorMessages <> checkMessages+    errorMessages = fmap SonarQubeError errors+    checkMessages = fmap (SonarQubeCheck filename) checks++printResults :: (VisualStream s,+  TraversableStream s,+  ShowErrorComponent e,+  Foldable f) => f (Result s e) -> IO ()+printResults results = B.putStr . encode $ object [ "issues" .= flattened ]+  where+    flattened = Foldl.fold (Foldl.premap formatResult Foldl.mconcat) results++toType :: DLSeverity -> Text.Text+toType DLErrorC = "BUG"+toType _ = "CODE_SMELL"++toSeverity :: DLSeverity -> Text.Text+toSeverity DLErrorC = "CRITICAL"+toSeverity DLWarningC = "MAJOR"+toSeverity DLInfoC = "MINOR"+toSeverity _ = "INFO"
src/Hadolint/Rule/DL4006.hs view
@@ -18,14 +18,15 @@      check _ st From {} = st |> replaceWith False -- Reset the state each time we find a new FROM     check _ st (Shell args)-      | foldArguments isPowerShell args = st |> replaceWith True+      | foldArguments isNonPosixShell args = st |> replaceWith True       | otherwise = st |> replaceWith (foldArguments hasPipefailOption args)     check line st@(State _ False) (Run (RunArgs args _))       | foldArguments hasPipes args = st |> addFail CheckFailure {..}       | otherwise = st     check _ st _ = st -    isPowerShell (Shell.ParsedShell orig _ _) = "pwsh" `Text.isPrefixOf` orig+    isNonPosixShell (Shell.ParsedShell orig _ _) =+      any (`Text.isPrefixOf` orig) Shell.nonPosixShells     hasPipes script = Shell.hasPipes script     hasPipefailOption script =       not $
src/Hadolint/Shell.hs view
@@ -61,8 +61,8 @@  shellcheck :: ShellOpts -> ParsedShell -> [PositionedComment] shellcheck (ShellOpts sh env) (ParsedShell txt _ _) =-  if "pwsh" `Text.isPrefixOf` sh-    then [] -- Do no run for powershell+  if any (`Text.isPrefixOf` sh) nonPosixShells+    then [] -- Do no run for non-posix shells i.e. powershell, cmd.exe     else runShellCheck   where     runShellCheck = crComments $ runIdentity $ checkScript si spec@@ -84,6 +84,9 @@      extractShell s = fromMaybe "" (listToMaybe . Text.words $ s)     printVars = Text.unlines . Set.toList $ Set.map (\v -> "export " <> v <> "=1") env++nonPosixShells :: [Text.Text]+nonPosixShells = ["pwsh", "powershell", "cmd"]  parseShell :: Text.Text -> ParsedShell parseShell txt = ParsedShell {original = txt, parsed = parsedResult, presentCommands = commands}
test/DL4006.hs view
@@ -81,3 +81,24 @@               "RUN wget -O - https://some.site | wc -l file > /number"             ]        in ruleCatches "DL4006" $ Text.unlines dockerFile+    it "ignore non posix shells: pwsh" $+      let dockerFile =+            [ "FROM mcr.microsoft.com/powershell:ubuntu-16.04",+              "SHELL [ \"pwsh\", \"-c\" ]",+              "RUN Get-Variable PSVersionTable | Select-Object -ExpandProperty Value"+            ]+       in ruleCatchesNot "DL4006" $ Text.unlines dockerFile+    it "ignore non posix shells: powershell" $+      let dockerFile =+            [ "FROM mcr.microsoft.com/powershell:ubuntu-16.04",+              "SHELL [ \"powershell.exe\" ]",+              "RUN Get-Variable PSVersionTable | Select-Object -ExpandProperty Value"+            ]+       in ruleCatchesNot "DL4006" $ Text.unlines dockerFile+    it "ignore non posix shells: cmd.exe" $+      let dockerFile =+            [ "FROM mcr.microsoft.com/powershell:ubuntu-16.04",+              "SHELL [ \"cmd.exe\", \"/c\" ]",+              "RUN Get-Variable PSVersionTable | Select-Object -ExpandProperty Value"+            ]+       in ruleCatchesNot "DL4006" $ Text.unlines dockerFile
test/Shellcheck.hs view
@@ -110,10 +110,19 @@             assertChecks dockerFile passesShellcheck             assertOnBuildChecks dockerFile passesShellcheck -    it "Does not complain on powershell" $+    it "Does not complain on non-posix shells: pwsh" $       let dockerFile =             Text.unlines               [ "SHELL [\"pwsh\", \"-c\"]",+                "RUN Get-Variable PSVersionTable | Select-Object -ExpandProperty Value"+              ]+       in do+            assertChecks dockerFile passesShellcheck+            assertOnBuildChecks dockerFile passesShellcheck+    it "Does not complain on non-posix shells: cmd.exe" $+      let dockerFile =+            Text.unlines+              [ "SHELL [\"cmd.exe\", \"/c\"]",                 "RUN Get-Variable PSVersionTable | Select-Object -ExpandProperty Value"               ]        in do