- infra/build-server/repro/rebuild-on-box.sh: one target path per target with each pass's artefacts copied aside (prost's protowire.rs embeds its OUT_DIR path, so a pass in a dir of another name differs), SOURCE_DATE_EPOCH from the node commit and TZ=UTC (libmimalloc-sys compiles mimalloc's C with __DATE__ and __TIME__), --reuse for a re-report, reason text for a shipped file that is not on hand (innoextract 1.9 cannot open the Inno Setup 6 installer; the 0.3.14 HiveOS tarball left dl/public when 0.3.15 published). - tools/repro/rebuild-release.sh: the plan's hashes always travel (the miners' 8-hex prefixes too), bash 3.2 empty-array fix, the box half's exit code is the script's. - docs/evidence/reproduced/0.3.14.md: igneumd 03f35e05..., igneum-miner 900c1f0b..., igneumd.exe 166e604e..., igneum-miner.exe fefd266c... identical across two clean passes; DIFFER against the shipped 934f393c... (zig, glibc 2.36) and 44fa74c0... (Homebrew mingw before the timestamp fix, from a worktree). - docs/plans/build-server.md 7.2 (night battery: timer, dry run 3 min 54 s, 10 pass, the fork's 22 cargo-audit advisories as the one FAIL and what follows) and 7.3 (repro: results, the two classes, SOURCE_DATE_EPOCH proposed for every build script). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
77 lines
6.4 KiB
Bash
Executable file
77 lines
6.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Rebuild a shipped release on igneum-build-1 from a clean checkout and compare the bytes with what shipped (6 October 2026).
|
|
# tools/repro/rebuild-release.sh 0.3.14 pins from docs/plans/release-0.3.14.md, shipped hashes from the public downloads
|
|
# tools/repro/rebuild-release.sh 0.3.14 --node-commit <sha> --app-commit <sha> explicit pins (the plan is not read)
|
|
# tools/repro/rebuild-release.sh 0.3.14 --shipped igneumd.exe=<sha256> add or override a shipped hash (the plan's bold hashes are also read)
|
|
# --passes N (default 2), --no-public (shipped hashes from the plan and --shipped only), --node-branch <name>
|
|
# Where the pins live (docs/plans/release-0.3.14.md): the section heading "## 3. Builds and artefacts (node <sha>, app <sha>)";
|
|
# the shipped hashes are the bold sha256 values in that table (the Linux row "The seed's Linux node", the Windows row "The
|
|
# Windows node exes") and, token-free, inside the public downloads (packaging/README-ship.md "The public downloads path":
|
|
# igneum-hive-<v>.tar.gz carries igneumd and igneum-miner, Igneum-Miner-Setup-<v>.exe carries the two exes).
|
|
# The work runs on the box (infra/build-server/repro/rebuild-on-box.sh, under build slots through remote-run.sh); this script
|
|
# makes sure the mirrors hold both commits (pushes them from this Mac's checkouts when they do not), ships the two scripts to
|
|
# /srv/builds/_bin, runs the rebuild, and fetches the evidence into docs/evidence/reproduced/<version>.md of THIS worktree.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)"
|
|
MAIN_REPO="${IGNEUM_MAIN_REPO:-/Users/joshm/Projects/igneum}"
|
|
# shellcheck disable=SC2034
|
|
BS_TOOL=repro
|
|
# shellcheck source=../../infra/build-server/lib.sh
|
|
. "$ROOT/infra/build-server/lib.sh"
|
|
VERSION="${1:-}"; shift || true
|
|
[ -n "$VERSION" ] || bs_die "usage: tools/repro/rebuild-release.sh <version> [--node-commit sha] [--app-commit sha] [--shipped name=sha256]... [--passes N] [--no-public]"
|
|
NODE_SHA=""; APP_SHA=""; NODE_BRANCH=""; PASSES=2; PUBLIC=1; REUSE=""; SHIPPED=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--node-commit) NODE_SHA="$2"; shift 2 ;; --app-commit) APP_SHA="$2"; shift 2 ;; --node-branch) NODE_BRANCH="$2"; shift 2 ;;
|
|
--shipped) SHIPPED+=(--shipped "$2"); shift 2 ;; --passes) PASSES="$2"; shift 2 ;; --no-public) PUBLIC=0; shift ;; --reuse) REUSE=--reuse; shift ;;
|
|
*) bs_die "unknown argument $1" ;;
|
|
esac
|
|
done
|
|
PLAN="$ROOT/docs/plans/release-$VERSION.md"
|
|
if [ -z "$NODE_SHA" ] || [ -z "$APP_SHA" ]; then
|
|
[ -f "$PLAN" ] || bs_die "no $PLAN and no --node-commit/--app-commit"
|
|
pins=$(grep -m1 -oE '\(node [0-9a-f]{7,40}, app [0-9a-f]{7,40}\)' "$PLAN" || true)
|
|
[ -n "$pins" ] || bs_die "no '(node <sha>, app <sha>)' heading in $PLAN; pass --node-commit and --app-commit"
|
|
[ -n "$NODE_SHA" ] || NODE_SHA=$(sed -E 's/.*node ([0-9a-f]+),.*/\1/' <<<"$pins")
|
|
[ -n "$APP_SHA" ] || APP_SHA=$(sed -E 's/.*app ([0-9a-f]+)\).*/\1/' <<<"$pins")
|
|
bs_log "pins from $PLAN: node $NODE_SHA, app $APP_SHA"
|
|
# the plan's bold hashes as a second source (full ones only; the Windows row names igneumd.exe, the Linux row igneumd)
|
|
lin=$(grep -m1 -E "^\| The seed's Linux node" "$PLAN" | grep -oE 'igneumd \*\*[0-9a-f]{64}\*\*' | grep -oE '[0-9a-f]{64}' || true)
|
|
win=$(grep -m1 -E '^\| The Windows node exes' "$PLAN" | grep -oE 'igneumd\.exe \*\*[0-9a-f]{64}\*\*' | grep -oE '[0-9a-f]{64}' || true)
|
|
linm=$(grep -m1 -E "^\| The seed's Linux node" "$PLAN" | grep -oE 'igneum-miner [0-9a-f]{8}\.\.\.' | grep -oE '[0-9a-f]{8}' || true)
|
|
winm=$(grep -m1 -E '^\| The Windows node exes' "$PLAN" | grep -oE 'igneum-miner\.exe [0-9a-f]{8}\.\.\.' | grep -oE '[0-9a-f]{8}' || true)
|
|
# the plan's hashes always travel; a public artefact that unpacks overrides them on the box (prefix-only ones compare as prefixes)
|
|
[ -n "$lin" ] && SHIPPED+=(--shipped "igneumd=$lin"); [ -n "$linm" ] && SHIPPED+=(--shipped "igneum-miner=$linm")
|
|
[ -n "$win" ] && SHIPPED+=(--shipped "igneumd.exe=$win"); [ -n "$winm" ] && SHIPPED+=(--shipped "igneum-miner.exe=$winm")
|
|
[ -n "$lin$win" ] && bs_log "plan hashes: igneumd ${lin:0:16}... igneum-miner ${linm}... igneumd.exe ${win:0:16}... igneum-miner.exe ${winm}... (a public artefact that unpacks wins)"
|
|
fi
|
|
[ -n "$NODE_BRANCH" ] || NODE_BRANCH="release-$VERSION-node"
|
|
bs_host
|
|
# the mirrors must hold both commits; push them from the Mac's checkouts when they do not (a ref per repro, never master)
|
|
ensure_commit() { # <local repo> <mirror> <sha> <label>
|
|
local repo="$1" mirror="$2" sha="$3" label="$4" full
|
|
if bs_ssh "git -C '$mirror' cat-file -e '$sha^{commit}' 2>/dev/null"; then bs_log "$label: $sha is on $mirror"; return; fi
|
|
full=$(git -C "$repo" rev-parse --verify "$sha^{commit}" 2>/dev/null) || bs_die "$label: $sha is neither on the box's $mirror nor in $repo"
|
|
GIT_SSH_COMMAND="$BS_SSH_CMD" git -C "$repo" push -q "$BS_HOST:$mirror" "$full:refs/heads/repro-$VERSION-$label" || bs_die "$label: push of $sha to $mirror failed"
|
|
bs_log "$label: pushed $full to $mirror as repro-$VERSION-$label"
|
|
}
|
|
ensure_commit "$MAIN_REPO" "$BS_MIRROR_REPO" "$APP_SHA" app
|
|
ensure_commit "$MAIN_REPO/vendor/igneum-node" "$BS_MIRROR_NODE" "$NODE_SHA" node
|
|
bs_ssh 'mkdir -p /srv/builds/_bin'
|
|
bs_rsync -q "$ROOT/infra/build-server/repro/rebuild-on-box.sh" "$ROOT/infra/build-server/remote-run.sh" "$BS_HOST:/srv/builds/_bin/"
|
|
bs_ssh 'chmod +x /srv/builds/_bin/*.sh'
|
|
args=(--version "$VERSION" --node-commit "$NODE_SHA" --app-commit "$APP_SHA" --node-branch "$NODE_BRANCH" --passes "$PASSES"); [ "$PUBLIC" = 1 ] && args+=(--public); [ -n "$REUSE" ] && args+=("$REUSE")
|
|
bs_log "rebuilding on the box: ${args[*]}"
|
|
t0=$(date +%s)
|
|
# bash 3.2 on the Mac treats an empty array as unbound under set -u (run-from-mac.sh met it): expand it only when it has members
|
|
[ "${#SHIPPED[@]}" -gt 0 ] && args+=("${SHIPPED[@]}")
|
|
set +e
|
|
bs_ssh "IGNEUM_AGENT=${IGNEUM_AGENT:-repro} /srv/builds/_bin/rebuild-on-box.sh $(printf '%q ' "${args[@]}")" 2>&1 | grep -v '^#\|^|\|^$\|^Full box\|^Reading'
|
|
rc=${PIPESTATUS[0]}
|
|
set -e
|
|
[ "$rc" = 0 ] || bs_die "the rebuild on the box failed (rc $rc); its log: ssh build@box cat /srv/builds/_repro/$VERSION/rebuild.log"
|
|
mkdir -p "$ROOT/docs/evidence/reproduced"
|
|
bs_rsync -q "$BS_HOST:/srv/builds/_repro/$VERSION/$VERSION.md" "$ROOT/docs/evidence/reproduced/$VERSION.md"
|
|
bs_log "done in $(bs_fmt_secs $(( $(date +%s) - t0 ))): docs/evidence/reproduced/$VERSION.md"
|
|
grep -E '^\| (igneumd|igneum-miner)' "$ROOT/docs/evidence/reproduced/$VERSION.md" | cut -c1-200
|