Skip to content

Commit 30c9c05

Browse files
committed
Add haddocks
1 parent 923bc3d commit 30c9c05

File tree

18 files changed

+1341
-100
lines changed

18 files changed

+1341
-100
lines changed

README.md

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,97 @@
1-
# Types for representing Github Actions workflows
1+
# Github Actions
2+
3+
[![Haskell-CI](https://github.com/bellroy/github-actions/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/bellroy/github-actions/actions/workflows/haskell-ci.yml)
4+
5+
This library provides types and instances for serializing and deserializing
6+
GitHub Actions YAML, so that workflows can be built and maintained in Haskell.
27

38
As specified here:
49
https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
10+
11+
## Usage Examples
12+
13+
### 1. Exporting a Workflow to YAML
14+
15+
You can create a workflow in Haskell and export it to YAML format:
16+
17+
```haskell
18+
{-# LANGUAGE OverloadedStrings #-}
19+
20+
import qualified Data.Yaml as YAML
21+
import qualified Data.Map as Map
22+
import qualified Data.Set as Set
23+
import Data.List.NonEmpty (NonEmpty(..))
24+
import Language.Github.Actions.Workflow
25+
import qualified Language.Github.Actions.Job as Job
26+
import qualified Language.Github.Actions.Job.Id as JobId
27+
import qualified Language.Github.Actions.Step as Step
28+
import qualified Language.Github.Actions.Workflow.Trigger as Trigger
29+
30+
-- Create a simple CI workflow
31+
myWorkflow :: Workflow
32+
myWorkflow = new
33+
{ workflowName = Just "CI"
34+
, on = Set.singleton (Trigger.PushTrigger Trigger.pushTriggerDefaults)
35+
, jobs = Map.singleton (JobId.JobId "build") buildJob
36+
}
37+
38+
buildJob :: Job.Job
39+
buildJob = Job.new
40+
{ Job.jobName = Just "Build and Test"
41+
, Job.runsOn = Just "ubuntu-latest"
42+
, Job.steps = Just $ checkoutStep :| [buildStep, testStep]
43+
}
44+
45+
checkoutStep :: Step.Step
46+
checkoutStep = Step.new
47+
{ Step.name = Just "Checkout repository"
48+
, Step.uses = Just "actions/checkout@v4"
49+
}
50+
51+
buildStep :: Step.Step
52+
buildStep = Step.new
53+
{ Step.name = Just "Build project"
54+
, Step.run = Just "npm install && npm run build"
55+
}
56+
57+
testStep :: Step.Step
58+
testStep = Step.new
59+
{ Step.name = Just "Run tests"
60+
, Step.run = Just "npm test"
61+
}
62+
63+
-- Export to YAML
64+
exportWorkflow :: IO ()
65+
exportWorkflow = YAML.encodeFile "workflow.yml" myWorkflow
66+
```
67+
68+
### 2. Importing a YAML file into a Workflow representation
69+
70+
You can load an existing GitHub Actions YAML file into a Haskell `Workflow` type:
71+
72+
```haskell
73+
{-# LANGUAGE TypeApplications #-}
74+
75+
import qualified Data.Yaml as YAML
76+
import Language.Github.Actions.Workflow (Workflow)
77+
78+
-- Import from YAML file
79+
importWorkflow :: FilePath -> IO (Either String Workflow)
80+
importWorkflow yamlFilePath = do
81+
result <- YAML.decodeFileEither @Workflow yamlFilePath
82+
case result of
83+
Left parseException ->
84+
return $ Left $ YAML.prettyPrintParseException parseException
85+
Right workflow ->
86+
return $ Right workflow
87+
88+
-- Example usage
89+
main :: IO ()
90+
main = do
91+
result <- importWorkflow ".github/workflows/ci.yml"
92+
case result of
93+
Left errorMsg -> putStrLn $ "Failed to parse workflow: " ++ errorMsg
94+
Right workflow -> do
95+
putStrLn "Successfully parsed workflow!"
96+
print workflow
97+
```

src/Language/Github/Actions/Concurrency.hs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
{-# LANGUAGE OverloadedStrings #-}
55
{-# LANGUAGE RecordWildCards #-}
66

7+
-- |
8+
-- Module : Language.Github.Actions.Concurrency
9+
-- Description : GitHub Actions concurrency settings
10+
-- Copyright : (c) 2025 Bellroy Pty Ltd
11+
-- License : BSD-3-Clause
12+
-- Maintainer : Bellroy Tech Team <[email protected]>
13+
--
14+
-- This module provides the 'Concurrency' type for controlling concurrent execution
15+
-- of GitHub Actions workflows and jobs.
16+
--
17+
-- Concurrency settings allow you to control how many workflow runs or job executions
18+
-- can happen simultaneously, helping to prevent resource conflicts and manage costs.
19+
--
20+
-- For more information about GitHub Actions concurrency, see:
21+
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency>
722
module Language.Github.Actions.Concurrency
823
( Concurrency (..),
924
gen,
@@ -18,8 +33,30 @@ import Hedgehog (MonadGen)
1833
import qualified Hedgehog.Gen as Gen
1934
import qualified Hedgehog.Range as Range
2035

36+
-- | Concurrency settings for workflows and jobs.
37+
--
38+
-- Concurrency allows you to control whether multiple workflow runs or job executions
39+
-- can happen simultaneously. This is useful for preventing conflicts when deploying
40+
-- or when you want to ensure only one workflow processes a particular resource at a time.
41+
--
42+
-- Example usage:
43+
--
44+
-- @
45+
-- import Language.Github.Actions.Concurrency
46+
--
47+
-- -- Only allow one deployment per branch
48+
-- deploymentConcurrency :: Concurrency
49+
-- deploymentConcurrency = Concurrency
50+
-- { group = Just "${{ github.ref }}"
51+
-- , cancelInProgress = Just True
52+
-- }
53+
-- @
54+
--
55+
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency>
2156
data Concurrency = Concurrency
22-
{ group :: Maybe Text,
57+
{ -- | Concurrency group identifier
58+
group :: Maybe Text,
59+
-- | Whether to cancel in-progress runs
2360
cancelInProgress :: Maybe Bool
2461
}
2562
deriving stock (Eq, Generic, Ord, Show)
@@ -37,6 +74,7 @@ instance ToJSON Concurrency where
3774
"cancel-in-progress" .= cancelInProgress
3875
]
3976

77+
-- | Generate a random 'Concurrency' for property-based testing.
4078
gen :: (MonadGen m) => m Concurrency
4179
gen = do
4280
group <- Gen.maybe (Gen.text (Range.linear 1 5) Gen.alphaNum)

src/Language/Github/Actions/Defaults.hs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
{-# LANGUAGE OverloadedStrings #-}
44
{-# LANGUAGE RecordWildCards #-}
55

6+
-- |
7+
-- Module : Language.Github.Actions.Defaults
8+
-- Description : Default settings for GitHub Actions workflows and jobs
9+
-- Copyright : (c) 2025 Bellroy Pty Ltd
10+
-- License : BSD-3-Clause
11+
-- Maintainer : Bellroy Tech Team <[email protected]>
12+
--
13+
-- This module provides the 'Defaults' type for setting default values that apply
14+
-- to all steps within a job or all jobs within a workflow.
15+
--
16+
-- Defaults allow you to specify common settings like shell type and working directory
17+
-- that will be inherited by all steps unless explicitly overridden at the step level.
18+
--
19+
-- For more information about GitHub Actions defaults, see:
20+
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaults>
621
module Language.Github.Actions.Defaults
722
( Defaults (..),
823
gen,
@@ -20,8 +35,39 @@ import qualified Hedgehog.Range as Range
2035
import Language.Github.Actions.Shell (Shell)
2136
import qualified Language.Github.Actions.Shell as Shell
2237

38+
-- | Default settings for steps within a job or jobs within a workflow.
39+
--
40+
-- Defaults provide a convenient way to set common configuration that applies to
41+
-- multiple steps without having to repeat the same settings everywhere.
42+
--
43+
-- Currently supports defaults for the 'run' command configuration:
44+
--
45+
-- Example usage:
46+
--
47+
-- @
48+
-- import Language.Github.Actions.Defaults
49+
-- import Language.Github.Actions.Shell
50+
--
51+
-- -- Set bash as default shell for all steps
52+
-- bashDefaults :: Defaults
53+
-- bashDefaults = Defaults
54+
-- { runShell = Just (Bash Nothing)
55+
-- , runWorkingDirectory = Nothing
56+
-- }
57+
--
58+
-- -- Set working directory for all steps
59+
-- workdirDefaults :: Defaults
60+
-- workdirDefaults = Defaults
61+
-- { runShell = Nothing
62+
-- , runWorkingDirectory = Just "\/src"
63+
-- }
64+
-- @
65+
--
66+
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaults>
2367
data Defaults = Defaults
24-
{ runShell :: Maybe Shell,
68+
{ -- | Default shell for run commands
69+
runShell :: Maybe Shell,
70+
-- | Default working directory for run commands
2571
runWorkingDirectory :: Maybe Text
2672
}
2773
deriving stock (Eq, Generic, Ord, Show)
@@ -45,6 +91,10 @@ instance ToJSON Defaults where
4591
)
4692
]
4793

94+
-- | Generate a random 'Defaults' for property-based testing.
95+
--
96+
-- This generator creates defaults with randomized properties suitable for testing
97+
-- JSON serialization roundtrips and other property-based tests.
4898
gen :: (MonadGen m) => m Defaults
4999
gen = do
50100
runShell <- Gen.maybe Shell.gen
Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,82 @@
11
{-# LANGUAGE ScopedTypeVariables #-}
22
{-# LANGUAGE TypeApplications #-}
33

4+
-- |
5+
-- Module : Language.Github.Actions.Internal
6+
-- Description : Internal utility functions for GitHub Actions library
7+
-- Copyright : (c) 2025 Bellroy Pty Ltd
8+
-- License : BSD-3-Clause
9+
-- Maintainer : Bellroy Tech Team <[email protected]>
10+
--
11+
-- This module provides internal utility functions used throughout the GitHub Actions
12+
-- library. These functions are not intended for external use but are exported for
13+
-- testing and internal module dependencies.
14+
--
15+
-- The main utility is 'inverseMap' which creates bidirectional mappings between
16+
-- enumeration values and their string representations, commonly used for JSON
17+
-- serialization of activity types and other enumerated values.
418
module Language.Github.Actions.Internal
519
( inverseMap,
620
)
721
where
822

923
import qualified Data.Map as Map
1024

11-
-- | https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Enum.html#inverseMap
25+
-- | Create an inverse mapping from keys back to enumeration values.
26+
--
27+
-- This function takes a function that converts enumeration values to keys
28+
-- and returns a function that can look up the original enumeration value
29+
-- from a key. This is commonly used for parsing JSON representations of
30+
-- enumeration types.
31+
--
32+
-- Example usage:
33+
--
34+
-- @
35+
-- data Color = Red | Green | Blue
36+
-- deriving (Bounded, Enum, Eq, Ord, Show)
37+
--
38+
-- colorToText :: Color -> Text
39+
-- colorToText Red = "red"
40+
-- colorToText Green = "green"
41+
-- colorToText Blue = "blue"
42+
--
43+
-- textToColor :: Text -> Maybe Color
44+
-- textToColor = inverseMap colorToText
45+
--
46+
-- -- Usage:
47+
-- textToColor "red" == Just Red
48+
-- textToColor "green" == Just Green
49+
-- textToColor "blue" == Just Blue
50+
-- textToColor "yellow" == Nothing
51+
-- @
52+
--
53+
-- Based on: <https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Enum.html#inverseMap>
1254
inverseMap ::
1355
forall a k.
1456
(Bounded a, Enum a, Ord k) =>
57+
-- | Function from enumeration values to keys
1558
(a -> k) ->
59+
-- | Function from keys to enumeration values
1660
(k -> Maybe a)
1761
inverseMap f = (`Map.lookup` dict)
1862
where
1963
dict :: Map.Map k a
2064
dict = Map.fromList (fmapToFst f (universe @a))
2165

22-
-- | https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Extra.Tuple.html#fmapToFst
66+
-- | Map a function over a functor, creating tuples with the result as the first element.
67+
--
68+
-- Based on: <https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Extra.Tuple.html#fmapToFst>
2369
fmapToFst :: (Functor f) => (a -> b) -> f a -> f (b, a)
2470
fmapToFst = fmap . toFst
2571

26-
-- | https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Enum.html#universe
72+
-- | Generate all values of a bounded enumeration type.
73+
--
74+
-- Based on: <https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Enum.html#universe>
2775
universe :: (Bounded a, Enum a) => [a]
2876
universe = [minBound .. maxBound]
2977

30-
-- | https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Extra.Tuple.html#toFst
78+
-- | Apply a function and create a tuple with the result as the first element.
79+
--
80+
-- Based on: <https://hackage.haskell.org/package/relude-1.2.2.0/docs/src/Relude.Extra.Tuple.html#toFst>
3181
toFst :: (a -> b) -> a -> (b, a)
3282
toFst f a = (f a, a)

0 commit comments

Comments
 (0)