Table of Contents
Github CI is free to use and easy to set up. Being a native CI solution, it has great integration with the rest of the Github UI. Jobs run simultaneously, your dependencies can be cached, and your build artefacts can be processed. Haskellers will also appreciate that basic config with reasonable defaults for Haskell projects is much simpler than the corresponding config for Travis.
By virtue of commiting legal config to .github/workflows/
, all subsequent pushes will use Github's CI, even in the PR that first introduces it. I typically call this file ci.yaml
. This defines a Pipeline which can contain one or more Jobs. By writing another .yaml
file to .github/workflows/
, you define another independent Pipeline. These will run simultaneously, as do the Jobs themselves.
...
jobs:
build:
name: CI
runs-on: ubuntu-latest
steps:
- name: Setup GHC
uses: actions/setup-haskell@v1.1
with:
ghc-version: "8.8.3"
enable-stack: true
- name: Clone project
uses: actions/checkout@v2
...
Github Actions are like standalone IO
functions that alter the Pipeline's environment. We can see that the official setup-haskell
Action has stack
support built in, so we activate that and set our preferred GHC version with with
. Notice that Actions have a version, which we can track by subscribing to releases:
You'll see config like this below:
- name: Cache dependencies
uses: actions/cache@v1
with:
path: ~/.stack
key: ${{ runner.os }}-${{ hashFiles('stack.yaml') }}
restore-keys: |
${{ runner.os }}-
This is about as simple as a cache definition can be. Here are some things to keep in mind about Github CI caching:
tar
before being saved. A 2.5gb .stack/
compresses down to around 475mb.master
can be used on PR branches, but not the other way around.key:
field, but failing that, it falls back to patterns defined in restore-key:
.For Haskell projects, I find that relying on the hash of the contents of stack.yaml is an accurate way to logically separate caches.
If you have a stack-based project and just want simple CI, feel free to copy-and-paste the config below as-is.
The following YAML configuration means:
master
.stack.yaml
. When your stack.yaml
changes, the saved cache will naturally update and be available to subsequent PRs. name: Tests
on:
pull_request:
push:
branches:
- master
jobs:
build:
name: CI
runs-on: ubuntu-latest
steps:
- name: Setup GHC
uses: actions/setup-haskell@v1.1
with:
ghc-version: "8.8.3"
enable-stack: true
- name: Clone project
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v1
with:
path: ~/.stack
key: ${{ runner.os }}-${{ hashFiles('stack.yaml') }}
restore-keys: |
${{ runner.os }}-
- name: Build and run tests
run: "stack test --fast --no-terminal --system-ghc"
The following YAML configuration means the same as above with respect to how often it's ran, but also says:
resolver
field specified in stack.yaml
. name: Tests
on:
pull_request:
push:
branches:
- master
jobs:
build:
name: CI
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
resolver: ['lts-15.6', 'lts-14.27', 'lts-12.26']
include:
- resolver: 'lts-15.6'
ghc: '8.8.3'
- resolver: 'lts-14.27'
ghc: '8.6.5'
- resolver: 'lts-12.26'
ghc: '8.4.4'
steps:
- name: Setup GHC
uses: actions/setup-haskell@v1.1
with:
ghc-version: ${{ matrix.ghc }}
enable-stack: true
- name: Clone project
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v1
with:
path: ~/.stack
key: ${{ matrix.resolver }}-${{ hashFiles('stack.yaml') }}
restore-keys: |
${{ runner.os }}-${{ matrix.resolver }}-
- name: Build and run tests
run: >
stack test
--fast
--no-terminal
--resolver=${{ matrix.resolver }}
--system-ghc
Like you may be used to from other CI services, you can add a badge to your README to report recent build statuses. The token that appears after /workflows/
needs to be the same as the name:
you put in your ci.yaml
.
[[https://github.com/fosskers/aura/workflows/Tests/badge.svg]]
Blog Archive