module Stackctl.Spec.List ( ListOptions (..) , parseListOptions , runList ) where import Stackctl.Prelude import Blammo.Logging.Logger (pushLoggerLn) import qualified Data.Text as T import Options.Applicative import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Colors import Stackctl.Config (HasConfig) import Stackctl.DirectoryOption (HasDirectoryOption (..)) import Stackctl.FilterOption (HasFilterOption) import Stackctl.Spec.Discover import Stackctl.StackSpec newtype ListOptions = ListOptions { loLegend :: Bool } parseListOptions :: Parser ListOptions parseListOptions = ListOptions <$> ( not <$> switch ( mconcat [ long "no-legend" , help "Don't print indicators legend at the end" ] ) ) runList :: ( MonadUnliftIO m , MonadMask m , MonadAWS m , MonadLogger m , MonadReader env m , HasAwsScope env , HasLogger env , HasConfig env , HasDirectoryOption env , HasFilterOption env ) => ListOptions -> m () runList ListOptions {..} = do colors@Colors {..} <- getColorsLogger forEachSpec_ $ \spec -> do let path = stackSpecFilePath spec name = stackSpecStackName spec mStackStatus <- fmap (^. stack_stackStatus) <$> awsCloudFormationDescribeStackMaybe name let indicator = maybe NotDeployed statusIndicator mStackStatus formatted :: Text formatted = " " <> indicatorIcon colors indicator <> " " <> cyan (unStackName name) <> " => " <> magenta (pack path) pushLoggerLn formatted let legendItem i = indicatorIcon colors i <> " " <> indicatorDescription i when loLegend $ pushLoggerLn $ "\nLegend:\n " <> T.intercalate ", " (map legendItem [minBound .. maxBound]) data Indicator = Deployed | DeployFailed | NotDeployed | Reviewing | Deploying | Unknown deriving stock (Bounded, Enum) indicatorIcon :: Colors -> Indicator -> Text indicatorIcon Colors {..} = \case Deployed -> green "✓" DeployFailed -> red "✗" NotDeployed -> yellow "_" Reviewing -> yellow "∇" Deploying -> cyan "⋅" Unknown -> magenta "?" indicatorDescription :: Indicator -> Text indicatorDescription = \case Deployed -> "deployed" DeployFailed -> "failed or rolled back" NotDeployed -> "doesn't exist" Reviewing -> "reviewing" Deploying -> "deploying" Unknown -> "unknown" statusIndicator :: StackStatus -> Indicator statusIndicator = \case StackStatus_REVIEW_IN_PROGRESS -> Reviewing StackStatus_ROLLBACK_COMPLETE -> DeployFailed x | statusSuffixed "_IN_PROGRESS" x -> Deploying x | statusSuffixed "_FAILED" x -> DeployFailed x | statusSuffixed "_ROLLBACK_COMPLETE" x -> DeployFailed x | statusSuffixed "_COMPLETE" x -> Deployed _ -> Unknown where statusSuffixed x = (x `T.isSuffixOf`) . fromStackStatus