github-actions-0.1.0.0: src/Language/Github/Actions/Job/Container.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
-- |
-- Module : Language.Github.Actions.Job.Container
-- Description : Container configuration for GitHub Actions jobs
-- Copyright : (c) 2025 Bellroy Pty Ltd
-- License : BSD-3-Clause
-- Maintainer : Bellroy Tech Team <haskell@bellroy.com>
--
-- This module provides the 'JobContainer' type for configuring Docker containers
-- that jobs run inside of in GitHub Actions workflows.
--
-- Job containers allow you to run job steps inside a Docker container with a specific
-- environment, dependencies, and configuration. This provides consistency across
-- different runner environments and enables the use of custom tooling.
--
-- For more information about GitHub Actions job containers, see:
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainer>
module Language.Github.Actions.Job.Container
( JobContainer (..),
gen,
)
where
import Data.Aeson (FromJSON, ToJSON, (.:?), (.=))
import qualified Data.Aeson as Aeson
import Data.Map (Map)
import Data.Maybe (catMaybes)
import Data.Text (Text)
import GHC.Generics (Generic)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
-- | Container configuration for running a job inside a Docker container.
--
-- Job containers provide an isolated, consistent environment for job execution.
-- This is useful for ensuring specific tool versions, operating system environments,
-- or complex dependency setups.
--
-- Example usage:
--
-- @
-- import Language.Github.Actions.Job.Container
-- import qualified Data.Map as Map
--
-- -- Node.js development container
-- nodeContainer :: JobContainer
-- nodeContainer = JobContainer
-- { image = Just "node:18"
-- , env = Just $ Map.fromList [("NODE_ENV", "test")]
-- , credentials = Nothing
-- , options = Nothing
-- , ports = Nothing
-- , volumes = Nothing
-- }
--
-- -- Database testing container with services
-- dbTestContainer :: JobContainer
-- dbTestContainer = JobContainer
-- { image = Just "ubuntu:22.04"
-- , env = Just $ Map.fromList [("DEBIAN_FRONTEND", "noninteractive")]
-- , credentials = Nothing
-- , options = Just "--network postgres"
-- , ports = Just ["8080:8080"]
-- , volumes = Just ["\${{ github.workspace }}:\/workspace"]
-- }
-- @
--
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainer>
data JobContainer = JobContainer
{ -- | Registry credentials for private images
credentials :: Maybe (Map Text Text),
-- | Environment variables for the container
env :: Maybe (Map Text Text),
-- | Docker image to use for the container
image :: Maybe Text,
-- | Additional Docker run options
options :: Maybe Text,
-- | Ports to expose from the container
ports :: Maybe [Text],
-- | Volumes to mount in the container
volumes :: Maybe [Text]
}
deriving stock (Eq, Generic, Ord, Show)
instance FromJSON JobContainer where
parseJSON = Aeson.withObject "JobContainer" $ \o -> do
credentials <- o .:? "credentials"
env <- o .:? "env"
image <- o .:? "image"
options <- o .:? "options"
ports <- o .:? "ports"
volumes <- o .:? "volumes"
pure JobContainer {..}
instance ToJSON JobContainer where
toJSON JobContainer {..} =
Aeson.object $
catMaybes
[ ("credentials" .=) <$> credentials,
("env" .=) <$> env,
("image" .=) <$> image,
("options" .=) <$> options,
("ports" .=) <$> ports,
("volumes" .=) <$> volumes
]
gen :: (MonadGen m) => m JobContainer
gen = do
credentials <- Gen.maybe genTextMap
env <- Gen.maybe genTextMap
image <- Gen.maybe genText
options <- Gen.maybe genText
ports <-
Gen.maybe . Gen.list (Range.linear 0 10) $
Gen.text (Range.linear 1 5) Gen.digit
volumes <- Gen.maybe $ Gen.list (Range.linear 0 10) genText
pure JobContainer {..}
where
genText = Gen.text (Range.linear 1 5) Gen.alphaNum
genTextMap = Gen.map (Range.linear 1 5) $ liftA2 (,) genText genText