packages feed

stackctl 1.1.0.5 → 1.1.1.0

raw patch · 9 files changed

+101/−25 lines, 9 files

Files

CHANGELOG.md view
@@ -1,4 +1,9 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.0.5...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.1.0...main)++## [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) 
src/Stackctl/AWS/CloudFormation.hs view
@@ -94,6 +94,7 @@ import qualified Data.UUID as UUID import qualified Data.UUID.V4 as UUID import Stackctl.AWS.Core+import Stackctl.Sort import UnliftIO.Exception.Lens (handling_, trying)  newtype StackId = StackId@@ -375,7 +376,7 @@   awsSimple "DescribeChangeSet" req $ \resp ->     ChangeSet       <$> (resp ^. describeChangeSetResponse_creationTime)-      <*> pure (resp ^. describeChangeSetResponse_changes)+      <*> pure (fmap sortChanges $ resp ^. describeChangeSetResponse_changes)       <*> (ChangeSetName <$> resp ^. describeChangeSetResponse_changeSetName)       <*> (resp ^. describeChangeSetResponse_executionStatus)       <*> (ChangeSetId <$> resp ^. describeChangeSetResponse_changeSetId)@@ -387,6 +388,23 @@       <*> pure (resp ^. describeChangeSetResponse_status)       <*> pure (resp ^. describeChangeSetResponse_statusReason)       <*> pure resp++sortChanges :: [Change] -> [Change]+sortChanges = sortByDependencies changeName changeCausedBy++changeName :: Change -> Text+changeName c = fromMaybe "" $ do+  ResourceChange' {..} <- resourceChange c+  logicalResourceId++changeCausedBy :: Change -> [Text]+changeCausedBy c = fromMaybe [] $ do+  ResourceChange' {..} <- resourceChange c+  mapMaybe detailCausingLogicalResourceId <$> details++detailCausingLogicalResourceId :: ResourceChangeDetail -> Maybe Text+detailCausingLogicalResourceId ResourceChangeDetail' {..} =+  T.takeWhile (/= '.') <$> causingEntity  awsCloudFormationExecuteChangeSet   :: (MonadResource m, MonadReader env m, HasAwsEnv env) => ChangeSetId -> m ()
src/Stackctl/DirectoryOption.hs view
@@ -21,4 +21,5 @@   , help "Operate on specifications in PATH"   , value "."   , showDefault+  , action "directory"   ]
+ src/Stackctl/ParameterOption.hs view
@@ -0,0 +1,25 @@+module Stackctl.ParameterOption+  ( parameterOption+  ) where++import Stackctl.Prelude++import qualified Data.Text as T+import Options.Applicative+import Stackctl.AWS.CloudFormation (Parameter, makeParameter)++parameterOption :: Parser Parameter+parameterOption = option (eitherReader readParameter) $ mconcat+  [ short 'p'+  , long "parameter"+  , metavar "KEY=[VALUE]"+  , help "Override the given Parameter for this operation"+  ]++readParameter :: String -> Either String Parameter+readParameter s = case T.breakOn "=" t of+  (_, v) | T.null v -> Left $ "No '=' found (" <> s <> ")"+  (k, _) | T.null k -> Left $ "Empty key (" <> s <> ")"+  (k, "=") -> Right $ makeParameter k $ Just ""+  (k, v) -> Right $ makeParameter k $ Just $ T.drop 1 v+  where t = pack s
+ src/Stackctl/Sort.hs view
@@ -0,0 +1,26 @@+module Stackctl.Sort+  ( sortByDependencies+  ) where++import Stackctl.Prelude++import Data.Graph (graphFromEdges, topSort)++sortByDependencies+  :: Ord k+  => (a -> k)+  -- ^ How to identify a given item+  -> (a -> [k])+  -- ^ How to get the given item's dependencies+  -> [a]+  -> [a]+sortByDependencies toName toDepends specs =+  map nodeFromVertex $ reverse $ topSort graph+ where+  (graph, tripleFromVertex, _) = graphFromEdges $ map tripleFromNode specs++  nodeFromVertex = nodeFromTriple . tripleFromVertex++  tripleFromNode n = (n, toName n, toDepends n)++  nodeFromTriple (n, _, _) = n
src/Stackctl/Spec/Changes.hs view
@@ -8,11 +8,12 @@  import qualified Data.Text.IO as T import Options.Applicative-import Stackctl.AWS+import Stackctl.AWS hiding (action) import Stackctl.AWS.Scope import Stackctl.Colors import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.FilterOption (HasFilterOption)+import Stackctl.ParameterOption import Stackctl.Spec.Changes.Format import Stackctl.Spec.Discover import Stackctl.StackSpec@@ -20,6 +21,7 @@  data ChangesOptions = ChangesOptions   { scoFormat :: Format+  , scoParameters :: [Parameter]   , scoOutput :: FilePath   } @@ -28,9 +30,11 @@ runChangesOptions :: Parser ChangesOptions runChangesOptions = ChangesOptions   <$> formatOption+  <*> many parameterOption   <*> argument str     (  metavar "PATH"     <> help "Where to write the changes summary"+    <> action "file"     )  runChanges@@ -55,7 +59,7 @@    for_ specs $ \spec -> do     withThreadContext ["stackName" .= stackSpecStackName spec] $ do-      emChangeSet <- createChangeSet spec+      emChangeSet <- createChangeSet spec scoParameters        case emChangeSet of         Left err -> do
src/Stackctl/Spec/Deploy.hs view
@@ -11,12 +11,13 @@ import qualified Data.Text as T import Data.Time (defaultTimeLocale, formatTime, utcToLocalZonedTime) import Options.Applicative-import Stackctl.AWS+import Stackctl.AWS hiding (action) import Stackctl.AWS.Scope import Stackctl.Action import Stackctl.Colors import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.FilterOption (HasFilterOption)+import Stackctl.ParameterOption import Stackctl.Prompt import Stackctl.Spec.Changes.Format import Stackctl.Spec.Discover@@ -25,7 +26,8 @@ import UnliftIO.Directory (createDirectoryIfMissing)  data DeployOptions = DeployOptions-  { sdoSaveChangeSets :: Maybe FilePath+  { sdoParameters :: [Parameter]+  , sdoSaveChangeSets :: Maybe FilePath   , sdoDeployConfirmation :: DeployConfirmation   , sdoClean :: Bool   }@@ -34,10 +36,12 @@  runDeployOptions :: Parser DeployOptions runDeployOptions = DeployOptions-  <$> optional (strOption+  <$> many parameterOption+  <*> optional (strOption     (  long "save-change-sets"     <> metavar "DIRECTORY"     <> help "Save executed changesets to DIRECTORY"+    <> action "directory"     ))   <*> flag DeployWithConfirmation DeployWithoutConfirmation     (  long "no-confirm"@@ -70,7 +74,7 @@     withThreadContext ["stackName" .= stackSpecStackName spec] $ do       handleRollbackComplete sdoDeployConfirmation $ stackSpecStackName spec -      emChangeSet <- createChangeSet spec+      emChangeSet <- createChangeSet spec sdoParameters        case emChangeSet of         Left err -> do
src/Stackctl/StackSpec.hs view
@@ -18,10 +18,11 @@  import qualified CfnFlip import Data.Aeson-import Data.Graph (graphFromEdges, topSort)+import Data.List.Extra (nubOrdOn) import qualified Data.Yaml as Yaml import Stackctl.AWS import Stackctl.Action+import Stackctl.Sort import Stackctl.StackSpecPath import Stackctl.StackSpecYaml import UnliftIO.Directory (createDirectoryIfMissing)@@ -99,10 +100,6 @@     throwString $ path <> " is invalid: " <> Yaml.prettyPrintParseException e  -- | Create a Change Set between a Stack Specification and deployed state------ We use @aws cloudformation deploy --no-execute-changeset@ because it handles--- the create-or-update logic for us.--- createChangeSet   :: ( MonadUnliftIO m      , MonadResource m@@ -111,21 +108,15 @@      , HasAwsEnv env      )   => StackSpec+  -> [Parameter]   -> m (Either Text (Maybe ChangeSet))-createChangeSet spec = awsCloudFormationCreateChangeSet+createChangeSet spec parameters = awsCloudFormationCreateChangeSet   (stackSpecStackName spec)   (stackSpecTemplateFile spec)-  (stackSpecParameters spec)+  (nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec+  )   (stackSpecCapabilities spec)   (stackSpecTags spec)  sortStackSpecs :: [StackSpec] -> [StackSpec]-sortStackSpecs specs = map nodeFromVertex $ reverse $ topSort graph- where-  (graph, tripleFromVertex, _) = graphFromEdges $ map tripleFromNode specs--  nodeFromVertex = nodeFromTriple . tripleFromVertex--  tripleFromNode n = (n, stackSpecStackName n, stackSpecDepends n)--  nodeFromTriple (n, _, _) = n+sortStackSpecs = sortByDependencies stackSpecStackName stackSpecDepends
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version:   1.18 name:            stackctl-version:         1.1.0.5+version:         1.1.1.0 license:         MIT license-file:    LICENSE copyright:       2022 Renaissance Learning Inc@@ -36,8 +36,10 @@         Stackctl.DirectoryOption         Stackctl.FilterOption         Stackctl.Options+        Stackctl.ParameterOption         Stackctl.Prelude         Stackctl.Prompt+        Stackctl.Sort         Stackctl.Spec.Capture         Stackctl.Spec.Cat         Stackctl.Spec.Changes