packages feed

stackctl 1.1.1.1 → 1.1.2.0

raw patch · 10 files changed

+110/−48 lines, 10 filesdep −fast-loggerdep ~Blammo

Dependencies removed: fast-logger

Dependency ranges changed: Blammo

Files

CHANGELOG.md view
@@ -1,4 +1,11 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.1.1...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.2.0...main)++## [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) 
src/Stackctl/CLI.hs view
@@ -75,7 +75,11 @@   -> AppT (App options) m a   -> m a runAppT options f = do-  envLogSettings <- liftIO LoggingEnv.parse+  envLogSettings <-+    liftIO+    . LoggingEnv.parseWith+    . setLogSettingsConcurrency (Just 1)+    $ defaultLogSettings    logger <- newLogger $ adjustLogSettings     (options ^. colorOptionL)
src/Stackctl/Colors.hs view
@@ -3,12 +3,14 @@   ( Colors(..)   , HasColorOption   , getColorsStdout+  , getColorsLogger   , noColors   ) where  import Stackctl.Prelude  import Blammo.Logging.Colors+import Blammo.Logging.Logger import Stackctl.ColorOption (HasColorOption(..), colorHandle)  -- | Return 'Colors' based on options and 'stdout'@@ -23,6 +25,10 @@   colorOption <- view colorOptionL   c <- colorHandle h colorOption   pure $ getColors c++-- | Return 'Colors' consistent with the ambient 'Logger'+getColorsLogger :: (MonadReader env m, HasLogger env) => m Colors+getColorsLogger = view $ loggerL . to (getColors . getLoggerShouldColor)  noColors :: Colors noColors = getColors False
src/Stackctl/Commands.hs view
@@ -10,7 +10,7 @@  import Stackctl.AWS import Stackctl.AWS.Scope-import Stackctl.ColorOption+import Stackctl.Colors import Stackctl.DirectoryOption import Stackctl.FilterOption import Stackctl.Spec.Capture@@ -46,11 +46,11 @@   }  changes-  :: ( HasAwsScope env+  :: ( HasLogger env+     , HasAwsScope env      , HasAwsEnv env      , HasDirectoryOption env      , HasFilterOption env-     , HasColorOption env      )   => Subcommand ChangesOptions env changes = Subcommand@@ -66,7 +66,6 @@      , HasAwsEnv env      , HasDirectoryOption env      , HasFilterOption env-     , HasColorOption env      )   => Subcommand DeployOptions env deploy = Subcommand
src/Stackctl/Spec/Capture.hs view
@@ -11,12 +11,14 @@ import Stackctl.AWS.Scope import Stackctl.DirectoryOption (HasDirectoryOption(..)) import Stackctl.Spec.Generate+import Stackctl.StackSpec  data CaptureOptions = CaptureOptions   { scoAccountName :: Maybe Text   , scoTemplatePath :: Maybe FilePath   , scoStackPath :: Maybe FilePath   , scoDepends :: Maybe [StackName]+  , scoTemplateFormat :: TemplateFormat   , scoStackName :: StackName   } @@ -47,6 +49,10 @@       <> metavar "STACK"       <> help "Add a dependency on STACK"       )))+    <*> flag TemplateFormatYaml TemplateFormatJson+      (  long "no-flip"+      <> help "Don't flip JSON templates to Yaml"+      )     <*> (StackName <$> argument str       (  metavar "STACK"       <> help "Name of deployed Stack to capture"@@ -76,6 +82,7 @@   void $ local (awsScopeL %~ setScopeName) $ generate Generate     { gOutputDirectory = dir     , gTemplatePath = scoTemplatePath+    , gTemplateFormat = scoTemplateFormat     , gStackPath = scoStackPath     , gStackName = scoStackName     , gDepends = scoDepends@@ -83,5 +90,5 @@     , gParameters = parameters stack     , gCapabilities = capabilities stack     , gTags = tags stack-    , gTemplate = template+    , gTemplateBody = templateBodyFromValue template     }
src/Stackctl/Spec/Changes.hs view
@@ -6,6 +6,7 @@  import Stackctl.Prelude +import Blammo.Logging.Logger (pushLoggerLn) import qualified Data.Text.IO as T import Options.Applicative import Stackctl.AWS hiding (action)@@ -22,7 +23,7 @@ data ChangesOptions = ChangesOptions   { scoFormat :: Format   , scoParameters :: [Parameter]-  , scoOutput :: FilePath+  , scoOutput :: Maybe FilePath   }  -- brittany-disable-next-binding@@ -31,11 +32,11 @@ runChangesOptions = ChangesOptions   <$> formatOption   <*> many parameterOption-  <*> argument str+  <*> optional (argument str     (  metavar "PATH"-    <> help "Where to write the changes summary"+    <> help "Write changes summary to PATH"     <> action "file"-    )+    ))  runChanges   :: ( MonadMask m@@ -43,17 +44,17 @@      , MonadResource m      , MonadLogger m      , MonadReader env m+     , HasLogger env      , HasAwsScope env      , HasAwsEnv env      , HasDirectoryOption env      , HasFilterOption env-     , HasColorOption env      )   => ChangesOptions   -> m () runChanges ChangesOptions {..} = do   -- Clear file before starting, as we have to use append for each spec-  liftIO $ T.writeFile scoOutput ""+  liftIO $ traverse_ (`T.writeFile` "") scoOutput    specs <- discoverSpecs @@ -66,8 +67,14 @@           logError $ "Error creating ChangeSet" :# ["error" .= err]           exitFailure         Right mChangeSet -> do-          colors <- getColorsStdout+          colors <- case scoOutput of+            Nothing -> getColorsLogger+            Just{} -> pure noColors+           let             name = pack $ stackSpecPathFilePath $ stackSpecSpecPath spec             formatted = formatChangeSet colors name scoFormat mChangeSet-          liftIO $ T.appendFile scoOutput $ formatted <> "\n"++          case scoOutput of+            Nothing -> pushLoggerLn formatted+            Just p -> liftIO $ T.appendFile p $ formatted <> "\n"
src/Stackctl/Spec/Deploy.hs view
@@ -7,7 +7,7 @@  import Stackctl.Prelude -import Blammo.Logging.Logger (pushLogStrLn)+import Blammo.Logging.Logger (pushLoggerLn) import qualified Data.Text as T import Data.Time (defaultTimeLocale, formatTime, utcToLocalZonedTime) import Options.Applicative@@ -22,7 +22,6 @@ import Stackctl.Spec.Changes.Format import Stackctl.Spec.Discover import Stackctl.StackSpec-import System.Log.FastLogger (toLogStr) import UnliftIO.Directory (createDirectoryIfMissing)  data DeployOptions = DeployOptions@@ -63,7 +62,6 @@      , HasAwsEnv env      , HasDirectoryOption env      , HasFilterOption env-     , HasColorOption env      )   => DeployOptions   -> m ()@@ -137,15 +135,14 @@      , MonadReader env m      , HasLogger env      , HasAwsEnv env-     , HasColorOption env      )   => DeployConfirmation   -> ChangeSet   -> m () deployChangeSet confirmation changeSet = do-  colors <- getColorsStdout+  colors <- getColorsLogger -  pushLogger $ formatTTY colors (unStackName stackName) $ Just changeSet+  pushLoggerLn $ formatTTY colors (unStackName stackName) $ Just changeSet    case confirmation of     DeployWithConfirmation -> promptContinue@@ -184,15 +181,14 @@      , MonadReader env m      , HasLogger env      , HasAwsEnv env-     , HasColorOption env      )   => StackName   -> Maybe Text -- ^ StackEventId   -> m a tailStackEventsSince stackName mLastId = do-  colors <- getColorsStdout+  colors <- getColorsLogger   events <- awsCloudFormationDescribeStackEvents stackName mLastId-  traverse_ (pushLogger <=< formatStackEvent colors) $ reverse events+  traverse_ (pushLoggerLn <=< formatStackEvent colors) $ reverse events    -- Without this small delay before looping, our requests seem to hang   -- intermittently (without errors) and often we miss events.@@ -229,8 +225,3 @@  getLastEventId :: [StackEvent] -> Maybe Text getLastEventId = fmap (^. stackEvent_eventId) . listToMaybe--pushLogger :: (MonadIO m, MonadReader env m, HasLogger env) => Text -> m ()-pushLogger msg = do-  logger <- view loggerL-  pushLogStrLn logger $ toLogStr msg
src/Stackctl/Spec/Generate.hs view
@@ -1,11 +1,11 @@ module Stackctl.Spec.Generate   ( Generate(..)   , generate+  , TemplateFormat(..)   ) where  import Stackctl.Prelude -import Data.Aeson import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Action@@ -17,18 +17,24 @@ data Generate = Generate   { gOutputDirectory :: FilePath   , gTemplatePath :: Maybe FilePath-  -- ^ If not given will use @{stack-name}.yaml@+  -- ^ If not given, will use @{stack-name}.(yaml|json)@+  , gTemplateFormat :: TemplateFormat+  -- ^ Ignored if 'gTemplatePath' is given   , gStackPath :: Maybe FilePath-  -- ^ If not given will use @{stack-name}.yaml@+  -- ^ If not given, will use @{stack-name}.yaml@   , gStackName :: StackName   , gDepends :: Maybe [StackName]   , gActions :: Maybe [Action]   , gParameters :: Maybe [Parameter]   , gCapabilities :: Maybe [Capability]   , gTags :: Maybe [Tag]-  , gTemplate :: Value+  , gTemplateBody :: TemplateBody   } +data TemplateFormat+  = TemplateFormatYaml+  | TemplateFormatJson+ generate   :: ( MonadMask m      , MonadUnliftIO m@@ -40,13 +46,18 @@   -> m FilePath generate Generate {..} = do   let-    path = unpack (unStackName gStackName) <.> "yaml"-    stackPath = fromMaybe path gStackPath+    defaultStackPath = unpack (unStackName gStackName) <.> "yaml"+    defaultTemplatePath =+      unpack (unStackName gStackName) <.> case gTemplateFormat of+        TemplateFormatYaml -> "yaml"+        TemplateFormatJson -> "json" +    stackPath = fromMaybe defaultStackPath gStackPath+   specPath <- buildSpecPath gStackName stackPath    let-    templatePath = fromMaybe path gTemplatePath+    templatePath = fromMaybe defaultTemplatePath gTemplatePath     specYaml = StackSpecYaml       { ssyTemplate = templatePath       , ssyDepends = gDepends@@ -60,5 +71,5 @@    withThreadContext ["stackName" .= stackSpecStackName stackSpec] $ do     logInfo "Generating specification"-    writeStackSpec gOutputDirectory stackSpec gTemplate+    writeStackSpec gOutputDirectory stackSpec gTemplateBody     pure $ stackSpecPathFilePath specPath
src/Stackctl/StackSpec.hs view
@@ -8,6 +8,8 @@   , stackSpecCapabilities   , stackSpecTags   , buildStackSpec+  , TemplateBody+  , templateBodyFromValue   , writeStackSpec   , readStackSpec   , createChangeSet@@ -18,6 +20,7 @@  import qualified CfnFlip import Data.Aeson+import qualified Data.ByteString.Lazy as BSL import Data.List.Extra (nubOrdOn) import qualified Data.Yaml as Yaml import Stackctl.AWS@@ -25,6 +28,7 @@ import Stackctl.Sort import Stackctl.StackSpecPath import Stackctl.StackSpecYaml+import System.FilePath (takeExtension) import UnliftIO.Directory (createDirectoryIfMissing)  data StackSpec = StackSpec@@ -65,20 +69,47 @@ buildStackSpec :: FilePath -> StackSpecPath -> StackSpecYaml -> StackSpec buildStackSpec = StackSpec +data TemplateBody+  = TemplateText Text+  | TemplateJson Value++newtype UnexpectedTemplateJson = UnexpectedTemplateJson+  { _unexpectedTemplateJsonExtension :: String+  }+  deriving stock Show++instance Exception UnexpectedTemplateJson where+  displayException (UnexpectedTemplateJson ext) =+    "TemplateJson must be written to .yaml or .json, encountered "+      <> ext+      <> ". To write to an arbitrary path, use TemplateText."++templateBodyFromValue :: Value -> TemplateBody+templateBodyFromValue = \case+  String x -> TemplateText x+  v -> TemplateJson v++writeTemplateBody :: MonadUnliftIO m => FilePath -> TemplateBody -> m ()+writeTemplateBody path body = do+  createDirectoryIfMissing True dir++  case (body, ext) of+    (TemplateText t, _) -> writeFileUtf8 path t+    (TemplateJson v, ".yaml") -> CfnFlip.jsonToYamlFile path v+    (TemplateJson v, ".json") -> writeFileBinary path $ BSL.toStrict $ encode v+    (TemplateJson _, _) -> throwIO $ UnexpectedTemplateJson ext+ where+  dir = takeDirectory path+  ext = takeExtension path+ writeStackSpec   :: MonadUnliftIO m   => FilePath -- ^ Parent directory   -> StackSpec-  -> Value -- ^ Template body+  -> TemplateBody   -> m () writeStackSpec parent stackSpec@StackSpec {..} templateBody = do-  createDirectoryIfMissing True $ takeDirectory templatePath--  case templateBody of-    -- Already Yaml-    String x -> writeFileUtf8 templatePath x-    _ -> CfnFlip.jsonToYamlFile templatePath templateBody-+  writeTemplateBody templatePath templateBody   createDirectoryIfMissing True $ takeDirectory specPath   liftIO $ Yaml.encodeFile specPath ssSpecBody  where
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version:   1.18 name:            stackctl-version:         1.1.1.1+version:         1.1.2.0 license:         MIT license-file:    LICENSE copyright:       2022 Renaissance Learning Inc@@ -74,7 +74,7 @@         -Wno-unsafe -optP-Wno-nonportable-include-path      build-depends:-        Blammo >=1.1.1.0,+        Blammo >=1.1.1.1,         Glob,         aeson,         aeson-casing,@@ -93,7 +93,6 @@         errors,         exceptions,         extra,-        fast-logger,         filepath,         lens,         lens-aeson,