module Hadolint.Rule.DL4006 (rule) where

import qualified Data.Text as Text
import Hadolint.Rule
import Hadolint.Shell (ParsedShell)
import qualified Hadolint.Shell as Shell
import Language.Docker.Syntax

rule :: Rule ParsedShell
rule = customRule check (emptyState False)
  where
    code = "DL4006"
    severity = DLWarningC
    message =
      "Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using \
      \/bin/sh in an alpine image or if your shell is symlinked to busybox then consider \
      \explicitly setting your SHELL to /bin/ash, or disable this check"

    check _ st From {} = st |> replaceWith False -- Reset the state each time we find a new FROM
    check _ st (Shell args)
      | 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

    isNonPosixShell (Shell.ParsedShell orig _ _) =
      any (`Text.isPrefixOf` orig) Shell.nonPosixShells
    hasPipes script = Shell.hasPipes script
    hasPipefailOption script =
      not $
        null
          [ True
            | cmd@(Shell.Command name arguments _) <- Shell.presentCommands script,
              validShell <- ["/bin/bash", "/bin/zsh", "/bin/ash", "bash", "zsh", "ash"],
              name == validShell,
              Shell.hasFlag "o" cmd,
              arg <- Shell.arg <$> arguments,
              arg == "pipefail"
          ]
{-# INLINEABLE rule #-}
