anwesen/.forgejo/workflows/build.yml
Andreas Brenner 5cd1f0799d ANW-30 CI: cross-compile arm64 on the host runner
The arm64 (apple-silicon) target assumed a native macOS runner, which
does not exist on the forge. There is one self-hosted runner, ws-brn,
registered linux-amd64:host -- jobs run directly on the host as brn,
with no docker. So cross-rs cannot run (no container engine), and it
cannot target apple-darwin from Linux regardless.

Build arm64 by native cross-compilation instead: rustup target add
aarch64-unknown-linux-gnu plus the host's aarch64-linux-gnu-gcc linker.
Same arm64 binary, no docker. The asset is now aarch64-linux.

Also drop the host-incompatible setup steps surfaced by running it on
the real runner: hurl is already installed (the sudo dpkg step failed,
brn has no passwordless sudo) and the aarch64 linker is already present.
Split a single test gate (test + hurl) from the per-target build matrix.

Pass inputs.tag via env to the staging step (sie review note, ANW-30).
2026-06-24 00:30:46 +03:00

135 lines
5.5 KiB
YAML

# Forgejo build + release for anwesen ([ANW-30]).
#
# The operator's Forgejo forge is the primary home (the GitHub mirror's
# release.yml under .github/ is the secondary). This workflow is the
# Forgejo-side counterpart: it builds the release binaries for both targets
# and publishes them to a Forgejo release.
#
# Unlike the GitHub workflow it is NOT driven by a release-published event.
# It runs on demand -- from the Forgejo UI ("Run workflow") or via the API:
#
# POST /api/v1/repos/{owner}/{repo}/actions/workflows/build.yml/dispatches
#
# so a build can be triggered for a test without first cutting a release.
# The `tag` input names the release the assets attach to; `prerelease`
# defaults true so test runs land as a replaceable prerelease rather than a
# stable release. forgejo-release creates the release (from the checked-out
# sha) if it does not exist, and `override: true` lets a re-run replace the
# assets.
#
# Runner topology. There is one self-hosted runner registered `linux-amd64`
# with `:host` execution -- jobs run directly on the host, not in a
# container. The host already provides node, git, rustup/cargo, hurl, and the
# aarch64 cross linker, so the workflow uses them in place rather than
# installing anything (the host user has no passwordless sudo). Both targets
# build on this one amd64 host: x86_64 natively, aarch64 by cross-compilation.
name: build
on:
workflow_dispatch:
inputs:
tag:
description: Release tag the binaries attach to (created if absent).
required: true
default: nightly
prerelease:
description: Mark the release as a prerelease.
type: boolean
default: true
# forgejo-release uploads assets to a release in this repository.
permissions:
contents: write
jobs:
# Single test gate for both targets. The suite and the HTTP contract
# harness are architecture-independent, so they run once, natively on the
# amd64 host. Both build jobs depend on this, so a release never ships a
# red build.
test:
name: test
runs-on: linux-amd64
steps:
- name: Check out the workflow ref
uses: actions/checkout@v4
# The :host runner runs each step in a non-login /bin/sh that does not
# have the host's ~/.cargo/bin on PATH. Prepend it in each step that
# needs the toolchain -- POSIX, self-contained, no reliance on a
# bashism (`source`) or on GITHUB_PATH propagating across steps.
- name: Run the Rust test suite
run: |
export PATH="$HOME/.cargo/bin:$PATH"
cargo test --locked
# The HTTP contract harness (tests/run-hurl.sh, per ADR-008) boots
# `anwesen serve` and runs hurl against it -- headless, so it runs in CI.
# hurl is on the host PATH; the harness builds anwesen, so cargo too.
- name: Run the HTTP contract tests
run: |
export PATH="$HOME/.cargo/bin:$PATH"
tests/run-hurl.sh
build:
name: ${{ matrix.label }}
needs: test
runs-on: linux-amd64
strategy:
# One target's failure should not cancel the other's build.
fail-fast: false
matrix:
include:
# amd64 Linux -- the deploy target. Built natively for the host.
- target: x86_64-unknown-linux-gnu
label: x86_64-linux
# arm64 Linux -- cross-compiled on the same amd64 host. cross-rs
# would need a container engine the host runner does not provide
# (and cannot target apple-darwin from Linux anyway), so this uses
# the rustup target plus the host's aarch64-linux-gnu-gcc linker.
- target: aarch64-unknown-linux-gnu
label: aarch64-linux
env:
# cargo picks the linker for the aarch64 target from this; the host
# already provides aarch64-linux-gnu-gcc. Harmless for the amd64 target.
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
steps:
- name: Check out the workflow ref
uses: actions/checkout@v4
# See the test job: prepend the host toolchain to PATH in each step. The
# host toolchain is rustup-managed; ensure the cross target's std is
# present (idempotent for the native target).
- name: Add the Rust target
run: |
export PATH="$HOME/.cargo/bin:$PATH"
rustup target add ${{ matrix.target }}
- name: Build the release binary
run: |
export PATH="$HOME/.cargo/bin:$PATH"
cargo build --release --locked --target ${{ matrix.target }}
# forgejo-release uploads everything under the release dir, so the
# staged name is the asset name -- make the target explicit. The tag
# goes through env, not direct interpolation into the run body.
- name: Stage the asset for its target
env:
TAG: ${{ inputs.tag }}
run: |
mkdir -p dist/release
install -m 0755 "target/${{ matrix.target }}/release/anwesen" \
"dist/release/anwesen-${TAG}-${{ matrix.label }}"
- name: Publish the binary to the Forgejo release
uses: https://code.forgejo.org/actions/forgejo-release@v2
with:
direction: upload
url: ${{ env.GITHUB_SERVER_URL }}
repo: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ inputs.tag }}
sha: ${{ github.sha }}
release-dir: dist/release
prerelease: ${{ inputs.prerelease }}
# A re-run for the same tag replaces the assets rather than failing.
override: true