Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 10 additions & 18 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,9 @@ jobs:
steps:
- uses: actions/checkout@v7

- name: Install Rust
shell: bash
run: |
installer=$(mktemp -d)/install-rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > $installer
bash $installer --default-toolchain $(cat rust-toolchain) -y

- name: Install compilation target
run: rustup target add ${{ matrix.target }}

- name: Build
run: cargo build --target ${{ matrix.target }} --release --all-features

- name: Strip all debug symbols
run: strip --strip-all target/${{ matrix.target }}/release/pdu
# The container image provides Rust, so no toolchain install step is needed.
- name: Build in pinned container
run: ./reproducible-build.sh ${{ matrix.target }}

- name: Attest build provenance
uses: actions/attest-build-provenance@v4
Expand Down Expand Up @@ -135,12 +123,13 @@ jobs:
- name: Install compilation target
run: rustup target add ${{ matrix.target }}

- name: Configure reproducible build environment
shell: bash
run: ./ci/github-actions/reproducible-build-env.sh

- name: Build
run: cargo build --target ${{ matrix.target }} --release --all-features

- name: Strip all debug symbols
run: strip target/${{ matrix.target }}/release/pdu

- name: Attest build provenance
uses: actions/attest-build-provenance@v4
with:
Expand Down Expand Up @@ -182,6 +171,9 @@ jobs:
- name: Install compilation target
run: rustup target add ${{ matrix.target }}

- name: Configure reproducible build environment
shell: bash
run: ./ci/github-actions/reproducible-build-env.sh

- name: Build
run: cargo build --target ${{ matrix.target }} --release --all-features
Expand Down
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,3 +531,14 @@ FMT=true LINT=true BUILD=true TEST=true DOC=true DYLINT=true ./test.sh

> [!NOTE]
> Some tests may fail with a hint about `TEST_SKIP`. Follow the hint and rerun with the suggested variable.

## Reproducible Builds

The Linux release binaries are built inside a container image pinned by digest,
so their exact bytes are identical on every host, independent of the host's
distribution, thread count, or clock.

To rebuild a release binary and compare its checksum against a published
release, run `./verify-reproducible-build.sh` (requires Docker or Podman). The
target defaults to `x86_64-unknown-linux-gnu`; pass another as the first
argument, such as `x86_64-unknown-linux-musl`.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ normalize-path = "0.2.1"
pretty_assertions = "1.4.1"
rand = "0.10.1"
which = "8.0.2"

[profile.release]
# Strip while linking, so the result does not depend on an external `strip`.
strip = "symbols"
19 changes: 19 additions & 0 deletions ci/build-in-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#! /bin/bash
set -o errexit -o pipefail -o nounset

# This script runs inside the container started by reproducible-build.sh, where
# the workspace is mounted at /build. It is not meant to be run on the host.
target="$1"
cd /build

# The chosen target is not always the image's host target.
rustup target add "$target" >/dev/null

# Keep the paths that the compiler embeds canonical, matching the remapping that
# a build outside the container performs.
commit_hash="$(rustc --version --verbose | awk '/^commit-hash:/ { print $2 }')"
rust_src="$(rustc --print sysroot)/lib/rustlib/src/rust"
export CARGO_INCREMENTAL=0
export RUSTFLAGS="--remap-path-prefix=${CARGO_HOME:-/usr/local/cargo}=/cargo --remap-path-prefix=$rust_src=/rustc/$commit_hash"

cargo build --release --all-features --target "$target"
34 changes: 34 additions & 0 deletions ci/github-actions/reproducible-build-env.sh
Comment thread
KSXGitHub marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#! /bin/bash
set -o errexit -o pipefail -o nounset

# Emit the environment variables that make the release binary reproducible into
# "$GITHUB_ENV", so that every later step in the job inherits them.

cargo_home="${CARGO_HOME:-$HOME/.cargo}"
workspace="${GITHUB_WORKSPACE:-$PWD}"

# Replace the machine-specific absolute prefixes that the compiler would
# otherwise embed into the binary. This is the stable equivalent of the
# still-unstable "-Z trim-paths" option.
remap="--remap-path-prefix=$cargo_home=/cargo"
remap="$remap --remap-path-prefix=$workspace=/build"

# When the "rust-src" component is installed, the compiler rewrites the standard
# library's "/rustc/<commit-hash>/..." paths to the local toolchain directory.
# Remap them back, so the result does not depend on whether it is installed.
commit_hash="$(rustc --version --verbose | awk '/^commit-hash:/ { print $2 }')"
rust_src="$(rustc --print sysroot)/lib/rustlib/src/rust"
if [[ -n "$commit_hash" ]]; then
remap="$remap --remap-path-prefix=$rust_src=/rustc/$commit_hash"
fi

if [[ -n "${RUSTFLAGS:-}" ]]; then
remap="$RUSTFLAGS $remap"
fi

{
echo "RUSTFLAGS=$remap"
echo "CARGO_INCREMENTAL=0"
# A fixed timestamp for any build script that would otherwise read the clock.
echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)"
} >>"$GITHUB_ENV"
46 changes: 46 additions & 0 deletions reproducible-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#! /bin/bash
set -o errexit -o pipefail -o nounset

cd "$(dirname "$0")"

# Build a release binary inside a pinned container image. The GNU target takes
# its C runtime from the build environment rather than from the Rust toolchain,
# so pinning the image is what makes the output identical on every host.
#
# The image ships rustc 1.97.1 on Debian bullseye, whose glibc 2.31 keeps the
# required glibc version low for broad compatibility.
target="${1:-x86_64-unknown-linux-gnu}"
image="rust:1.97.1-slim-bullseye@sha256:dd159e8e44aaa84dffbb7f24a742a96b8d0a26920bd3b46ec2b1778be0da1cdf"

# Detect a container runtime. PDU_CONTAINER_RUNTIME overrides the choice.
runtime="${PDU_CONTAINER_RUNTIME:-}"
if [[ -z "$runtime" ]]; then
for candidate in docker podman; do
if command -v "$candidate" >/dev/null; then
runtime="$candidate"
break
fi
done
fi
if [[ -z "$runtime" ]]; then
echo "error: no container runtime found; install docker or podman, or set PDU_CONTAINER_RUNTIME" >&2
exit 1
fi
echo "reproducible-build: using $runtime with $image" >&2

mkdir -p target

# The workspace is mounted read-only, so only the target directory is writable.
# PDU_CONTAINER_RUN_ARGS passes extra runtime flags for unusual environments,
# such as a custom CA certificate mount.
# shellcheck disable=SC2086
"$runtime" run --rm --interactive \
--env HTTPS_PROXY --env HTTP_PROXY --env NO_PROXY \
--env https_proxy --env http_proxy --env no_proxy \
--volume "$PWD":/build:ro \
--volume "$PWD/target":/build/target \
${PDU_CONTAINER_RUN_ARGS:-} \
"$image" \
bash /build/ci/build-in-container.sh "$target"

echo "reproducible-build: built target/$target/release/pdu" >&2
11 changes: 11 additions & 0 deletions verify-reproducible-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /bin/bash
set -o errexit -o pipefail -o nounset

cd "$(dirname "$0")"

# Rebuild a release binary and print its checksum for comparison against a
# published release.
target="${1:-x86_64-unknown-linux-gnu}"

./reproducible-build.sh "$target"
sha256sum "target/$target/release/pdu"
Loading