boot2

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

release.sh (6958B)


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