boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

release.sh (6141B)


      1 #!/bin/sh
      2 ## release.sh — mint a validated boot2 release tarball.
      3 ##
      4 ## Releases are critical, so the path to minting one is paranoid:
      5 ##
      6 ##   1. `make clean` — wipe build/ entirely.
      7 ##   2. `make package` — full prep-src → boot0..boot6 → mkrelease.sh chain.
      8 ##      Capture tarball A's sha256.
      9 ##   3. `make clean` again — fresh state, no cached intermediates.
     10 ##   4. `make package` again — full rebuild + repackage.
     11 ##      Capture tarball B's sha256.
     12 ##   5. Assert A == B. Bit-for-bit reproducibility check; if this drifts,
     13 ##      something in the input set or build is nondeterministic and the
     14 ##      release is not safe to publish.
     15 ##   6. Extract one tarball into a fresh dir under $HOME (so the macOS
     16 ##      podman VM can see it) and run the bundled verify.sh. verify.sh
     17 ##      re-runs boot0..boot6 off the bundled inputs and diffs each
     18 ##      artifact's sha256 against OUTPUT_MANIFEST.txt. End-to-end proof
     19 ##      that the shipped tarball reproduces the claimed outputs.
     20 ##   7. Promote the validated tarball to dist/<name>.tar.gz with a
     21 ##      sha256 sidecar. dist/ is the only directory mkrelease.sh /
     22 ##      release.sh ever writes to that's intended for publication.
     23 ##
     24 ## On any failure the dist/ output is NOT created; the operator gets a
     25 ## clear error pointing at which pass failed. The two intermediate
     26 ## tarballs are kept under build/<arch>/release/ for forensic diffing.
     27 ##
     28 ## Usage:  tools/release.sh <arch>
     29 ##   <arch> ∈ {aarch64, amd64, riscv64}
     30 ## Env:
     31 ##   DRIVER  podman (default) | seed   — passed through to make.
     32 ##
     33 ## Tarball name is `boot2-<arch>.tar.gz` — no git rev embedded, so its
     34 ## sha256 is a pure content hash. Provenance (rev, build date, build
     35 ## host) is written to `dist/boot2-<arch>.tar.gz.provenance` next to
     36 ## the tarball.
     37 
     38 set -eu
     39 
     40 ARCH=${1:-}
     41 case "$ARCH" in
     42     aarch64|amd64|riscv64) ;;
     43     *) echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2 ;;
     44 esac
     45 
     46 DRIVER=${DRIVER:-podman}
     47 case "$DRIVER" in
     48     podman|seed) ;;
     49     *) echo "[release] unknown DRIVER=$DRIVER" >&2; exit 2 ;;
     50 esac
     51 
     52 ROOT=$(cd "$(dirname "$0")/.." && pwd)
     53 cd "$ROOT"
     54 
     55 REV=$(git rev-parse --short HEAD 2>/dev/null || echo norev)
     56 DIRTY=
     57 if ! git diff --quiet HEAD 2>/dev/null || \
     58    ! git diff --quiet --cached HEAD 2>/dev/null; then
     59     DIRTY=-dirty
     60 fi
     61 NAME=boot2-$ARCH
     62 REL_DIR=build/$ARCH/release
     63 DIST=dist
     64 
     65 # Pass-tarball vault — must live OUTSIDE build/ so it survives the
     66 # `make clean` between passes. mktemp under /tmp; cleaned on exit.
     67 VAULT=$(mktemp -d -t boot2-release-XXXXXX)
     68 trap 'rm -rf "$VAULT"' EXIT
     69 
     70 # Portable sha256.
     71 if command -v sha256sum >/dev/null 2>&1; then
     72     sha256() { sha256sum "$1" | awk '{print $1}'; }
     73 else
     74     sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
     75 fi
     76 
     77 log() { printf '[release] %s\n' "$*"; }
     78 hr()  { printf '[release] ===================== %s =====================\n' "$*"; }
     79 
     80 t0=$(date +%s)
     81 
     82 # Two passes from a clean state. Each pass: make clean + make package.
     83 # Save each tarball aside under a pass-specific name so we can compare
     84 # them even if the recipe is rerun by hand later.
     85 do_pass() {
     86     _label=$1; _outvar=$2
     87     hr "pass $_label"
     88     log "make clean"
     89     make clean >/dev/null
     90     log "make package ARCH=$ARCH DRIVER=$DRIVER"
     91     make package ARCH="$ARCH" DRIVER="$DRIVER"
     92     _src=$REL_DIR/$NAME.tar.gz
     93     _dst=$VAULT/$NAME.pass-$_label.tar.gz
     94     [ -f "$_src" ] || { log "FAIL: pass $_label produced no $_src"; exit 1; }
     95     cp "$_src" "$_dst"
     96     _h=$(sha256 "$_dst")
     97     log "pass $_label sha256: $_h"
     98     eval "$_outvar=\$_h"
     99 }
    100 
    101 do_pass A SHA_A
    102 do_pass B SHA_B
    103 
    104 hr "compare"
    105 if [ "$SHA_A" != "$SHA_B" ]; then
    106     log "FAIL: pass A and pass B produced DIFFERENT tarballs"
    107     log "  A: $SHA_A  ($VAULT/$NAME.pass-A.tar.gz)"
    108     log "  B: $SHA_B  ($VAULT/$NAME.pass-B.tar.gz)"
    109     log ""
    110     log "Copying tarballs to dist/.failed-passes/ for inspection (vault is auto-cleaned)."
    111     mkdir -p "$DIST/.failed-passes"
    112     cp "$VAULT/$NAME.pass-A.tar.gz" "$DIST/.failed-passes/"
    113     cp "$VAULT/$NAME.pass-B.tar.gz" "$DIST/.failed-passes/"
    114     log "To investigate, extract both and diff the trees:"
    115     log "  mkdir /tmp/A /tmp/B"
    116     log "  tar xzf $DIST/.failed-passes/$NAME.pass-A.tar.gz -C /tmp/A"
    117     log "  tar xzf $DIST/.failed-passes/$NAME.pass-B.tar.gz -C /tmp/B"
    118     log "  diff -r /tmp/A /tmp/B"
    119     log "(check INPUT_MANIFEST.txt and OUTPUT_MANIFEST.txt for hash drift)"
    120     exit 1
    121 fi
    122 log "OK: both passes produced identical tarballs"
    123 log "    sha256 = $SHA_A"
    124 
    125 # End-to-end verify: extract the (still-installed) tarball, run its
    126 # verify.sh, which re-runs boot0..boot6 inside a fresh tree and
    127 # hash-diffs every artifact in OUTPUT_MANIFEST.txt.
    128 hr "verify"
    129 # macOS podman VM only mounts /Users/, so the verify dir must live
    130 # under $HOME. ~/.cache is a stable, throwaway-friendly location.
    131 VERIFY_BASE=$HOME/.cache/boot2-release-verify/$ARCH
    132 log "extract -> $VERIFY_BASE/$NAME"
    133 rm -rf "$VERIFY_BASE"
    134 mkdir -p "$VERIFY_BASE"
    135 tar xzf "$VAULT/$NAME.pass-A.tar.gz" -C "$VERIFY_BASE"
    136 
    137 log "running ./verify.sh (DRIVER=$DRIVER) — this rebuilds the chain"
    138 ( cd "$VERIFY_BASE/$NAME" && DRIVER=$DRIVER ./verify.sh )
    139 
    140 # Promote to dist/ — the only directory we treat as "publishable".
    141 hr "mint"
    142 mkdir -p "$DIST"
    143 cp "$VAULT/$NAME.pass-A.tar.gz" "$DIST/$NAME.tar.gz"
    144 printf '%s  %s\n' "$SHA_A" "$NAME.tar.gz" > "$DIST/$NAME.tar.gz.sha256"
    145 
    146 # Provenance sidecar — keeps git rev / build host / driver / timestamp
    147 # outside the tarball so they don't perturb the content hash.
    148 {
    149     printf 'tarball:    %s.tar.gz\n' "$NAME"
    150     printf 'sha256:     %s\n' "$SHA_A"
    151     printf 'arch:       %s\n' "$ARCH"
    152     printf 'driver:     %s\n' "$DRIVER"
    153     printf 'git_rev:    %s%s\n' "$REV" "$DIRTY"
    154     printf 'built_at:   %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
    155     printf 'built_on:   %s %s\n' "$(uname -s)" "$(uname -m)"
    156 } > "$DIST/$NAME.tar.gz.provenance"
    157 
    158 # Clean up the verify dir (it's reproducible from the tarball).
    159 rm -rf "$VERIFY_BASE"
    160 
    161 elapsed=$(( $(date +%s) - t0 ))
    162 bytes=$(wc -c < "$DIST/$NAME.tar.gz" | tr -d ' ')
    163 log "MINTED in ${elapsed}s"
    164 log "  tarball : $DIST/$NAME.tar.gz  ($bytes bytes)"
    165 log "  sha256  : $SHA_A"