Reproducible builds

Linux and macOS release archives (.tar.gz) are produced deterministically: the gzip header carries mtime=0 and no filename, and every tar entry has sorted order, fixed uid/gid 0, empty owner/group names, and mtime set to SOURCE_DATE_EPOCH derived from the tagged commit. Packaging is performed by scripts/pack.py, and the same script is used both at release time and during validation, so each archive's SHA-256 can be reproduced bit-for-bit from source.

Windows .zip archives **cannot** be byte-reproduced at the archive level — the .exe inside is Authenticode-signed by Azure Trusted Signing and the signing key is, deliberately, not available to rebuilders. Instead, the reproducibility workflow verifies Windows builds at the .exe level (issue #78, Option E): it strips the Authenticode signature off the published .exe and compares its sha256 against a locally-rebuilt unsigned .exe. The stripping is performed by ghr validate strip-authenticode — a small subcommand that reverses exactly what Trusted Signing appends (the certificate table at end of file, the IMAGE_DIRECTORY_ENTRY_SECURITY entry, and OptionalHeader.CheckSum).

The Reproducibility workflow runs automatically after each successful Release workflow and can also be triggered manually with workflow_dispatch (provide the tag, e.g. v0.3.0). For every release target it:

  1. checks out the source at the tag,
  2. installs the Zig version selected from the checked-out tag's manifest,
  3. rebuilds with the same flags as release.yml,
  4. for non-Windows targets: repackages with scripts/pack.py, downloads the published .tar.gz, verifies it against GitHub's published asset digest, and fails on hash mismatch against the rebuild (with tar tvf and cmp diffs as a diagnostic artifact),
  5. for Windows targets: also builds a host-native ghr.exe as the stripper, downloads the published .zip, extracts bin/ghr.exe, strips its Authenticode signature, and fails if the stripped sha256 doesn't match the locally-rebuilt unsigned .exe. Diagnostics include cmp -l byte differences and side-by-side header dumps.

Releases tagged at or before v0.2.1 predate deterministic packaging and the Authenticode signing pipeline; they will not reproduce.

Compiler selection

Release and reproducibility jobs read .minimum_zig_version from the checked-out source's build.zig.zon, after checkout. For the supported release history this also records the exact release compiler:

Checked-out manifest Signed compiler release Optimization flag
0.16.0 cataggar/zig@v0.16.0 -Doptimize=ReleaseSafe
0.17.0 cataggar/zig@v0.17.0 -Doptimize=safe

Both installations require the existing trusted minisign key. Missing, ambiguous, or unknown manifest versions fail explicitly; there is no silent fallback to the newest compiler. Selection stays inline in the workflows because old tags do not contain newly added helper scripts. Future compiler upgrades must extend this allowlist without changing the historical rows.

For a local rebuild, first check out the release tag, install its matching official compiler, set SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct HEAD), and use that tag's release target, version, strip, and optimization flags. Current source builds require official Zig 0.17.0; its optimization enum is debug / safe / fast / small. Do not pass these new spellings to old tags built with Zig 0.16.0, and do not rebuild those tags with Zig 0.17.0. The compiler selection does not alter archive metadata, packaging scripts, or the Windows stripped-signed comparison.

Running the strip locally

ghr validate strip-authenticode <input.exe> <output.exe> reads a signed PE, removes the embedded WIN_CERTIFICATE table at end of file, zeroes the security data-directory entry, and zeroes OptionalHeader.CheckSum. Given a deterministic Zig-built unsigned .exe, signing-then-stripping returns the exact bytes the compiler emitted — the foundation of Option E.

$ ghr validate strip-authenticode published/ghr.exe stripped.exe
stripped published/ghr.exe -> stripped.exe: dropped 15672 bytes (cert table at offset 0x238be00)
$ sha256sum stripped.exe locally-rebuilt-ghr.exe

View Markdown source