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.
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¶
- Existing Nix/GCC test matrix still runs on:
ubuntu-24.04ubuntu-24.04-arm
Zig/static PR checks (native per-arch Docker-in-Ubuntu)¶
Each architecture gets a combined build+test job on its native runner. The build runs inside an Alpine Docker container, then tests run on the bare Ubuntu host — proving the static musl binaries are portable.
zig-static / {arch} — combined job per architecture:
- Host (Ubuntu): JS actions run checkout and setup-zig.
- Alpine Docker:
docker run alpine:3.21with source bind-mounted:- Installs build prerequisites via
apk. - Zig binary (statically linked) is bind-mounted from host.
- CMake configure + build produces a static ELF binary.
- Installs build prerequisites via
- Host (Ubuntu): Verification and testing:
- Static linkage verification (
ldd,readelf -d). - Unit tests (GoogleTest executables run directly).
- E2e dependencies installed via
apt-getandpip3. - Full e2e test suite (
test/e2e).
- Static linkage verification (
This proves Alpine-built static binaries run on a glibc host that had no part in building them, and each architecture builds and tests natively.
zig-macos / arm64 dylib-audit:
- Native Release Zig build.
otool -Laudit onbuild-zig-macos/sipi.- Exactly one allowed dependency:
/usr/lib/libSystem.B.dylib
Forked PR behavior¶
Zig static jobs are intentionally skipped for forked PRs because private inputs (for example Kakadu/private dependency paths) are not available there. Standard CI behavior remains active for forks.
Tag Release CI/CD¶
Workflow: .github/workflows/publish.yml
Trigger:
- Tag push matching v*
Gate model:
1. validate-static / {arch} builds, tests, and packages each architecture
natively (same Docker-in-Ubuntu pattern as PR workflow).
2. validate-docker must pass.
3. release-gate requires validate-docker and validate-static.
4. Publish side effects run only after release-gate succeeds.
Static artifact flow¶
Each architecture is built, tested, and packaged in its own validate-static
job:
- Build static binary (native on each arch's runner).
- Verify static linkage.
- Run unit tests and e2e tests.
- Split debug symbols (
objcopy --only-keep-debug). - Strip binary.
- Add debug link (
objcopy --add-gnu-debuglink). - Package
.tar.gz+.sha256. - Upload
static-linux-release-{arch}artifact.
Release attachment and symbols¶
- Static archives/checksums are attached to the existing tag release.
- Static debug symbols are uploaded to Sentry per architecture.
- Docker debug symbols and SBOM flow continue in parallel.
Local Reproduction¶
Zig local workflow (native build + e2e on host)¶
Static Zig build in Docker (mirrors CI build job)¶
make zig-static-docker-arm64 # Alpine build + ctest for arm64
make zig-static-docker-amd64 # Alpine build + ctest for amd64
These targets mirror the validate-static CI job: Alpine 3.21 container,
Zig toolchain, cmake build, and unit tests. They do not run e2e tests.
The CI's portability proof (e2e on bare Ubuntu) is not reproduced locally because the Docker targets produce a Linux ELF binary that cannot run on macOS. On a Linux workstation you could extract the binary and run e2e manually, but this is not wrapped in a Make target — CI is the authoritative portability check.
Linux static validation commands¶
# (local Makefile targets use build-static/; CI uses build/)
file build-static/sipi
ldd build-static/sipi
readelf -d build-static/sipi | grep NEEDED
Expected:
- ldd indicates static.
- readelf returns no NEEDED entries.
macOS dylib audit command¶
Expected:
- Only /usr/lib/libSystem.B.dylib.