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
      { Acc -> BaseImage
current :: BaseImage,
        Acc -> Map Text Linenumber
active :: Map.Map Text.Text Linenumber,
        Acc -> Map Linenumber BaseImage
inactive :: Map.Map Linenumber BaseImage
      }
  | Empty
  deriving (Linenumber -> Acc -> ShowS
[Acc] -> ShowS
Acc -> String
(Linenumber -> Acc -> ShowS)
-> (Acc -> String) -> ([Acc] -> ShowS) -> Show Acc
forall a.
(Linenumber -> a -> ShowS)
-> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Acc] -> ShowS
$cshowList :: [Acc] -> ShowS
show :: Acc -> String
$cshow :: Acc -> String
showsPrec :: Linenumber -> Acc -> ShowS
$cshowsPrec :: Linenumber -> Acc -> ShowS
Show)

rule :: Rule Shell.ParsedShell
rule :: Rule ParsedShell
rule = (Linenumber -> State Acc -> Instruction ParsedShell -> State Acc)
-> State Acc -> (State Acc -> Failures) -> Rule ParsedShell
forall a args.
(Linenumber -> State a -> Instruction args -> State a)
-> State a -> (State a -> Failures) -> Rule args
veryCustomRule Linenumber -> State Acc -> Instruction ParsedShell -> State Acc
check (Acc -> State Acc
forall a. a -> State a
emptyState Acc
Empty) State Acc -> Failures
markFailures
  where
    code :: RuleCode
code = RuleCode
"DL3060"
    severity :: DLSeverity
severity = DLSeverity
DLInfoC
    message :: Text
message = Text
"`yarn cache clean` missing after `yarn install` was run."

    check :: Linenumber -> State Acc -> Instruction ParsedShell -> State Acc
check Linenumber
line State Acc
st (From BaseImage
from) =
      State Acc
st State Acc -> (State Acc -> State Acc) -> State Acc
forall a b. a -> (a -> b) -> b
|> (Acc -> Acc) -> State Acc -> State Acc
forall a. (a -> a) -> State a -> State a
modify (Linenumber -> BaseImage -> Acc -> Acc
rememberStage Linenumber
line BaseImage
from)
    check Linenumber
line State Acc
st (Run (RunArgs Arguments ParsedShell
args RunFlags
_))
      | (ParsedShell -> Bool) -> Arguments ParsedShell -> Bool
forall a b. (a -> b) -> Arguments a -> b
foldArguments ((Command -> Bool) -> ParsedShell -> Bool
Shell.anyCommands Command -> Bool
yarnInstall) Arguments ParsedShell
args
          Bool -> Bool -> Bool
&& (ParsedShell -> Bool) -> Arguments ParsedShell -> Bool
forall a b. (a -> b) -> Arguments a -> b
foldArguments ((Command -> Bool) -> ParsedShell -> Bool
Shell.noCommands Command -> Bool
yarnCacheClean) Arguments ParsedShell
args =
        State Acc
st State Acc -> (State Acc -> State Acc) -> State Acc
forall a b. a -> (a -> b) -> b
|> (Acc -> Acc) -> State Acc -> State Acc
forall a. (a -> a) -> State a -> State a
modify (Linenumber -> Acc -> Acc
rememberLine Linenumber
line)
      | Bool
otherwise = State Acc
st
    check Linenumber
_ State Acc
st Instruction ParsedShell
_ = State Acc
st

    -- Produce failures from the final state Acc
    markFailures :: State Acc -> Failures
    markFailures :: State Acc -> Failures
markFailures (State Failures
fails Acc
Empty) = Failures
fails
    markFailures (State Failures
_ Acc {Map Linenumber BaseImage
Map Text Linenumber
BaseImage
inactive :: Map Linenumber BaseImage
active :: Map Text Linenumber
current :: BaseImage
inactive :: Acc -> Map Linenumber BaseImage
active :: Acc -> Map Text Linenumber
current :: Acc -> BaseImage
..}) = Map Linenumber BaseImage
inactive Map Linenumber BaseImage
-> (Map Linenumber BaseImage -> Failures) -> Failures
forall a b. a -> (a -> b) -> b
|> (Linenumber -> BaseImage -> Failures)
-> Map Linenumber BaseImage -> Failures
forall m k a. Monoid m => (k -> a -> m) -> Map k a -> m
Map.foldMapWithKey Linenumber -> BaseImage -> Failures
forall (f :: * -> *).
(Applicative f, Monoid (f CheckFailure)) =>
Linenumber -> BaseImage -> f CheckFailure
mapFail
      where
        mapFail :: Linenumber -> BaseImage -> f CheckFailure
mapFail Linenumber
line BaseImage
from
          | BaseImage
from BaseImage -> BaseImage -> Bool
forall a. Eq a => a -> a -> Bool
== BaseImage
current = CheckFailure -> f CheckFailure
forall (f :: * -> *) a. Applicative f => a -> f a
pure CheckFailure :: RuleCode -> DLSeverity -> Text -> Linenumber -> CheckFailure
CheckFailure {Linenumber
Text
RuleCode
DLSeverity
line :: Linenumber
message :: Text
severity :: DLSeverity
code :: RuleCode
line :: Linenumber
message :: Text
severity :: DLSeverity
code :: RuleCode
..}
          | BaseImage {$sel:alias:BaseImage :: BaseImage -> Maybe ImageAlias
alias = Just (ImageAlias Text
als)} <- BaseImage
from,
            Just Linenumber
_ <- Text -> Map Text Linenumber -> Maybe Linenumber
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
als Map Text Linenumber
active = CheckFailure -> f CheckFailure
forall (f :: * -> *) a. Applicative f => a -> f a
pure CheckFailure :: RuleCode -> DLSeverity -> Text -> Linenumber -> CheckFailure
CheckFailure {Linenumber
Text
RuleCode
DLSeverity
line :: Linenumber
message :: Text
severity :: DLSeverity
code :: RuleCode
line :: Linenumber
message :: Text
severity :: DLSeverity
code :: RuleCode
..}
          | Bool
otherwise = f CheckFailure
forall a. Monoid a => a
mempty
{-# INLINEABLE rule #-}

rememberStage :: Linenumber -> BaseImage -> Acc -> Acc
rememberStage :: Linenumber -> BaseImage -> Acc -> Acc
rememberStage Linenumber
line stage :: BaseImage
stage@BaseImage {$sel:image:BaseImage :: BaseImage -> Image
image = Image Maybe Registry
_ Text
als} Acc
Empty =
  BaseImage -> Map Text Linenumber -> Map Linenumber BaseImage -> Acc
Acc BaseImage
stage (Text -> Linenumber -> Map Text Linenumber
forall k a. k -> a -> Map k a
Map.singleton Text
als Linenumber
line) Map Linenumber BaseImage
forall k a. Map k a
Map.empty
rememberStage Linenumber
line stage :: BaseImage
stage@BaseImage {$sel:image:BaseImage :: BaseImage -> Image
image = Image Maybe Registry
_ Text
als} (Acc BaseImage
_ Map Text Linenumber
stages Map Linenumber BaseImage
o) =
  BaseImage -> Map Text Linenumber -> Map Linenumber BaseImage -> Acc
Acc BaseImage
stage (Text -> Linenumber -> Map Text Linenumber -> Map Text Linenumber
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Text
als Linenumber
line Map Text Linenumber
stages) Map Linenumber BaseImage
o

rememberLine :: Linenumber -> Acc -> Acc
rememberLine :: Linenumber -> Acc -> Acc
rememberLine Linenumber
line Acc
Empty = BaseImage -> Map Text Linenumber -> Map Linenumber BaseImage -> Acc
Acc BaseImage
scratch Map Text Linenumber
forall a. Monoid a => a
mempty (Linenumber -> BaseImage -> Map Linenumber BaseImage
forall k a. k -> a -> Map k a
Map.singleton Linenumber
line BaseImage
scratch)
rememberLine Linenumber
line (Acc BaseImage
from Map Text Linenumber
stages Map Linenumber BaseImage
o) = BaseImage -> Map Text Linenumber -> Map Linenumber BaseImage -> Acc
Acc BaseImage
from Map Text Linenumber
stages (Linenumber
-> BaseImage
-> Map Linenumber BaseImage
-> Map Linenumber BaseImage
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Linenumber
line BaseImage
from Map Linenumber BaseImage
o)

yarnInstall :: Shell.Command -> Bool
yarnInstall :: Command -> Bool
yarnInstall = Text -> [Text] -> Command -> Bool
Shell.cmdHasArgs Text
"yarn" [Text
"install"]

yarnCacheClean :: Shell.Command -> Bool
yarnCacheClean :: Command -> Bool
yarnCacheClean = Text -> [Text] -> Command -> Bool
Shell.cmdHasArgs Text
"yarn" [Text
"cache", Text
"clean"]

-- | This is needed as placeholder when no FROM statement has yet been
-- envountered.
scratch :: BaseImage
scratch :: BaseImage
scratch =
  ( BaseImage :: Image
-> Maybe Tag
-> Maybe Digest
-> Maybe ImageAlias
-> Maybe Text
-> BaseImage
BaseImage
      { $sel:image:BaseImage :: Image
image = Image
"scratch",
        $sel:tag:BaseImage :: Maybe Tag
tag = Maybe Tag
forall a. Maybe a
Nothing,
        $sel:digest:BaseImage :: Maybe Digest
digest = Maybe Digest
forall a. Maybe a
Nothing,
        $sel:alias:BaseImage :: Maybe ImageAlias
alias = Maybe ImageAlias
forall a. Maybe a
Nothing,
        $sel:platform:BaseImage :: Maybe Text
platform = Maybe Text
forall a. Maybe a
Nothing
      }
  )