ci(deploy): reproducible build - #453
Conversation
The release binary must be determined by the source code and the pinned toolchain alone, not by the host that performs the build. Two host-specific inputs previously leaked into the artifact. First, the dependency source paths from the cargo home were embedded verbatim into the binary through the `file!()` macro, so the absolute registry path of the build machine became part of the output. A new `ci/github-actions/reproducible-build-env.sh` script, run before each build in the three deployment build jobs, sets `--remap-path-prefix` for the cargo home and the workspace. This replaces those prefixes with fixed placeholders and is the stable equivalent of the still-unstable `-Z trim-paths` option. The script also pins `CARGO_INCREMENTAL=0` and provides `SOURCE_DATE_EPOCH`. Second, the binary was stripped by a separate `strip` step whose output depends on the version of the host `strip` program. The release profile now sets `strip = "symbols"`, so the toolchain strips the binary while linking, and the redundant strip steps are removed. This also strips the Windows binaries, which the removed steps never covered. Parallel code generation is kept. On a fixed toolchain it is already deterministic, verified by two independent builds producing byte-identical binaries, so `codegen-units` stays at its default rather than being serialized to 1. The value is pinned to preserve that parallelism against a future change to the default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Explain that release binaries are reproducible, that parallel code generation is kept because it is deterministic on a fixed toolchain, and how the `strip = "symbols"` profile setting and the `--remap-path-prefix` script achieve this. Include the commands to reproduce a binary locally and compare its checksum. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Performance Regression Reportscommit: d10b54c --quantity=apparent-size --max-depth=1 --min-ratio=0.01
LogsJSON{
"results": [
{
"command": "pdu",
"mean": 0.09973020600615386,
"stddev": 0.008895469906717872,
"median": 0.10104151766000001,
"user": 0.05848137076923077,
"system": 0.29090631999999994,
"min": 0.08746757166000001,
"max": 0.11857844866000002,
"times": [
0.11251379566000001,
0.10473839766000001,
0.10136644466000001,
0.10125647966000001,
0.10096956366000001,
0.10111347166000001,
0.10382615566,
0.10084970666000001,
0.10755325266000001,
0.09848332166000001,
0.10649456666000001,
0.11857844866000002,
0.10367837766000002,
0.10073530266000001,
0.10936900166,
0.10961615466,
0.08869916466000001,
0.08987802766000001,
0.10791025266000001,
0.09575915966000001,
0.08818595066000001,
0.08897310966000001,
0.08753371466000001,
0.08900129466000001,
0.08746757166000001,
0.08843466866000001
],
"exit_codes": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
{
"command": "pdu-0.20.0",
"mean": 0.08819384672060607,
"stddev": 0.0007217436062005055,
"median": 0.08808050166,
"user": 0.0552747006060606,
"system": 0.28937974424242413,
"min": 0.08689557666,
"max": 0.08997798566000001,
"times": [
0.08918075966000001,
0.08793511966,
0.08773765866000001,
0.08789187866,
0.08838791966000001,
0.08858943866,
0.08750491166,
0.08868850566000001,
0.08731997366000001,
0.08792816566,
0.08833554066,
0.08802472766000001,
0.08879494466000001,
0.08767191466,
0.08763388366000001,
0.08843344566000001,
0.08733424966,
0.08708551266,
0.08865953966000001,
0.08689557666,
0.08717685466000001,
0.08837964166000001,
0.08808050166,
0.08794039966,
0.08767585466000001,
0.08761583966000001,
0.08862688866000001,
0.08868387466000001,
0.08940006066,
0.08862001566000001,
0.08997798566000001,
0.08893974666000001,
0.08924561066
],
"exit_codes": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
}
]
} |
KSXGitHub
left a comment
There was a problem hiding this comment.
Claude Code,
Below are many different reviews. Each may belong to different categories.
I wish that you do not collapse all the unrelated tasks into a single commit.
You shall analyze the reviews. You shall make groups of related reviews.
Each group of related reviews correspond to a task and a git commit.
A git commit should have its purpose clear and focused.
If there would be multiple groups of related reviews, then I expect multiple commits.
The pin was not required for reproducibility. Because the toolchain is pinned, the release build uses the same default codegen-units on every host, and that default is already deterministic. Verified by building the same sources with the pin removed at `-j1`, `-j16`, and `-j64`, and under a restricted CPU affinity, each producing an identical binary. The default also keeps parallel code generation, so removing the pin does not serialize the build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Remove the paragraph and the bullet list that restated what the `Cargo.toml` profile and the reproducible-build script already document in their own comments. The section now states the reproducibility guarantee and the way to verify it, without duplicating the mechanism details. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Move the local reproduction commands out of `CONTRIBUTING.md` and into `verify-reproducible-build.sh`, alongside `run.sh`, `test.sh`, and `generate-completions.sh`. The script rebuilds a release binary with the reproducible environment, mirroring the deployment workflow including the explicit target, and prints its checksum for comparison against a published release. The target defaults to the primary Linux target and can be overridden. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
The standard library embeds its own source paths into the binary through panic locations. They normally read `/rustc/<commit-hash>/...`, but when the `rust-src` component is installed the compiler rewrites them to the local toolchain directory under the sysroot, which differs from one machine to the next. The reproducible-build environment remapped the cargo home and the workspace but not the sysroot, so a build on a machine with `rust-src` installed produced a different binary from one without it. Add a third `--remap-path-prefix` that maps the sysroot rust source directory back to the canonical `/rustc/<commit-hash>` form, in both the deployment environment script and `verify-reproducible-build.sh`. Verified by building with `rust-src` installed: the result is now byte-identical to the build without it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
The GNU target links its C runtime, the glibc startup objects and the GCC version string, from the build environment rather than from the Rust toolchain. A bare host build therefore varies between distributions even after paths are remapped, so two hosts produce different binaries. Add `reproducible-build.sh`, which builds a release binary inside a container image pinned by digest. The image ships rustc 1.97.1 on Debian bullseye, so the GCC and glibc are fixed and the result is identical on every host. Bullseye's glibc 2.31 keeps the required glibc version low for broad compatibility, and the binary remains an ordinary dynamically linked ELF rather than being tied to any special filesystem. The script detects Docker or Podman, forwards proxy variables for networks that need them, and mounts the workspace read-only. The `build_linux` job now calls this script instead of installing a toolchain and building on the runner, and `verify-reproducible-build.sh` delegates to it so a local checksum matches the released artifact. The macOS and Windows jobs are unchanged, since a Linux container cannot build them. Verified in a container for both targets: `x86_64-unknown-linux-gnu` and `x86_64-unknown-linux-musl` each build byte-identically across repeated runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Update the reproducible-builds section to describe the pinned container image as the source of host independence, and note that the verification script needs Docker or Podman and accepts a target argument. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
KSXGitHub
left a comment
There was a problem hiding this comment.
Claude Code,
Below are many different reviews. Each may belong to different categories.
I wish that you do not collapse all the unrelated tasks into a single commit.
You shall analyze the reviews. You shall make groups of related reviews.
Each group of related reviews correspond to a task and a git commit.
A git commit should have its purpose clear and focused.
If there would be multiple groups of related reviews, then I expect multiple commits.
The commands that run inside the container lived in a heredoc, where they were invisible to `bash -n` and to shellcheck, and awkward to edit. Move them to `ci/build-in-container.sh`, which the container runs from the mounted workspace. The resulting binaries are unchanged, verified by rebuilding both Linux targets and comparing their checksums against the previous ones. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
Several comments explained more than a reader needs, restating what the adjacent line already says or recording rationale from the review discussion. Keep the parts that carry a fact which is not evident from the code, such as the pinned toolchain versions, the reason the GNU target depends on the build environment, and the effect of the `rust-src` component on embedded paths. Remove or shorten the rest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UjrvAEQT3ryDqfspssN3g
No description provided.