#! /bin/bash # # usage: # maint/build-repro attest # maint/build-repro rebuild # maint/build-repro ci-rebuild # # attest: # # Saves the inputs and environment. # Builds the release binary. # *Commits the information to the git tree in perpetuity*! # # The first time you run this you should look in .build-repro # before pushing the output anywhere, in case it has anything # you don't want to publish. # # rebuild: # # Run the rebuild locally and check that it corresponds. # # ci-rebuild: # # See if the tree has apparently changed since attestation, # and run the rebuild if it hasn't. # (Also, use the default toolchain, not the pinned +... one.) # # Instructions: # # 1. Run maint/build-repro attest # Maybe check the output in .build-repro # # 2. Push the result as an MR to Salsa. # The CI will fail if the build is no longer reproducible. # # 3. Retrieve the built artifacts from Salsa. # Why this approach? # # This allows a committer to demonstrate that the build is reproducible, # in a way that gitlab CI can verify. The artifacts are only produced # the committer correctly *predicted* their checksums. # (To cheat, a committer would probably have to run a separate # salsa CI job, modified to save the output even on mismatch.) set -e set -o pipefail rust_toolchain=1.79.0 fail () { echo >&2 "error: $*" exit 12 } run_build () { cargo ${rust_toolchain:++}$rust_toolchain --locked build --release } git_rm_old () { git rm --ignore-unmatch -rf .build-repro } #---------- attest ---------- mode_attest () { attest_precheck if test -e ../Cargo.nail; then exit_rc=12 trap ' git clean -xdff git reset --hard exit $exit_rc ' 0 git_rm_old nailing-cargo -EE --- maint/build-repro attest attest_commit exit_rc=0 else attest_generate attest_commit fi } attest_precheck () { git_status=$(git status --porcelain) if [ "$git_status" != '' ]; then fail "tree is dirty $git_status" fi } attest_generate () { if [ "${SCHROOT_SESSION_ID}" = '' ]; then # other forms of separated build environment would also be OK # but we don't yet support them fail 'Must be running within schroot!' fi git_rm_old mkdir .build-repro dpkg-query --showformat='${Package} ${Architecture} ${Version}\n' \ --show >.build-repro/debian-packages rustup run $rust_toolchain env \ | grep -P -v '^SCHROOT_' \ > .build-repro/env rustc +$rust_toolchain -vV >.build-repro/rustc git rev-parse HEAD~0 >.build-repro/git-head run_build sha256sum target/release/tag2upload-service-manager \ >.build-repro/artifacts } attest_commit () { git add -Af .build-repro git commit -m '[autogenerated] build-repro: commit build info and results' } #---------- rebuild ---------- mode_rebuild () { if test -e ../Cargo.nail; then nailing-cargo --- maint/build-repro rebuild else rebuild_precheck rebuild_artifacts fi } rebuild_precheck () { why_not=$(rebuild_whynot) if [ "$why_not" != '' ]; then cat >&2 <artifacts/MISSING.txt Tree does not seem to be the same as last attestation. $why_not Not doing repro artifact build. END cat artifacts/MISSING.txt exit 0 fi } rebuild_ci_insist_lockfile () { get_reference_commit why_fail=$( git --no-pager diff --stat "$reference_commit" -- \ :Cargo.lock :Cargo.toml :build.rs ) if [ "$why_fail" = '' ]; then return 0; fi cat <&2 Error: tree differs fron last attestation in cargo control files $why_fail Error: When changing dependencies etc., run maint/build-repro attest END exit 1 } case "$1.$#" in attest.1) mode_attest ;; rebuild.1) mode_rebuild ;; ci-rebuild.1) mode_ci_rebuild ;; *) echo >&2 'bad usage'; exit 8 ;; esac