boot2

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

release.sh (6463B)


      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. Clean-build and package prep-src → boot0..boot6 once.
      7 ##   2. Extract the archive into a fresh directory under $HOME.
      8 ##   3. Clean-regenerate the checkout's canonical src/ tree and compare
      9 ##      its full boot/ + src/ hash manifest with the bundled
     10 ##      INPUT_MANIFEST.txt.
     11 ##   4. Repackage the extracted sealed payload and assert that its tarball
     12 ##      is byte-identical to the original.
     13 ##   5. Run the bundled verify.sh. It rebuilds boot0..boot6 from only the
     14 ##      sealed inputs and compares every artifact with OUTPUT_MANIFEST.txt,
     15 ##      proving both build determinism and archive completeness.
     16 ##   6. Promote the validated tarball to dist/<name>.tar.gz with a
     17 ##      sha256 sidecar. dist/ is the only directory mkrelease.sh /
     18 ##      release.sh ever writes to that's intended for publication.
     19 ##
     20 ## On any failure no new dist/ output is promoted.
     21 ##
     22 ## Usage:  tools/release.sh <arch>
     23 ##   <arch> ∈ {aarch64, amd64, riscv64}
     24 ## Env:
     25 ##   DRIVER  podman (default) | seed   — passed through to make.
     26 ##
     27 ## Tarball name is `boot2-<arch>.tar.gz` — no git rev embedded, so its
     28 ## sha256 is a pure content hash. Provenance (rev, build date, build
     29 ## host) is written to `dist/boot2-<arch>.tar.gz.provenance` next to
     30 ## the tarball.
     31 
     32 set -eu
     33 
     34 ARCH=${1:-}
     35 case "$ARCH" in
     36     aarch64|amd64|riscv64) ;;
     37     *) echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2 ;;
     38 esac
     39 
     40 DRIVER=${DRIVER:-podman}
     41 case "$DRIVER" in
     42     podman|seed) ;;
     43     *) echo "[release] unknown DRIVER=$DRIVER" >&2; exit 2 ;;
     44 esac
     45 
     46 ROOT=$(cd "$(dirname "$0")/.." && pwd)
     47 cd "$ROOT"
     48 
     49 REV=$(git rev-parse --short HEAD 2>/dev/null || echo norev)
     50 DIRTY=
     51 if ! git diff --quiet HEAD 2>/dev/null || \
     52    ! git diff --quiet --cached HEAD 2>/dev/null; then
     53     DIRTY=-dirty
     54 fi
     55 NAME=boot2-$ARCH
     56 REL_DIR=build/$ARCH/release
     57 DIST=dist
     58 
     59 # Validation vault — must live outside build/ so it survives make clean.
     60 VAULT=$(mktemp -d -t boot2-release-XXXXXX)
     61 trap 'rm -rf "$VAULT"' EXIT
     62 
     63 # Portable sha256.
     64 if command -v sha256sum >/dev/null 2>&1; then
     65     sha256() { sha256sum "$1" | awk '{print $1}'; }
     66 else
     67     sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
     68 fi
     69 
     70 log() { printf '[release] %s\n' "$*"; }
     71 hr()  { printf '[release] ===================== %s =====================\n' "$*"; }
     72 
     73 t0=$(date +%s)
     74 
     75 hr "build"
     76 log "make clean"
     77 make clean >/dev/null
     78 log "make package ARCH=$ARCH DRIVER=$DRIVER"
     79 make package ARCH="$ARCH" DRIVER="$DRIVER"
     80 ARCHIVE=$VAULT/$NAME.original.tar.gz
     81 [ -f "$REL_DIR/$NAME.tar.gz" ] || { log "FAIL: package produced no archive"; exit 1; }
     82 cp "$REL_DIR/$NAME.tar.gz" "$ARCHIVE"
     83 SHA_A=$(sha256 "$ARCHIVE")
     84 log "archive sha256: $SHA_A"
     85 
     86 # Extract before regenerating checkout inputs. The verify directory must
     87 # live under $HOME because the macOS podman VM only mounts /Users/.
     88 hr "extract"
     89 # macOS podman VM only mounts /Users/, so the verify dir must live
     90 # under $HOME. ~/.cache is a stable, throwaway-friendly location.
     91 VERIFY_BASE=$HOME/.cache/boot2-release-verify/$ARCH
     92 log "extract -> $VERIFY_BASE/$NAME"
     93 rm -rf "$VERIFY_BASE"
     94 mkdir -p "$VERIFY_BASE"
     95 tar xzf "$ARCHIVE" -C "$VERIFY_BASE"
     96 
     97 SEALED=$VERIFY_BASE/$NAME
     98 INPUT_MANIFEST=$SEALED/INPUT_MANIFEST.txt
     99 OUTPUT_MANIFEST=$SEALED/OUTPUT_MANIFEST.txt
    100 [ -f "$INPUT_MANIFEST" ] || { log "FAIL: archive has no INPUT_MANIFEST.txt"; exit 1; }
    101 [ -f "$OUTPUT_MANIFEST" ] || { log "FAIL: archive has no OUTPUT_MANIFEST.txt"; exit 1; }
    102 
    103 # Independently regenerate canonical inputs from the checkout. This is
    104 # cheap compared with boot3 and proves prep-src produces the sealed tree.
    105 hr "inputs"
    106 log "make clean"
    107 make clean >/dev/null
    108 log "make src ARCH=$ARCH DRIVER=$DRIVER"
    109 make src ARCH="$ARCH" DRIVER="$DRIVER"
    110 REGENERATED_INPUTS=$VAULT/$NAME.inputs.regenerated.txt
    111 tools/release/input-manifest.sh "build/$ARCH/src" boot "$REGENERATED_INPUTS"
    112 if ! cmp -s "$INPUT_MANIFEST" "$REGENERATED_INPUTS"; then
    113     log "FAIL: regenerated canonical inputs differ from INPUT_MANIFEST.txt"
    114     diff -u "$INPUT_MANIFEST" "$REGENERATED_INPUTS" || true
    115     exit 1
    116 fi
    117 log "OK: regenerated canonical inputs match ($(wc -l < "$INPUT_MANIFEST" | tr -d ' ') files)"
    118 
    119 # Repackage the extracted payload with the same deterministic archiver.
    120 # This proves the published tar bytes are stable without rebuilding boot3.
    121 hr "repackage"
    122 REPACKED=$VAULT/$NAME.repacked.tar.gz
    123 tools/release/tar-payload.sh "$SEALED" "$REPACKED"
    124 SHA_REPACKED=$(sha256 "$REPACKED")
    125 if [ "$SHA_A" != "$SHA_REPACKED" ]; then
    126     log "FAIL: repackaged payload differs from original archive"
    127     log "  original : $SHA_A"
    128     log "  repacked : $SHA_REPACKED"
    129     exit 1
    130 fi
    131 log "OK: repackaged payload is byte-identical"
    132 log "    sha256 = $SHA_A"
    133 
    134 # End-to-end build from the archive. Always run podman first: a seed
    135 # verification needs the podman-built boot6 kernel as its runtime.
    136 hr "verify"
    137 
    138 log "running ./verify.sh (DRIVER=podman) — this rebuilds the chain"
    139 ( cd "$SEALED" && DRIVER=podman ./verify.sh )
    140 if [ "$DRIVER" = seed ]; then
    141     log "running ./verify.sh (DRIVER=seed) — this closes the kernel loop"
    142     ( cd "$SEALED" && DRIVER=seed ./verify.sh )
    143 fi
    144 
    145 OUTPUT_MANIFEST_SHA=$(sha256 "$OUTPUT_MANIFEST")
    146 
    147 # Promote to dist/ — the only directory we treat as "publishable".
    148 hr "mint"
    149 mkdir -p "$DIST"
    150 cp "$ARCHIVE" "$DIST/$NAME.tar.gz"
    151 printf '%s  %s\n' "$SHA_A" "$NAME.tar.gz" > "$DIST/$NAME.tar.gz.sha256"
    152 cp "$OUTPUT_MANIFEST" "$DIST/$NAME.outputs.sha256"
    153 
    154 # Provenance sidecar — keeps git rev / build host / driver / timestamp
    155 # outside the tarball so they don't perturb the content hash.
    156 {
    157     printf 'tarball:    %s.tar.gz\n' "$NAME"
    158     printf 'sha256:     %s\n' "$SHA_A"
    159     printf 'outputs:    %s.outputs.sha256\n' "$NAME"
    160     printf 'outputs_sha256: %s\n' "$OUTPUT_MANIFEST_SHA"
    161     printf 'arch:       %s\n' "$ARCH"
    162     printf 'driver:     %s\n' "$DRIVER"
    163     printf 'git_rev:    %s%s\n' "$REV" "$DIRTY"
    164     printf 'built_at:   %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
    165     printf 'built_on:   %s %s\n' "$(uname -s)" "$(uname -m)"
    166 } > "$DIST/$NAME.tar.gz.provenance"
    167 
    168 # Clean up the verify dir (it's reproducible from the tarball).
    169 rm -rf "$VERIFY_BASE"
    170 
    171 elapsed=$(( $(date +%s) - t0 ))
    172 bytes=$(wc -c < "$DIST/$NAME.tar.gz" | tr -d ' ')
    173 log "MINTED in ${elapsed}s"
    174 log "  tarball : $DIST/$NAME.tar.gz  ($bytes bytes)"
    175 log "  sha256  : $SHA_A"
    176 log "  outputs : $DIST/$NAME.outputs.sha256"