packages feed

hadolint 1.17.0 → 1.17.1

raw patch · 5 files changed

+156/−31 lines, 5 filesdep +HsYAMLdep −yamlPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: HsYAML

Dependencies removed: yaml

API changes (from Hackage documentation)

- Hadolint.Config: instance Data.Aeson.Types.Class.FromJSON Hadolint.Config.ConfigFile
- Hadolint.Formatter.Codacy: instance Data.Aeson.Types.Class.ToJSON Hadolint.Formatter.Codacy.Issue
- 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.Json: instance (Text.Megaparsec.Stream.Stream s, Text.Megaparsec.Error.ShowErrorComponent e) => Data.Aeson.Types.Class.ToJSON (Hadolint.Formatter.Json.JsonFormat s e)
+ Hadolint.Config: ConfigFile :: Maybe [IgnoreRule] -> Maybe [TrustedRegistry] -> ConfigFile
+ Hadolint.Config: [ignoredRules] :: ConfigFile -> Maybe [IgnoreRule]
+ Hadolint.Config: [trustedRegistries] :: ConfigFile -> Maybe [TrustedRegistry]
+ Hadolint.Config: data ConfigFile
+ Hadolint.Config: instance Data.YAML.FromYAML Hadolint.Config.ConfigFile
+ Hadolint.Formatter.Codacy: instance Data.Aeson.Types.ToJSON.ToJSON Hadolint.Formatter.Codacy.Issue
+ 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.Json: instance (Text.Megaparsec.Stream.Stream s, Text.Megaparsec.Error.ShowErrorComponent e) => Data.Aeson.Types.ToJSON.ToJSON (Hadolint.Formatter.Json.JsonFormat s e)

Files

hadolint.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ad3384cc7d2f156af93ef1a394947b12be4d8ca81081af3f44fe90c9c2e0bf94+-- hash: 099ae897b9dd6e8bd846e847751883f65ef076c560a6157945fbeb7be74a5859  name:           hadolint-version:        1.17.0+version:        1.17.1 synopsis:       Dockerfile Linter JavaScript API description:    A smarter Dockerfile linter that helps you build best practice Docker images. category:       Development@@ -44,7 +44,8 @@       src   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path   build-depends:-      ShellCheck >=0.6.0+      HsYAML+    , ShellCheck >=0.6.0     , aeson     , base >=4.8 && <5     , bytestring@@ -57,7 +58,6 @@     , split >=0.2     , text     , void-    , yaml   default-language: Haskell2010  executable hadolint@@ -84,12 +84,14 @@   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:+      ConfigSpec       Paths_hadolint   hs-source-dirs:       test   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path   build-depends:       HUnit >=1.2+    , HsYAML     , ShellCheck >=0.6.0     , aeson     , base >=4.8 && <5
src/Hadolint/Config.hs view
@@ -1,12 +1,15 @@ {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-} -module Hadolint.Config (applyConfig) where+module Hadolint.Config (applyConfig, ConfigFile(..)) where  import Control.Monad (filterM) import Data.Coerce (coerce) import Data.Maybe (fromMaybe, listToMaybe)+import qualified Data.ByteString as Bytes import qualified Data.Set as Set-import qualified Data.Yaml as Yaml+import qualified Data.YAML as Yaml+import Data.YAML ((.:?)) import GHC.Generics import qualified Language.Docker as Docker import System.Directory@@ -18,11 +21,14 @@ import qualified Hadolint.Rules as Rules  data ConfigFile = ConfigFile-    { ignored :: Maybe [Lint.IgnoreRule]+    { ignoredRules :: Maybe [Lint.IgnoreRule]     , trustedRegistries :: Maybe [Lint.TrustedRegistry]     } deriving (Show, Eq, Generic) -instance Yaml.FromJSON ConfigFile+instance Yaml.FromYAML ConfigFile where+  parseYAML = Yaml.withMap "ConfigFile" $ \m -> ConfigFile+       <$> m .:? "ignored"+       <*> m .:? "trustedRegistries"  -- | If both the ignoreRules and rulesConfig properties of Lint options are empty -- then this function will fill them with the default found in the passed config@@ -46,8 +52,8 @@         listToMaybe <$> filterM doesFileExist [localConfigFile, configFile]     parseAndApply :: FilePath -> IO (Either String Lint.LintOptions)     parseAndApply configFile = do-        result <- Yaml.decodeFileEither configFile-        case result of+        contents <- Bytes.readFile configFile+        case Yaml.decode1Strict contents of             Left err -> return $ Left (formatError err configFile)             Right (ConfigFile ignore trusted) -> return (Right (override ignore trusted))     -- | Applies the configuration found in the file to the passed Lint.LintOptions@@ -64,21 +70,16 @@     toRules (Just trusted) = Rules.RulesConfig (Set.fromList . coerce $ trusted)     toRules _ = mempty     formatError err config =-        case err of-            Yaml.AesonException e ->-                unlines-                    [ "Error parsing your config file in  '" ++ config ++ "':"-                    , "It should contain one of the keys 'ignored' or 'trustedRegistries'. For example:\n"-                    , "ignored:"-                    , "\t- DL3000"-                    , "\t- SC1099\n\n"-                    , "The key 'trustedRegistries' should contain the names of the allowed docker registries:\n"-                    , "allowedRegistries:"-                    , "\t- docker.io"-                    , "\t- my-company.com"-                    , ""-                    , e-                    ]-            _ ->-                "Error parsing your config file in  '" ++-                config ++ "': " ++ Yaml.prettyPrintParseException err+      unlines+          [ "Error parsing your config file in  '" ++ config ++ "':"+          , "It should contain one of the keys 'ignored' or 'trustedRegistries'. For example:\n"+          , "ignored:"+          , "\t- DL3000"+          , "\t- SC1099\n\n"+          , "The key 'trustedRegistries' should contain the names of the allowed docker registries:\n"+          , "allowedRegistries:"+          , "\t- docker.io"+          , "\t- my-company.com"+          , ""+          , err+          ]
src/Hadolint/Rules.hs view
@@ -326,6 +326,7 @@     severity = WarningC     message = "Either use Wget or Curl but not both"     check state _ (Run args) = argumentsRule (detectDoubleUsage state) args+    check _ _ (From _) = withState Set.empty True -- Reset the state for each stage     check state _ _ = withState state True     detectDoubleUsage state args =         let newArgs = extractCommands args@@ -546,7 +547,23 @@             , format <- archiveFormats             ]     check _ = True-    archiveFormats = [".tar", ".gz", ".bz2", "xz"]+    archiveFormats =+      [ ".tar"+      , ".tar.bz2"+      , ".tb2"+      , ".tbz"+      , ".tbz2"+      , ".tar.gz"+      , ".tgz"+      , ".tpz"+      , ".tar.lz"+      , ".tar.lzma"+      , ".tlz"+      , ".tar.xz"+      , ".txz"+      , ".tar.Z"+      , ".tZ"+      ]  invalidPort :: Rule invalidPort = instructionRule code severity message check@@ -830,7 +847,9 @@     [ arg     | cmd <- Shell.findCommands args     , Shell.cmdHasArgs "gem" ["install", "i"] cmd+    , not (Shell.cmdHasArgs "gem" ["-v"] cmd)     , arg <- Shell.getArgsNoFlags cmd     , arg /= "install"     , arg /= "i"+    , not ("--" `isPrefixOf` arg)     ]
+ test/ConfigSpec.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE OverloadedStrings #-}+module ConfigSpec where++import Test.HUnit+import Test.Hspec+import Control.Monad (unless)+import qualified Data.ByteString.Char8 as Bytes+import qualified Data.YAML as Yaml++import Hadolint.Config++tests :: SpecWith ()+tests =+    describe "Config" $ do+        it "Parses config with only ignores" $+            let configFile =+                    [ "ignored:"+                    , "- DL3000"+                    , "- SC1010"+                    ]+                expected = ConfigFile (Just ["DL3000", "SC1010"]) Nothing+            in assertConfig expected (Bytes.unlines configFile)++        it "Parses config with only trustedRegistries" $+            let configFile =+                    [ "trustedRegistries:"+                    , "- hub.docker.com"+                    , "- my.shady.xyz"+                    ]+                expected = ConfigFile Nothing (Just ["hub.docker.com", "my.shady.xyz"])+            in assertConfig expected (Bytes.unlines configFile)++        it "Parses full file" $+            let configFile =+                    [ "trustedRegistries:"+                    , "- hub.docker.com"+                    , ""+                    , "ignored:"+                    , "- DL3000"+                    ]+                expected = ConfigFile (Just ["DL3000"]) (Just ["hub.docker.com"])+            in assertConfig expected (Bytes.unlines configFile)++assertConfig :: HasCallStack => ConfigFile -> Bytes.ByteString -> Assertion+assertConfig config s =+    case Yaml.decode1Strict s of+        Left err ->+            assertFailure err+        Right result ->+            checkResult result+  where+    checkResult result =+        unless (result == config) $+            assertFailure ("Config \n\n" ++ show config  ++ "\n\n is not \n\n" ++ show result)
test/Spec.hs view
@@ -12,6 +12,8 @@ import Data.Semigroup ((<>)) import qualified Data.Text as Text +import qualified ConfigSpec+ main :: IO () main =     hspec $ do@@ -173,6 +175,12 @@               it "pinned" $ do                 ruleCatchesNot gemVersionPinned "RUN gem install bundler:1"                 onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler:1"+              it "does not warn on -v" $ do+                ruleCatchesNot gemVersionPinned "RUN gem install bundler -v '2.0.1'"+                onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler -v '2.0.1'"+              it "does not warn on extra flags" $ do+                ruleCatchesNot gemVersionPinned "RUN gem install bundler:2.0.1 -- --use-system-libraries=true"+                onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler:2.0.1 -- --use-system-libraries=true"         --         describe "apt-get rules" $ do             it "apt" $@@ -1064,6 +1072,44 @@                         ]                 in ruleCatchesNot (registryIsAllowed ["random.com"]) $ Text.unlines dockerFile         --+        describe "Wget or Curl" $ do+            it "warns when using both wget and curl" $+                let dockerFile =+                        [ "FROM node as foo"+                        , "RUN wget my.xyz"+                        , "RUN curl localhost"+                        ]+                in ruleCatches wgetOrCurl $ Text.unlines dockerFile+            it "warns when using both wget and curl in same instruction" $+                let dockerFile =+                        [ "FROM node as foo"+                        , "RUN wget my.xyz && curl localhost"+                        ]+                in ruleCatches wgetOrCurl $ Text.unlines dockerFile+            it "does not warn when using only wget" $+                let dockerFile =+                        [ "FROM node as foo"+                        , "RUN wget my.xyz"+                        ]+                in ruleCatchesNot wgetOrCurl $ Text.unlines dockerFile+            it "does not warn when using both curl and wget in different stages" $+                let dockerFile =+                        [ "FROM node as foo"+                        , "RUN wget my.xyz"+                        , "FROM scratch"+                        , "RUN curl localhost"+                        ]+                in ruleCatchesNot wgetOrCurl $ Text.unlines dockerFile+            it "does not warns when using both, on a single stage" $+                let dockerFile =+                        [ "FROM node as foo"+                        , "RUN wget my.xyz"+                        , "RUN curl localhost"+                        , "FROM scratch"+                        , "RUN curl localhost"+                        ]+                in ruleCatches wgetOrCurl $ Text.unlines dockerFile+        --         describe "Regression Tests" $             it "Comments with backslashes at the end are just comments" $                 let dockerFile =@@ -1077,6 +1123,9 @@                         ]                 in ruleCatches usePipefail $ Text.unlines dockerFile +        -- Run tests for the Config module+        ConfigSpec.tests+ assertChecks :: HasCallStack => Rule -> Text.Text -> ([RuleCheck] -> IO a) -> IO a assertChecks rule s makeAssertions =     case parseText (s <> "\n") of@@ -1099,8 +1148,8 @@ ruleCatches rule s = assertChecks rule s f   where     f checks = do-      when (length checks /= 1) $-        assertFailure $ "Too many errors found: \n" ++ (Text.unpack . Text.unlines . formatChecks $ checks)+      when (null checks) $+        assertFailure "I was expecting to catch at least one error"       assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]  onBuildRuleCatches :: HasCallStack => Rule -> Text.Text -> Assertion