Artifact 865b7187a57c6c5313d36817b305dc696da555cbefc3212616463442c6482d4f:
- File
.github/workflows/release.yml
— part of check-in
[3106100d42]
at
2026-09-16 23:09:17
on branch trunk
— ci: keep the Zig cache and toolchain across runs
The runners are self-hosted but checkout cleaned the workspace, so every step recompiled the same sources into its own binary from nothing; unit tests and e2e were 6 and 4 minutes on the ARM64 critical path, mostly compile. Zig's cache is content-addressed, so it now lives outside the workspace and warms across runs, in both workflows. The toolchain tarball is kept the same way and still verified against the pinned digest every run. Also pins the tag-ignore trigger the previous commit introduced, which the workflow pin test had rejected. (user: dmestas, size: 13944) [annotate] [blame] [check-ins using]
name: Zit Release Candidate on: push: tags: - "version-*" workflow_dispatch: inputs: version: description: "Existing Fossil release version, without version- prefix" required: true type: string permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false jobs: source: runs-on: [self-hosted, Linux, X64, linux-x64-zit] timeout-minutes: 10 outputs: version: ${{ steps.version.outputs.version }} steps: - name: Resolve release version id: version env: REQUESTED_VERSION: ${{ inputs.version }} run: | set -euo pipefail if [ "${GITHUB_REF_TYPE}" = "tag" ]; then case "${GITHUB_REF_NAME}" in version-*) version="${GITHUB_REF_NAME#version-}" ;; *) echo "release workflow requires a version-* tag" >&2; exit 1 ;; esac else version="${REQUESTED_VERSION}" fi if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "invalid release version: ${version}" >&2 exit 1 fi tag="version-${version}" echo "version=${version}" >> "${GITHUB_OUTPUT}" echo "tag=${tag}" >> "${GITHUB_OUTPUT}" - name: Materialize exact Fossil release source env: VERSION: ${{ steps.version.outputs.version }} TAG: ${{ steps.version.outputs.tag }} run: | set -euo pipefail command -v fossil >/dev/null fossil version version="${VERSION}" tag="version-${version}" repo="${RUNNER_TEMP}/zit-${version}.fossil" checkout="${RUNNER_TEMP}/zit-${version}-checkout" rm -rf "${repo}" "${checkout}" fossil clone https://fossil.craftdesign.group/zit "${repo}" mkdir -p "${checkout}" cd "${checkout}" fossil open "${repo}" "${tag}" fossil info current | tee "${RUNNER_TEMP}/fossil-info.txt" grep -F ".version = \"${version}\"" build.zig.zon >/dev/null grep -F "zit ${version} (targets Fossil RFC rev " src/main.zig >/dev/null fossil tarball --name "zit-${version}" "${tag}" \ "${RUNNER_TEMP}/zit-${version}-source.tar.gz" -R "${repo}" cd "${RUNNER_TEMP}" sha256sum "zit-${version}-source.tar.gz" > "zit-${version}-source.tar.gz.sha256" - name: Upload exact release source uses: actions/upload-artifact@v4 with: name: release-source path: | ${{ runner.temp }}/zit-${{ steps.version.outputs.version }}-source.tar.gz ${{ runner.temp }}/zit-${{ steps.version.outputs.version }}-source.tar.gz.sha256 ${{ runner.temp }}/fossil-info.txt if-no-files-found: error retention-days: 30 build: needs: source timeout-minutes: 60 strategy: fail-fast: false matrix: include: - os: macOS arch: ARM64 runner: mac-mini-zit artifact: macos-aarch64 zig_target: aarch64-macos zig_package_target: aarch64-macos zig_shasum: b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489 smoke_repetitions: 3 - os: Linux arch: X64 runner: linux-x64-zit artifact: linux-x86_64 zig_target: x86_64-linux zig_package_target: x86_64-linux zig_shasum: 70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00 smoke_repetitions: 10 - os: Linux arch: ARM64 runner: linux-arm64-zit artifact: linux-aarch64 zig_target: aarch64-linux zig_package_target: aarch64-linux zig_shasum: ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17 smoke_repetitions: 10 runs-on: [self-hosted, "${{ matrix.os }}", "${{ matrix.arch }}", "${{ matrix.runner }}"] env: ZIG_VERSION: 0.16.0 ZIG_TARGET: ${{ matrix.zig_target }} ZIG_PACKAGE_TARGET: ${{ matrix.zig_package_target }} ZIG_SHASUM: ${{ matrix.zig_shasum }} VERSION: ${{ needs.source.outputs.version }} ARTIFACT: ${{ matrix.artifact }} SMOKE_REPETITIONS: ${{ matrix.smoke_repetitions }} steps: - name: Download exact release source uses: actions/download-artifact@v4 with: name: release-source path: ${{ runner.temp }}/release-source - name: Verify and extract exact release source run: | set -euo pipefail cd "${RUNNER_TEMP}/release-source" if command -v sha256sum >/dev/null 2>&1; then sha256sum -c "zit-${VERSION}-source.tar.gz.sha256" else shasum -a 256 -c "zit-${VERSION}-source.tar.gz.sha256" fi rm -rf "${RUNNER_TEMP}/zit-${VERSION}" tar -xzf "zit-${VERSION}-source.tar.gz" -C "${RUNNER_TEMP}" - name: Install Zig 0.16.0 run: | set -euo pipefail # Same persistent, digest-verified cache as ci.yml: the runners are # self-hosted, and a warm content-addressed Zig cache outside the # per-run temp turns this build's unit and end-to-end steps from cold # compiles into warm ones. The tarball is refetched whenever the # kept copy fails the pinned digest, never trusted for being there. cache="${HOME}/.cache/zit-ci" mkdir -p "${cache}/zig-local" "${cache}/zig-global" "${cache}/toolchains" echo "ZIG_LOCAL_CACHE_DIR=${cache}/zig-local" >> "$GITHUB_ENV" echo "ZIG_GLOBAL_CACHE_DIR=${cache}/zig-global" >> "$GITHUB_ENV" cd "${RUNNER_TEMP}" tarball="zig-${ZIG_PACKAGE_TARGET}-${ZIG_VERSION}.tar.xz" kept="${cache}/toolchains/${tarball}" digest() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}' else shasum -a 256 "$1" | awk '{print $1}' fi } if [ ! -f "${kept}" ] || [ "$(digest "${kept}")" != "${ZIG_SHASUM}" ]; then rm -f "${kept}" curl -fsSL -o "${kept}.part" "https://ziglang.org/download/${ZIG_VERSION}/${tarball}" mv "${kept}.part" "${kept}" fi actual="$(digest "${kept}")" if [ "${actual}" != "${ZIG_SHASUM}" ]; then echo "zig tarball digest mismatch: expected ${ZIG_SHASUM}, got ${actual}" >&2 rm -f "${kept}" exit 1 fi rm -rf "zig-${ZIG_PACKAGE_TARGET}-${ZIG_VERSION}" tar -xf "${kept}" echo "${RUNNER_TEMP}/zig-${ZIG_PACKAGE_TARGET}-${ZIG_VERSION}" >> "${GITHUB_PATH}" - name: Format check working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: zig fmt --check build.zig src - name: Build portable ReleaseFast binary working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: zig build --release=fast -Dtarget="${ZIG_TARGET}" - name: Unit tests working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: zig build test - name: End-to-end tests working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: zig build e2e - name: Deterministic simulation working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: zig build dst -- --seed 1 --steps 1000 - name: Seeded fuzz sweep working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: | set -euo pipefail targets="$(zig build fuzz -- --list)" [ -n "${targets}" ] || { echo "no fuzz targets enumerated" >&2; exit 1; } for target in ${targets}; do zig build fuzz -- "${target}" --seed 1 --rounds 100 done - name: Fossil interoperability smoke working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: | set -euo pipefail for n in $(seq 1 "${SMOKE_REPETITIONS}"); do echo "smoke ${n}/${SMOKE_REPETITIONS}" zig build smoke done - name: Rebuild and package qualified ReleaseFast binary working-directory: ${{ runner.temp }}/zit-${{ needs.source.outputs.version }} run: | set -euo pipefail zig build --release=fast -Dtarget="${ZIG_TARGET}" ./zig-out/bin/zit version dist="${RUNNER_TEMP}/dist-${ARTIFACT}" stage="${dist}/zit-${VERSION}" archive="zit-${VERSION}-${ARTIFACT}.tar.gz" rm -rf "${dist}" mkdir -p "${stage}" install -m 0755 zig-out/bin/zit "${stage}/zit" cp README.md LICENSE "${stage}/" cp "docs/RELEASE-NOTES-${VERSION}.md" "${stage}/RELEASE-NOTES.md" tar -C "${dist}" -czf "${dist}/${archive}" "zit-${VERSION}" if command -v sha256sum >/dev/null 2>&1; then binary_sha="$(sha256sum "${stage}/zit" | awk '{print $1}')" archive_sha="$(sha256sum "${dist}/${archive}" | awk '{print $1}')" else binary_sha="$(shasum -a 256 "${stage}/zit" | awk '{print $1}')" archive_sha="$(shasum -a 256 "${dist}/${archive}" | awk '{print $1}')" fi printf '%s %s\n' "${archive_sha}" "${archive}" > "${dist}/${archive}.sha256" python3 - "${dist}/${archive}.metadata.json" "${archive}" "${ARTIFACT}" \ "${ZIG_TARGET}" "${archive_sha}" "${binary_sha}" <<'PY' import json, pathlib, sys output, archive, artifact, target, archive_sha, binary_sha = sys.argv[1:] data = { "artifact": artifact, "binary_sha256": binary_sha, "bytes": pathlib.Path(output).with_name(archive).stat().st_size, "name": archive, "qualification": "passed", "sha256": archive_sha, "target": target, } pathlib.Path(output).write_text(json.dumps(data, indent=2, sort_keys=True) + "\n") PY - name: Upload qualified binary package uses: actions/upload-artifact@v4 with: name: release-${{ matrix.artifact }} path: | ${{ runner.temp }}/dist-${{ matrix.artifact }}/zit-${{ needs.source.outputs.version }}-${{ matrix.artifact }}.tar.gz ${{ runner.temp }}/dist-${{ matrix.artifact }}/zit-${{ needs.source.outputs.version }}-${{ matrix.artifact }}.tar.gz.sha256 ${{ runner.temp }}/dist-${{ matrix.artifact }}/zit-${{ needs.source.outputs.version }}-${{ matrix.artifact }}.tar.gz.metadata.json if-no-files-found: error retention-days: 30 qualify-linux: needs: [source, build] runs-on: ubuntu-24.04 timeout-minutes: 10 strategy: fail-fast: false matrix: include: - artifact: linux-x86_64 emulator: x86_64 - artifact: linux-aarch64 emulator: aarch64 env: VERSION: ${{ needs.source.outputs.version }} ARTIFACT: ${{ matrix.artifact }} steps: - name: Download packaged binary uses: actions/download-artifact@v4 with: name: release-${{ matrix.artifact }} path: dist - name: Install deterministic CPU emulators run: | sudo apt-get update sudo apt-get install -y qemu-user - name: Verify package digest and low-feature execution env: EMULATOR: ${{ matrix.emulator }} run: | set -euo pipefail cd dist archive="zit-${VERSION}-${ARTIFACT}.tar.gz" sha256sum -c "${archive}.sha256" mkdir package tar -xzf "${archive}" -C package binary="${PWD}/package/zit-${VERSION}/zit" if [ "${EMULATOR}" = "x86_64" ]; then "${binary}" version qemu-x86_64 -cpu Nehalem "${binary}" version else qemu-aarch64 -cpu cortex-a53 "${binary}" version fi qualify-macos: needs: [source, build] runs-on: [self-hosted, macOS, ARM64, mac-mini-zit] timeout-minutes: 5 env: VERSION: ${{ needs.source.outputs.version }} steps: - name: Download packaged binary uses: actions/download-artifact@v4 with: name: release-macos-aarch64 path: dist - name: Verify packaged macOS binary run: | set -euo pipefail cd dist archive="zit-${VERSION}-macos-aarch64.tar.gz" shasum -a 256 -c "${archive}.sha256" mkdir package tar -xzf "${archive}" -C package "${PWD}/package/zit-${VERSION}/zit" version bundle: needs: [source, qualify-linux, qualify-macos] runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Download release source uses: actions/download-artifact@v4 with: name: release-source path: candidate - name: Download qualified binary packages uses: actions/download-artifact@v4 with: pattern: release-*-* path: candidate merge-multiple: true - name: Assemble candidate checksums run: | set -euo pipefail cd candidate cat ./*.tar.gz.sha256 | sort -k2 > SHA256SUMS sha256sum -c SHA256SUMS - name: Upload publication candidate uses: actions/upload-artifact@v4 with: name: zit-${{ needs.source.outputs.version }}-publication-candidate path: candidate if-no-files-found: error retention-days: 30