Skip to content

ci(deploy): reproducible build - #453

Draft
KSXGitHub wants to merge 10 commits into
masterfrom
claude/rust-reproducible-builds-54t6la
Draft

ci(deploy): reproducible build#453
KSXGitHub wants to merge 10 commits into
masterfrom
claude/rust-reproducible-builds-54t6la

Conversation

@KSXGitHub

Copy link
Copy Markdown
Owner

No description provided.

claude added 2 commits July 25, 2026 20:23
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
@KSXGitHub KSXGitHub changed the title ci: reproducible build ci(deploy): reproducible build Jul 25, 2026
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

Performance Regression Reports

commit: d10b54c

--quantity=apparent-size --max-depth=1 --min-ratio=0.01
Command Mean [ms] Min [ms] Max [ms] Relative
pdu 99.7 ± 8.9 87.5 118.6 1.13 ± 0.10
pdu-0.20.0 88.2 ± 0.7 86.9 90.0 1.00
Logs
Benchmark 1: pdu
  Time (mean ± σ):      99.7 ms ±   8.9 ms    [User: 58.5 ms, System: 290.9 ms]
  Range (min … max):    87.5 ms … 118.6 ms    26 runs
 
Benchmark 2: pdu-0.20.0
  Time (mean ± σ):      88.2 ms ±   0.7 ms    [User: 55.3 ms, System: 289.4 ms]
  Range (min … max):    86.9 ms …  90.0 ms    33 runs
 
Summary
  pdu-0.20.0 ran
    1.13 ± 0.10 times faster than pdu
JSON
{
  "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 KSXGitHub left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread Cargo.toml Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
claude added 6 commits July 25, 2026 21:02
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 KSXGitHub left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread reproducible-build.sh Outdated
Comment thread verify-reproducible-build.sh Outdated
Comment thread reproducible-build.sh Outdated
Comment thread reproducible-build.sh Outdated
Comment thread Cargo.toml Outdated
Comment thread Cargo.toml Outdated
Comment thread ci/github-actions/reproducible-build-env.sh
Comment thread .github/workflows/deploy.yaml Outdated
claude added 2 commits July 31, 2026 18:07
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants