packages feed

stackctl 1.7.3.0 → 1.7.3.2

raw patch · 30 files changed

+228/−477 lines, 30 filesdep ~Blammodep ~QuickCheckdep ~aeson

Dependency ranges changed: Blammo, QuickCheck, aeson, aeson-pretty, bytestring, conduit, containers, envparse, exceptions, extra, filepath, hspec, http-types, lens, lens-aeson, monad-logger, mtl, optparse-applicative, resourcet, text, text-metrics, time, transformers, typed-process, unix, unordered-containers, uuid, yaml

Files

CHANGELOG.md view
@@ -1,242 +1,1 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.7.2.0...main)--## [v1.7.2.0](https://github.com/freckle/stackctl/compare/v1.7.1.0...v1.7.2.0)--- Automatically cancel any ongoing update-stack operations on `^C`--## [v1.7.1.0](https://github.com/freckle/stackctl/compare/v1.7.0.0...v1.7.1.0)--- Add `withAssumedRole`, deprecate `assumeRole`--## [v1.7.0.0](https://github.com/freckle/stackctl/compare/v1.6.1.2...v1.7.0.0)--- Retain numeric parameter types, and add boolean parameter handling, in our own-  yaml generation--  This required changing the `Generate` to use `ParametersYaml` instead of-  `[Parameter]`, and ultimately led us to removing it, hence the major version-  bump.--## [v1.6.1.2](https://github.com/freckle/stackctl/compare/v1.6.1.1...v1.6.1.2)--- Require Blammo-1.2.2.3--## [v1.6.1.1](https://github.com/freckle/stackctl/compare/v1.6.1.0...v1.6.1.1)--- Fix: finding removed stacks now respects `STACKCTL_DIRECTORY`--## [v1.6.1.0](https://github.com/freckle/stackctl/compare/v1.6.0.0...v1.6.1.0)--- Add `Ord` instance on `RequiredVersion` and `RequiredVersionOp`--## [v1.6.0.0](https://github.com/freckle/stackctl/compare/v1.5.0.1...v1.6.0.0)--- Re-implement `Stackctl.AWS` with `amazonka-mtl`.--_No CLI or behavior changes._--## [v1.5.0.1](https://github.com/freckle/stackctl/compare/v1.5.0.0...v1.5.0.1)--- Handle missing-or-empty specs directory more explicitly-- Add warning for `Depends` pointing to non-existent spec-- Fix formatting of required version in warning message--## [v1.5.0.0](https://github.com/freckle/stackctl/compare/v1.4.4.0...v1.5.0.0)--Breaking changes:--- Don't require a name argument to the `awsSimple` function--New features:--- Add `Exec` and `Shell` features in `actions[].run`-- Support lists in `actions[].run` (single items still work)-- Add more granular status indicators in `stack-ls(1)` output, print a legend of-  these indicators as a footer (disable with `--no-legend`)--Fixes:--- Fix for redundant change-set creation errors in logging output-- Fix globbing bug in auto-expansion of `--filter` arguments--## [v1.4.4.0](https://github.com/freckle/stackctl/compare/v1.4.3.0...v1.4.4.0)--- Add `awsSilently`--## [v1.4.3.0](https://github.com/freckle/stackctl/compare/v1.4.2.2...v1.4.3.0)--- Add `awsWithAuth`-- Add `forEachSpec_`--## [v1.4.2.2](https://github.com/freckle/stackctl/compare/v1.4.2.1...v1.4.2.2)--- Use `amazonka-2.0` :tada:-- Finalize update to `UnliftIO.Exception.Lens`-- Re-export upstreamed `Blammo.Logging.Colors`--## [v1.4.2.1](https://github.com/freckle/stackctl/compare/v1.4.2.0...v1.4.2.1)--No changes. Bumped to trigger release workflow.--## [v1.4.2.0](https://github.com/freckle/stackctl/compare/v1.4.0.1...v1.4.2.0)--- Add `stackctl-ls` for listing stacks and their deployed status-- Add `--auto-sso` option for automating `aws sso login` when required--## [v1.4.0.1](https://github.com/freckle/stackctl/compare/v1.4.0.0...v1.4.0.1)--- Document and read a consistently-named `STACKCTL_FILTER` for `--filter`. For-  now, the old and incorrect `STACKCTL_FILTERS` will still work.--## [v1.4.0.0](https://github.com/freckle/stackctl/compare/v1.3.0.2...v1.4.0.0)--- Add `awsAssumeRole` for running an action as an assumed role-- Refactor `Generate` interface to better support generating stacks with-  pre-existing templates--## [v1.3.0.2](https://github.com/freckle/stackctl/compare/v1.3.0.1...v1.3.0.2)--- Adjust timeout when invoking Lambdas to allow up to Lambda's own execution-  timeout (15 minutes).--## [v1.3.0.1](https://github.com/freckle/stackctl/compare/v1.3.0.0...v1.3.0.1)--- Fix bug where `LOG_COLOR` was never respected-- Also accept `"required_version: == <version>"`-- Add `Eq`, `ToJSON` instance on `RequiredVersion`--## [v1.3.0.0](https://github.com/freckle/stackctl/compare/v1.2.0.1...v1.3.0.0)--- Fix it so commands like `version` don't need a valid AWS environment--  This changes the `Subcommand` interface and so is a major version update for-  the purposes of those using Stackctl as a library.--## [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)--- Support matching Stacks by glob in `capture`-- Add `--tag` to `changes` and `deploy`--## [v1.1.3.1](https://github.com/freckle/stackctl/compare/v1.1.3.0...v1.1.3.1)--- Fix JSON formatting bugs in generating specification--## [v1.1.3.0](https://github.com/freckle/stackctl/compare/v1.1.2.2...v1.1.3.0)--- Repository-local configuration--  See https://github.com/freckle/stackctl/commit/564678203fe70b5c4c46c655dd3daeaafb6de9e0--- Don't duplicate re-used templates in `stackctl-cat`-- Improve `--filter`--  - Match against stack name and template, in addition to spec path.-  - Automatically prepend `**/` (unless there is already a leading wildcard) and-    append `{/*,.yaml,.json}` (unless there is already a trailing wildcard or-    extension).--  In general, this aims to make `--filter` match more things more intuitively-  for operators, but still match exactly in programmatic use-cases.--- Various documentation improvements-- Support more natural `{Key}: {Value}` syntax in `Parameters` and `Tags`-- Fix bug where we may generate an `{}` element in `Parameters`--## [v1.1.2.2](https://github.com/freckle/stackctl/compare/v1.1.2.1...v1.1.2.2)--- Add support for Stack descriptions--## [v1.1.2.1](https://github.com/freckle/stackctl/compare/v1.1.2.0...v1.1.2.1)--- Build with LTS-20.4 / GHC 9.2--## [v1.1.2.0](https://github.com/freckle/stackctl/compare/v1.1.1.1...v1.1.2.0)--- Fix incorrect ordering of log-messages by setting `LOG_CONCURRENCY=1`-- Fix potential coloring of changes being redirected to a file-- Make `PATH` optional (again) in `stackctl changes`-- Add `--no-flip` to `stackctl capture`--## [v1.1.1.1](https://github.com/freckle/stackctl/compare/v1.1.1.0...v1.1.1.1)--- Trigger release--## [v1.1.1.0](https://github.com/freckle/stackctl/compare/v1.1.0.5...v1.1.1.0)--- Add `--parameter` to `changes` and `deploy`-- Sort changes by causing-before-caused--## [v1.1.0.5](https://github.com/freckle/stackctl/compare/v1.1.0.4...v1.1.0.5)--- Trigger release workflow--## [v1.1.0.4](https://github.com/freckle/stackctl/compare/v1.1.0.3...v1.1.0.4)--- Fix bug where only the last spec in a multi-spec case had its changes present-  in the output file generated by `changes`.--## [v1.1.0.3](https://github.com/freckle/stackctl/compare/v1.1.0.2...v1.1.0.3)--- Require Blammo-1.1.1.0--## [v1.1.0.2](https://github.com/freckle/stackctl/compare/v1.1.0.1...v1.1.0.2)--- Log responses from `awsLambdaInvoke` when running actions-- Clarify discovery logging-- Add install script--## [v1.1.0.1](https://github.com/freckle/stackctl/compare/v1.1.0.0...v1.1.0.1)--- Update to `cfn-flip-0.1.0.3`--## [v1.1.0.0](https://github.com/freckle/stackctl/compare/v1.0.2.0...v1.1.0.0)--- Fix interleaved or out-of-order output bugs by streaming deployment events-  through the Logger instead of directly to `stdout`-- Logging goes to `stdout` by default (`LOG_DESTINATION` can still be used)-- The `changes` subcommand now requires a `PATH` argument--## [v1.0.2.0](https://github.com/freckle/stackctl/compare/v1.0.1.2...v1.0.2.0)--- Add `Stackctl.Action`--  Support for taking actions during Stack management, currently we support-  invoking a lambda post-deployment. In the future, we can add more, such as-  running local pre-deploy validation or preparation scripts.--- Add `awsCloudFormationDescribeStackOutputs`--## [v1.0.1.2](https://github.com/freckle/stackctl/compare/v1.0.1.1...v1.0.1.2)--- Always flush log messages before our own output--## [v1.0.1.1](https://github.com/freckle/stackctl/compare/v1.0.1.0...v1.0.1.1)--- Respect `LOG_DESTINATION` (the default remains `stderr`)--## [v1.0.1.0](https://github.com/freckle/stackctl/compare/v1.0.0.2...v1.0.1.0)--- Support reading CloudGenesis specifications--  - Accept account paths like `id.name` or `name.id`-  - Read `Parameters` as `Parameter{Key,Value}` or `{Name,Value}`--  This allows us to work with specifications directories originally implemented-  for, and potentially still used with, the CloudGenesis tooling.--## [v1.0.0.2](https://github.com/freckle/stackctl/compare/v1.0.0.1...v1.0.0.2)--- Fix tailing all events to read most recent, causing Throttling errors--## [v1.0.0.1](https://github.com/freckle/stackctl/compare/v1.0.0.0...v1.0.0.1)--- Fix non-portable paths issue in OSX executable build--## [v1.0.0.0](https://github.com/freckle/stackctl/tree/v1.0.0.0)--First release+See https://github.com/freckle/stackctl/releases
app/Main.hs view
@@ -11,8 +11,8 @@ main =   runSubcommand     $ subcommand Commands.cat-    <> subcommand Commands.capture-    <> subcommand Commands.changes-    <> subcommand Commands.deploy-    <> subcommand Commands.list-    <> subcommand Commands.version+      <> subcommand Commands.capture+      <> subcommand Commands.changes+      <> subcommand Commands.deploy+      <> subcommand Commands.list+      <> subcommand Commands.version
src/Stackctl/AWS/CloudFormation.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DuplicateRecordFields #-}+ module Stackctl.AWS.CloudFormation   ( Stack (..)   , stack_stackName@@ -194,7 +196,7 @@   handling_ _ValidationError (pure Nothing)     $ awsSilently -- don't log said 400     $ Just-    <$> awsCloudFormationDescribeStack stackName+      <$> awsCloudFormationDescribeStack stackName  awsCloudFormationDescribeStackOutputs   :: (MonadIO m, MonadAWS m)@@ -218,10 +220,10 @@    runConduit     $ AWS.paginate req-    .| mapC (fromMaybe [] . (^. describeStackEventsResponse_stackEvents))-    .| concatC-    .| takeWhileC (\e -> Just (e ^. stackEvent_eventId) /= mLastId)-    .| sinkList+      .| mapC (fromMaybe [] . (^. describeStackEventsResponse_stackEvents))+      .| concatC+      .| takeWhileC (\e -> Just (e ^. stackEvent_eventId) /= mLastId)+      .| sinkList  awsCloudFormationGetStackNamesMatching   :: (MonadIO m, MonadAWS m)@@ -232,12 +234,12 @@    runConduit     $ AWS.paginate req-    .| concatMapC (^. listStacksResponse_stackSummaries)-    .| concatC-    .| mapC (^. stackSummary_stackName)-    .| filterC ((p `match`) . unpack)-    .| mapC StackName-    .| sinkList+      .| concatMapC (^. listStacksResponse_stackSummaries)+      .| concatC+      .| mapC (^. stackSummary_stackName)+      .| filterC ((p `match`) . unpack)+      .| mapC StackName+      .| sinkList  awsCloudFormationGetMostRecentStackEventId   :: (MonadIO m, MonadAWS m)@@ -258,9 +260,9 @@    AWS.simple req     $ pure-    . getFirstEventId-    . fromMaybe []-    . (^. describeStackEventsResponse_stackEvents)+      . getFirstEventId+      . fromMaybe []+      . (^. describeStackEventsResponse_stackEvents)  awsCloudFormationDeleteStack   :: (MonadIO m, MonadLogger m, MonadAWS m)@@ -314,7 +316,7 @@ awaitStack waiter stackName =   AWS.await waiter     $ newDescribeStacks-    & describeStacks_stackName ?~ unStackName stackName+      & describeStacks_stackName ?~ unStackName stackName  makeParameter :: Text -> Maybe Text -> Parameter makeParameter k v =@@ -424,7 +426,7 @@        logInfo         $ "Creating changeset..."-        :# ["name" .= name, "type" .= changeSetType]+          :# ["name" .= name, "type" .= changeSetType]       csId <- AWS.simple req (^. createChangeSetResponse_id)        logDebug "Awaiting CREATE_COMPLETE"@@ -471,16 +473,16 @@   logInfo "Deleting all changesets"   runConduit     $ AWS.paginate (newListChangeSets $ unStackName stackName)-    .| concatMapC-      ( \resp -> fromMaybe [] $ do-          ss <- resp ^. listChangeSetsResponse_summaries-          pure $ mapMaybe Summary.changeSetId ss-      )-    .| mapM_C-      ( \csId -> do-          logInfo $ "Enqueing delete" :# ["changeSetId" .= csId]-          void $ AWS.send $ newDeleteChangeSet csId-      )+      .| concatMapC+        ( \resp -> fromMaybe [] $ do+            ss <- resp ^. listChangeSetsResponse_summaries+            pure $ mapMaybe Summary.changeSetId ss+        )+      .| mapM_C+        ( \csId -> do+            logInfo $ "Enqueing delete" :# ["changeSetId" .= csId]+            void $ AWS.send $ newDeleteChangeSet csId+        )  -- | Did we abandoned this Stack's first ever ChangeSet? --
src/Stackctl/AWS/Core.hs view
@@ -77,8 +77,6 @@      , MonadIO m      , MonadAWS m      , AWSRequest a-     , Typeable a-     , Typeable (AWSResponse a)      )   => a   -> (AWSResponse a -> Maybe b)@@ -160,10 +158,10 @@   handling _ServiceError $ \e -> do     logError       $ "Exiting due to AWS Service error"-      :# [ "code" .= toText (e ^. serviceError_code)-         , "message" .= fmap toText (e ^. serviceError_message)-         , "requestId" .= fmap toText (e ^. serviceError_requestId)-         ]+        :# [ "code" .= toText (e ^. serviceError_code)+           , "message" .= fmap toText (e ^. serviceError_message)+           , "requestId" .= fmap toText (e ^. serviceError_requestId)+           ]     exitFailure  formatServiceError :: ServiceError -> Text
src/Stackctl/AWS/Lambda.hs view
@@ -93,11 +93,11 @@    logDebug     $ "Function result"-    :# [ "name" .= name-       , "status" .= status-       , "error" .= mError-       , "functionError" .= mFunctionError-       ]+      :# [ "name" .= name+         , "status" .= status+         , "error" .= mError+         , "functionError" .= mFunctionError+         ]    pure     $ if
src/Stackctl/AWS/Orphans.hs view
@@ -14,6 +14,9 @@ import Data.Aeson import GHC.Generics (Rep) +-- TODO: upstream+deriving newtype instance MonadUnliftIO m => MonadUnliftIO (WithLogger env m)+ -- Makes it syntactally easier to do a bunch of these newtype Generically a = Generically {unGenerically :: a} 
src/Stackctl/AWS/Scope.hs view
@@ -26,20 +26,16 @@ awsScopeSpecPatterns AwsScope {..} =   [ compile       $ "stacks"-      </> unpack (unAccountId awsAccountId)-      <> ".*"-      </> unpack (fromRegion awsRegion)-      <> "**"-      </> "*"-      <.> "yaml"+        </> unpack (unAccountId awsAccountId) <> ".*"+        </> unpack (fromRegion awsRegion)+        </> "**"+        </> "*" <.> "yaml"   , compile       $ "stacks"-      </> "*."-      <> unpack (unAccountId awsAccountId)-      </> unpack (fromRegion awsRegion)-      <> "**"-      </> "*"-      <.> "yaml"+        </> "*." <> unpack (unAccountId awsAccountId)+        </> unpack (fromRegion awsRegion)+        </> "**"+        </> "*" <.> "yaml"   ]  awsScopeSpecStackName :: AwsScope -> FilePath -> Maybe StackName@@ -50,13 +46,13 @@   -- pretty fast and loose with the "parsing" step   pure     $ path -- stacks/account/region/x/y.yaml-    & splitPath -- [stacks/, account/, region/, x/, y.yaml]-    & drop 3 -- [x, y.yaml]-    & joinPath -- x/y.yaml-    & dropExtension -- x/y-    & pack-    & T.replace "/" "-" -- x-y-    & StackName+      & splitPath -- [stacks/, account/, region/, x/, y.yaml]+      & drop 3 -- [x, y.yaml]+      & joinPath -- x/y.yaml+      & dropExtension -- x/y+      & pack+      & T.replace "/" "-" -- x-y+      & StackName  class HasAwsScope env where   awsScopeL :: Lens' env AwsScope
src/Stackctl/Action.hs view
@@ -128,10 +128,10 @@         Nothing -> do           logError             $ "Output not found"-            :# [ "stackName" .= stackName-               , "desiredOutput" .= outputName-               , "availableOutputs" .= map (^. output_outputKey) outputs-               ]+              :# [ "stackName" .= stackName+                 , "desiredOutput" .= outputName+                 , "availableOutputs" .= map (^. output_outputKey) outputs+                 ]           throwIO NoSuchOutput         Just name -> invoke name     InvokeLambdaByName name -> invoke name
src/Stackctl/CLI.hs view
@@ -7,6 +7,7 @@  import Stackctl.Prelude +import Blammo.Logging.LogSettings import qualified Blammo.Logging.LogSettings.Env as LoggingEnv import Control.Monad.AWS as AWS import Control.Monad.AWS.ViaReader as AWS@@ -60,7 +61,7 @@   autoSSOOptionL = optionsL . autoSSOOptionL  newtype AppT app m a = AppT-  { unAppT :: ReaderT app (LoggingT (ResourceT m)) a+  { unAppT :: ReaderT app (ResourceT m) a   }   deriving newtype     ( Functor@@ -70,12 +71,13 @@     , MonadUnliftIO     , MonadResource     , MonadReader app-    , MonadLogger     , MonadThrow     , MonadCatch     , MonadMask     )   deriving (MonadAWS) via (ReaderAWS (AppT app m))+  deriving (MonadLogger) via (WithLogger app (ResourceT m))+  deriving (MonadLoggerIO) via (WithLogger app (ResourceT m))  runAppT   :: ( MonadMask m@@ -94,36 +96,33 @@       . setLogSettingsConcurrency (Just 1)       $ defaultLogSettings -  logger <--    newLogger-      $ adjustLogSettings-        (options ^. colorOptionL)-        (options ^. verboseOptionL)-        envLogSettings+  let logSettings =+        adjustLogSettings+          (options ^. colorOptionL)+          (options ^. verboseOptionL)+          envLogSettings -  app <- runResourceT $ runLoggerLoggingT logger $ do-    aws <- runReaderT (handleAutoSSO options AWS.discover) logger+  withLogger logSettings $ \appLogger -> do+    appAwsEnv <- runWithLogger appLogger $ handleAutoSSO options AWS.discover+    appConfig <- runWithLogger appLogger loadConfigOrExit+    appAwsScope <- AWS.runEnvT fetchAwsScope appAwsEnv -    App logger-      <$> loadConfigOrExit-      <*> pure options-      <*> AWS.runEnvT fetchAwsScope aws-      <*> pure aws+    let+      AwsScope {..} = appAwsScope -  let-    AwsScope {..} = appAwsScope app+      context =+        [ "region" .= awsRegion+        , "accountId" .= awsAccountId+        , "accountName" .= awsAccountName+        ] -    context =-      [ "region" .= awsRegion-      , "accountId" .= awsAccountId-      , "accountName" .= awsAccountName-      ]+      appOptions = options+      app = App {..} -  runResourceT-    $ runLoggerLoggingT app-    $ flip runReaderT app-    $ withThreadContext context-    $ unAppT f+    runResourceT+      $ flip runReaderT app+      $ withThreadContext context+      $ unAppT f  adjustLogSettings   :: Maybe ColorOption -> Verbosity -> LogSettings -> LogSettings
src/Stackctl/Config.hs view
@@ -75,9 +75,10 @@  loadConfig :: MonadIO m => m (Either ConfigError Config) loadConfig =-  runExceptT $ getConfigFile >>= \case-    Nothing -> pure emptyConfig-    Just cf -> loadConfigFrom cf+  runExceptT+    $ getConfigFile >>= \case+      Nothing -> pure emptyConfig+      Just cf -> loadConfigFrom cf  loadConfigFrom :: (MonadIO m, MonadError ConfigError m) => FilePath -> m Config loadConfigFrom path = loadConfigFromBytes =<< liftIO (readFileBinary path)
src/Stackctl/Config/RequiredVersion.hs view
@@ -54,7 +54,7 @@     ws ->       Left         $ show (unpack $ T.unwords ws)-        <> " did not parse as optional operator and version string"+          <> " did not parse as optional operator and version string"    parseRequiredVersion :: Text -> Text -> Either String RequiredVersion   parseRequiredVersion op w = RequiredVersion <$> parseOp op <*> parseVersion w@@ -71,8 +71,8 @@     op ->       Left         $ "Invalid comparison operator ("-        <> unpack op-        <> "), may only be =, <, <=, >, >=, or =~"+          <> unpack op+          <> "), may only be =, <, <=, >, >=, or =~"    parseVersion :: Text -> Either String Version   parseVersion t =
src/Stackctl/FilterOption.hs view
@@ -44,8 +44,8 @@     Env.var (first Env.UnreadError . readFilterOption) name       $ Env.help       $ "Filter "-      <> items-      <> " by patterns"+        <> items+        <> " by patterns"  filterOption :: String -> Parser FilterOption filterOption items =
src/Stackctl/Options.hs view
@@ -55,11 +55,11 @@ envParser =   Env.prefixed "STACKCTL_"     $ Options-    <$> optional envDirectoryOption-    <*> optional (envFilterOption "specifications")-    <*> pure mempty -- use LOG_COLOR-    <*> pure mempty -- use LOG_LEVEL-    <*> optional envAutoSSOOption+      <$> optional envDirectoryOption+      <*> optional (envFilterOption "specifications")+      <*> pure mempty -- use LOG_COLOR+      <*> pure mempty -- use LOG_LEVEL+      <*> optional envAutoSSOOption  -- brittany-disable-next-binding 
src/Stackctl/Prelude.hs view
@@ -20,6 +20,8 @@   )  import Blammo.Logging as X+import Blammo.Logging.Setup as X+import Blammo.Logging.ThreadContext as X import Control.Error.Util as X (hush, note) import Data.Aeson as X (ToJSON (..), object) import Data.Text as X (pack, unpack)
src/Stackctl/Spec/Capture.hs view
@@ -132,9 +132,8 @@   case results of     [] -> do       logError-        $ "No Active Stacks match "-        <> pack (decompile scoStackName)-        :# []+        $ "No Active Stacks match " <> pack (decompile scoStackName)+          :# []       exitFailure     [stackName] -> do       stack <- awsCloudFormationDescribeStack stackName
src/Stackctl/Spec/Cat.hs view
@@ -138,11 +138,11 @@     kvs <- f <$> mA     pure       $ [cyan label <> ":"]-      <> map-        ( \(k, mV) ->-            "  " <> cyan k <> ":" <> maybe "" (\v -> " " <> green v) mV-        )-        kvs+        <> map+          ( \(k, mV) ->+              "  " <> cyan k <> ":" <> maybe "" (\v -> " " <> green v) mV+          )+          kvs    ppList :: Text -> (a -> [Text]) -> Maybe a -> [Text]   ppList label f = maybe [] (((cyan label <> ":") :) . f)@@ -186,8 +186,8 @@   displayObjectProperty =     displayPropertyWith @(HashMap Text Value)       $ map (("  - " <>) . green)-      . sort-      . HashMap.keys+        . sort+        . HashMap.keys    displayPropertyWith     :: (FromJSON a, ToJSON a) => (a -> [Text]) -> Text -> [Text]
src/Stackctl/Spec/Changes/Format.hs view
@@ -76,10 +76,11 @@   (Nothing, _) -> "No changes for " <> name   (_, Nothing) -> "Metadata only changes (e.g. Tags or Outputs)"   (_, Just rcs) ->-    ("\n" <>) $ (<> "\n") $ mconcat $ ("Changes for " <> cyan name <> ":")-      : map-        (("\n  " <>) . formatResourceChange)-        (NE.toList rcs)+    ("\n" <>)+      $ (<> "\n")+      $ mconcat+      $ ("Changes for " <> cyan name <> ":")+        : map (("\n  " <>) . formatResourceChange) (NE.toList rcs)  where   rChanges = do     cs <- mChangeSet@@ -149,20 +150,20 @@       , "\n| Action | Logical Id | Physical Id | Type | Replacement | Scope | Details |"       , "\n| ---    | ---        | ---         | ---  | ---         | ---   | ---     |"       ]-    <> map commentTableRow (NE.toList rcs)-    <> case omitFull of-      OmitFull -> []-      IncludeFull ->-        [ "\n"-        , "\n<details>"-        , "\n<summary>Full changes</summary>"-        , "\n"-        , "\n```json"-        , "\n" <> changeSetJSON cs-        , "\n```"-        , "\n"-        , "\n</details>"-        ]+      <> map commentTableRow (NE.toList rcs)+      <> case omitFull of+        OmitFull -> []+        IncludeFull ->+          [ "\n"+          , "\n<details>"+          , "\n<summary>Full changes</summary>"+          , "\n"+          , "\n```json"+          , "\n" <> changeSetJSON cs+          , "\n```"+          , "\n"+          , "\n</details>"+          ]  commentTableRow :: ResourceChange -> Text commentTableRow ResourceChange' {..} =@@ -194,10 +195,10 @@    pure     $ toText c-    <> maybe "" ((" in " <>) . toText) attr-    <> maybe "" (\x -> " (" <> magenta (toText x) <> ")") n-    <> maybe "" ((", recreation " <>) . formatRR) rr-    <> maybe "" ((", caused by " <>) . toText) causingEntity+      <> maybe "" ((" in " <>) . toText) attr+      <> maybe "" (\x -> " (" <> magenta (toText x) <> ")") n+      <> maybe "" ((", recreation " <>) . formatRR) rr+      <> maybe "" ((", caused by " <>) . toText) causingEntity  where   formatRR = \case     x@RequiresRecreation_Always -> red (toText x)
src/Stackctl/Spec/Deploy.hs view
@@ -266,8 +266,7 @@   timestamp <-     liftIO       $ formatTime defaultTimeLocale "%F %T %Z"-      <$> utcToLocalZonedTime-        (e ^. stackEvent_timestamp)+        <$> utcToLocalZonedTime (e ^. stackEvent_timestamp)    pure     $ mconcat@@ -276,8 +275,7 @@       , maybe "" colorStatus $ e ^. stackEvent_resourceStatus       , maybe "" (magenta . (" " <>)) $ e ^. stackEvent_logicalResourceId       , maybe "" ((\x -> " (" <> x <> ")") . T.strip)-          $ e-          ^. stackEvent_resourceStatusReason+          $ e ^. stackEvent_resourceStatusReason       ]  where   colorStatus = \case
src/Stackctl/Spec/Discover.hs view
@@ -105,9 +105,9 @@        logError         $ "Multiple specifications produced the same Stack name"-        :# [ "name" .= stackSpecPathStackName (NE.head specPaths)-           , "paths" .= collidingPaths-           ]+          :# [ "name" .= stackSpecPathStackName (NE.head specPaths)+             , "paths" .= collidingPaths+             ]      exitFailure @@ -130,18 +130,17 @@     for_ depends $ \depend -> do       let (nearest, _distance) =             NE.minimumBy1 (comparing snd)-              $ (id &&& getDistance depend)-              <$> known+              $ (id &&& getDistance depend) <$> known        logWarn         $ "Stack lists dependency that does not exist"-        :# [ "dependency"-              .= ( unStackName (stackSpecStackName spec)-                    <> " -> "-                    <> unStackName depend-                 )-           , "hint" .= ("Did you mean " <> unStackName nearest <> "?")-           ]+          :# [ "dependency"+                 .= ( unStackName (stackSpecStackName spec)+                        <> " -> "+                        <> unStackName depend+                    )+             , "hint" .= ("Did you mean " <> unStackName nearest <> "?")+             ]    getDistance = levenshtein `on` unStackName 
src/Stackctl/Spec/List.hs view
@@ -79,7 +79,7 @@   when loLegend     $ pushLoggerLn     $ "\nLegend:\n  "-    <> T.intercalate ", " (map legendItem [minBound .. maxBound])+      <> T.intercalate ", " (map legendItem [minBound .. maxBound])  data Indicator   = Deployed
src/Stackctl/StackSpec.hs view
@@ -81,8 +81,7 @@ stackSpecTemplate spec =   StackTemplate     $ FilePath.normalise-    $ ssSpecRoot spec-    </> stackSpecTemplateFile spec+    $ ssSpecRoot spec </> stackSpecTemplateFile spec  stackSpecParameters :: StackSpec -> [Parameter] stackSpecParameters =@@ -197,8 +196,7 @@     (stackSpecStackName spec)     (stackSpecStackDescription spec)     (stackSpecTemplate spec)-    ( nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec-    )+    (nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec)     (stackSpecCapabilities spec)     (nubOrdOn (^. tag_key) $ tags <> stackSpecTags spec) 
src/Stackctl/StackSpecPath.hs view
@@ -83,16 +83,16 @@       unless (pathAccountId == awsAccountId)         $ Left         $ "Unexpected account: "-        <> unpack (unAccountId pathAccountId)-        <> " != "-        <> unpack (unAccountId awsAccountId)+          <> unpack (unAccountId pathAccountId)+          <> " != "+          <> unpack (unAccountId awsAccountId)        unless (unpack (fromRegion awsRegion) == pathRegion)         $ Left         $ "Unexpected region: "-        <> pathRegion-        <> " != "-        <> unpack (fromRegion awsRegion)+          <> pathRegion+          <> " != "+          <> unpack (fromRegion awsRegion)        stackName <-         maybe (Left "Must end in .yaml") (Right . StackName)@@ -117,6 +117,6 @@   _ ->     Left       $ "Path matches neither {account-id}.{account-name}, nor {account-name}.{account-id}: "-      <> path+        <> path  where   isAccountId x = T.length x == 12 && T.all isDigit x
src/Stackctl/StackSpecYaml.hs view
@@ -85,8 +85,7 @@       $ KeyMap.toList       $ KeyMap.fromListWith (<>)       $ map (pyKey &&& pyValue)-      $ bs -- flipped to make sure Last-wins-      <> as+      $ bs <> as -- flipped to make sure Last-wins  instance FromJSON ParametersYaml where   parseJSON = \case@@ -108,7 +107,7 @@   toJSON = object . parametersYamlPairs   toEncoding = pairs . mconcat . parametersYamlPairs -parametersYamlPairs :: KeyValue kv => ParametersYaml -> [kv]+parametersYamlPairs :: KeyValue e kv => ParametersYaml -> [kv] parametersYamlPairs = map parameterYamlPair . unParametersYaml  parametersYaml :: [ParameterYaml] -> ParametersYaml@@ -125,7 +124,7 @@     (mkParameterYaml <$> o .: "Name" <*> o .:? "Value")       <|> (mkParameterYaml <$> o .: "ParameterKey" <*> o .:? "ParameterValue") -parameterYamlPair :: KeyValue kv => ParameterYaml -> kv+parameterYamlPair :: KeyValue e kv => ParameterYaml -> kv parameterYamlPair ParameterYaml {..} = pyKey .= pyValue  parameterYaml :: Parameter -> Maybe ParameterYaml@@ -215,8 +214,7 @@       $ HashMap.toList       $ HashMap.fromList       $ map (toPair . unTagYaml)-      $ as-      <> bs+      $ as <> bs    where     toPair :: Tag -> (Text, Text)     toPair = (^. tag_key) &&& (^. tag_value)@@ -237,7 +235,7 @@   toJSON = object . tagsYamlPairs   toEncoding = pairs . mconcat . tagsYamlPairs -tagsYamlPairs :: KeyValue kv => TagsYaml -> [kv]+tagsYamlPairs :: KeyValue e kv => TagsYaml -> [kv] tagsYamlPairs = map tagYamlPair . unTagsYaml  tagsYaml :: [TagYaml] -> TagsYaml@@ -253,5 +251,5 @@     t <- newTag <$> o .: "Key" <*> o .: "Value"     pure $ TagYaml t -tagYamlPair :: KeyValue kv => TagYaml -> kv+tagYamlPair :: KeyValue e kv => TagYaml -> kv tagYamlPair (TagYaml t) = Key.fromText (t ^. tag_key) .= (t ^. tag_value)
src/Stackctl/VerboseOption.hs view
@@ -7,6 +7,7 @@  import Stackctl.Prelude +import Blammo.Logging.LogSettings import Blammo.Logging.LogSettings.LogLevels import Options.Applicative 
src/Stackctl/Version.hs view
@@ -3,10 +3,10 @@   ) where  import Stackctl.Prelude+import Prelude (putStrLn)  import Data.Version import qualified Paths_stackctl as Pkg-import Prelude (putStrLn)  logVersion :: MonadIO m => m () logVersion = liftIO $ putStrLn $ ("Stackctl v" <>) $ showVersion Pkg.version
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version:   1.18 name:            stackctl-version:         1.7.3.0+version:         1.7.3.2 license:         MIT license-file:    LICENSE copyright:       2022 Renaissance Learning Inc@@ -79,17 +79,17 @@         -fignore-optim-changes -fwrite-ide-info -Weverything         -Wno-all-missed-specialisations -Wno-missed-specialisations         -Wno-missing-import-lists -Wno-missing-kind-signatures-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode-        -Wno-prepositive-qualified-module -Wno-unsafe-        -optP-Wno-nonportable-include-path+        -Wno-missing-local-signatures -Wno-missing-role-annotations+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+        -Wno-unsafe -optP-Wno-nonportable-include-path      build-depends:         Blammo >=1.1.2.3,         Glob >=0.10.2,-        QuickCheck >=2.14.2,-        aeson >=2.0.3.0,+        QuickCheck >=2.14.3,+        aeson >=2.2.3.0,         aeson-casing >=0.2.0.0,-        aeson-pretty >=0.8.9,+        aeson-pretty >=0.8.10,         amazonka >=2.0,         amazonka-cloudformation >=2.0,         amazonka-core >=2.0,@@ -99,33 +99,33 @@         amazonka-sso >=2.0,         amazonka-sts >=2.0,         base >=4 && <5,-        bytestring >=0.11.3.1,+        bytestring >=0.12.1.0,         cfn-flip >=0.1.0.3,-        conduit >=1.3.4.3,-        containers >=0.6.5.1,-        envparse >=0.5.0,+        conduit >=1.3.6,+        containers >=0.6.8,+        envparse >=0.6.0,         errors >=2.3.0,-        exceptions >=0.10.4,-        extra >=1.7.12,-        filepath >=1.4.2.2,-        lens >=5.1.1,-        lens-aeson >=1.2.2,-        monad-logger >=0.3.37,-        mtl >=2.2.2,-        optparse-applicative >=0.17.0.0,-        resourcet >=1.2.6,+        exceptions >=0.10.7,+        extra >=1.7.16,+        filepath >=1.4.301.0,+        lens >=5.3.3,+        lens-aeson >=1.2.3,+        monad-logger >=0.3.41,+        mtl >=2.3.1,+        optparse-applicative >=0.18.1.0,+        resourcet >=1.3.0,         rio >=0.1.22.0,         semigroups >=0.20,-        text >=1.2.5.0,-        text-metrics >=0.3.2,-        time >=1.11.1.1,-        transformers >=0.5.6.2,-        typed-process >=0.2.10.1,-        unix >=2.7.2.2,+        text >=2.1.1,+        text-metrics >=0.3.3,+        time >=1.12.2,+        transformers >=0.6.1.0,+        typed-process >=0.2.12.0,+        unix >=2.8.6.0,         unliftio >=0.2.25.0,-        unordered-containers >=0.2.19.1,-        uuid >=1.3.15,-        yaml >=0.11.8.0+        unordered-containers >=0.2.20,+        uuid >=1.3.16,+        yaml >=0.11.11.2  executable stackctl     main-is:            Main.hs@@ -145,9 +145,9 @@         -fignore-optim-changes -fwrite-ide-info -Weverything         -Wno-all-missed-specialisations -Wno-missed-specialisations         -Wno-missing-import-lists -Wno-missing-kind-signatures-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode-        -Wno-prepositive-qualified-module -Wno-unsafe-        -optP-Wno-nonportable-include-path -threaded -rtsopts+        -Wno-missing-local-signatures -Wno-missing-role-annotations+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+        -Wno-unsafe -optP-Wno-nonportable-include-path -threaded -rtsopts         -with-rtsopts=-N      build-depends:@@ -190,31 +190,31 @@         -fignore-optim-changes -fwrite-ide-info -Weverything         -Wno-all-missed-specialisations -Wno-missed-specialisations         -Wno-missing-import-lists -Wno-missing-kind-signatures-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode-        -Wno-prepositive-qualified-module -Wno-unsafe-        -optP-Wno-nonportable-include-path+        -Wno-missing-local-signatures -Wno-missing-role-annotations+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+        -Wno-unsafe -optP-Wno-nonportable-include-path      build-depends:-        Blammo >=1.1.2.3,+        Blammo >=2.1.1.0,         Glob >=0.10.2,-        QuickCheck >=2.14.2,-        aeson >=2.0.3.0,+        QuickCheck >=2.14.3,+        aeson >=2.2.3.0,         amazonka >=2.0,         amazonka-cloudformation >=2.0,         amazonka-ec2 >=2.0,         amazonka-lambda >=2.0,         amazonka-mtl >=0.1.1.0,         base >=4 && <5,-        bytestring >=0.11.3.1,-        filepath >=1.4.2.2,-        hspec >=2.9.7,+        bytestring >=0.12.1.0,+        filepath >=1.4.301.0,+        hspec >=2.11.10,         hspec-expectations-lifted >=0.10.0,         hspec-golden >=0.2.1.0,-        http-types >=0.12.3,-        lens >=5.1.1,-        mtl >=2.2.2,+        http-types >=0.12.4,+        lens >=5.3.3,+        mtl >=2.3.1,         stackctl,-        text >=1.2.5.0,-        time >=1.11.1.1,+        text >=2.1.1,+        time >=1.12.2,         unliftio >=0.2.25.0,-        yaml >=0.11.8.0+        yaml >=0.11.11.2
test/Stackctl/AWS/EC2Spec.hs view
@@ -23,7 +23,7 @@             $ Right             $ newDescribeAvailabilityZonesResponse 200             & describeAvailabilityZonesResponse_availabilityZones-            ?~ zones+              ?~ zones        withMatcher matcher awsEc2DescribeFirstAvailabilityZoneRegionName         `shouldReturn` "us-east-1"
test/Stackctl/AWS/LambdaSpec.hs view
@@ -33,18 +33,16 @@           [ SendMatcher (isInvocation "lambda-1")               $ Right               $ newInvokeResponse 200-              & invokeResponse_payload-              ?~ "<response>"+              & invokeResponse_payload ?~ "<response>"           , SendMatcher (isInvocation "lambda-2")               $ Right               $ newInvokeResponse 200-              & invokeResponse_payload-              ?~ BSL.toStrict (encode lambdaError)+              & invokeResponse_payload ?~ BSL.toStrict (encode lambdaError)           , SendMatcher (isInvocation "lambda-3")               $ Right               $ newInvokeResponse 500               & (invokeResponse_payload ?~ "<response>")-              . (invokeResponse_functionError ?~ "<error>")+                . (invokeResponse_functionError ?~ "<error>")           ]        withMatchers matchers $ do
test/Stackctl/ConfigSpec.hs view
@@ -32,8 +32,7 @@       case result of         Left err -> do           expectationFailure-            $ "Expected to load a Config, got error: "-            <> show err+            $ "Expected to load a Config, got error: " <> show err         Right config -> do           configParameters config             `shouldBe` Just (toParametersYaml [("Some", Just "Parameter")])@@ -56,15 +55,15 @@         Right config =           loadConfigFromBytes             $ "defaults:"-            <> "\n  tags:"-            <> "\n    From: Defaults"-            <> "\n    Keep: \"You?\""+              <> "\n  tags:"+              <> "\n    From: Defaults"+              <> "\n    Keep: \"You?\""          Just tags = ssyTags (applyConfig config specYaml)        tags         `shouldBe` toTagsYaml-          [("From", "Defaults"), ("Hi", "There"), ("Keep", "Me")]+          [("Hi", "There"), ("From", "Defaults"), ("Keep", "Me")]  loadConfigFromLines :: MonadError ConfigError m => [ByteString] -> m Config loadConfigFromLines = loadConfigFromBytes . mconcat . map (<> "\n")
test/Stackctl/Test/App.hs view
@@ -15,6 +15,7 @@  import Stackctl.Prelude +import Blammo.Logging.LogSettings (defaultLogSettings) import Blammo.Logging.Logger (newTestLogger) import Control.Lens ((?~)) import Control.Monad.AWS@@ -92,5 +93,4 @@   "stacks"     </> "0123456789.test"     </> "us-east-1"-    </> unpack base-    <.> "yaml"+    </> unpack base <.> "yaml"