github-actions 0.1.0.0 → 0.1.1.0
raw patch · 11 files changed
+477/−125 lines, 11 filesdep ~basedep ~bytestringdep ~hedgehogPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, bytestring, hedgehog, tasty-discover
API changes (from Hackage documentation)
+ Language.Github.Actions.Job.Needs: JobNeedsArray :: NonEmpty JobId -> JobNeeds
+ Language.Github.Actions.Job.Needs: JobNeedsString :: JobId -> JobNeeds
+ Language.Github.Actions.Job.Needs: data JobNeeds
+ Language.Github.Actions.Job.Needs: gen :: MonadGen m => m JobNeeds
+ Language.Github.Actions.Job.Needs: instance Data.Aeson.Types.FromJSON.FromJSON Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.Job.Needs: instance Data.Aeson.Types.ToJSON.ToJSON Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.Job.Needs: instance GHC.Classes.Eq Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.Job.Needs: instance GHC.Classes.Ord Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.Job.Needs: instance GHC.Generics.Generic Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.Job.Needs: instance GHC.Show.Show Language.Github.Actions.Job.Needs.JobNeeds
+ Language.Github.Actions.RunIf: RunIfBool :: Bool -> RunIf
+ Language.Github.Actions.RunIf: RunIfString :: Text -> RunIf
+ Language.Github.Actions.RunIf: data RunIf
+ Language.Github.Actions.RunIf: gen :: MonadGen m => m RunIf
+ Language.Github.Actions.RunIf: instance Data.Aeson.Types.FromJSON.FromJSON Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.RunIf: instance Data.Aeson.Types.ToJSON.ToJSON Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.RunIf: instance GHC.Classes.Eq Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.RunIf: instance GHC.Classes.Ord Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.RunIf: instance GHC.Generics.Generic Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.RunIf: instance GHC.Show.Show Language.Github.Actions.RunIf.RunIf
+ Language.Github.Actions.UnstructuredMap: UnstructuredMap :: Map Text UnstructuredValue -> UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: UnstructuredValueBool :: Bool -> UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: UnstructuredValueNumber :: Double -> UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: UnstructuredValueString :: Text -> UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: data UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: gen :: MonadGen m => m UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance Data.Aeson.Types.FromJSON.FromJSON Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance Data.Aeson.Types.FromJSON.FromJSON Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: instance Data.Aeson.Types.ToJSON.ToJSON Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance Data.Aeson.Types.ToJSON.ToJSON Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: instance GHC.Classes.Eq Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance GHC.Classes.Eq Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: instance GHC.Classes.Ord Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance GHC.Classes.Ord Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: instance GHC.Generics.Generic Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance GHC.Generics.Generic Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: instance GHC.Show.Show Language.Github.Actions.UnstructuredMap.UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: instance GHC.Show.Show Language.Github.Actions.UnstructuredMap.UnstructuredValue
+ Language.Github.Actions.UnstructuredMap: newtype UnstructuredMap
+ Language.Github.Actions.UnstructuredMap: renderUnstructuredValue :: UnstructuredValue -> Text
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInput :: Maybe Text -> Maybe Value -> Maybe Bool -> Maybe WorkflowDispatchInputType -> WorkflowDispatchInput
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInputTypeBoolean :: WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInputTypeChoice :: NonEmpty Text -> WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInputTypeEnvironment :: WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInputTypeNumber :: WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: WorkflowDispatchInputTypeString :: WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: [default_] :: WorkflowDispatchInput -> Maybe Value
+ Language.Github.Actions.Workflow.Trigger: [description] :: WorkflowDispatchInput -> Maybe Text
+ Language.Github.Actions.Workflow.Trigger: [required] :: WorkflowDispatchInput -> Maybe Bool
+ Language.Github.Actions.Workflow.Trigger: [type_] :: WorkflowDispatchInput -> Maybe WorkflowDispatchInputType
+ Language.Github.Actions.Workflow.Trigger: data WorkflowDispatchInput
+ Language.Github.Actions.Workflow.Trigger: data WorkflowDispatchInputType
- Language.Github.Actions.Job: Job :: Maybe Concurrency -> Maybe JobContainer -> Maybe Bool -> Maybe Defaults -> Map Text Text -> Maybe JobEnvironment -> Maybe Text -> Maybe (NonEmpty JobId) -> Map Text Text -> Maybe Permissions -> Maybe Text -> Maybe Text -> Map Text Text -> Map ServiceId Service -> Maybe (NonEmpty Step) -> Maybe JobStrategy -> Maybe Int -> Maybe Text -> Map Text Text -> Job
+ Language.Github.Actions.Job: Job :: Maybe Concurrency -> Maybe JobContainer -> Maybe Bool -> Maybe Defaults -> Map Text Text -> Maybe JobEnvironment -> Maybe Text -> Maybe JobNeeds -> Map Text Text -> Maybe Permissions -> Maybe RunIf -> Maybe Text -> Map Text Text -> Map ServiceId Service -> Maybe (NonEmpty Step) -> Maybe JobStrategy -> Maybe Int -> Maybe Text -> Map Text Text -> Job
- Language.Github.Actions.Job: [needs] :: Job -> Maybe (NonEmpty JobId)
+ Language.Github.Actions.Job: [needs] :: Job -> Maybe JobNeeds
- Language.Github.Actions.Job: [runIf] :: Job -> Maybe Text
+ Language.Github.Actions.Job: [runIf] :: Job -> Maybe RunIf
- Language.Github.Actions.Step: Step :: Bool -> Map Text Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Shell -> Maybe StepId -> Maybe Int -> Maybe Text -> Maybe StepWith -> Maybe Text -> Step
+ Language.Github.Actions.Step: Step :: Bool -> Map Text Text -> Maybe Text -> Maybe Text -> Maybe RunIf -> Maybe Shell -> Maybe StepId -> Maybe Int -> Maybe Text -> Maybe StepWith -> Maybe Text -> Step
- Language.Github.Actions.Step: [runIf] :: Step -> Maybe Text
+ Language.Github.Actions.Step: [runIf] :: Step -> Maybe RunIf
- Language.Github.Actions.Step.With: StepWithEnv :: Map Text Text -> StepWith
+ Language.Github.Actions.Step.With: StepWithEnv :: UnstructuredMap -> StepWith
Files
- CHANGELOG.md +5/−0
- github-actions.cabal +10/−4
- src/Language/Github/Actions/Job.hs +8/−6
- src/Language/Github/Actions/Job/Needs.hs +78/−0
- src/Language/Github/Actions/RunIf.hs +76/−0
- src/Language/Github/Actions/Step.hs +4/−2
- src/Language/Github/Actions/Step/With.hs +5/−5
- src/Language/Github/Actions/UnstructuredMap.hs +92/−0
- src/Language/Github/Actions/Workflow/Trigger.hs +2/−0
- test/Language/Github/Actions/WorkflowTest.hs +1/−1
- test/golden/configuration-main.hs.txt +196/−107
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# CHANGELOG++## Version 0.1.0 (2025-07-01)++Released upon an unsuspecting world
github-actions.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: github-actions-version: 0.1.0.0+version: 0.1.1.0 synopsis: Github Actions description: This library provides types and instances for serializing and deserializing@@ -15,8 +15,11 @@ copyright: Copyright (C) 2025 Bellroy Pty Ltd category: Language build-type: Simple-extra-source-files:+extra-doc-files:+ CHANGELOG.md README.md++extra-source-files: test/golden/configuration-main.golden.yml test/golden/configuration-main.hs.txt test/golden/configuration-main.yml@@ -36,11 +39,11 @@ build-depends: , aeson ^>=2.2.3.0 , base >=4.14 && <4.22- , containers ==0.6.7 || ==0.6.8 || ==0.7 || ==0.8+ , containers ^>=0.6.7 || ^>=0.7 || ^>=0.8 , hedgehog ^>=1.5 , hoist-error ^>=0.3 , string-interpolate ^>=0.3.3- , text ==1.2.4.1 || ==2.0.2 || ==2.1.1+ , text ^>=1.2.4.1 || ^>=2.0.2 || ^>=2.1.1 , vector ^>=0.13.0.0 library@@ -56,14 +59,17 @@ Language.Github.Actions.Job.Container Language.Github.Actions.Job.Environment Language.Github.Actions.Job.Id+ Language.Github.Actions.Job.Needs Language.Github.Actions.Job.Strategy Language.Github.Actions.Permissions+ Language.Github.Actions.RunIf Language.Github.Actions.Service Language.Github.Actions.Service.Id Language.Github.Actions.Shell Language.Github.Actions.Step Language.Github.Actions.Step.Id Language.Github.Actions.Step.With+ Language.Github.Actions.UnstructuredMap Language.Github.Actions.Workflow Language.Github.Actions.Workflow.Trigger
src/Language/Github/Actions/Job.hs view
@@ -43,12 +43,14 @@ import qualified Language.Github.Actions.Job.Container as JobContainer import Language.Github.Actions.Job.Environment (JobEnvironment) import qualified Language.Github.Actions.Job.Environment as JobEnvironment-import Language.Github.Actions.Job.Id (JobId)-import qualified Language.Github.Actions.Job.Id as JobId+import Language.Github.Actions.Job.Needs (JobNeeds)+import qualified Language.Github.Actions.Job.Needs as JobNeeds import Language.Github.Actions.Job.Strategy (JobStrategy) import qualified Language.Github.Actions.Job.Strategy as JobStrategy import Language.Github.Actions.Permissions (Permissions) import qualified Language.Github.Actions.Permissions as Permissions+import Language.Github.Actions.RunIf (RunIf)+import qualified Language.Github.Actions.RunIf as RunIf import Language.Github.Actions.Service (Service) import qualified Language.Github.Actions.Service as Service import Language.Github.Actions.Service.Id (ServiceId)@@ -93,13 +95,13 @@ -- | Display name for the job jobName :: Maybe Text, -- | Jobs this job depends on- needs :: Maybe (NonEmpty JobId),+ needs :: Maybe JobNeeds, -- | Outputs from this job outputs :: Map Text Text, -- | Permissions for this job permissions :: Maybe Permissions, -- | Condition for running this job- runIf :: Maybe Text,+ runIf :: Maybe RunIf, -- | Runner type (e.g., "ubuntu-latest") runsOn :: Maybe Text, -- | Secrets available to this job@@ -179,10 +181,10 @@ env <- genTextMap environment <- Gen.maybe JobEnvironment.gen jobName <- Gen.maybe genText- needs <- Gen.maybe (Gen.nonEmpty (Range.linear 1 5) JobId.gen)+ needs <- Gen.maybe JobNeeds.gen outputs <- genTextMap permissions <- Gen.maybe Permissions.gen- runIf <- Gen.maybe genText+ runIf <- Gen.maybe RunIf.gen runsOn <- Gen.maybe genText secrets <- genTextMap services <- Gen.map (Range.linear 1 5) $ liftA2 (,) ServiceId.gen Service.gen
+ src/Language/Github/Actions/Job/Needs.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- |+-- Module : Language.Github.Actions.Job.Needs+-- Description : Job dependency specification for GitHub Actions+-- Copyright : (c) 2025 Bellroy Pty Ltd+-- License : BSD-3-Clause+-- Maintainer : Bellroy Tech Team <haskell@bellroy.com>+--+-- This module provides the 'JobNeeds' type for representing job dependencies+-- in GitHub Actions workflows. GitHub Actions allows both strings and+-- lists of strings for the 'needs' field.+--+-- Examples of valid 'needs' specifications:+-- * @needs: build@ - Single job specified as a string+-- * @needs: [build]@ - Single job specified as a list of strings+-- * @needs: [build, test]@ - Multiple job dependencies specified as a list of strings+--+-- For more information about GitHub Actions job dependencies, see:+-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds>+module Language.Github.Actions.Job.Needs+ ( JobNeeds (..),+ gen,+ )+where++import Data.Aeson (FromJSON, ToJSON (..), Value (..))+import qualified Data.Aeson as Aeson+import Data.List.NonEmpty (NonEmpty)+import GHC.Generics (Generic)+import Hedgehog (MonadGen)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import Language.Github.Actions.Job.Id (JobId)+import qualified Language.Github.Actions.Job.Id as JobId++-- | Job dependency specification that preserves YAML representation.+--+-- GitHub Actions supports flexible job dependency specification:+--+-- * 'JobNeedsString' - Single job dependency as string like @needs: build@+-- * 'JobNeedsArray' - Multiple job dependencies as array like @needs: [build, test]@+--+-- Examples:+--+-- @+-- -- Single job dependency (string form)+-- stringDep :: JobNeeds+-- stringDep = JobNeedsString (JobId "build")+--+-- -- Multiple job dependencies (array form)+-- arrayDeps :: JobNeeds+-- arrayDeps = JobNeedsArray (JobId "build" :| [JobId "test", JobId "lint"])+-- @+--+-- The type preserves the original YAML format during round-trip serialization.+-- A string input will serialize back to a string, and an array input will+-- serialize back to an array, preventing information loss.+data JobNeeds+ = JobNeedsString JobId+ | JobNeedsArray (NonEmpty JobId)+ deriving stock (Eq, Generic, Ord, Show)++instance FromJSON JobNeeds where+ parseJSON v@(Array _) = JobNeedsArray <$> Aeson.parseJSON v+ parseJSON v = JobNeedsString <$> Aeson.parseJSON v++instance ToJSON JobNeeds where+ toJSON (JobNeedsString jobId) = toJSON jobId+ toJSON (JobNeedsArray jobIds) = toJSON jobIds++gen :: (MonadGen m) => m JobNeeds+gen =+ Gen.choice+ [ JobNeedsString <$> JobId.gen,+ JobNeedsArray <$> Gen.nonEmpty (Range.linear 1 5) JobId.gen+ ]
+ src/Language/Github/Actions/RunIf.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module : Language.Github.Actions.RunIf+-- Description : Flexible conditional expressions for GitHub Actions+-- Copyright : (c) 2025 Bellroy Pty Ltd+-- License : BSD-3-Clause+-- Maintainer : Bellroy Tech Team <haskell@bellroy.com>+--+-- This module provides the 'RunIf' type for representing conditional expressions+-- in GitHub Actions workflows. GitHub Actions allows both boolean and string+-- expressions in 'if' conditions for jobs and steps.+--+-- Examples of valid 'if' conditions:+-- * @if: false@ - Boolean+-- * @if: "github.ref == 'refs/heads/main'"@ - String+--+-- For more information about GitHub Actions conditional expressions, see:+-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idif>+module Language.Github.Actions.RunIf+ ( RunIf (..),+ gen,+ )+where++import Data.Aeson (FromJSON, ToJSON (..), Value (..))+import qualified Data.Aeson as Aeson+import Data.Text (Text)+import GHC.Generics (Generic)+import Hedgehog (MonadGen)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range++-- | A conditional expression that can be either a boolean or string.+--+-- GitHub Actions supports flexible 'if' conditions:+--+-- * 'RunIfBool' - Simple boolean values like @true@ or @false@+-- * 'RunIfString' - GitHub expressions like @"github.ref == 'refs/heads/main'"@+--+-- Examples:+--+-- @+-- -- Simple boolean condition+-- simpleFalse :: RunIf+-- simpleFalse = RunIfBool False+--+-- -- GitHub expression condition+-- branchCheck :: RunIf+-- branchCheck = RunIfString "github.ref == 'refs/heads/main'"+-- @+--+-- The type preserves the original format during round-trip serialization,+-- so a boolean input remains a boolean in the output YAML.+data RunIf+ = RunIfBool Bool+ | RunIfString Text+ deriving stock (Eq, Generic, Ord, Show)++instance FromJSON RunIf where+ parseJSON (Bool b) = pure $ RunIfBool b+ parseJSON (String s) = pure $ RunIfString s+ parseJSON v = fail $ "Expected Bool or String for RunIf, got: " ++ show v++instance ToJSON RunIf where+ toJSON (RunIfBool b) = Bool b+ toJSON (RunIfString s) = String s++gen :: (MonadGen m) => m RunIf+gen =+ Gen.choice+ [ RunIfBool <$> Gen.bool,+ RunIfString <$> Gen.text (Range.linear 5 50) Gen.alphaNum+ ]
src/Language/Github/Actions/Step.hs view
@@ -35,6 +35,8 @@ import Hedgehog (MonadGen) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range+import Language.Github.Actions.RunIf (RunIf)+import qualified Language.Github.Actions.RunIf as RunIf import Language.Github.Actions.Shell (Shell) import qualified Language.Github.Actions.Shell as Shell import Language.Github.Actions.Step.Id (StepId)@@ -82,7 +84,7 @@ -- | Command or script to run run :: Maybe Text, -- | Condition for running this step- runIf :: Maybe Text,+ runIf :: Maybe RunIf, -- | Shell to use for running commands shell :: Maybe Shell, -- | Unique identifier for this step@@ -139,7 +141,7 @@ env <- genTextMap name <- Gen.maybe genText run <- Gen.maybe genText- runIf <- Gen.maybe genText+ runIf <- Gen.maybe RunIf.gen shell <- Gen.maybe Shell.gen stepId <- Gen.maybe StepId.gen timeoutMinutes <- Gen.maybe $ Gen.int (Range.linear 1 120)
src/Language/Github/Actions/Step/With.hs view
@@ -30,7 +30,6 @@ import Data.Aeson (FromJSON, ToJSON (..), (.:), (.:?), (.=)) import qualified Data.Aeson as Aeson import qualified Data.Aeson.KeyMap as AesonKeyMap-import Data.Map (Map) import Data.Maybe (catMaybes) import qualified Data.Set as Set import Data.Text (Text)@@ -38,6 +37,8 @@ import Hedgehog (MonadGen) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range+import Language.Github.Actions.UnstructuredMap (UnstructuredMap)+import qualified Language.Github.Actions.UnstructuredMap as UnstructuredMap -- | Docker container arguments for Docker actions. --@@ -95,14 +96,14 @@ = -- | Docker action arguments StepWithDockerArgs StepWithDockerArgsAttrs | -- | Environment variables/general inputs- StepWithEnv (Map Text Text)+ StepWithEnv UnstructuredMap deriving stock (Eq, Generic, Ord, Show) instance FromJSON StepWith where parseJSON = Aeson.withObject "StepWith" $ \o -> let objectKeySet = Set.fromList (AesonKeyMap.keys o) dockerKeySet = Set.fromList ["entryPoint", "args"]- in if objectKeySet `Set.isSubsetOf` dockerKeySet+ in if not (null objectKeySet) && objectKeySet `Set.isSubsetOf` dockerKeySet then do entryPoint <- o .: "entryPoint" args <- o .:? "args"@@ -127,8 +128,7 @@ entryPoint <- genText args <- Gen.maybe genText pure StepWithDockerArgsAttrs {..},- StepWithEnv <$> genTextMap+ StepWithEnv <$> UnstructuredMap.gen ] where genText = Gen.text (Range.linear 1 5) Gen.alphaNum- genTextMap = Gen.map (Range.linear 1 5) $ liftA2 (,) genText genText
+ src/Language/Github/Actions/UnstructuredMap.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module : Language.Github.Actions.UnstructuredMap+-- Description : Flexible value types for GitHub Actions YAML parsing+-- Copyright : (c) 2025 Bellroy Pty Ltd+-- License : BSD-3-Clause+-- Maintainer : Bellroy Tech Team <haskell@bellroy.com>+--+-- This module provides the 'UnstructuredMap' type for representing a map of+-- values that can be strings, numbers, or booleans in GitHub Actions YAML files.+--+-- GitHub Actions allows flexible typing in many contexts:+-- * @retention-days: 1@ (number)+-- * @retention-days: "1"@ (string)+-- * @should-retain: false@ (boolean)+--+-- This type preserves the original YAML type during round-trip parsing,+-- ensuring that numeric values remain numeric and strings remain strings.+module Language.Github.Actions.UnstructuredMap+ ( UnstructuredValue (..),+ UnstructuredMap (..),+ renderUnstructuredValue,+ gen,+ )+where++import Data.Aeson (FromJSON, ToJSON (..), Value (..))+import qualified Data.Aeson as Aeson+import Data.Map (Map)+import Data.Text (Text)+import qualified Data.Text as Text+import GHC.Generics (Generic)+import Hedgehog (MonadGen)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range++-- | A map that can have values of string, number, or boolean.+--+-- This type is designed to handle the flexible typing that GitHub Actions+-- allows in YAML files.+--+-- The type preserves the original format during round-trip serialization,+-- so numeric inputs remain numeric in the output YAML.+data UnstructuredValue+ = UnstructuredValueString Text+ | UnstructuredValueNumber Double+ | UnstructuredValueBool Bool+ deriving stock (Eq, Generic, Ord, Show)++instance FromJSON UnstructuredValue where+ parseJSON (String s) = pure $ UnstructuredValueString s+ parseJSON (Number n) = pure $ UnstructuredValueNumber (realToFrac n)+ parseJSON (Bool b) = pure $ UnstructuredValueBool b+ parseJSON v = fail $ "Expected String, Number, or Bool for UnstructuredValue, got: " ++ show v++instance ToJSON UnstructuredValue where+ toJSON (UnstructuredValueString s) = String s+ toJSON (UnstructuredValueNumber n) = Number (fromRational (toRational n))+ toJSON (UnstructuredValueBool b) = Bool b++renderUnstructuredValue :: UnstructuredValue -> Text+renderUnstructuredValue (UnstructuredValueString s) = s+renderUnstructuredValue (UnstructuredValueNumber n) =+ -- Format numbers nicely, avoiding unnecessary decimal places for integers+ if n == fromInteger (round n)+ then Text.pack (show (round n :: Integer))+ else Text.pack (show n)+renderUnstructuredValue (UnstructuredValueBool b) = if b then "true" else "false"++newtype UnstructuredMap = UnstructuredMap (Map Text UnstructuredValue)+ deriving stock (Eq, Generic, Ord, Show)+ deriving newtype (FromJSON, ToJSON)++genUnstructuredValue :: (MonadGen m) => m UnstructuredValue+genUnstructuredValue =+ Gen.choice+ [ UnstructuredValueString <$> Gen.text (Range.linear 1 20) Gen.alphaNum,+ UnstructuredValueNumber <$> Gen.realFloat (Range.linearFrac 0 1000),+ UnstructuredValueBool <$> Gen.bool+ ]++gen :: (MonadGen m) => m UnstructuredMap+gen = UnstructuredMap <$> Gen.map (Range.linear 0 10) genKeyValue+ where+ genKeyValue = do+ key <- Gen.text (Range.linear 1 20) Gen.alphaNum+ value <- genUnstructuredValue+ pure (key, value)
src/Language/Github/Actions/Workflow/Trigger.hs view
@@ -75,6 +75,8 @@ ReleaseActivityType (..), WorkflowCallAttributes (..), WorkflowDispatchAttributes (..),+ WorkflowDispatchInput (..),+ WorkflowDispatchInputType (..), WorkflowRunActivityType (..), WorkflowRunTriggerAttributes (..), WorkflowTrigger (..),
test/Language/Github/Actions/WorkflowTest.hs view
@@ -50,7 +50,7 @@ putStrLn $ "roundtrip " <> takeBaseName testYamlFilePath eitherWorkflow <- Yaml.decodeFileEither @Workflow testYamlFilePath either- (BS.writeFile outputFilePath >> (\_ -> fail "YAML decoding failed"))+ (BS.writeFile outputFilePath >> (\e -> fail $ "YAML decoding failed: " ++ show e)) (\workflow -> writeOutputFiles outputFilePath haskellOutputFilePath workflow >> pure workflow) $ first (encodeUtf8 . Text.pack . Yaml.prettyPrintParseException) eitherWorkflow writeOutputFiles :: FilePath -> FilePath -> Workflow -> IO ()
test/golden/configuration-main.hs.txt view
@@ -18,7 +18,7 @@ , env = fromList [] , environment = Nothing , jobName = Nothing- , needs = Just (JobId "validateContent" :| [])+ , needs = Just (JobNeedsArray (JobId "validateContent" :| [])) , outputs = fromList [] , permissions = Nothing , runIf = Nothing@@ -34,7 +34,8 @@ , run = Nothing , runIf = Just- "github.ref == 'refs/heads/master' || github.ref == 'refs/heads/stable'"+ (RunIfString+ "github.ref == 'refs/heads/master' || github.ref == 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -42,10 +43,11 @@ , with = Just (StepWithEnv- (fromList- [ ( "fetch-depth" , "0" )- , ( "token" , "${{ env.GH_TOKEN }}" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "fetch-depth" , UnstructuredValueString "0" )+ , ( "token" , UnstructuredValueString "${{ env.GH_TOKEN }}" )+ ]))) , workingDirectory = Nothing } :| [ Step@@ -55,7 +57,8 @@ , run = Nothing , runIf = Just- "github.ref != 'refs/heads/master' && github.ref != 'refs/heads/stable'"+ (RunIfString+ "github.ref != 'refs/heads/master' && github.ref != 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -63,10 +66,13 @@ , with = Just (StepWithEnv- (fromList- [ ( "fetch-depth" , "1" )- , ( "token" , "${{ env.GH_TOKEN }}" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "fetch-depth" , UnstructuredValueString "1" )+ , ( "token"+ , UnstructuredValueString "${{ env.GH_TOKEN }}"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -91,7 +97,7 @@ , run = Just "echo \"DESTINATION=Staging Environment\" >> $GITHUB_ENV\necho \"ENVIRONMENT=staging\" >> $GITHUB_ENV\n"- , runIf = Just "github.ref == 'refs/heads/master'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -106,7 +112,7 @@ , run = Just "echo \"DESTINATION=Production Environment\" >> $GITHUB_ENV\necho \"ENVIRONMENT=production\" >> $GITHUB_ENV\n"- , runIf = Just "github.ref == 'refs/heads/stable'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -127,12 +133,16 @@ , with = Just (StepWithEnv- (fromList- [ ( "bellroy-nix-cache-access" , "none" )- , ( "nix-config-access-tokens"- , "github.com=${{ secrets.READ_HASKELL_REPO_PAT }}"- )- ]))+ (UnstructuredMap+ (fromList+ [ ( "bellroy-nix-cache-access"+ , UnstructuredValueString "none"+ )+ , ( "nix-config-access-tokens"+ , UnstructuredValueString+ "github.com=${{ secrets.READ_HASKELL_REPO_PAT }}"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -179,7 +189,13 @@ , with = Just (StepWithEnv- (fromList [ ( "path" , "${{ env.GIT_REPOSITORY_PATH }}" ) ]))+ (UnstructuredMap+ (fromList+ [ ( "path"+ , UnstructuredValueString+ "${{ env.GIT_REPOSITORY_PATH }}"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -206,7 +222,8 @@ , run = Just "set +e\nIFS= output=$(./scripts/check_configuration_csvs_sorted 2>&1)\nstatus=$?\n\nif (($status)); then\n echo \"Configuration CSV files are not sorted:\" >>/tmp/pr-comment.txt\n echo \"\" >>/tmp/pr-comment.txt\n echo \"\\`\\`\\`\" >>/tmp/pr-comment.txt\n echo $output >>/tmp/pr-comment.txt\n echo \"\\`\\`\\`\" >>/tmp/pr-comment.txt\n exit $status\nelse\n echo \"All configuration CSV files are sorted.\"\nfi\n"- , runIf = Just "steps.check_changes.outputs.csv_changed == '1'"+ , runIf =+ Just (RunIfString "steps.check_changes.outputs.csv_changed == '1'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -227,11 +244,15 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "byproduct" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name" , UnstructuredValueString "byproduct" )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -247,11 +268,15 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "gitapult" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name" , UnstructuredValueString "gitapult" )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -267,11 +292,17 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "config-rules-cli" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name"+ , UnstructuredValueString "config-rules-cli"+ )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -287,11 +318,15 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "powerful-owl" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name" , UnstructuredValueString "powerful-owl" )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -376,7 +411,7 @@ , run = Just "set +e\nIFS= output=$(./gitapult changeset --git_root \"$GITHUB_WORKSPACE\" --from \"${{ env.MERGE_BASE }}\" | ./byproduct validate --markdown 2>&1)\nstatus=$?\n\nif (($status)); then\n echo $output >>/tmp/pr-comment.txt\n exit $status\nelse\n echo \"Byproduct validation succeeded.\"\nfi\n"- , runIf = Just "env.PR_NUMBER != 0"+ , runIf = Just (RunIfString "env.PR_NUMBER != 0") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -468,7 +503,8 @@ "if [ $(git status shipping/tests --porcelain=v1 | wc -l) -gt 0 ]\nthen\n git add shipping/tests\n git commit -m \"Update shipping tests\"\n echo \"GENERATED_ANOTHER_COMMIT=true\" >> $GITHUB_ENV\nfi\n" , runIf = Just- "env.PR_NUMBER != 0 && github.ref != 'refs/heads/stable' && github.ref != 'refs/heads/master'"+ (RunIfString+ "env.PR_NUMBER != 0 && github.ref != 'refs/heads/stable' && github.ref != 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -515,7 +551,8 @@ "if [ $(git status promotions/tests --porcelain=v1 | wc -l) -gt 0 ]\nthen\n git add promotions/tests\n git commit -m \"Update promotion tests\"\n echo \"GENERATED_ANOTHER_COMMIT=true\" >> $GITHUB_ENV\nfi\n" , runIf = Just- "env.PR_NUMBER != 0 && github.ref != 'refs/heads/stable' && github.ref != 'refs/heads/master'"+ (RunIfString+ "env.PR_NUMBER != 0 && github.ref != 'refs/heads/stable' && github.ref != 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -531,7 +568,8 @@ , run = Just "git push --set-upstream origin HEAD\n\ngh run cancel ${{ github.run_id }}\ngh run watch ${{ github.run_id }}\n"- , runIf = Just "env.GENERATED_ANOTHER_COMMIT == 'true'"+ , runIf =+ Just (RunIfString "env.GENERATED_ANOTHER_COMMIT == 'true'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -563,7 +601,8 @@ "echo \"COMMIT_CHANGES<<EOF\" >> $GITHUB_ENV\necho \"$(git log --oneline --format=\\\"%s\\\" $(git tag -l [0-9]*_${{ env.ENVIRONMENT }} | tail -n 1)..${{ steps.extract_branch.outputs.branch }} | grep -v 'Merge pull request' | grep -v \"Merge branch 'master' into\" | sed -e 's/^\"//' -e 's/\"$//' | cat)\" >> $GITHUB_ENV\necho \"EOF\" >> $GITHUB_ENV\n" , runIf = Just- "github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master'"+ (RunIfString+ "github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master'") , shell = Just (Bash Nothing) , stepId = Just (StepId "extract_changes") , timeoutMinutes = Nothing@@ -580,7 +619,8 @@ , run = Nothing , runIf = Just- "github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master'"+ (RunIfString+ "github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Just (StepId "slack-deploy") , timeoutMinutes = Nothing@@ -590,12 +630,16 @@ , with = Just (StepWithEnv- (fromList- [ ( "channel-id" , "${{ env.SLACK_CHANNEL_ID }}" )- , ( "payload"- , "{\n \"text\": \":loudspeaker: *${{ github.actor }}* is deploying *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}* from *${{ steps.extract_branch.outputs.branch }}*\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"ffd966\",\n \"fields\": [\n {\n \"title\": \"What is being deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"In Progress\"\n }\n ]\n }\n ]\n}\n"- )- ]))+ (UnstructuredMap+ (fromList+ [ ( "channel-id"+ , UnstructuredValueString "${{ env.SLACK_CHANNEL_ID }}"+ )+ , ( "payload"+ , UnstructuredValueString+ "{\n \"text\": \":loudspeaker: *${{ github.actor }}* is deploying *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}* from *${{ steps.extract_branch.outputs.branch }}*\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"ffd966\",\n \"fields\": [\n {\n \"title\": \"What is being deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"In Progress\"\n }\n ]\n }\n ]\n}\n"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -605,7 +649,7 @@ , run = Just "set -e\n# XXX: Gitapult cannot distinguish datasets, so we push all data to\n# both endpoints and allow the server to decide what to do.\n# Kafka Webhooks accepts all datasets at once\n./gitapult run \\\n --git_root \"$GITHUB_WORKSPACE\" \\\n --authorization_token \"$KW_STAGING_API_KEY\" \\\n --changeset_url \"$KW_STAGING_BASE_URL/gitapult/configuration/records\" \\\n --revision_url \"$KW_STAGING_BASE_URL/gitapult/configuration/revision\" \\\n --api_key \\\n --compress \\\n --max-payload-bytes 3000000\n\n# XXX: V3 doesn't care about rendering data, but it is too large to send.\n# As at 2024-02-14 gitapult does not support selecting which data to send.\ngit -C \"$GIT_REPOSITORY_PATH\" rm feeds/rendering_data/.gitapult.json\n./gitapult run \\\n --git_root \"$GITHUB_WORKSPACE\" \\\n --authorization_token \"$SF_STAGING_API_KEY\" \\\n --changeset_url \"$SF_STAGING_BASE_URL/api/v1/configuration/shipping\" \\\n --revision_url \"$SF_STAGING_BASE_URL/api/v1/configuration/shipping/revisions\" && \\\ngit -C \"$GIT_REPOSITORY_PATH\" restore --staged feeds/rendering_data/.gitapult.json\ngit -C \"$GIT_REPOSITORY_PATH\" restore feeds/rendering_data/.gitapult.json\n"- , runIf = Just "github.ref == 'refs/heads/master'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -620,7 +664,8 @@ , run = Just "git tag \"${{ join(steps.current-time.outputs.*, '\\n') }}_staging\" && git push --tags\n"- , runIf = Just "success() && github.ref == 'refs/heads/master'"+ , runIf =+ Just (RunIfString "success() && github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -635,7 +680,7 @@ , run = Just "curl -X POST -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/pulls -d '{\"head\":\"master\",\"base\":\"stable\",\"title\":\"Merge branch master into stable\",\"body\":\"This PR was automatically generated by CI.\"}'\nPR_URL=$(curl -sX GET -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/pulls\\?head\\=bellroy:master\\&base\\=stable | jq -r '.[0].html_url | select(length>0)')\necho \"PR_URL=$PR_URL\" >> $GITHUB_ENV\n"- , runIf = Just "github.ref == 'refs/heads/master'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -650,7 +695,7 @@ , run = Just "PR_RESPONSE=$(curl -sX GET -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/pulls\\?head\\=bellroy:master\\&base\\=stable | jq -r '.[0]')\nPR_NUM=$(echo \"$PR_RESPONSE\" | jq -r \".number\")\nPR_ASSIGNEE=$(echo \"$PR_RESPONSE\" | jq -r \".assignee\")\nCOMMITS_RESPONSE=$(curl -sX GET -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUM/commits)\nFIRST_COMMITTER=$(curl -sX GET -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUM/commits | jq -r '. | sort_by(.commit.author.date) | .[0] | .author.login')\n# special case for squashed PRs created by trike-deploy e.g. gha-workflows\nif [[ \"$FIRST_COMMITTER\" == \"trike-deploy\" ]]; then\n FIRST_COMMITTER=$(echo \"$COMMITS_RESPONSE\" | jq -r '. | sort_by(.commit.author.date) | .[0]' | sed -En \"s/.*Co-authored-by: (.*) <.*@.*>\\\",/\\1/p\")\nfi\nif [[ \"$PR_ASSIGNEE\" == \"null\" ]]; then\n curl -sX POST -H \"Accept: application/vnd.github.v3+json\" -H \"Authorization: token ${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}\" https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUM/assignees -d \"{\\\"assignees\\\":[\\\"$FIRST_COMMITTER\\\"]}\"\nfi\n"- , runIf = Just "github.ref == 'refs/heads/master'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -665,7 +710,7 @@ , run = Just "set -e\n# XXX: Gitapult cannot distinguish datasets, so we push all data to\n# both endpoints and allow the server to decide what to do.\n# Kafka Webhooks accepts all datasets at once\n./gitapult run \\\n --git_root \"$GITHUB_WORKSPACE\" \\\n --authorization_token \"$KW_PRODUCTION_API_KEY\" \\\n --changeset_url \"$KW_PRODUCTION_BASE_URL/gitapult/configuration/records\" \\\n --revision_url \"$KW_PRODUCTION_BASE_URL/gitapult/configuration/revision\" \\\n --api_key \\\n --compress \\\n --max-payload-bytes 3000000\n\n# XXX: V3 doesn't care about rendering data, but it is too large to send.\n# As at 2024-02-14 gitapult does not support selecting which data to send.\ngit -C \"$GIT_REPOSITORY_PATH\" rm feeds/rendering_data/.gitapult.json\n./gitapult run \\\n --git_root \"$GITHUB_WORKSPACE\" \\\n --authorization_token \"$SF_PRODUCTION_API_KEY\" \\\n --changeset_url \"$SF_PRODUCTION_BASE_URL/api/v1/configuration/shipping\" \\\n --revision_url \"$SF_PRODUCTION_BASE_URL/api/v1/configuration/shipping/revisions\" && \\\ngit -C \"$GIT_REPOSITORY_PATH\" restore --staged feeds/rendering_data/.gitapult.json\ngit -C \"$GIT_REPOSITORY_PATH\" restore feeds/rendering_data/.gitapult.json\n"- , runIf = Just "github.ref == 'refs/heads/stable'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -680,7 +725,8 @@ , run = Just "git tag \"${{ join(steps.current-time.outputs.*, '\\n') }}_production\" && git push --tags\n"- , runIf = Just "success() && github.ref == 'refs/heads/stable'"+ , runIf =+ Just (RunIfString "success() && github.ref == 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -695,7 +741,7 @@ , run = Just "echo \"SUCCESS_MESSAGE=:tada: \\nA PR has been opened against the stable branch: ${{ env.PR_URL }}\" >> $GITHUB_ENV\necho \"SUCCESS_MESSAGE_BODY=https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks\\n\\n*PR for Production:* ${{ env.PR_URL }}\" >> $GITHUB_ENV\n"- , runIf = Just "github.ref == 'refs/heads/master'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/master'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -710,7 +756,7 @@ , run = Just "echo \"SUCCESS_MESSAGE=:tada:\" >> $GITHUB_ENV\necho \"SUCCESS_MESSAGE_BODY=https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks\" >> $GITHUB_ENV\n"- , runIf = Just "github.ref == 'refs/heads/stable'"+ , runIf = Just (RunIfString "github.ref == 'refs/heads/stable'") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -727,7 +773,8 @@ , run = Nothing , runIf = Just- "success() && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master')"+ (RunIfString+ "success() && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master')") , shell = Nothing , stepId = Just (StepId "slack-deploy-success") , timeoutMinutes = Nothing@@ -737,13 +784,20 @@ , with = Just (StepWithEnv- (fromList- [ ( "channel-id" , "${{ env.SLACK_CHANNEL_ID }}" )- , ( "payload"- , "{\n \"text\": \":loudspeaker: *${{ github.actor }}* has deployed *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}* from *${{ steps.extract_branch.outputs.branch }}* ${{ env.SUCCESS_MESSAGE }}\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"00ff00\",\n \"fields\": [\n {\n \"title\": \"What was deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"Deployed\"\n },\n {\n \"title\": \"Output\",\n \"short\": false,\n \"value\": \"${{ env.SUCCESS_MESSAGE_BODY }}\"\n }\n ]\n }\n ]\n}\n"- )- , ( "update-ts" , "${{ steps.slack-deploy.outputs.ts }}" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "channel-id"+ , UnstructuredValueString "${{ env.SLACK_CHANNEL_ID }}"+ )+ , ( "payload"+ , UnstructuredValueString+ "{\n \"text\": \":loudspeaker: *${{ github.actor }}* has deployed *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}* from *${{ steps.extract_branch.outputs.branch }}* ${{ env.SUCCESS_MESSAGE }}\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"00ff00\",\n \"fields\": [\n {\n \"title\": \"What was deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"Deployed\"\n },\n {\n \"title\": \"Output\",\n \"short\": false,\n \"value\": \"${{ env.SUCCESS_MESSAGE_BODY }}\"\n }\n ]\n }\n ]\n}\n"+ )+ , ( "update-ts"+ , UnstructuredValueString+ "${{ steps.slack-deploy.outputs.ts }}"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -755,7 +809,8 @@ , run = Nothing , runIf = Just- "failure() && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master')"+ (RunIfString+ "failure() && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/master')") , shell = Nothing , stepId = Just (StepId "slack-deploy-failed") , timeoutMinutes = Nothing@@ -765,13 +820,20 @@ , with = Just (StepWithEnv- (fromList- [ ( "channel-id" , "${{ env.SLACK_CHANNEL_ID }}" )- , ( "payload"- , "{ \"text\": \":loudspeaker: *BUILD/DEPLOY FAILURE* of *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}*\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"ff0000\",\n \"fields\": [\n {\n \"title\": \"What was being deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"Failed\"\n },\n {\n \"title\": \"Who broke it\",\n \"value\": \"${{ github.actor }}\"\n },\n {\n \"title\": \"Output\",\n \"short\": false,\n \"value\": \"https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks\"\n }\n ]\n }\n ]\n}\n"- )- , ( "update-ts" , "${{ steps.slack-deploy.outputs.ts }}" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "channel-id"+ , UnstructuredValueString "${{ env.SLACK_CHANNEL_ID }}"+ )+ , ( "payload"+ , UnstructuredValueString+ "{ \"text\": \":loudspeaker: *BUILD/DEPLOY FAILURE* of *${{ env.SYSTEM_NAME }}* to *${{ env.DESTINATION }}*\",\n \"attachments\": [\n {\n \"fallback\": \"Deployment summary\",\n \"color\": \"ff0000\",\n \"fields\": [\n {\n \"title\": \"What was being deployed\",\n \"value\": ${{ toJSON(env.COMMIT_CHANGES) }},\n \"short\": false\n },\n {\n \"title\": \"Status\",\n \"short\": true,\n \"value\": \"Failed\"\n },\n {\n \"title\": \"Who broke it\",\n \"value\": \"${{ github.actor }}\"\n },\n {\n \"title\": \"Output\",\n \"short\": false,\n \"value\": \"https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks\"\n }\n ]\n }\n ]\n}\n"+ )+ , ( "update-ts"+ , UnstructuredValueString+ "${{ steps.slack-deploy.outputs.ts }}"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -781,7 +843,7 @@ , run = Just "gh pr comment ${{ env.PR_NUMBER }} --body-file /tmp/pr-comment.txt || true"- , runIf = Just "always() && env.PR_NUMBER != 0"+ , runIf = Just (RunIfString "always() && env.PR_NUMBER != 0") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -827,11 +889,17 @@ , with = Just (StepWithEnv- (fromList- [ ( "fetch-depth" , "1" )- , ( "path" , "${{ env.GITHUB_WORKSPACE }}" )- , ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "fetch-depth" , UnstructuredValueString "1" )+ , ( "path"+ , UnstructuredValueString "${{ env.GITHUB_WORKSPACE }}"+ )+ , ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ ]))) , workingDirectory = Nothing } :| [ Step@@ -861,7 +929,12 @@ , with = Just (StepWithEnv- (fromList [ ( "bellroy-nix-cache-access" , "none" ) ]))+ (UnstructuredMap+ (fromList+ [ ( "bellroy-nix-cache-access"+ , UnstructuredValueString "none"+ )+ ]))) , workingDirectory = Nothing } , Step@@ -877,11 +950,15 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "gitapult" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name" , UnstructuredValueString "gitapult" )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -897,11 +974,15 @@ , with = Just (StepWithEnv- (fromList- [ ( "token" , "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}" )- , ( "tool-name" , "powerful-owl" )- , ( "tool-version" , "latest" )- ]))+ (UnstructuredMap+ (fromList+ [ ( "token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_DEPLOY_USER_TOKEN }}"+ )+ , ( "tool-name" , UnstructuredValueString "powerful-owl" )+ , ( "tool-version" , UnstructuredValueString "latest" )+ ]))) , workingDirectory = Nothing } , Step@@ -968,7 +1049,7 @@ , run = Just "gh pr comment ${{ env.PR_NUMBER }} --body-file /tmp/pr-comment.txt || true"- , runIf = Just "always() && env.PR_NUMBER != 0"+ , runIf = Just (RunIfString "always() && env.PR_NUMBER != 0") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -983,7 +1064,8 @@ , run = Nothing , runIf = Just- "failure() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/stable')"+ (RunIfString+ "failure() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/stable')") , shell = Nothing , stepId = Nothing , timeoutMinutes = Nothing@@ -991,17 +1073,24 @@ , with = Just (StepWithEnv- (fromList- [ ( "branch"- , "${{ steps.checkout_branch.outputs.branch }}"- )- , ( "channel-id" , "${{ env.SLACK_CHANNEL_ID }}" )- , ( "message-type" , "build-fail" )- , ( "show-authors" , "true" )- , ( "slack-bot-token"- , "${{ secrets.BELLROY_SLACK_TOKEN }}"- )- ]))+ (UnstructuredMap+ (fromList+ [ ( "branch"+ , UnstructuredValueString+ "${{ steps.checkout_branch.outputs.branch }}"+ )+ , ( "channel-id"+ , UnstructuredValueString "${{ env.SLACK_CHANNEL_ID }}"+ )+ , ( "message-type"+ , UnstructuredValueString "build-fail"+ )+ , ( "show-authors" , UnstructuredValueString "true" )+ , ( "slack-bot-token"+ , UnstructuredValueString+ "${{ secrets.BELLROY_SLACK_TOKEN }}"+ )+ ]))) , workingDirectory = Nothing } ])