stackctl 1.1.4.0 → 1.2.0.0
raw patch · 13 files changed
+125/−45 lines, 13 filesdep +envparsedep +semigroups
Dependencies added: envparse, semigroups
Files
- CHANGELOG.md +6/−1
- src/Stackctl/CLI.hs +1/−1
- src/Stackctl/ColorOption.hs +22/−12
- src/Stackctl/DirectoryOption.hs +22/−6
- src/Stackctl/FilterOption.hs +13/−2
- src/Stackctl/Options.hs +35/−12
- src/Stackctl/Prelude.hs +4/−0
- src/Stackctl/Spec/Capture.hs +2/−2
- src/Stackctl/Spec/Cat.hs +2/−2
- src/Stackctl/Spec/Discover.hs +2/−2
- src/Stackctl/Subcommand.hs +12/−4
- src/Stackctl/VerboseOption.hs +1/−0
- stackctl.cabal +3/−1
CHANGELOG.md view
@@ -1,4 +1,9 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.4.0...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.2.0.0...main)++## [v1.2.0.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.2.0.0)++- Use more specific types in `Has{Directory,Filter,Color}Option`+- Add environment variable configuration for `STACKCTL_{DIRECTORY,FILTERS}` ## [v1.1.4.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.1.4.0)
src/Stackctl/CLI.hs view
@@ -87,7 +87,7 @@ $ defaultLogSettings logger <- newLogger $ adjustLogSettings- (options ^. colorOptionL)+ (options ^. colorOptionL . to unColorOption) (options ^. verboseOptionL) envLogSettings
src/Stackctl/ColorOption.hs view
@@ -1,5 +1,6 @@ module Stackctl.ColorOption- ( LogColor(..)+ ( ColorOption(..)+ , defaultColorOption , HasColorOption(..) , colorOption , colorHandle@@ -8,29 +9,38 @@ import Stackctl.Prelude import Blammo.Logging.LogSettings+import Data.Semigroup (Last(..)) import Options.Applicative +newtype ColorOption = ColorOption+ { unColorOption :: LogColor+ }+ deriving Semigroup via Last ColorOption++defaultColorOption :: ColorOption+defaultColorOption = ColorOption LogColorAuto+ class HasColorOption env where- colorOptionL :: Lens' env LogColor+ colorOptionL :: Lens' env ColorOption -instance HasColorOption LogColor where+instance HasColorOption ColorOption where colorOptionL = id -colorOption :: Parser LogColor-colorOption = option (eitherReader readLogColor) $ mconcat+colorOption :: Parser ColorOption+colorOption = option (eitherReader $ fmap ColorOption . readLogColor) $ mconcat [ long "color" , help "When to colorize output" , metavar "auto|always|never"- , value LogColorAuto- , showDefaultWith showLogColor+ , value defaultColorOption+ , showDefaultWith showColorOption ] -showLogColor :: LogColor -> String-showLogColor = \case+showColorOption :: ColorOption -> String+showColorOption co = case unColorOption co of LogColorAuto -> "auto" LogColorAlways -> "always" LogColorNever -> "never" -colorHandle :: MonadIO m => Handle -> LogColor -> m Bool-colorHandle h lc = shouldColorHandle settings h- where settings = setLogSettingsColor lc defaultLogSettings+colorHandle :: MonadIO m => Handle -> ColorOption -> m Bool+colorHandle h co = shouldColorHandle settings h+ where settings = setLogSettingsColor (unColorOption co) defaultLogSettings
src/Stackctl/DirectoryOption.hs view
@@ -1,25 +1,41 @@ module Stackctl.DirectoryOption- ( HasDirectoryOption(..)+ ( DirectoryOption(..)+ , defaultDirectoryOption+ , HasDirectoryOption(..)+ , envDirectoryOption , directoryOption ) where import Stackctl.Prelude +import Data.Semigroup (Last(..))+import qualified Env import Options.Applicative +newtype DirectoryOption = DirectoryOption+ { unDirectoryOption :: FilePath+ }+ deriving newtype IsString+ deriving Semigroup via Last DirectoryOption++defaultDirectoryOption :: DirectoryOption+defaultDirectoryOption = "."+ class HasDirectoryOption env where- directoryOptionL :: Lens' env FilePath+ directoryOptionL :: Lens' env DirectoryOption -instance HasDirectoryOption FilePath where+instance HasDirectoryOption DirectoryOption where directoryOptionL = id -directoryOption :: Parser FilePath+envDirectoryOption :: Env.Parser Env.Error DirectoryOption+envDirectoryOption = Env.var (Env.str <=< Env.nonempty) "DIRECTORY"+ $ Env.help "Operate on specifications in this directory"++directoryOption :: Parser DirectoryOption directoryOption = option str $ mconcat [ short 'd' , long "directory" , metavar "PATH" , help "Operate on specifications in PATH"- , value "."- , showDefault , action "directory" ]
src/Stackctl/FilterOption.hs view
@@ -1,6 +1,8 @@ module Stackctl.FilterOption ( FilterOption+ , defaultFilterOption , HasFilterOption(..)+ , envFilterOption , filterOption , filterOptionFromPaths , filterOptionFromText@@ -10,7 +12,9 @@ import Stackctl.Prelude import qualified Data.List.NonEmpty as NE+import Data.Semigroup (Last(..)) import qualified Data.Text as T+import qualified Env import Options.Applicative import Stackctl.AWS.CloudFormation (StackName(..)) import Stackctl.StackSpec@@ -20,6 +24,7 @@ newtype FilterOption = FilterOption { unFilterOption :: NonEmpty Pattern }+ deriving Semigroup via Last FilterOption instance ToJSON FilterOption where toJSON = toJSON . showFilterOption@@ -31,13 +36,19 @@ instance HasFilterOption FilterOption where filterOptionL = id +envFilterOption :: String -> Env.Parser Env.Error FilterOption+envFilterOption items =+ Env.var (first Env.UnreadError . readFilterOption) "FILTERS"+ $ Env.help+ $ "Filter "+ <> items+ <> " by patterns"+ filterOption :: String -> Parser FilterOption filterOption items = option (eitherReader readFilterOption) $ mconcat [ long "filter" , metavar "PATTERN[,PATTERN]" , help $ "Filter " <> items <> " to match PATTERN(s)"- , value defaultFilterOption- , showDefaultWith showFilterOption ] filterOptionFromPaths :: NonEmpty FilePath -> FilterOption
src/Stackctl/Options.hs view
@@ -1,10 +1,13 @@ module Stackctl.Options- ( Options(..)+ ( Options+ , envParser , optionsParser ) where import Stackctl.Prelude +import Data.Semigroup.Generic+import qualified Env import Options.Applicative import Stackctl.ColorOption import Stackctl.DirectoryOption@@ -12,29 +15,49 @@ import Stackctl.VerboseOption data Options = Options- { oDirectory :: FilePath- , oFilterOption :: FilterOption- , oColor :: LogColor+ { oDirectory :: Maybe DirectoryOption+ , oFilter :: Maybe FilterOption+ , oColor :: Maybe ColorOption , oVerbose :: Verbosity }+ deriving stock Generic+ deriving Semigroup via GenericSemigroupMonoid Options -instance HasDirectoryOption Options where- directoryOptionL = lens oDirectory $ \x y -> x { oDirectory = y }+directoryL :: Lens' Options (Maybe DirectoryOption)+directoryL = lens oDirectory $ \x y -> x { oDirectory = y } -instance HasColorOption Options where- colorOptionL = lens oColor $ \x y -> x { oColor = y }+filterL :: Lens' Options (Maybe FilterOption)+filterL = lens oFilter $ \x y -> x { oFilter = y } +colorL :: Lens' Options (Maybe ColorOption)+colorL = lens oColor $ \x y -> x { oColor = y }++instance HasDirectoryOption Options where+ directoryOptionL = directoryL . maybeLens defaultDirectoryOption+ instance HasFilterOption Options where- filterOptionL = lens oFilterOption $ \x y -> x { oFilterOption = y }+ filterOptionL = filterL . maybeLens defaultFilterOption +instance HasColorOption Options where+ colorOptionL = colorL . maybeLens defaultColorOption+ instance HasVerboseOption Options where verboseOptionL = lens oVerbose $ \x y -> x { oVerbose = y } -- brittany-disable-next-binding +envParser :: Env.Parser Env.Error Options+envParser = Env.prefixed "STACKCTL_" $ Options+ <$> optional envDirectoryOption+ <*> optional (envFilterOption "specifications")+ <*> pure mempty -- use LOG_COLOR+ <*> pure mempty -- use LOG_LEVEL++-- brittany-disable-next-binding+ optionsParser :: Parser Options optionsParser = Options- <$> directoryOption- <*> filterOption "specifications"- <*> colorOption+ <$> optional directoryOption+ <*> optional (filterOption "specifications")+ <*> (Just <$> colorOption) <*> verboseOption
src/Stackctl/Prelude.hs view
@@ -1,6 +1,7 @@ module Stackctl.Prelude ( module X , decodeUtf8+ , maybeLens ) where import RIO as X hiding@@ -30,3 +31,6 @@ decodeUtf8 :: ByteString -> Text decodeUtf8 = decodeUtf8With lenientDecode++maybeLens :: a -> Lens' (Maybe a) a+maybeLens x = lens (fromMaybe x) $ const Just
src/Stackctl/Spec/Capture.hs view
@@ -10,7 +10,7 @@ import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Config (HasConfig)-import Stackctl.DirectoryOption (HasDirectoryOption(..))+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption) import Stackctl.Spec.Generate import Stackctl.StackSpec import System.FilePath.Glob@@ -74,7 +74,7 @@ => CaptureOptions -> m () runCapture CaptureOptions {..} = do- dir <- view directoryOptionL+ dir <- unDirectoryOption <$> view directoryOptionL let setScopeName scope =
src/Stackctl/Spec/Cat.hs view
@@ -21,7 +21,7 @@ import Stackctl.AWS.Scope import Stackctl.Colors import Stackctl.Config (HasConfig)-import Stackctl.DirectoryOption (HasDirectoryOption(..))+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption) import Stackctl.FilterOption (HasFilterOption) import Stackctl.Spec.Discover import Stackctl.StackSpec@@ -67,7 +67,7 @@ => CatOptions -> m () runCat CatOptions {..} = do- dir <- view directoryOptionL+ dir <- unDirectoryOption <$> view directoryOptionL colors@Colors {..} <- getColorsStdout tree <- specTree <$> discoverSpecs
src/Stackctl/Spec/Discover.hs view
@@ -10,7 +10,7 @@ import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Config (HasConfig)-import Stackctl.DirectoryOption (HasDirectoryOption(..))+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption) import Stackctl.FilterOption (HasFilterOption(..), filterStackSpecs) import Stackctl.StackSpec import Stackctl.StackSpecPath@@ -29,7 +29,7 @@ ) => m [StackSpec] discoverSpecs = do- dir <- view directoryOptionL+ dir <- unDirectoryOption <$> view directoryOptionL scope@AwsScope {..} <- view awsScopeL paths <- globRelativeTo dir
src/Stackctl/Subcommand.hs view
@@ -7,6 +7,7 @@ import Stackctl.Prelude +import qualified Env import Options.Applicative import qualified Stackctl.CLI as CLI import Stackctl.Colors@@ -25,17 +26,24 @@ command (unpack name) (run <$> withInfo description parse) runSubcommand :: Mod CommandFields (CLI.AppT (CLI.App Options) IO ()) -> IO ()-runSubcommand = runSubcommand' "Work with Stack specifications" optionsParser+runSubcommand =+ runSubcommand' "Work with Stack specifications" envParser optionsParser +-- brittany-disable-next-binding+ runSubcommand'- :: (HasVerboseOption options, HasColorOption options)+ :: (Semigroup options, HasVerboseOption options, HasColorOption options) => Text+ -> Env.Parser Env.Error options -> Parser options -> Mod CommandFields (CLI.AppT (CLI.App options) IO ()) -> IO ()-runSubcommand' x op sp = do- (options, act) <- execParser $ withInfo x $ (,) <$> op <*> subparser sp+runSubcommand' title parseEnv parseCLI sp = do+ (options, act) <- applyEnv+ <$> Env.parse (Env.header $ unpack title) parseEnv+ <*> execParser (withInfo title $ (,) <$> parseCLI <*> subparser sp) CLI.runAppT options act+ where applyEnv env = first (env <>) withInfo :: Text -> Parser a -> ParserInfo a withInfo d p = info (p <**> helper) $ progDesc (unpack d) <> fullDesc
src/Stackctl/VerboseOption.hs view
@@ -11,6 +11,7 @@ import Options.Applicative newtype Verbosity = Verbosity [()]+ deriving newtype (Semigroup, Monoid) verbositySetLogLevels :: Verbosity -> (LogSettings -> LogSettings) verbositySetLogLevels (Verbosity bs) = case bs of
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: stackctl-version: 1.1.4.0+version: 1.2.0.0 license: MIT license-file: LICENSE copyright: 2022 Renaissance Learning Inc@@ -95,6 +95,7 @@ cfn-flip >=0.1.0.3, conduit, containers,+ envparse, errors, exceptions, extra,@@ -106,6 +107,7 @@ optparse-applicative, resourcet, rio,+ semigroups, text, time, unliftio,