Release a static aarch64 Linux binary #13

Open
claude-bot wants to merge 3 commits from ci/arm64 into master
Collaborator

Closes #12

What

A release tag now publishes sporah-<version>-aarch64-linux.tar.gz next to the x86_64 one. It is cross-compiled on the existing x86 runner with zig and cargo-zigbuild, and smoke-tested under qemu. CI builds aarch64 on every PR, so a broken cross-build shows up before tagging. The README install picks the architecture with uname -m, and its checksum step now also works with busybox sha256sum.

Blast radius

Tier: trunk (the repo has no CLAUDE.md with a blast-radius list, so everything counts as trunk)
Trunk paths touched: .forgejo/workflows/release.yml, .forgejo/workflows/ci.yml, .forgejo/scripts/install-zig.sh (new), README.md
Gate / rollback: revert this merge commit. Earlier releases only have x86_64, and nothing else depends on the aarch64 asset.

Proof

  • Release workflow: the release.yml run steps were extracted and run in rust:1-bookworm, the CI image base, exit 0. cargo test: 9 passed. file: x86_64 is "static-pie linked" and aarch64 is "statically linked". Both print sporah-cli 0.1.0 and fetch live Berlin data; aarch64 ran under qemu-aarch64-static. Both tarballs and SHA256SUMS are produced.
  • README install on the aarch64 tarball in debian:bookworm-slim with no CA store: checksum OK; the tarball has the binary, README, LICENSE and completions; a live HTTPS fetch under qemu exits 0.
  • Static check: accepts the release binaries and rejects /bin/ls (with file 5.44, as in the CI image).
  • README checksum line: passes in busybox and GNU when only one tarball is downloaded. It fails on a corrupted tarball, and on empty input in busybox (rc=1).
  • ci.yml cross-aarch64 job steps, in a fresh rust:1-bookworm container: the build succeeds; the cargo-zigbuild install took about 12s and the build about 18s.
  • Not proven: a real Forgejo runner executing these workflows, the publish step, the aarch64 binary on real ARM hardware (only qemu), the timeout and crash branches of the smoke test, and the wrong-architecture rejection.

Review

Reviewer: fresh-context review at high, then at medium on the fixes: 15 findings, all low. 9 fixed, 1 refuted (busybox empty-input pass; tested and it exits 1). 5 declined or accepted: a qemu crash should block the release; the arch list covers only two targets, each built with a different command; about 12s to compile cargo-zigbuild and no CI cache are acceptable; network fetches are pinned and sha256-checked.
Review-Confidence: 40/100. Every changed file was opened and the review depth was right, but four findings were closed with reasoning rather than evidence, and the new crash and timeout branches were never run.

Confidence by part

  • aarch64 build (zig and cargo-zigbuild, pinned and sha256-checked): high. Built in the release simulation and in the CI job steps.
  • Static-linking check: high. Tested on both the accepting and the rejecting path.
  • Architecture check: medium. Only the accepting path was run.
  • Smoke-test timeout and crash handling: medium. Simple shell, but never triggered.
  • Publishing both assets with forgejo-release: medium. The x86-only version from #11 hasn't run against a real tag yet either.
  • aarch64 on real hardware: medium. Only tested under qemu user-mode.

Merge with Create merge commit, so the commit trailers are kept.

🤖 Generated with Claude Code

Closes #12 ## What A release tag now publishes `sporah-<version>-aarch64-linux.tar.gz` next to the x86_64 one. It is cross-compiled on the existing x86 runner with zig and cargo-zigbuild, and smoke-tested under qemu. CI builds aarch64 on every PR, so a broken cross-build shows up before tagging. The README install picks the architecture with `uname -m`, and its checksum step now also works with busybox `sha256sum`. ## Blast radius **Tier:** trunk (the repo has no CLAUDE.md with a blast-radius list, so everything counts as trunk) **Trunk paths touched:** `.forgejo/workflows/release.yml`, `.forgejo/workflows/ci.yml`, `.forgejo/scripts/install-zig.sh` (new), `README.md` **Gate / rollback:** revert this merge commit. Earlier releases only have x86_64, and nothing else depends on the aarch64 asset. ## Proof - Release workflow: the release.yml run steps were extracted and run in rust:1-bookworm, the CI image base, exit 0. `cargo test`: 9 passed. `file`: x86_64 is "static-pie linked" and aarch64 is "statically linked". Both print `sporah-cli 0.1.0` and fetch live Berlin data; aarch64 ran under `qemu-aarch64-static`. Both tarballs and `SHA256SUMS` are produced. - README install on the aarch64 tarball in debian:bookworm-slim with no CA store: checksum OK; the tarball has the binary, README, LICENSE and completions; a live HTTPS fetch under qemu exits 0. - Static check: accepts the release binaries and rejects `/bin/ls` (with `file` 5.44, as in the CI image). - README checksum line: passes in busybox and GNU when only one tarball is downloaded. It fails on a corrupted tarball, and on empty input in busybox (rc=1). - `ci.yml` cross-aarch64 job steps, in a fresh rust:1-bookworm container: the build succeeds; the cargo-zigbuild install took about 12s and the build about 18s. - **Not proven:** a real Forgejo runner executing these workflows, the publish step, the aarch64 binary on real ARM hardware (only qemu), the timeout and crash branches of the smoke test, and the wrong-architecture rejection. ## Review **Reviewer:** fresh-context review at `high`, then at `medium` on the fixes: 15 findings, all low. 9 fixed, 1 refuted (busybox empty-input pass; tested and it exits 1). 5 declined or accepted: a qemu crash should block the release; the arch list covers only two targets, each built with a different command; about 12s to compile cargo-zigbuild and no CI cache are acceptable; network fetches are pinned and sha256-checked. **Review-Confidence:** 40/100. Every changed file was opened and the review depth was right, but four findings were closed with reasoning rather than evidence, and the new crash and timeout branches were never run. ## Confidence by part - aarch64 build (zig and cargo-zigbuild, pinned and sha256-checked): high. Built in the release simulation and in the CI job steps. - Static-linking check: high. Tested on both the accepting and the rejecting path. - Architecture check: medium. Only the accepting path was run. - Smoke-test timeout and crash handling: medium. Simple shell, but never triggered. - Publishing both assets with forgejo-release: medium. The x86-only version from #11 hasn't run against a real tag yet either. - aarch64 on real hardware: medium. Only tested under qemu user-mode. Merge with **Create merge commit**, so the commit trailers are kept. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Cross-compile aarch64-unknown-linux-musl on the x86 runner with
cargo-zigbuild (zig 0.13.0, checksum-pinned) next to the existing x86_64
build. Both binaries are checked for architecture and static linking
with `file`, and both make the live HTTPS smoke call; the aarch64 one
runs under qemu-user-static. A crash (exit > 128) now also fails the
smoke check. The release gets a second tarball, and SHA256SUMS covers
both.

README picks the tarball by `uname -m` and uses
`sha256sum -c --ignore-missing`, since users download only one tarball.

Closes #12

Review-Tier: trunk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups:
- ci.yml gets a cross-aarch64 job, so a broken zig cross-build shows up
  on the PR instead of after a release tag is pushed.
- zig/cargo-zigbuild setup moves to .forgejo/scripts/install-zig.sh,
  shared by both workflows; the zig download retries.
- Static check only rejects binaries with an interpreter: older `file`
  versions print "dynamically linked" for static-pie binaries.
- README checksum step greps the one tarball's line instead of using
  GNU-only `--ignore-missing`, so it works with busybox sha256sum too;
  it also says which `uname -m` values have a binary.

Review-Tier: trunk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Note install-zig.sh's xz-utils prerequisite
All checks were successful
CI / ci (pull_request) Successful in 30s
CI / audit (pull_request) Successful in 11s
CI / cross-aarch64 (pull_request) Successful in 1m54s
94126afb60
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review-Confidence: 40
Review-Tier: trunk
@ -0,0 +12,4 @@
cargo_zigbuild_version=0.23.4
rustup target add aarch64-unknown-linux-musl
curl -sSfL --retry 3 -o /tmp/zig.tar.xz \
Owner

Would rather see a zig-related image in the infrastructure project created so the builds don't have to download every time.

Would rather see a zig-related image in the infrastructure project created so the builds don't have to download every time.
All checks were successful
CI / ci (pull_request) Successful in 30s
CI / audit (pull_request) Successful in 11s
CI / cross-aarch64 (pull_request) Successful in 1m54s
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin "ci/arm64:ci/arm64"
git switch "ci/arm64"

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch "master"
git merge --no-ff "ci/arm64"
git switch "ci/arm64"
git rebase "master"
git switch "master"
git merge --ff-only "ci/arm64"
git switch "ci/arm64"
git rebase "master"
git switch "master"
git merge --no-ff "ci/arm64"
git switch "master"
git merge --squash "ci/arm64"
git switch "master"
git merge --ff-only "ci/arm64"
git switch "master"
git merge "ci/arm64"
git push origin "master"
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
schaefera/sporah-cli!13
No description provided.