Skip to content

Refactor CI

Refactor CI #139

Workflow file for this run

name: Haskell CI
on:
push:
branches:
- "main"
pull_request:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
# Set the default shell on all platforms.
defaults:
run:
shell: sh
jobs:
################################################################################
# Build
################################################################################
build:
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
fail-fast: false
# Picking matrix combinations is tricky as it's a trade-off: on the one
# hand we want to test as many interesting combinations as possible, but
# on the other hand we don't want combinatorial explosion. We strike a
# balance as follows:
#
# * Build and test with all combinations of OS/GHC/Cabal, but with a fixed
# Botan version, preferably the latest version which is currently
# Botan-3.9.0.
#
# * Build and test with all Botan versions, but with a fixed OS/GHC/Cabal
# combination, preferably Linux/GHC-9.6/Cabal-3.12
#
# TODO: ideally, we would be able to detect automatically that the matrix
# should be updated to include newer Botan versions (or GHC versions for
# that matter). The setup-botan action already contains a TODO that would
# allow us to specify incomplete Botan versions like 3 and 3.8 that would
# then automatically be resolved to the greatest complete versions, e.g.,
# 3.9.0 and 3.8.1. Similarly, haskell-actions/setup@v2 allows specifying
# incomplete GHC and Cabal versions that are resolved to complete
# versions. However, if a new Botan MAJOR and/or MINOR version is released
# (or a new GHC major version), then we would want to include it as a new
# matrix combination while keeping the older combinations. Automatic
# resolving does not solve this.
matrix:
os: ["ubuntu-latest", "macOS-latest", "windows-latest"]
#ghc-version: ["9.2", "9.4", "9.6", "9.8", "9.10", "9.12"]
ghc-version: ["9.6"]
cabal-version: ["3.12"]
botan-version: ["jdral/3.9.0"]
#include:
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.0.0"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.1.1"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.2.0"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.3.0"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.4.0"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.5.0"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.6.1"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.7.1"
#- os: "ubuntu-latest"
# ghc-version: "9.6"
# cabal-version: "3.12"
# botan-version: "3.8.1"
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v5
- name: 🛠️ Setup Haskell
id: setup-haskell
uses: haskell-actions/setup@v2
with:
ghc-version: ${{ matrix.ghc-version }}
cabal-version: ${{ matrix.cabal-version }}
- name: "🛠️ [Linux]: Update environment variables"
if: ${{ runner.os == 'Linux' }}
run: |
echo "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
- name: "🛠️ [Windows]: Update environment variables"
if: ${{ runner.os == 'Windows' }}
run: |
echo "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1" >> $GITHUB_ENV
echo "PKG_CONFIG_ALLOW_SYSTEM_LIBS=1" >> $GITHUB_ENV
ghcup run -m -- pacman --noconfirm -S mingw-w64-x86_64-pkg-config
echo "C:/msys64/mingw64/bin" >> "$GITHUB_PATH"
- name: 🛠️ Setup Botan
uses: ./.github/actions/setup-botan
# Time out this step. If it's taking too long to finish, something is
# wrong with the setup-botan action. Maybe it's compiling too many things?
timeout-minutes: 30
with:
botan-version: ${{ matrix.botan-version }}
run-tests: ${{ runner.os == 'Windows' && true || false }}
- name: "🛠️ [Windows]: check botan install"
if: ${{ runner.os == 'Windows' }}
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
run: |
ls -la C:/msys64/mingw64/bin
ls -la C:/msys64/mingw64/lib
botan --version
cp C:/msys64/mingw64/bin/**.dll .
ls -la .
# TODO: should we set the C compiler options so that (some) warnings are
# also interpreted as errors, like we set -Werror for GHC?
- name: 🛠️ Configure
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
run: |
cabal configure \
--enable-tests \
--enable-benchmarks \
--disable-documentation \
${{ runner.os == 'Windows' && '--extra-prog-path=C:/msys64/mingw64/bin' }} \
${{ runner.os == 'Windows' && '--extra-prog-path=./' }} \
${{ runner.os == 'Windows' && '--extra-include-dirs=C:/msys64/mingw64/include' }} \
${{ runner.os == 'Windows' && '--extra-lib-dirs=C:/msys64/mingw64/lib' }} \
--constraint="botan-bindings +pkg-config" \
--ghc-options="-Werror"
cat "cabal.project.local"
# This step will generate a dist-newstyle/cache/plan.json file. This file is
# hashed and the resulting hash is included in the cache key. Any changes in
# the build plan, for example due to changed dependencies, will therefore
# create new caches.
- name: 💾 Generate Cabal plan
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
run: |
cabal build all --dry-run
- name: 💾 Restore cache
uses: actions/cache/restore@v4
id: restore-cabal-cache
env:
key: build-botan-${{ runner.os }}-ghc-${{ steps.setup-haskell.outputs.ghc-version }}-cabal-${{ steps.setup-haskell.outputs.cabal-version }}-Botan-${{ matrix.botan-version }}
with:
path: ${{ steps.setup-haskell.outputs.cabal-store }}
key: ${{ env.key }}-plan-${{ hashFiles('dist-newstyle/cache/plan.json') }}
# We allow restoring caches with outdated build plans. An upside to this
# is that changing the build plan does not require rebuilding all
# dependencies, because it is likely that not all dependencies changed.
# A downside of this is that the caches are cumulative (in size): new
# dependencies are built and installed in the cabal store, but old
# dependencies are never deleted. As a result, caches have to be reset
# every once in a while when they become too large, for example by
# changing the env.key value.
#
# TODO: we should investigate whether it is possible to automatically
# garbage collect old unused dependencies to combat that the size of
# caches increase over time, and whether it is useful. We recall there
# being a custom action out on GitHub somewhere that would do this
# garbage collection for us.
restore-keys: ${{ env.key }}-
- name: 🛠️ Build Cabal dependencies
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
run: |
cabal build all --only-dependencies
- name: 💾 Save cache
uses: actions/cache/save@v4
if: ${{ steps.restore-cabal-cache.outputs.cache-hit != 'true' }}
with:
path: ${{ steps.setup-haskell.outputs.cabal-store }}
key: ${{ steps.restore-cabal-cache.outputs.cache-primary-key }}
# TODO: on Windows we prefix the cabal command with 'ghcup run -m --' to be
# able to use pkg-config. For uniformity, we originally wanted to include
# the prefix on Linux and MacOS too (even though it's not necessary), but
# the ghcup executable is not found on the PATH on MacOS, see
# https://github.com/haskell-actions/setup/issues/81.
- name: 🏗️ Build
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
run: |
cabal build all
# TODO: see the TODO on the Build step
- name: 🧪 Test
shell: C:/msys64/usr/bin/bash.exe -e '{0}'
id: test
env:
# Most tests in the repositoy use hspec instead of Tasty, but we make
# sure to include a timeout for Tasty tests regardless.
TASTY_TIMEOUT: "5m"
run: |
echo $PATH
cabal run --verbose botan-low-bcrypt-tests
cabal test all -j1 --test-show-details=direct