CI and Release¶
This page documents SIPI's CI pipeline, release automation, and the Zig/static build hardening that runs in parallel with Docker during rollout.
Release Automation (release-please)¶
Releases are fully automated via release-please.
When commits are merged to main, release-please reads their
Conventional Commit prefixes to
determine the SemVer bump and generate the changelog.
Configuration files:
.github/release-please/config.json— changelog sections, release type.github/release-please/manifest.json— current version.github/workflows/release-please.yml— GitHub Actions workflow
How commit types map to releases:
| Prefix | SemVer Effect | Changelog Section |
|---|---|---|
feat: |
minor bump | Features |
fix: |
patch bump | Bug Fixes |
feat!: / fix!: |
major bump | Breaking Changes |
perf: |
patch bump | Performance Improvements |
docs:, style:, refactor:, test:, build:, ci:, chore: |
no bump | hidden |
Correct commit prefixes are critical
A commit without a valid Conventional Commit prefix will be invisible to release-please — it won't trigger a release or appear in the changelog. See Commit Message Schema for the full format specification.
Nightly Fuzz Testing¶
A nightly fuzz workflow (.github/workflows/fuzz.yml) runs libFuzzer against
the IIIF URL parser to find crashes and edge cases. Fuzz corpora are persisted
as artifacts across runs so coverage accumulates over time.
See Fuzzing for details on the fuzz harness, corpus management, and how to reproduce crashes locally.
Scope¶
- Keep Docker publishing and Zig/static artifacts in parallel.
- Enforce Zig/static validation as required gates before release side effects.
- Produce fully static Linux binaries (
x86_64-linux-musl,aarch64-linux-musl). - Enforce strict macOS Zig dylib policy (
/usr/lib/libSystem.B.dylibonly).
Zig Version and Build Policy¶
- Zig is pinned to
0.15.2in CI workflows. - Linux static targets:
x86_64-linux-musl(amd64)aarch64-linux-musl(arm64)- CI uses native per-arch builds via Docker-in-Ubuntu: each architecture
gets its own runner (
ubuntu-24.04for amd64,ubuntu-24.04-armfor arm64). JS actions (checkout, setup-zig, upload-artifact) run on the bare Ubuntu host. The build itself runs insidedocker run alpine:3.21with the source bind-mounted — Alpine is required because Zig has a bug where it doesn't ignore/usr/includeeven with-target, and Ubuntu's glibc headers would contaminate musl builds. - LTO is disabled for musl static builds (
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF).
Pull Request CI¶
Workflow: .github/workflows/test.yml
Standard test matrix¶
- Nix/Clang test matrix runs on:
ubuntu-24.04ubuntu-24.04-arm
Static and macOS PR checks¶
Both static Linux and macOS validation run through the Nix flake — Zig-in-Nix for musl cross-compile and Nix's default clang/libc++ stdenv for Darwin.
nix-static / {arch} (ubuntu-24.04, ubuntu-24.04-arm):
nix build .#static-${arch}— builds viamkStaticBuildinflake.nix, using Zig as the C/C++ cross-compiler targeting{x86_64,aarch64}-linux-musl. Kakadu is fetched by the FOD (GH_TOKEN: ${{ secrets.DASCHBOT_PAT }},configurable-impure-envenabled on the daemon).- Static linkage assertion:
readelf -dmust not report anyNEEDEDentries. nix develop --command ... cargo testintest/e2e-rustwithSIPI_BIN=$GITHUB_WORKSPACE/result/bin/sipi— proves the Nix-built static musl binary runs on a glibc host.
nix-macos / arm64 dylib-audit (macos-14):
nix build .#defaulton Darwin (clang + libc++).otool -Laudit: only/usr/lib/libSystem.B.dyliband/System/Library/{Frameworks,PrivateFrameworks}/...allowed.
Forked PR behavior¶
nix-static and nix-macos-audit are skipped for forked PRs because
the Kakadu FOD needs secrets.DASCHBOT_PAT, which isn't shared with
forks. Standard CI (nix-clang) still runs.
Tag Release CI/CD¶
Workflow: .github/workflows/publish.yml
Trigger: tag push matching v*.
Gate model:
1. validate-docker must pass.
2. release-gate fires on validate-docker success.
3. Publish jobs run in parallel after the gate:
- publish-docker / {arch} — builds, tests, pushes Docker images.
- publish-static-release / {arch} — builds release archive via
Nix (nix build .#release-archive-${arch}), uploads to GitHub
Release, pushes debug symbols to Sentry.
- manifest — multi-arch Docker manifest.
- docs — mkdocs deploy.
4. sentry finalises the release after manifest + static publishes.
Static artifact flow¶
A single per-arch publish-static-release job produces everything:
nix build .#release-archive-${arch}emits inresult/:sipi-v<version>-linux-${arch}.tar.gz(binary + config + scripts + server)sipi-v<version>-linux-${arch}.tar.gz.sha256sipi-linux-${arch}.debug(split debug symbols)gh release uploadattaches the three files to the tag release.sentry-cli debug-files uploadpushes the.debugfile to Sentry.
Local Reproduction¶
# Native dev build + unit + e2e (inside nix develop)
nix develop --command bash -c "just nix-build && just nix-test && just rust-test-e2e"
# Static musl binaries (no shell needed)
nix build .#static-amd64
nix build .#static-arm64
# Validation
file result/bin/sipi
! readelf -d result/bin/sipi | grep NEEDED
# Darwin build (on macOS)
nix build .#default
otool -L result/bin/sipi