Skip to content

Commit

Permalink
Formatting with ormolu
Browse files Browse the repository at this point in the history
It is a more modern alternative and the default option for the haskell
language server
  • Loading branch information
lorenzo committed Oct 18, 2020
1 parent 49d63be commit b88b823
Show file tree
Hide file tree
Showing 14 changed files with 2,028 additions and 1,970 deletions.
134 changes: 71 additions & 63 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TemplateHaskell #-}

module Main where

Expand All @@ -11,19 +11,19 @@ import qualified Data.Set as Set
import Data.String
import qualified Data.Version
import qualified Development.GitRev
import qualified Hadolint
import Options.Applicative hiding (ParseError)
import qualified Paths_hadolint -- version from hadolint.cabal file
-- version from hadolint.cabal file
import qualified Paths_hadolint
import System.Exit (exitFailure, exitSuccess)

import qualified Hadolint

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

toOutputFormat :: String -> Maybe Hadolint.OutputFormat
toOutputFormat "json" = Just Hadolint.Json
Expand All @@ -42,81 +42,89 @@ showFormat Hadolint.Codacy = "codacy"

parseOptions :: Parser CommandOptions
parseOptions =
CommandOptions <$> -- CLI options parser definition
version <*>
configFile <*>
outputFormat <*>
files <*>
lintOptions
CommandOptions
<$> version -- CLI options parser definition
<*> 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"))
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"])
option
(maybeReader toOutputFormat)
( long "format"
<> short 'f' -- options for the output format
<> help
"The output format for the results [tty | json | checkstyle | codeclimate | codacy]"
<> value Hadolint.TTY
<> showDefaultWith showFormat -- The default value
<> 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"))
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)"))
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
cmd <- execParser opts
execute cmd
where
execute CommandOptions {showVersion = True} = putStrLn getVersion >> exitSuccess
execute CommandOptions {dockerfiles = []} =
putStrLn "Please provide a Dockerfile" >> exitFailure
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
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")
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)
| $(Development.GitRev.gitDescribe) == "UNKNOWN" =
"Haskell Dockerfile Linter " ++ Data.Version.showVersion Paths_hadolint.version ++ "-no-git"
| otherwise = "Haskell Dockerfile Linter " ++ $(Development.GitRev.gitDescribe)
9 changes: 5 additions & 4 deletions src/Hadolint.hs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module Hadolint
( module Hadolint.Lint
, module Hadolint.Rules
, module Hadolint.Config
) where
( module Hadolint.Lint,
module Hadolint.Rules,
module Hadolint.Config,
)
where

import Hadolint.Config
import Hadolint.Lint
Expand Down
111 changes: 59 additions & 52 deletions src/Hadolint/Config.hs
Original file line number Diff line number Diff line change
@@ -1,86 +1,93 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module Hadolint.Config (applyConfig, ConfigFile(..)) where
module Hadolint.Config (applyConfig, ConfigFile (..)) where

import Control.Monad (filterM)
import qualified Data.ByteString as Bytes
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 Data.YAML ((.:?))
import GHC.Generics
import qualified Data.YAML as Yaml
import GHC.Generics (Generic)
import qualified Hadolint.Lint as Lint
import qualified Hadolint.Rules as Rules
import qualified Language.Docker as Docker
import System.Directory
(XdgDirectory(..), doesFileExist, getCurrentDirectory,
getXdgDirectory)
( XdgDirectory (..),
doesFileExist,
getCurrentDirectory,
getXdgDirectory,
)
import System.FilePath ((</>))

import qualified Hadolint.Lint as Lint
import qualified Hadolint.Rules as Rules

data ConfigFile = ConfigFile
{ ignoredRules :: Maybe [Lint.IgnoreRule]
, trustedRegistries :: Maybe [Lint.TrustedRegistry]
} deriving (Show, Eq, Generic)
{ ignoredRules :: Maybe [Lint.IgnoreRule],
trustedRegistries :: Maybe [Lint.TrustedRegistry]
}
deriving (Show, Eq, Generic)

instance Yaml.FromYAML ConfigFile where
parseYAML = Yaml.withMap "ConfigFile" $ \m -> ConfigFile
<$> m .:? "ignored"
<*> m .:? "trustedRegistries"
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
-- file. If there is an error parsing the default config file, this function will
-- return the error string.
applyConfig :: Maybe FilePath -> Lint.LintOptions -> IO (Either String Lint.LintOptions)
applyConfig maybeConfig o
| not (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
Nothing -> return (Right o)
Just config -> parseAndApply config
| not (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
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]
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
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
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))

override ignore trusted = applyTrusted trusted . applyIgnore ignore $ o
applyIgnore ignore opts =
case Lint.ignoreRules opts of
[] -> opts {Lint.ignoreRules = fromMaybe [] ignore}
_ -> opts
case Lint.ignoreRules opts of
[] -> opts {Lint.ignoreRules = fromMaybe [] ignore}
_ -> opts

applyTrusted trusted opts
| null (Rules.allowedRegistries (Lint.rulesConfig opts)) =
opts {Lint.rulesConfig = toRules trusted <> Lint.rulesConfig opts}
| otherwise = opts
-- | Converts a list of TrustedRegistry to a RulesConfig record
| null (Rules.allowedRegistries (Lint.rulesConfig opts)) =
opts {Lint.rulesConfig = toRules trusted <> Lint.rulesConfig opts}
| otherwise = opts

toRules (Just trusted) = Rules.RulesConfig (Set.fromList . coerce $ trusted)
toRules _ = mempty

formatError err config =
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
]
[ "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
]
Loading

0 comments on commit b88b823

Please sign in to comment.