Haskell on Actions, Part 2
by @pbrisbin on May 20, 2021
This is the second post in a series about our Haskell projects on GitHub Actions.
At Freckle, we strive to automate as much of our processes as possible; so in this post, we’ll be updating our library example to auto-release to Hackage, and add a new example for executable releases.
- Building a simple project including caching concerns
- Automated releases of libraries or executables
- Docker-based deployments from Actions
Automated release
For our OSS libraries, we want to automate our Hackage releases, but still have
a Human involved in choosing version and updating CHANGELOGs. To support this,
we define a Workflow to run another custom Action when a
tag is created starting with v
:
.github/workflows/release.yml:
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: freckle/stack-upload-action@main
env:
# https://docs.github.com/en/actions/reference/encrypted-secrets
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
Given this, our release process is:
- Open a PR bumping version and authoring CHANGELOG
- Seek review and merge the PR
-
Push the correct
v{version}
tag:git tag -s -m vA.B.C.D vA.B.C.D git push --follow-tags
Automatic release
Step (3) above is a bit tedious and actually error-prone (if you push a tag that
doesn’t match what’s in the latest package.yaml
). So we’re starting to
experiment with automating away that part too.
haskell-tag-action
helps accomplish this. The idea is to
infer version from package.yaml
and, if one doesn’t yet exist, push a version
tag. My original intent was to have that trigger our existing release.yml
, but
GitHub doesn’t allow actions taken in one Workflow, using the built-in token, to
trigger other Workflows. (How dare they infringe on my right to trigger infinite
Workflow loops!) So instead, we just do it in release.yml
:
on:
push:
branches: main
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: freckle/haskell-tag-action@main
- if: ${{ steps.tag.outputs.tag }}
env:
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
uses: freckle/stack-upload-action@main
Now, our release process is:
- Open a PR bumping version and authoring CHANGELOG
- Seek review and merge the PR
Executables
One of our Haskell projects is an internal CLI, so we build binaries for Linux and OSX in response to a tag being pushed (we haven’t yet reached automatic release for this, only automated):
name: Release
on:
push:
tags:
- 'v*'
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- uses: actions/create-release@v1
id: create-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
outputs:
upload_url: ${{ steps.create-release.outputs.upload_url }}
upload-assets:
needs: create-release
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
suffix: x86_64-linux
- os: macOS-latest
suffix: x86_64-osx
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: freckle/stack-cache-action@main
# Actual build steps ellided
- run: make dist/my-tool.tar.gz
- uses: actions/upload-release-asset@v1
id: upload-release-asset
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./dist/my-tool.tar.gz
asset_name: my-tool-${{ matrix.suffix }}.tar.gz
asset_content_type: application/gzip
For internal applications which are not libraries or executables, our automated releases involve building Docker images and updating AWS services. That’ll be the subject of our last and final post in this series. Stay tuned!