Skip to content

;ci: binaries-*: add more hx-based workflows, and arm linux binaries #4

;ci: binaries-*: add more hx-based workflows, and arm linux binaries

;ci: binaries-*: add more hx-based workflows, and arm linux binaries #4

# TRIGGER: Runs on any push to binaries-linux-arm64-hx or binaries branches.
# ACTION: Builds, tests and saves linux arm64 DYNAMIC binaries with hx,
# using the ghc configured in hx.toml and the deps saved in hx.lock.
name: binaries-linux-arm64-hx
on:
push:
branches: [ binaries-linux-arm64-hx, binaries ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-24.04-arm
# https://github.com/actions/partner-runner-images/blob/main/images/arm-ubuntu-24-image.md
env:
GH_TOKEN: ${{ github.token }} # lets `gh release download` authenticate & dodge rate limits
HX_AUTO_INSTALL: "true" # install missing GHC toolchain without prompting (hx wants true/false, not 1)
ghc: "9.14.1" # matches hx.toml [toolchain] ghc / stack.yaml compiler
cabal: "3.16.1.0" # cabal-install version to fetch (official self-contained binary)
hxasset: "aarch64-unknown-linux-gnu.tar.gz" # hx release asset suffix for this platform
cabalarch: "aarch64-linux-deb12" # cabal-install bindist arch for this platform (deb12 = portable glibc)
steps:
- name: Show platform info
run: |
arch
uname -a
- name: Install system build deps
run: |
sudo apt-get update -qq
sudo apt-get install -y libgmp-dev zlib1g-dev libncurses-dev
- name: Check out
uses: actions/checkout@v7
# have to fetch everything for git describe for --version
with:
fetch-depth: 0
- name: Check embedded files
run: |
tools/checkembeddedfiles
- name: Cache - hx toolchains (GHC bindist)
uses: actions/cache@v6
with:
# ~/.ghcup too: on arm64 hx installs GHC via ghcup (its direct download lacks a checksum here), so GHC lands there
path: |
~/.hx
~/.ghcup
key: ${{ runner.os }}-arm64-hx-toolchain-${{ env.ghc }}
- name: Cache - hx compiled dependency store
uses: actions/cache@v6
with:
# hx's compiled-dep store on linux is ~/.cache/hx: the `directories` crate uses the bare app
# name under XDG, unlike macos which uses the reverse-DNS io.raskell.hx. (~800 MB; confirmed on CI.)
path: ~/.cache/hx
key: ${{ runner.os }}-arm64-hx-store-${{ env.ghc }}-${{ hashFiles('hx.lock') }}
restore-keys: |
${{ runner.os }}-arm64-hx-store-${{ env.ghc }}-
- name: Cache - cabal Hackage index (used by the system cabal)
uses: actions/cache@v6
with:
path: ~/.cache/cabal
key: ${{ runner.os }}-arm64-cabal-index-${{ hashFiles('hx.lock') }}
restore-keys: |
${{ runner.os }}-arm64-cabal-index-
- name: Cache - cabal binary
uses: actions/cache@v6
with:
path: ~/.local/bin/cabal
key: ${{ runner.os }}-arm64-cabal-bin-${{ env.cabal }}
# NB: the project build (.hx/cabal/dist-newstyle) is deliberately NOT cached (stale --version commit otherwise).
- name: Ensure ~/.local/bin exists and is in PATH
run: |
mkdir -p "$HOME/.local/bin"
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install hx (latest release)
run: |
mkdir -p /tmp/hxdl
gh release download --repo arcanist-sh/hx --pattern "*$hxasset" --dir /tmp/hxdl --clobber
tar xzf /tmp/hxdl/*"$hxasset" -C /tmp/hxdl
install -m 755 "$(find /tmp/hxdl -type f -name hx | head -1)" "$HOME/.local/bin/hx"
hx --version
hx doctor || true
- name: Install ghcup (arm64 runner lacks it; hx needs it for a verified GHC)
run: |
# Unlike the x64 ubuntu image, ubuntu-24.04-arm has no preinstalled ghcup. hx's direct GHC
# download is refused here (no published aarch64 checksum for this GHC), so it falls back to
# ghcup - which must be present. Install ghcup only (no GHC/cabal); hx then drives it.
if ! command -v ghcup >/dev/null; then
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | \
BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 sh
echo "$HOME/.ghcup/bin" >> "$GITHUB_PATH"
fi
"$HOME/.ghcup/bin/ghcup" --version
- name: Install GHC and cabal
run: |
hx toolchain install --set "$ghc" # delegates to ghcup (installed above) for the verified aarch64 GHC
# fetch the official cabal binary unless already present (arm64 has no preinstalled cabal)
if ! command -v cabal >/dev/null; then
mkdir -p /tmp/cabaldl
curl -fsSL "https://downloads.haskell.org/cabal/cabal-install-$cabal/cabal-install-$cabal-$cabalarch.tar.xz" -o /tmp/cabaldl/cabal.tar.xz
tar xJf /tmp/cabaldl/cabal.tar.xz -C /tmp/cabaldl
install -m 755 "$(find /tmp/cabaldl -type f -name cabal | head -1)" "$HOME/.local/bin/cabal"
fi
cabal --version
# the system cabal has no Hackage index yet, so download it (kept fresh each run, cheap when ~/.cache/cabal is warm)
cabal update
hx toolchain status || true
- name: List dependencies
run: |
hx list
- name: Build hledger
# and run unit + doc tests
run: |
# no --release: that forces -O2; we use hx.toml [build] optimization = 1 to match stack's -O1
hx build
# hx test
- name: Collect built binaries onto PATH
run: |
# hx builds via cabal into .hx/cabal/dist-newstyle; the exe is under .../x/<exe>/{,opt/}build/<exe>/<exe>
for exe in hledger hledger-ui hledger-web; do
bin=$(find .hx/cabal/dist-newstyle -type f -name "$exe" -path "*/x/$exe/*build/$exe/$exe" | head -1)
if [[ -z "$bin" ]]; then
echo "ERROR: $exe executable not found under .hx/cabal/dist-newstyle" >&2
find .hx/cabal/dist-newstyle -type f -name "$exe" >&2 || true
exit 1
fi
echo "$exe -> $bin"
cp "$bin" "$HOME/.local/bin/$exe"
done
hledger --version
- name: Install shelltestrunner
run: |
command -v shelltest >/dev/null || cabal install --overwrite-policy=always shelltestrunner-1.11
shelltest --version
- name: Test functional tests (excluding addons)
run: |
COLUMNS=80 shelltest --execdir -j16 hledger/test -x /_ -x /addons -x ledger-compat/ledger-baseline -x ledger-compat/ledger-regress -x ledger-compat/ledger-collected
# artifacts:
- name: Gather binaries
run: |
mkdir tmp
cp ~/.local/bin/hledger tmp
cp ~/.local/bin/hledger-ui tmp
cp ~/.local/bin/hledger-web tmp
cp hledger/embeddedfiles/*.1 tmp
cp hledger/embeddedfiles/*.info tmp
cp hledger/shell-completion/hledger-completion.bash tmp
strip tmp/hledger
strip tmp/hledger-ui
strip tmp/hledger-web
cd tmp
tar cvf hledger-linux-arm64.tar hledger hledger-ui hledger-web *.1 *.info hledger-completion.bash
file hledger
ldd hledger || true
./hledger --version
./hledger-ui --version
./hledger-web --version
# upload-artifact loses execute permissions, so we tar the binaries to preserve them.
- name: Upload binaries artifact
uses: actions/upload-artifact@v7
with:
name: hledger-linux-arm64
path: tmp/hledger-linux-arm64.tar