Artifact ea07314fee7c0374c8f662fe439a220458ef767eea381dbc2bd6ff1f18d9f079:
- File
.github/workflows/ci.yml
— part of check-in
[8a003b2ada]
at
2026-07-18 22:03:38
on branch trunk
— Add CI: private git mirror + GitHub Actions running the release battery
CI is the multiplier for the rest of the 0.1.0 sequence: from here every fix lands with fmt/build/test/e2e/dst/smoke gated automatically, and the smoke conformance claim — zit artifacts and deltas validated against the real fossil binary — becomes continuously enforced instead of milestone-manual.
- .github/workflows/ci.yml: single ubuntu-latest gating job on push and PR. Zig 0.16.0 pinned by version AND sha256 digest, verified with sha256sum --check --strict before use; fossil installed for smoke; the battery runs in order with a 20-minute job ceiling and a 5-minute bound on smoke, which is the one step that can hang without producing output. - src/ci_test.zig: structural pins over the workflow, so the file cannot drift from what it claims. Each pin is anchored to a whole line with its YAML indentation — a loose substring is satisfied by the phrase appearing in a comment while the block that governs the run says something else. The digest pin names ZIG_SHASUM and the tarball together, so the digest this repo pins is provably the digest the workflow checks. - src/packaging_test.zig: a negative-space pin asserting every path build.zig embeds is version-controlled. This diff is why: build.zig came to depend on .github/workflows/ci.yml while that file was untracked, and bare 'fossil extras' does not list dotfiles, so the commit-discipline check reported clean. The pin runs 'fossil ls' and fails on any embed that is not tracked.
The mirror is PRIVATE (github.com/danmestas/zit) and stays private; publishing is a separate decision that is deliberately not part of this release. (user: dmestas, size: 4313) [annotate] [blame] [check-ins using]
name: ci # Every push on every branch, plus pull requests. Deliberately unfiltered: the # 0.1.0 release is developed on `release-0.1`, and a default-branch-only gate # would leave the entire release unverified until the merge that is supposed to # be the safe part. on: push: pull_request: # Least privilege: the battery only reads the checkout. Without an explicit # block the job inherits whatever the repo/org default grants, which is # read/write on many configurations. permissions: contents: read # Unfiltered `push:` plus `pull_request:` means a PR branch runs the whole # battery twice per push. Superseded runs are cancelled rather than left to # finish against a commit nobody is waiting on. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # One gating job. A macOS leg is 0.2 material; splitting the battery across # several jobs would read as broader coverage than it is. jobs: battery: runs-on: ubuntu-latest # An explicit ceiling, per the bounded-waits rule: every wait needs an # interval and a timeout. Without this the job inherits GitHub's 360-minute # default, so a hang — most plausibly smoke's spawned server never exiting — # turns a red gate into a six-hour silent stall instead of a failure. timeout-minutes: 20 env: # Kept in lockstep with src/ci_test.zig's `pinned_zig_version` and # `pinned_zig_shasum`, which fail `zig build test` if this file drifts. # The digest is the published one from ziglang.org/download/index.json, # so a swapped tarball is a build failure rather than a silent event. ZIG_VERSION: 0.16.0 ZIG_SHASUM: 70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00 steps: - uses: actions/checkout@v4 # smoke drives the real fossil binary — it is the only step that proves # the conformance claim against the reference implementation rather than # against our own reading of the RFCs. The runner's fossil is whatever # apt ships and will lag the local 2.28, so print it: the release notes # must name the exact version smoke passed against (readiness item S7). - name: Install fossil run: | sudo apt-get update sudo apt-get install -y fossil fossil version # Installed by hand rather than through a setup action: the toolchain is # one tarball, the URL is stable, and verifying the published digest here # is both simpler and stronger than trusting a third-party action to pin # the same thing. - name: Install Zig ${{ env.ZIG_VERSION }} run: | set -euo pipefail tarball="zig-x86_64-linux-${ZIG_VERSION}.tar.xz" curl -fsSL -o "$tarball" "https://ziglang.org/download/${ZIG_VERSION}/${tarball}" echo "${ZIG_SHASUM} ${tarball}" | sha256sum --check --strict tar -xf "$tarball" echo "${GITHUB_WORKSPACE}/zig-x86_64-linux-${ZIG_VERSION}" >> "$GITHUB_PATH" - name: Zig version run: zig version # `--check build.zig src` rather than `--check .`: the bare dot walks # .zig-cache/ and zig-out/, whose generated sources are not ours to # format. Naming the source roots keeps the check meaning the same thing # on a cold runner and in a warm local tree. - name: Format check run: zig fmt --check build.zig src - name: Build run: zig build # The battery runs each step explicitly. Local semantics are deliberately # unchanged: e2e staying compile-only under `zig build test` is a good # inner-loop design, so CI names the steps rather than the build graph # being rewired to fold them in. - name: Unit tests run: zig build test - name: End-to-end tests run: zig build e2e - name: Deterministic simulation run: zig build dst # The tightest per-step bound in the battery, and deliberately so: smoke # spawns a server, which is the one step that can hang without producing # output. The job ceiling would eventually catch it; this catches it while # the log still reads as a failed step rather than a dead run. - name: Fossil interop smoke timeout-minutes: 5 run: zig build smoke