boot2

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

mkrelease.sh (7530B)


      1 #!/bin/sh
      2 ## mkrelease.sh — package a per-arch boot2 release tarball.
      3 ##
      4 ## A release tarball is a self-contained bundle that lets anyone
      5 ## reproduce the full boot0..boot6 chain off the bundled inputs and
      6 ## byte-compare the outputs against a hash manifest. Layout:
      7 ##
      8 ##   boot2-<arch>[-<rev>].tar.gz
      9 ##     boot2-<arch>[-<rev>]/
     10 ##       README.md             extract + run instructions
     11 ##       verify.sh             drives boot0..boot6 + diffs OUTPUT_MANIFEST
     12 ##       INPUT_MANIFEST.txt    sha256 of every input under src/ + boot/
     13 ##       OUTPUT_MANIFEST.txt   sha256 of expected per-stage artifacts
     14 ##                             (driver-agnostic; the project's seed-accept
     15 ##                              harness verifies podman vs seed equivalence)
     16 ##       src/                  the sealed source tree (from
     17 ##                             build/<arch>/src/, produced by prep-src.sh)
     18 ##       boot/                 boot0..boot6 stage drivers + libs +
     19 ##                             containers/Containerfile.*
     20 ##
     21 ## The output manifest is generated from the current build outputs in
     22 ## build/<arch>/<driver>/boot{0..6}/. mkrelease.sh does NOT rebuild;
     23 ## prereqs (`make all ARCH=<arch>`) must already have run.
     24 ##
     25 ## Usage: tools/mkrelease.sh <arch>
     26 ##   <arch> ∈ {aarch64, amd64, riscv64}
     27 ## Env:
     28 ##   DRIVER  podman (default) | seed   — which build tree to hash for the
     29 ##                                       output manifest. The manifest is
     30 ##                                       claimed driver-agnostic regardless.
     31 ##
     32 ## The tarball name is `boot2-<arch>.tar.gz` — deliberately rev-free, so
     33 ## the tarball's sha256 reflects content and nothing else. Provenance
     34 ## (git rev, build timestamp) lives in a sidecar produced by the
     35 ## validated mint path (tools/release.sh), not inside the tarball.
     36 
     37 set -eu
     38 
     39 ARCH=${1:-}
     40 case "$ARCH" in
     41     aarch64|amd64|riscv64) ;;
     42     *) echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2 ;;
     43 esac
     44 
     45 DRIVER=${DRIVER:-podman}
     46 case "$DRIVER" in
     47     podman|seed) ;;
     48     *) echo "[mkrelease] unknown DRIVER=$DRIVER (expected podman|seed)" >&2; exit 2 ;;
     49 esac
     50 
     51 ROOT=$(cd "$(dirname "$0")/.." && pwd)
     52 cd "$ROOT"
     53 
     54 case "$ARCH" in
     55     aarch64) KERNEL_NAME=Image ;;
     56     amd64)   KERNEL_NAME=kernel.elf ;;
     57     riscv64) KERNEL_NAME=kernel.elf ;;
     58 esac
     59 
     60 NAME=boot2-$ARCH
     61 
     62 SRC_TREE=build/$ARCH/src
     63 BUILD_TREE=build/$ARCH/$DRIVER
     64 REL_DIR=build/$ARCH/release
     65 STAGING=$REL_DIR/$NAME
     66 TARBALL=$REL_DIR/$NAME.tar.gz
     67 
     68 [ -d "$SRC_TREE" ]                || { echo "[mkrelease] missing $SRC_TREE — run bootprep/prep-src.sh $ARCH" >&2; exit 1; }
     69 [ -f "$BUILD_TREE/boot6/$KERNEL_NAME" ] || { echo "[mkrelease] missing $BUILD_TREE/boot6/$KERNEL_NAME — run 'make all ARCH=$ARCH DRIVER=$DRIVER'" >&2; exit 1; }
     70 
     71 # Portable sha256. Use sha256sum if present; else shasum -a 256.
     72 if command -v sha256sum >/dev/null 2>&1; then
     73     sha256() { sha256sum "$1" | awk '{print $1}'; }
     74 else
     75     sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
     76 fi
     77 
     78 echo "[mkrelease] staging -> $STAGING"
     79 rm -rf "$STAGING"
     80 mkdir -p "$STAGING"
     81 
     82 # ── (1) sealed source tree ────────────────────────────────────────────
     83 cp -R "$SRC_TREE" "$STAGING/src"
     84 
     85 # ── (2) boot drivers (boot/*.sh, lib-*.sh, containers/Containerfile.*) ─
     86 cp -R boot "$STAGING/boot"
     87 
     88 # ── (3) README + verify.sh templates ──────────────────────────────────
     89 cp tools/release/README.md "$STAGING/README.md"
     90 cp tools/release/verify.sh "$STAGING/verify.sh"
     91 chmod +x "$STAGING/verify.sh"
     92 
     93 # Substitute @ARCH@ / @KERNEL_NAME@ into shipped docs/scripts. No git
     94 # rev is embedded — content stays rev-free so sha256 reflects content.
     95 for f in "$STAGING/README.md" "$STAGING/verify.sh"; do
     96     sed -i.bak \
     97         -e "s/@ARCH@/$ARCH/g" \
     98         -e "s/@KERNEL_NAME@/$KERNEL_NAME/g" \
     99         "$f"
    100     rm -f "$f.bak"
    101 done
    102 
    103 # ── (4) INPUT_MANIFEST.txt ────────────────────────────────────────────
    104 echo "[mkrelease] input manifest"
    105 INMAN=$STAGING/INPUT_MANIFEST.txt
    106 (
    107     cd "$STAGING"
    108     find src boot -type f | LC_ALL=C sort | while read -r rel; do
    109         h=$(sha256 "$rel")
    110         printf '%s  %s\n' "$h" "$rel"
    111     done
    112 ) > "$INMAN"
    113 n_in=$(wc -l < "$INMAN" | tr -d ' ')
    114 
    115 # ── (5) OUTPUT_MANIFEST.txt ───────────────────────────────────────────
    116 # Per-stage key artifacts (mirrors the stamp-anchored declarations in
    117 # the top-level Makefile). Driver-agnostic.
    118 gen_outputs() {
    119     cat <<EOF
    120 boot0/hex2
    121 boot0/catm
    122 boot0/M0
    123 boot1/M1pp
    124 boot1/hex2pp
    125 boot2/catm
    126 boot2/scheme1
    127 boot3/tcc0
    128 boot3/libc.P1pp
    129 boot3/tcc.flat.P1pp
    130 boot4/tcc1
    131 boot4/tcc2
    132 boot4/tcc3
    133 boot4/hello
    134 boot4/crt1.o
    135 boot4/libc.a
    136 boot4/libtcc1.a
    137 boot5/libc.a
    138 boot5/crt1.o
    139 boot5/crti.o
    140 boot5/crtn.o
    141 boot5/hello
    142 boot6/$KERNEL_NAME
    143 EOF
    144 }
    145 
    146 echo "[mkrelease] output manifest (from $BUILD_TREE)"
    147 OUTMAN=$STAGING/OUTPUT_MANIFEST.txt
    148 : > "$OUTMAN"
    149 rm -f "$OUTMAN.missing"
    150 gen_outputs | while read -r rel; do
    151     [ -n "$rel" ] || continue
    152     f=$BUILD_TREE/$rel
    153     if [ -e "$f" ]; then
    154         h=$(sha256 "$f")
    155         printf '%s  %s\n' "$h" "$rel" >> "$OUTMAN"
    156     else
    157         printf '%s\n' "$rel" >> "$OUTMAN.missing"
    158     fi
    159 done
    160 if [ -s "$OUTMAN.missing" ]; then
    161     echo "[mkrelease] FAIL: missing expected outputs under $BUILD_TREE:" >&2
    162     sed 's/^/  /' "$OUTMAN.missing" >&2
    163     echo "[mkrelease]   run 'make all ARCH=$ARCH DRIVER=$DRIVER' (and boot5) first" >&2
    164     rm -f "$OUTMAN.missing"
    165     exit 1
    166 fi
    167 n_out=$(wc -l < "$OUTMAN" | tr -d ' ')
    168 
    169 # ── (6) tarball — deterministic ──────────────────────────────────────
    170 # Identical inputs must yield byte-identical tarballs. Sources of drift:
    171 #   (a) gzip header embeds wall-clock mtime + original filename
    172 #       → use `gzip -n` (no name, no timestamp).
    173 #   (b) tar entries embed per-file mtime + uid/gid/uname/gname.
    174 #       → normalize on-disk mtimes with `touch -t`; override ownership
    175 #         in the tar headers via --uid/--gid/--uname/--gname.
    176 #   (c) tar entry order = filesystem readdir order.
    177 #       → sorted -T file list.
    178 # Note: macOS bsdtar refuses `--mtime` together with `-T -`. We avoid
    179 # that combo by normalizing mtimes on disk first.
    180 # Also: a pipeline like `... | tar | gzip > out` silently produces an
    181 # empty gzip if tar fails. Build the uncompressed tar to a tempfile so
    182 # `set -e` catches tar's exit code, then gzip from disk.
    183 echo "[mkrelease] tar -> $TARBALL"
    184 find "$STAGING" -exec touch -t 200001010000.00 {} +
    185 
    186 TAR_TMP=$PWD/$REL_DIR/$NAME.tar
    187 TARBALL_ABS=$PWD/$TARBALL
    188 rm -f "$TAR_TMP" "$TARBALL"
    189 (
    190     cd "$REL_DIR"
    191     find "$NAME" -print0 | LC_ALL=C sort -z | \
    192         tar --null -T - \
    193             --uid 0 --gid 0 --uname '' --gname '' \
    194             --format ustar \
    195             -cf "$TAR_TMP"
    196 )
    197 gzip -n -9 < "$TAR_TMP" > "$TARBALL_ABS"
    198 rm -f "$TAR_TMP"
    199 
    200 # Tarball digest (echoed for release notes; not embedded in the tar).
    201 TAR_SHA=$(sha256 "$TARBALL")
    202 printf '%s  %s\n' "$TAR_SHA" "$NAME.tar.gz" > "$REL_DIR/$NAME.tar.gz.sha256"
    203 
    204 bytes=$(wc -c < "$TARBALL" | tr -d ' ')
    205 echo "[mkrelease] OK"
    206 echo "[mkrelease]   tarball : $TARBALL  ($bytes bytes)"
    207 echo "[mkrelease]   sha256  : $TAR_SHA"
    208 echo "[mkrelease]   inputs  : $n_in files"
    209 echo "[mkrelease]   outputs : $n_out artifacts"