packages feed

hadolint-1.13.0: app/Main.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

module Main where

import Control.Applicative
import qualified Data.List.NonEmpty as NonEmpty
import Data.Semigroup ((<>))
import qualified Data.Set as Set
import Data.String
import qualified Data.Version
import qualified Development.GitRev
import Options.Applicative hiding (ParseError)
import qualified Paths_hadolint -- version from hadolint.cabal file
import System.Exit (exitFailure, exitSuccess)

import qualified Hadolint

data CommandOptions = CommandOptions
    { showVersion :: Bool
    , configFile :: Maybe FilePath
    , format :: Hadolint.OutputFormat
    , dockerfiles :: [String]
    , lintingOptions :: Hadolint.LintOptions
    }

toOutputFormat :: String -> Maybe Hadolint.OutputFormat
toOutputFormat "json" = Just Hadolint.Json
toOutputFormat "tty" = Just Hadolint.TTY
toOutputFormat "codeclimate" = Just Hadolint.CodeclimateJson
toOutputFormat "checkstyle" = Just Hadolint.Checkstyle
toOutputFormat "codacy" = Just Hadolint.Codacy
toOutputFormat _ = Nothing

showFormat :: Hadolint.OutputFormat -> String
showFormat Hadolint.Json = "json"
showFormat Hadolint.TTY = "tty"
showFormat Hadolint.CodeclimateJson = "codeclimate"
showFormat Hadolint.Checkstyle = "checkstyle"
showFormat Hadolint.Codacy = "codacy"

parseOptions :: Parser CommandOptions
parseOptions =
    CommandOptions <$> -- CLI options parser definition
    version <*>
    configFile <*>
    outputFormat <*>
    files <*>
    lintOptions
  where
    version = switch (long "version" <> short 'v' <> help "Show version")
    --
    -- | Parse the config filename to use
    configFile =
        optional
            (strOption
                 (long "config" <> short 'c' <> metavar "FILENAME" <>
                  help "Path to the configuration file"))
    --
    -- | Parse the output format option
    outputFormat =
        option
            (maybeReader toOutputFormat)
            (long "format" <> -- options for the output format
             short 'f' <>
             help
                 "The output format for the results [tty | json | checkstyle | codeclimate | codacy]" <>
             value Hadolint.TTY <> -- The default value
             showDefaultWith showFormat <>
             completeWith ["tty", "json", "checkstyle", "codeclimate", "codacy"])
    --
    -- | Parse a list of ignored rules
    ignoreList =
        many
            (strOption
                 (long "ignore" <>
                  help "A rule to ignore. If present, the ignore list in the config file is ignored" <>
                  metavar "RULECODE"))
    --
    -- | Parse a list of dockerfile names
    files = many (argument str (metavar "DOCKERFILE..." <> action "file"))
    --
    -- | Parse the rule ignore list and the rules configuration into a LintOptions
    lintOptions = Hadolint.LintOptions <$> ignoreList <*> parseRulesConfig
    --
    -- | Parse all the optional rules configuration
    parseRulesConfig =
        Hadolint.RulesConfig . Set.fromList . fmap fromString <$>
        many
            (strOption
                 (long "trusted-registry" <>
                  help "A docker registry to allow to appear in FROM instructions" <>
                  metavar "REGISTRY (e.g. docker.io)"))

main :: IO ()
main = do
    cmd <- execParser opts
    execute cmd
  where
    execute CommandOptions {showVersion = True} = putStrLn getVersion >> exitSuccess
    execute CommandOptions {dockerfiles = []} =
        putStrLn "Please provide a Dockerfile" >> exitFailure
    execute cmd = do
        lintConfig <- Hadolint.applyConfig (configFile cmd) (lintingOptions cmd)
        let files = NonEmpty.fromList (dockerfiles cmd)
        case lintConfig of
            Left err -> error err
            Right conf -> do
                res <- Hadolint.lint conf files
                Hadolint.printResultsAndExit (format cmd) res
    opts =
        info
            (helper <*> parseOptions)
            (fullDesc <> progDesc "Lint Dockerfile for errors and best practices" <>
             header "hadolint - Dockerfile Linter written in Haskell")

getVersion :: String
getVersion
    | $(Development.GitRev.gitDescribe) == "UNKNOWN" =
        "Haskell Dockerfile Linter " ++ Data.Version.showVersion Paths_hadolint.version ++ "-no-git"
    | otherwise = "Haskell Dockerfile Linter " ++ $(Development.GitRev.gitDescribe)