packages feed

hadolint 2.3.0 → 2.4.0

raw patch · 10 files changed

+274/−75 lines, 10 filesdep ~language-dockerPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: language-docker

API changes (from Hackage documentation)

+ Hadolint.Config: [failureThreshold] :: ConfigFile -> Maybe DLSeverity
+ Hadolint.Config: getConfig :: Maybe FilePath -> IO (Maybe FilePath)
+ Hadolint.Lint: [failThreshold] :: LintOptions -> DLSeverity
+ Hadolint.Rule: instance Data.YAML.FromYAML Hadolint.Rule.DLSeverity
+ Hadolint.Rule: instance GHC.Base.Monoid Hadolint.Rule.DLSeverity
+ Hadolint.Rule: instance GHC.Base.Semigroup Hadolint.Rule.DLSeverity
+ Hadolint.Rule: instance GHC.Read.Read Hadolint.Rule.DLSeverity
+ Hadolint.Rule: readSeverity :: Text -> Either Text DLSeverity
+ Hadolint.Rule: withSeverity :: (DLSeverity -> Parser a) -> Node Pos -> Parser a
+ Hadolint.Rule.DL3060: instance GHC.Show.Show Hadolint.Rule.DL3060.Acc
- Hadolint.Config: ConfigFile :: Maybe OverrideConfig -> Maybe [IgnoreRule] -> Maybe [TrustedRegistry] -> Maybe LabelSchema -> Maybe Bool -> ConfigFile
+ Hadolint.Config: ConfigFile :: Maybe OverrideConfig -> Maybe [IgnoreRule] -> Maybe [TrustedRegistry] -> Maybe LabelSchema -> Maybe Bool -> Maybe DLSeverity -> ConfigFile
- Hadolint.Lint: LintOptions :: [ErrorRule] -> [WarningRule] -> [InfoRule] -> [StyleRule] -> [IgnoreRule] -> RulesConfig -> LintOptions
+ Hadolint.Lint: LintOptions :: [ErrorRule] -> [WarningRule] -> [InfoRule] -> [StyleRule] -> [IgnoreRule] -> RulesConfig -> DLSeverity -> LintOptions
- Hadolint.Lint: type ErrorRule = Text
+ Hadolint.Lint: type ErrorRule = RuleCode
- Hadolint.Lint: type InfoRule = Text
+ Hadolint.Lint: type InfoRule = RuleCode
- Hadolint.Lint: type StyleRule = Text
+ Hadolint.Lint: type StyleRule = RuleCode
- Hadolint.Lint: type WarningRule = Text
+ Hadolint.Lint: type WarningRule = RuleCode

Files

README.md view
@@ -127,11 +127,12 @@ ```  Configuration files can be used globally or per project. By default, `hadolint` will look for-a configuration file in the current directory with the name `.hadolint.yaml`+a configuration file in the current directory with the name `.hadolint.yaml` or+`.hadolint.yml`  The global configuration file should be placed in the folder specified by `XDG_CONFIG_HOME`,-with the name `hadolint.yaml`. In summary, the following locations are valid for the configuration-file, in order or preference:+with the name `hadolint.yaml` or `hadolint.yml`. In summary, the following locations are valid+for the configuration file, in order or preference:  - `$PWD/.hadolint.yaml` - `$XDG_CONFIG_HOME/hadolint.yaml`
app/Main.hs view
@@ -1,11 +1,12 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}  module Main where  import Control.Applicative+import Control.Monad (when) import qualified Data.Bifunctor as Bifunctor import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map as Map@@ -17,9 +18,11 @@ import qualified Data.Version import qualified Development.GitRev import qualified Hadolint+import qualified Hadolint.Formatter.Format as Format import qualified Hadolint.Rule as Rule import Options.Applicative   ( Parser,+    ReadM,     action,     argument,     completeWith,@@ -35,7 +38,6 @@     metavar,     option,     progDesc,-    ReadM,     short,     showDefaultWith,     str,@@ -47,12 +49,14 @@ import qualified Paths_hadolint as Meta import System.Environment import System.Exit (exitFailure, exitSuccess)+import System.IO (hPutStrLn, stderr)  data CommandOptions = CommandOptions   { showVersion :: Bool,     noFail :: Bool,     nocolor :: Bool,     configFile :: Maybe FilePath,+    isVerbose :: Bool,     format :: Hadolint.OutputFormat,     dockerfiles :: [String],     lintingOptions :: Hadolint.LintOptions@@ -75,6 +79,15 @@ showFormat Hadolint.Checkstyle = "checkstyle" showFormat Hadolint.Codacy = "codacy" +toNofailSeverity :: String -> Maybe Rule.DLSeverity+toNofailSeverity "error" = Just Rule.DLErrorC+toNofailSeverity "warning" = Just Rule.DLWarningC+toNofailSeverity "info" = Just Rule.DLInfoC+toNofailSeverity "style" = Just Rule.DLStyleC+toNofailSeverity "ignore" = Just Rule.DLIgnoreC+toNofailSeverity "none" = Just Rule.DLIgnoreC+toNofailSeverity _ = Nothing+ parseOptions :: Parser CommandOptions parseOptions =   CommandOptions@@ -82,6 +95,7 @@     <*> noFail     <*> nocolor     <*> configFile+    <*> isVerbose     <*> outputFormat     <*> files     <*> lintOptions@@ -90,10 +104,28 @@      noFail = switch (long "no-fail" <> help "Don't exit with a failure status code when any rule is violated") +    noFailCutoff =+      option+        (maybeReader toNofailSeverity)+        ( short 't'+            <> long "failure-theshold"+            <> help+              "Exit with failure code only when rules with a severity \+              \above THRESHOLD are violated. Accepted values: \+              \[error | warning | info | style | ignore | none]"+            <> value Rule.DLInfoC+            <> metavar "THRESHOLD"+            <> showDefaultWith (Text.unpack . Format.severityText)+            <> completeWith ["error", "warning", "info", "style", "ignore", "none"]+        )+     nocolor = switch (long "no-color" <> help "Don't colorize output") -    strictlabels = switch (long "strict-labels"-        <> help "Do not permit labels other than specified in `label-schema`")+    strictlabels =+      switch+        ( long "strict-labels"+            <> help "Do not permit labels other than specified in `label-schema`"+        )      configFile =       optional@@ -103,6 +135,8 @@             )         ) +    isVerbose = switch (long "verbose" <> short 'V' <> help "Enables verbose logging of hadolint's output to stderr")+     outputFormat =       option         (maybeReader toOutputFormat)@@ -170,10 +204,13 @@         <*> styleList         <*> ignoreList         <*> parseRulesConfig+        <*> noFailCutoff -    labels = Map.fromList+    labels =+      Map.fromList         <$> many-          ( option readSingleLabelSchema+          ( option+              readSingleLabelSchema               ( long "require-label"                   <> help "The option --require-label=label:format makes Hadolint check that the label `label` conforms to format requirement `format`"                   <> metavar "LABELSCHEMA (e.g. maintainer:text)"@@ -186,7 +223,8 @@         <*> labels         <*> strictlabels -    parseAllowedRegistries = Set.fromList . fmap fromString+    parseAllowedRegistries =+      Set.fromList . fmap fromString         <$> many           ( strOption               ( long "trusted-registry"@@ -202,19 +240,26 @@  labelParser :: Text.Text -> Either String (Rule.LabelName, Rule.LabelType) labelParser l =-    case Bifunctor.second (Rule.read . Text.drop 1) $ Text.breakOn ":" l of-      (ln, Right lt) -> Right (ln, lt)-      (_, Left e) -> Left $ Text.unpack e+  case Bifunctor.second (Rule.read . Text.drop 1) $ Text.breakOn ":" l of+    (ln, Right lt) -> Right (ln, lt)+    (_, Left e) -> Left $ Text.unpack e -noFailure :: Hadolint.Result s e -> Bool-noFailure (Hadolint.Result _ Seq.Empty Seq.Empty) = True-noFailure _ = False+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)+noFailure _ _ = False -exitProgram :: Foldable f => CommandOptions -> f (Hadolint.Result s e) -> IO ()-exitProgram cmd res+exitProgram ::+  Foldable f =>+  CommandOptions ->+  Hadolint.LintOptions ->+  f (Hadolint.Result s e) ->+  IO ()+exitProgram cmd conf res   | noFail cmd = exitSuccess   | Hadolint.shallSkipErrorStatus (format cmd) = exitSuccess-  | all noFailure res = exitSuccess+  | all (`noFailure` Hadolint.failThreshold conf) res = exitSuccess   | otherwise = exitFailure  runLint :: CommandOptions -> Hadolint.LintOptions -> NonEmpty.NonEmpty String -> IO ()@@ -223,7 +268,7 @@   noColorEnv <- lookupEnv "NO_COLOR"   let noColor = nocolor cmd || isJust noColorEnv   Hadolint.printResults (format cmd) noColor res-  exitProgram cmd res+  exitProgram cmd conf res  main :: IO () main = do@@ -234,7 +279,9 @@     execute CommandOptions {dockerfiles = []} =       putStrLn "Please provide a Dockerfile" >> exitFailure     execute cmd = do-      lintConfig <- Hadolint.applyConfig (configFile cmd) (lintingOptions cmd)+      maybeConfig <- Hadolint.getConfig (configFile cmd)+      when (isVerbose cmd) (hPutStrLn stderr $ getFilePathDescription maybeConfig)+      lintConfig <- Hadolint.applyConfig maybeConfig (lintingOptions cmd)       let files = NonEmpty.fromList (dockerfiles cmd)       case lintConfig of         Left err -> error err@@ -253,3 +300,7 @@   | otherwise = "Haskell Dockerfile Linter " ++ version   where     version = $(Development.GitRev.gitDescribe)++getFilePathDescription :: Maybe FilePath -> String+getFilePathDescription Nothing = "No configuration was specified. Using default configuration"+getFilePathDescription (Just filepath) = "Configuration file used: " ++ filepath
hadolint.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 76044842a02febfc721b4776d4e4bbfdb11c9327f25de10eca894d0d19c1af6b+-- hash: 0122b347f3d7e60bbead61f38f32acd69b57e1794597b5243bb2b6cabf2a358f  name:           hadolint-version:        2.3.0+version:        2.4.0 synopsis:       Dockerfile Linter JavaScript API description:    A smarter Dockerfile linter that helps you build best practice Docker images. category:       Development@@ -137,7 +137,7 @@     , filepath     , foldl     , ilist-    , language-docker >=9.3.0 && <10+    , language-docker >=10.0.0 && <11     , megaparsec >=9.0.0     , mtl     , network-uri@@ -158,14 +158,14 @@       Paths_hadolint   hs-source-dirs:       app-  default-extensions: OverloadedStrings NamedFieldPuns DeriveGeneric DeriveAnyClass RecordWildCards StrictData ScopedTypeVariables PatternSynonyms+  default-extensions: OverloadedStrings NamedFieldPuns DeriveGeneric DeriveAnyClass RecordWildCards StrictData ScopedTypeVariables PatternSynonyms TemplateHaskell   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path -flate-dmd-anal -O2 -threaded -rtsopts "-with-rtsopts=-N5 -A4m"   build-depends:       base >=4.8 && <5     , containers     , gitrev >=1.3.1     , hadolint-    , language-docker >=9.3.0 && <10+    , language-docker >=10.0.0 && <11     , megaparsec >=9.0.0     , optparse-applicative >=0.14.0     , text@@ -264,7 +264,7 @@     , foldl     , hadolint     , hspec-    , language-docker >=9.3.0 && <10+    , language-docker >=10.0.0 && <11     , megaparsec >=9.0.0     , split >=0.2     , text
src/Hadolint/Config.hs view
@@ -1,5 +1,6 @@ module Hadolint.Config   ( applyConfig,+    getConfig,     ConfigFile (..),     OverrideConfig (..),   )@@ -47,17 +48,22 @@     ignoredRules :: Maybe [Lint.IgnoreRule],     trustedRegistries :: Maybe [Lint.TrustedRegistry],     labelSchemaConfig :: Maybe Rule.LabelSchema,-    strictLabelSchema :: Maybe Bool+    strictLabelSchema :: Maybe Bool,+    failureThreshold :: Maybe Rule.DLSeverity   }   deriving (Show, Eq, Generic)  instance Yaml.FromYAML OverrideConfig where-  parseYAML = Yaml.withMap "OverrideConfig" $ \m ->-    OverrideConfig-      <$> m .:? "error"-      <*> m .:? "warning"-      <*> m .:? "info"-      <*> m .:? "style"+  parseYAML = Yaml.withMap "OverrideConfig" $ \m -> do+    err <- m .:? "error"+    wrn <- m .:? "warning"+    inf <- m .:? "info"+    sty <- m .:? "style"+    let overrideErrorRules = coerce (err :: Maybe [Text.Text])+        overrideWarningRules = coerce (wrn :: Maybe [Text.Text])+        overrideInfoRules = coerce (inf :: Maybe [Text.Text])+        overrideStyleRules = coerce (sty:: Maybe [Text.Text])+    return OverrideConfig {..}  instance Yaml.FromYAML ConfigFile where   parseYAML = Yaml.withMap "ConfigFile" $ \m -> do@@ -67,6 +73,7 @@     trustedRegistries <- m .:? "trustedRegistries"     labelSchemaConfig <- m .:? "label-schema"     strictLabelSchema <- m .:? "strict-labels"+    failureThreshold <- m .:? "failure-threshold"     return ConfigFile {..}  -- | If both the ignoreRules and rulesConfig properties of Lint options are empty@@ -77,19 +84,10 @@ applyConfig maybeConfig o   | not (Prelude.null (Lint.ignoreRules o)) && Lint.rulesConfig o /= mempty = return (Right o)   | otherwise = do-    theConfig <--      case maybeConfig of-        Nothing -> findConfig-        c -> return c-    case theConfig of+    case maybeConfig of       Nothing -> return (Right o)       Just config -> parseAndApply config   where-    findConfig = do-      localConfigFile <- (</> ".hadolint.yaml") <$> getCurrentDirectory-      configFile <- getXdgDirectory XdgConfig "hadolint.yaml"-      listToMaybe <$> filterM doesFileExist [localConfigFile, configFile]-     parseAndApply :: FilePath -> IO (Either String Lint.LintOptions)     parseAndApply configFile = do       contents <- Bytes.readFile configFile@@ -106,6 +104,7 @@         overrideInfo <- overrideInfoRules <|> Just mempty         overrideStyle <- overrideStyleRules <|> Just mempty         overrideIgnored <- ignoredRules <|> Just mempty+        overrideThreshold <- failureThreshold <|> Just mempty          trusted <- Set.fromList . coerce <$> (trustedRegistries <|> Just mempty)         schema <- labelSchemaConfig <|> Just mempty@@ -125,7 +124,8 @@                   { Process.allowedRegistries = Process.allowedRegistries rulesConfig `ifNull` trusted,                     Process.labelSchema = Process.labelSchema rulesConfig `ifNull` schema,                     Process.strictLabels = Process.strictLabels rulesConfig || strictLabels-                  }+                  },+            Lint.failThreshold = Lint.failThreshold o <> overrideThreshold             }      ifNull value override = if null value then override else value@@ -151,3 +151,20 @@           "",           err         ]++-- | Gets the configuration file which Hadolint uses+getConfig :: Maybe FilePath -> IO (Maybe FilePath)+getConfig maybeConfig =+  case maybeConfig of+    Nothing -> findConfig+    _ -> return maybeConfig+  where+    findConfig :: IO (Maybe FilePath)+    findConfig = do+      localConfigFiles <- traverse+                            (\filePath -> (</> filePath) <$> getCurrentDirectory)+                            (fmap ("."++) acceptedConfigs)+      configFiles <- traverse (getXdgDirectory XdgConfig) acceptedConfigs+      listToMaybe <$> filterM doesFileExist (localConfigFiles ++ configFiles)+      where+        acceptedConfigs = ["hadolint.yaml", "hadolint.yml"]
src/Hadolint/Lint.hs view
@@ -24,13 +24,13 @@ import Language.Docker.Parser (DockerfileError, Error) import Language.Docker.Syntax (Dockerfile) -type ErrorRule = Text+type ErrorRule = Hadolint.Rule.RuleCode -type WarningRule = Text+type WarningRule = Hadolint.Rule.RuleCode -type InfoRule = Text+type InfoRule = Hadolint.Rule.RuleCode -type StyleRule = Text+type StyleRule = Hadolint.Rule.RuleCode  type IgnoreRule = Hadolint.Rule.RuleCode @@ -42,12 +42,13 @@     infoRules :: [InfoRule],     styleRules :: [StyleRule],     ignoreRules :: [IgnoreRule],-    rulesConfig :: Hadolint.Process.RulesConfig+    rulesConfig :: Hadolint.Process.RulesConfig,+    failThreshold :: Hadolint.Rule.DLSeverity   }   deriving (Show)  instance Semigroup LintOptions where-  LintOptions a1 a2 a3 a4 a5 a6 <> LintOptions b1 b2 b3 b4 b5 b6 =+  LintOptions a1 a2 a3 a4 a5 a6 a7 <> LintOptions b1 b2 b3 b4 b5 b6 b7 =     LintOptions       (a1 <> b1)       (a2 <> b2)@@ -55,9 +56,10 @@       (a4 <> b4)       (a5 <> b5)       (a6 <> b6)+      (a7 <> b7)  instance Monoid LintOptions where-  mempty = LintOptions mempty mempty mempty mempty mempty mempty+  mempty = LintOptions mempty mempty mempty mempty mempty mempty mempty  -- | Performs the process of parsing the dockerfile and analyzing it with all the applicable -- rules, depending on the list of ignored rules.@@ -99,10 +101,10 @@ fixSeverity LintOptions {..} = Seq.filter ignoredRules . Seq.mapWithIndex (const correctSeverity)   where     correctSeverity =-      makeSeverity Hadolint.Rule.DLErrorC (fmap Hadolint.Rule.RuleCode errorRules)-        . makeSeverity Hadolint.Rule.DLWarningC (fmap Hadolint.Rule.RuleCode warningRules)-        . makeSeverity Hadolint.Rule.DLInfoC (fmap Hadolint.Rule.RuleCode infoRules)-        . makeSeverity Hadolint.Rule.DLStyleC (fmap Hadolint.Rule.RuleCode styleRules)+      makeSeverity Hadolint.Rule.DLErrorC errorRules+        . makeSeverity Hadolint.Rule.DLWarningC warningRules+        . makeSeverity Hadolint.Rule.DLInfoC infoRules+        . makeSeverity Hadolint.Rule.DLStyleC styleRules      ignoredRules = ignoreFilter ignoreRules 
src/Hadolint/Rule.hs view
@@ -21,7 +21,30 @@   | DLInfoC   | DLStyleC   | DLIgnoreC-  deriving (Show, Eq, Ord, Generic, NFData)+  deriving (Show, Read, Eq, Ord, Generic, NFData)++instance Yaml.FromYAML DLSeverity where+  parseYAML = withSeverity pure++withSeverity :: (DLSeverity -> Yaml.Parser a) -> Yaml.Node Yaml.Pos -> Yaml.Parser a+withSeverity f v@(Yaml.Scalar _ (Yaml.SStr b)) =+  case Hadolint.Rule.readSeverity b of+    Right s -> f s+    Left _ -> Yaml.typeMismatch "severity" v+withSeverity _ v = Yaml.typeMismatch "severity" v++readSeverity :: Text.Text -> Either Text.Text DLSeverity+readSeverity "error" = Right DLErrorC+readSeverity "warning" = Right DLWarningC+readSeverity "info" = Right DLInfoC+readSeverity "style" = Right DLStyleC+readSeverity "ignore" = Right DLIgnoreC+readSeverity "none" = Right DLIgnoreC+readSeverity t = Left ("Invalid severity: " <> t)++instance Semigroup DLSeverity where s1 <> s2 = min s1 s2++instance Monoid DLSeverity where mempty = DLIgnoreC  newtype RuleCode = RuleCode {unRuleCode :: Text.Text}   deriving (Show, Eq, Ord)
src/Hadolint/Rule/DL3060.hs view
@@ -1,27 +1,74 @@ module Hadolint.Rule.DL3060 (rule) where +import qualified Data.Map.Strict as Map+import qualified Data.Text as Text import Hadolint.Rule import qualified Hadolint.Shell as Shell import Language.Docker.Syntax  +data Acc+  = Acc+      { current :: BaseImage,+        active :: Map.Map Text.Text Linenumber,+        inactive :: Map.Map Linenumber BaseImage+      }+  | Empty+  deriving (Show)+ rule :: Rule Shell.ParsedShell-rule = simpleRule code severity message check+rule = veryCustomRule check (emptyState Empty) markFailures   where     code = "DL3060"     severity = DLInfoC     message = "`yarn cache clean` missing after `yarn install` was run." -    check (Run (RunArgs args _)) =-      foldArguments (Shell.noCommands yarnInstall) args-        || ( foldArguments (Shell.anyCommands yarnInstall) args-              && foldArguments (Shell.anyCommands yarnCacheClean) args-           )-    check _ = True+    check line st (From from) =+      st |> modify (rememberStage line from)+    check line st (Run (RunArgs args _))+      | foldArguments (Shell.anyCommands yarnInstall) args+          && foldArguments (Shell.noCommands yarnCacheClean) args =+        st |> modify (rememberLine line)+      | otherwise = st+    check _ st _ = st++    -- Produce failures from the final state Acc+    markFailures :: State Acc -> Failures+    markFailures (State fails Empty) = fails+    markFailures (State _ Acc {..}) = inactive |> Map.foldMapWithKey mapFail+      where+        mapFail line from+          | from == current = pure CheckFailure {..}+          | BaseImage {alias = Just (ImageAlias als)} <- from,+            Just _ <- Map.lookup als active = pure CheckFailure {..}+          | otherwise = mempty {-# INLINEABLE rule #-} +rememberStage :: Linenumber -> BaseImage -> Acc -> Acc+rememberStage line stage@BaseImage {image = Image _ als} Empty =+  Acc stage (Map.singleton als line) Map.empty+rememberStage line stage@BaseImage {image = Image _ als} (Acc _ stages o) =+  Acc stage (Map.insert als line stages) o++rememberLine :: Linenumber -> Acc -> Acc+rememberLine line Empty = Acc scratch mempty (Map.singleton line scratch)+rememberLine line (Acc from stages o) = Acc from stages (Map.insert line from o)+ yarnInstall :: Shell.Command -> Bool yarnInstall = Shell.cmdHasArgs "yarn" ["install"]  yarnCacheClean :: Shell.Command -> Bool yarnCacheClean = Shell.cmdHasArgs "yarn" ["cache", "clean"]++-- | This is needed as placeholder when no FROM statement has yet been+-- envountered.+scratch :: BaseImage+scratch =+  ( BaseImage+      { image = "scratch",+        tag = Nothing,+        digest = Nothing,+        alias = Nothing,+        platform = Nothing+      }+  )
test/ConfigSpec.hs view
@@ -20,7 +20,7 @@               "    - SC1010"             ]           override = Just (OverrideConfig (Just ["DL3000", "SC1010"]) Nothing Nothing Nothing)-          expected = ConfigFile override Nothing Nothing Nothing Nothing+          expected = ConfigFile override Nothing Nothing Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only warning severities" $@@ -31,7 +31,7 @@               "    - SC1010"             ]           override = Just (OverrideConfig Nothing (Just ["DL3000", "SC1010"]) Nothing Nothing)-          expected = ConfigFile override Nothing Nothing Nothing Nothing+          expected = ConfigFile override Nothing Nothing Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only info severities" $@@ -42,7 +42,7 @@               "    - SC1010"             ]           override = Just (OverrideConfig Nothing Nothing (Just ["DL3000", "SC1010"]) Nothing)-          expected = ConfigFile override Nothing Nothing Nothing Nothing+          expected = ConfigFile override Nothing Nothing Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only style severities" $@@ -53,7 +53,7 @@               "    - SC1010"             ]           override = Just (OverrideConfig Nothing Nothing Nothing (Just ["DL3000", "SC1010"]))-          expected = ConfigFile override Nothing Nothing Nothing Nothing+          expected = ConfigFile override Nothing Nothing Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only ignores" $@@ -62,7 +62,7 @@               "- DL3000",               "- SC1010"             ]-          expected = ConfigFile Nothing (Just ["DL3000", "SC1010"]) Nothing Nothing Nothing+          expected = ConfigFile Nothing (Just ["DL3000", "SC1010"]) Nothing Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only trustedRegistries" $@@ -71,7 +71,7 @@               "- hub.docker.com",               "- my.shady.xyz"             ]-          expected = ConfigFile Nothing Nothing (Just ["hub.docker.com", "my.shady.xyz"]) Nothing Nothing+          expected = ConfigFile Nothing Nothing (Just ["hub.docker.com", "my.shady.xyz"]) Nothing Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only label-schema" $@@ -80,14 +80,19 @@               "  author: text",               "  url: url"             ]-          expected = ConfigFile Nothing Nothing Nothing (Just (fromList [("author", Rule.RawText), ("url", Rule.Url)])) Nothing+          expected = ConfigFile Nothing Nothing Nothing (Just (fromList [("author", Rule.RawText), ("url", Rule.Url)])) Nothing Nothing        in assertConfig expected (Bytes.unlines configFile)      it "Parses config with only label-schema" $       let configFile = [ "strict-labels: true" ]-          expected = ConfigFile Nothing Nothing Nothing Nothing (Just True)+          expected = ConfigFile Nothing Nothing Nothing Nothing (Just True) Nothing        in assertConfig expected (Bytes.unlines configFile) +    it "Parses config with failure-threshold" $+      let configFile = [ "failure-threshold: error" ]+          expected = ConfigFile Nothing Nothing Nothing Nothing Nothing (Just Rule.DLErrorC)+       in assertConfig expected (Bytes.unlines configFile)+     it "Parses full file" $       let configFile =             [ "override:",@@ -109,11 +114,13 @@               "strict-labels: false",               "label-schema:",               "  author: text",-              "  url: url"+              "  url: url",+              "",+              "failure-threshold: style"             ]           override = Just (OverrideConfig (Just ["DL3001"]) (Just ["DL3003"]) (Just ["DL3002"]) (Just ["DL3004"]))           labelschema = Just (fromList [("author", Rule.RawText), ("url", Rule.Url)])-          expected = ConfigFile override (Just ["DL3000"]) (Just ["hub.docker.com"]) labelschema (Just False)+          expected = ConfigFile override (Just ["DL3000"]) (Just ["hub.docker.com"]) labelschema (Just False) (Just Rule.DLStyleC)        in assertConfig expected (Bytes.unlines configFile)  assertConfig :: HasCallStack => ConfigFile -> Bytes.ByteString -> Assertion
test/DL3060.hs view
@@ -1,5 +1,6 @@ module DL3060 (tests) where +import qualified Data.Text as Text import Helpers import Test.Hspec @@ -17,3 +18,51 @@     it "ok with cache clean" $ do       ruleCatchesNot "DL3060" "RUN yarn install bar && yarn cache clean"       onBuildRuleCatchesNot "DL3060" "RUN yarn install bar && yarn cache clean"++    it "not ok when yarn install is in last stage w/o yarn clean" $+      let dockerFile =+            Text.unlines+              [ "FROM node:lts-alpine as foo",+                "RUN hey!",+                "FROM scratch",+                "RUN yarn install"+              ]+       in do+            ruleCatches "DL3060" dockerFile+            onBuildRuleCatches "DL3060" dockerFile++    it "not ok when inheriting from stage with yarn install w/o yarn clean" $+      let dockerFile =+            Text.unlines+              [ "FROM node:lts-alpine as foo",+                "RUN yarn install",+                "FROM foo",+                "RUN hey!"+              ]+       in do+            ruleCatches "DL3060" dockerFile+            onBuildRuleCatches "DL3060" dockerFile++    it "ok when inheriting from stage with yarn cache clear" $+      let dockerFile =+            Text.unlines+              [ "FROM node:lts-alpine as foo",+                "RUN yarn install && yarn cache clean",+                "FROM foo",+                "RUN hey!"+              ]+       in do+            ruleCatchesNot "DL3060" dockerFile+            onBuildRuleCatchesNot "DL3060" dockerFile++    it "ok when omitting yarn cache clean in stage that is not reused later" $+      let dockerFile =+            Text.unlines+              [ "FROM node:lts-alpine as foo",+                "RUN yarn install foo",+                "FROM scratch",+                "RUN hey!"+              ]+       in do+            ruleCatchesNot "DL3060" dockerFile+            onBuildRuleCatches "DL3060" dockerFile
test/Spec.hs view
@@ -60,6 +60,7 @@ import qualified DL3057 import qualified DL3058 import qualified DL3059+import qualified DL3060 import qualified DL4001 import qualified DL4003 import qualified DL4004@@ -84,7 +85,7 @@             expectedMsg =               "<string>:1:1 unexpected 'F' expecting '#', ADD, ARG, CMD, COPY, ENTRYPOINT, "                 <> "ENV, EXPOSE, FROM, HEALTHCHECK, LABEL, MAINTAINER, ONBUILD, RUN, SHELL, STOPSIGNAL, "-                <> "USER, VOLUME, WORKDIR, or end of input "+                <> "USER, VOLUME, WORKDIR, a pragma, end of input, or whitespaces "         case ast of           Left err -> assertEqual "Unexpected error msg" expectedMsg (formatError err)           Right _ -> assertFailure "AST should fail parsing"@@ -230,6 +231,7 @@     DL3057.tests     DL3058.tests     DL3059.tests+    DL3060.tests     DL4001.tests     DL4003.tests     DL4004.tests