Logo

dhall-buildkite / 1.0.0 / Lib / Cmds.dhall

Description

This module provides a low-level mapping for executing commands in Buildkite.
It includes functions for running commands, chaining them together, and
running them in Docker containers. It also provides a way to handle
caching through Docker.
The module is designed to be used as a building block for higher-level
command execution and caching strategies.
Example usage:
 
...
, commands = [ Cmd.run "echo hello world in docker" ]
...

Above code is used in Base.dhall to define the `commands` field in a Buildkite

Source

let Prelude = ../External/Prelude.dhall

let P = Prelude

let Optional/map = P.Optional.map

let Text/concatSep = P.Text.concatSep

let Text/concatMap = P.Text.concatMap

let module =
      \(environment : List Text) ->
        let Docker =
              { Type =
                  { image : Text
                  , extraEnv : List Text
                  , privileged : Bool
                  , useBash : Bool
                  }
              , default =
                { extraEnv = [] : List Text
                , privileged = False
                , useBash = True
                }
              }

        let Cmd = { line : Text, readable : Optional Text }

        let run
            : Text -> Cmd
            = \(script : Text) -> { line = script, readable = Some script }

        let chain
            : List Text -> Cmd
            = \(chainOfCommands : List Text) ->
                run (Text/concatSep " && " chainOfCommands)

        let quietly
            : Text -> Cmd
            = \(script : Text) -> { line = script, readable = None Text }

        let true
            : Cmd
            = quietly "true"

        let false
            : Cmd
            = quietly "false"

        let inDocker
            : Docker.Type -> Cmd -> Cmd
            = \(docker : Docker.Type) ->
              \(inner : Cmd) ->
                let envVars =
                      Text/concatMap
                        Text
                        (\(var : Text) -> " --env ${var}")
                        (docker.extraEnv # environment)

                let outerDir
                    : Text
                    = "\\\$BUILDKITE_BUILD_CHECKOUT_PATH"

                let sharedDir
                    : Text
                    = "/var/buildkite/shared"

                let entrypoint
                    : Text
                    = if docker.useBash then "/bin/bash" else "/bin/sh"

                in  { line =
                        "docker run -it --rm --entrypoint ${entrypoint} --init --volume /var/secrets:/var/secrets --volume ${sharedDir}:/shared --volume ${outerDir}:/workdir --workdir /workdir${envVars}${if    docker.privileged
                                                                                                                                                                                                            then  " --privileged"
                                                                                                                                                                                                            else  ""} ${docker.image} -c '${inner.line}'"
                    , readable =
                        Optional/map
                          Text
                          Text
                          ( \(readable : Text) ->
                              "Docker@${docker.image} ( ${readable} )"
                          )
                          inner.readable
                    }

        let runInDocker
            : Docker.Type -> Text -> Cmd
            = \(docker : Docker.Type) ->
              \(script : Text) ->
                inDocker docker (run script)

        let CacheSetupCmd =
              { Type = { create : Cmd, package : Cmd }, default = {=} }

        let format
            : Cmd -> Text
            = \(cmd : Cmd) -> cmd.line

        let cacheThrough
            : Docker.Type -> Text -> CacheSetupCmd.Type -> Cmd
            = \(docker : Docker.Type) ->
              \(cachePath : Text) ->
              \(cmd : CacheSetupCmd.Type) ->
                let missScript =
                      format cmd.create ++ " && " ++ format cmd.package

                let missCmd = runInDocker docker missScript

                in  { line =
                        "./buildkite/scripts/cache-through.sh ${cachePath} \"${format
                                                                                 missCmd}\""
                    , readable =
                        Optional/map
                          Text
                          Text
                          ( \(readable : Text) ->
                              "Cache@${cachePath} ( onMiss = ${readable} )"
                          )
                          missCmd.readable
                    }

        in  { Type = Cmd
            , Docker
            , CacheSetupCmd
            , quietly
            , run
            , chain
            , true
            , false
            , runInDocker
            , inDocker
            , cacheThrough
            , format
            }

let tests =
      let M = module [ "TEST" ]

      let dockerExample =
              assert
            :     { line =
                      "docker run -it --rm --entrypoint /bin/bash --init --volume /var/secrets:/var/secrets --volume /var/buildkite/shared:/shared --volume \\\$BUILDKITE_BUILD_CHECKOUT_PATH:/workdir --workdir /workdir --env ENV1 --env ENV2 --env TEST foo/bar:tag -c 'echo hello'"
                  , readable = Some "Docker@foo/bar:tag ( echo hello )"
                  }
              ===  M.inDocker
                     M.Docker::{
                     , image = "foo/bar:tag"
                     , extraEnv = [ "ENV1", "ENV2" ]
                     }
                     (M.run "echo hello")

      let cacheExample =
              assert
            :     "./buildkite/scripts/cache-through.sh data.tar \"docker run -it --rm --entrypoint /bin/bash --init --volume /var/secrets:/var/secrets --volume /var/buildkite/shared:/shared --volume \\\$BUILDKITE_BUILD_CHECKOUT_PATH:/workdir --workdir /workdir --env ENV1 --env ENV2 --env TEST foo/bar:tag -c 'echo hello > /tmp/data/foo.txt && tar cvf data.tar /tmp/data'\""
              ===  M.format
                     ( M.cacheThrough
                         M.Docker::{
                         , image = "foo/bar:tag"
                         , extraEnv = [ "ENV1", "ENV2" ]
                         }
                         "data.tar"
                         M.CacheSetupCmd::{
                         , create = M.run "echo hello > /tmp/data/foo.txt"
                         , package = M.run "tar cvf data.tar /tmp/data"
                         }
                     )

      in  ""

in  module ../Constants/ContainerEnvVars.dhall