fix: install pinned drill inputs by commit

This commit is contained in:
codex-bot-andresmgsl 2026-08-18 07:14:26 +00:00
parent 45c29d6dbc
commit e1e4fc77db
10 changed files with 83 additions and 47 deletions

View file

@ -49,13 +49,15 @@ By default that installs the **latest release** — the installer resolves the
release tag off GitHub's `releases/latest` redirect (no API, no token) and release tag off GitHub's `releases/latest` redirect (no API, no token) and
downloads exactly that tree, so two operators running it get the same box. downloads exactly that tree, so two operators running it get the same box.
If the resolution fails it says so and stops — it never silently hands out If the resolution fails it says so and stops — it never silently hands out
`main`. `BOX_REF` picks another channel (a set ref is tried as a tag first, `main`. `BOX_REF` picks another channel: a named ref is tried as a tag first,
then as a branch — [#83](https://github.com/heavy-duty/box/issues/83)): then as a branch ([#83](https://github.com/heavy-duty/box/issues/83)), while a
full 40-hex commit SHA selects GitHub's immutable commit archive:
```sh ```sh
curl -fsSL .../install.sh | bash # the latest release (default) curl -fsSL .../install.sh | bash # the latest release (default)
curl -fsSL .../install.sh | BOX_REF=0.6.0 bash # pin a release curl -fsSL .../install.sh | BOX_REF=0.6.0 bash # pin a release
curl -fsSL .../install.sh | BOX_REF=main bash # the development tip curl -fsSL .../install.sh | BOX_REF=main bash # the development tip
curl -fsSL .../install.sh | BOX_REF=<full-commit-sha> bash # exact tree
``` ```
(A dev tree's `VERSION` carries a `-dev` suffix, so it lands beside your (A dev tree's `VERSION` carries a `-dev` suffix, so it lands beside your

32
bin/box
View file

@ -1096,36 +1096,48 @@ load_template() {
T_DISK="${disk:-${BOX_DISK:-${T_DISK:-60GiB}}}" T_DISK="${disk:-${BOX_DISK:-${T_DISK:-60GiB}}}"
} }
# The ONE substitution a template gets — user-data.yaml is otherwise passed to # The ONE substitution a template gets — @RIG_INSTALL@ — while user-data.yaml
# Incus verbatim. The tenant seeds preinstall rig, which inverts the rig→box # is otherwise passed to Incus verbatim. The tenant seeds preinstall rig, which
# install edge (rig#28: rig installs box on hosts; now box guests install rig), # inverts the rig→box install edge (rig#28: rig installs box on hosts; now box
# and that edge needs a pin point (#81): the seed carries @RIG_REPO@ / # guests install rig), and that edge needs a pin point (#81). The renderer
# @RIG_REF@ tokens, resolved here from the mint environment — RIG_REPO # resolves the mint environment's RIG_REPO (default heavy-duty/rig) and RIG_REF
# (default heavy-duty/rig) and RIG_REF (default main). Both directions track # (default main) into the whole installer command. Named refs keep the normal
# main unpinned today, said honestly (the same treatment rig#29 gave box's own # tag/branch channel; a drill-pinned commit carries its immutable archive as
# unpinned install) until a release flow exists (rig#32 / #83). The values are # RIG_INSTALL_SOURCE (#152). Both values are
# allowlist-validated BEFORE touching the YAML: they land inside a runcmd # allowlist-validated BEFORE touching the YAML: they land inside a runcmd
# shell line, so a quote, a space or a newline smuggled through the # shell line, so a quote, a space or a newline smuggled through the
# environment must die on the host, never execute in the guest. bash's =~ # environment must die on the host, never execute in the guest. bash's =~
# anchors to the whole string — a multi-line value cannot sneak one clean # anchors to the whole string — a multi-line value cannot sneak one clean
# line past it the way a line-oriented grep would. # line past it the way a line-oriented grep would.
# The rig pin, resolved from the mint environment, in ONE place: render_userdata # The rig pin, resolved from the mint environment, in ONE place: render_userdata
# substitutes it into the seed, and the mint stamp (#103) records it onto the # builds it into the seed, and the mint stamp (#103) records it onto the
# instance. Two spellings of the same default would eventually disagree, and a # instance. Two spellings of the same default would eventually disagree, and a
# stamp that disagrees with the seed is worse than no stamp at all. # stamp that disagrees with the seed is worse than no stamp at all.
rig_repo() { printf '%s\n' "${RIG_REPO:-heavy-duty/rig}"; } rig_repo() { printf '%s\n' "${RIG_REPO:-heavy-duty/rig}"; }
rig_ref() { printf '%s\n' "${RIG_REF:-main}"; } rig_ref() { printf '%s\n' "${RIG_REF:-main}"; }
render_userdata() { render_userdata() {
local f="$1" repo data ref local f="$1" repo data ref install
repo="$(rig_repo)"; ref="$(rig_ref)" repo="$(rig_repo)"; ref="$(rig_ref)"
[[ "$repo" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] \ [[ "$repo" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] \
|| die "RIG_REPO must look like owner/repo: $repo" || die "RIG_REPO must look like owner/repo: $repo"
[[ "$ref" =~ ^[A-Za-z0-9._/-]+$ ]] \ [[ "$ref" =~ ^[A-Za-z0-9._/-]+$ ]] \
|| die "RIG_REF must be a plain ref name (letters, digits, . _ / -): $ref" || die "RIG_REF must be a plain ref name (letters, digits, . _ / -): $ref"
if [[ "$ref" =~ ^[0-9a-f]{40}$ ]]; then
# rig's installer currently resolves named tags/branches. A release drill
# pins the moving ref before mint, so hand that installer the immutable
# commit archive as a local source instead of asking it to resolve again
# inside the guest (#152).
printf -v install '%s' \
"rig_source=/tmp/rig-$ref.tar.gz; rig_installer=/tmp/rig-install-$ref.sh; curl -fsSL https://github.com/$repo/archive/$ref.tar.gz -o \"\$rig_source\" && curl -fsSL https://raw.githubusercontent.com/$repo/$ref/install.sh -o \"\$rig_installer\" && HOME=/root RIG_REPO=\"$repo\" RIG_REF=\"$ref\" RIG_INSTALL_SOURCE=\"\$rig_source\" bash \"\$rig_installer\"; rig_rc=\$?; rm -f \"\$rig_source\" \"\$rig_installer\"; exit \"\$rig_rc\""
else
printf -v install 'curl -fsSL https://raw.githubusercontent.com/%s/%s/install.sh | HOME=/root RIG_REPO="%s" RIG_REF="%s" bash' \
"$repo" "$ref" "$repo" "$ref"
fi
data="$(cat "$f")" data="$(cat "$f")"
data="${data//@RIG_REPO@/$repo}" data="${data//@RIG_REPO@/$repo}"
data="${data//@RIG_REF@/$ref}" data="${data//@RIG_REF@/$ref}"
data="${data//@RIG_INSTALL@/$install}"
printf '%s\n' "$data" printf '%s\n' "$data"
} }

View file

@ -24,10 +24,11 @@ set -euo pipefail
# downloading — for CI and the drill, so what lands is the code under review. # downloading — for CI and the drill, so what lands is the code under review.
REPO="${BOX_REPO:-heavy-duty/box}" REPO="${BOX_REPO:-heavy-duty/box}"
# Three install channels, one knob (#83): BOX_REF unset installs the LATEST # Four install channels, one knob (#83, #152): BOX_REF unset installs the LATEST
# RELEASE (the tag resolved from GitHub's releases/latest redirect, below); # RELEASE (the tag resolved from GitHub's releases/latest redirect, below);
# BOX_REF=<tag> pins a release; BOX_REF=<branch> (say, main) is the dev # BOX_REF=<tag> pins a release; BOX_REF=<branch> (say, main) is the dev
# channel. A set ref is tried as a tag first, then as a branch. # channel; and a full 40-hex commit SHA is fetched immutably. A named ref is
# tried as a tag first, then as a branch.
REF="${BOX_REF:-}" REF="${BOX_REF:-}"
# Root installs GLOBALLY, non-root installs per-user. box's install tree is # Root installs GLOBALLY, non-root installs per-user. box's install tree is
# EXECUTED by other users (the multi-user host path: rig installs box once, every # EXECUTED by other users (the multi-user host path: rig installs box once, every
@ -232,17 +233,27 @@ else
fi fi
INSTALLED_FROM="$REPO@$REF" INSTALLED_FROM="$REPO@$REF"
log "installing box from $REPO@$REF" log "installing box from $REPO@$REF"
# A ref is a TAG first (the pinned-release channel), a branch second (the if [[ "$REF" =~ ^[0-9a-f]{40}$ ]]; then
# dev channel, BOX_REF=main) — and the fallback only exists for a ref the # The drill resolves moving candidate refs before it mutates a host, then
# OPERATOR named: a resolved latest tag has no branch to fall through to. # installs the exact object it measured (#152). GitHub's commit archive is
URL="https://github.com/$REPO/archive/refs/tags/$REF.tar.gz" # outside refs/{tags,heads}; guessing either namespace makes every SHA 404.
log "downloading $URL" URL="https://github.com/$REPO/archive/$REF.tar.gz"
if ! curl -fsSL "$URL" -o "$TMPDIR/box.tar.gz"; then log "downloading immutable commit $URL"
[ -n "${BOX_REF:-}" ] || die "failed to download $URL"
URL="https://github.com/$REPO/archive/refs/heads/$REF.tar.gz"
log "no tag '$REF' — trying it as a branch: $URL"
curl -fsSL "$URL" -o "$TMPDIR/box.tar.gz" \ curl -fsSL "$URL" -o "$TMPDIR/box.tar.gz" \
|| die "failed to download it as either — '$REF' is neither a tag nor a branch of $REPO" || die "failed to download commit $REF from $REPO"
else
# A named ref is a TAG first (the pinned-release channel), a branch second
# (the dev channel, BOX_REF=main). The fallback only exists for a ref the
# OPERATOR named: a resolved latest tag has no branch to fall through to.
URL="https://github.com/$REPO/archive/refs/tags/$REF.tar.gz"
log "downloading $URL"
if ! curl -fsSL "$URL" -o "$TMPDIR/box.tar.gz"; then
[ -n "${BOX_REF:-}" ] || die "failed to download $URL"
URL="https://github.com/$REPO/archive/refs/heads/$REF.tar.gz"
log "no tag '$REF' — trying it as a branch: $URL"
curl -fsSL "$URL" -o "$TMPDIR/box.tar.gz" \
|| die "failed to download it as either — '$REF' is neither a tag nor a branch of $REPO"
fi
fi fi
log "extracting archive" log "extracting archive"

View file

@ -30,4 +30,4 @@ runcmd:
# HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the # HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the
# environment, and the rig installer (set -u) reads $HOME for its DEST — # environment, and the rig installer (set -u) reads $HOME for its DEST —
# measured live: the mint died with "HOME: unbound variable" without it. # measured live: the mint died with "HOME: unbound variable" without it.
- curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | HOME=/root RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash - '@RIG_INSTALL@'

View file

@ -30,4 +30,4 @@ runcmd:
# HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the # HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the
# environment, and the rig installer (set -u) reads $HOME for its DEST — # environment, and the rig installer (set -u) reads $HOME for its DEST —
# measured live: the mint died with "HOME: unbound variable" without it. # measured live: the mint died with "HOME: unbound variable" without it.
- curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | HOME=/root RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash - '@RIG_INSTALL@'

View file

@ -30,4 +30,4 @@ runcmd:
# HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the # HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the
# environment, and the rig installer (set -u) reads $HOME for its DEST — # environment, and the rig installer (set -u) reads $HOME for its DEST —
# measured live: the mint died with "HOME: unbound variable" without it. # measured live: the mint died with "HOME: unbound variable" without it.
- curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | HOME=/root RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash - '@RIG_INSTALL@'

View file

@ -30,4 +30,4 @@ runcmd:
# HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the # HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the
# environment, and the rig installer (set -u) reads $HOME for its DEST — # environment, and the rig installer (set -u) reads $HOME for its DEST —
# measured live: the mint died with "HOME: unbound variable" without it. # measured live: the mint died with "HOME: unbound variable" without it.
- curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | HOME=/root RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash - '@RIG_INSTALL@'

View file

@ -31,4 +31,4 @@ runcmd:
# HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the # HOME=/root: cloud-init runs runcmd as root but with NO $HOME in the
# environment, and the rig installer (set -u) reads $HOME for its DEST — # environment, and the rig installer (set -u) reads $HOME for its DEST —
# measured live: the mint died with "HOME: unbound variable" without it. # measured live: the mint died with "HOME: unbound variable" without it.
- curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | HOME=/root RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash - '@RIG_INSTALL@'

View file

@ -266,15 +266,28 @@ check "render_userdata: the pin's defaults came with it (guards the grep)" 0 "he
check "render_userdata: the extracted function is valid bash" 0 "" bash -n "$RUFN" check "render_userdata: the extracted function is valid bash" 0 "" bash -n "$RUFN"
SEED="$(mktemp)" SEED="$(mktemp)"
printf '#cloud-config\nruncmd:\n - curl -fsSL https://raw.githubusercontent.com/@RIG_REPO@/@RIG_REF@/install.sh | RIG_REPO="@RIG_REPO@" RIG_REF="@RIG_REF@" bash\n' > "$SEED" printf "#cloud-config\nruncmd:\n - '@RIG_INSTALL@'\n" > "$SEED"
# shellcheck disable=SC2016 # $0/$1 expand in the child shell, by design # shellcheck disable=SC2016 # $0/$1 expand in the child shell, by design
rud() { # rud [VAR=val ...] — render the fixture seed through the real function rud() { # rud [VAR=val ...] — render the fixture seed through the real function
env "$@" bash -c 'die() { echo "box: $*" >&2; exit 1; }; . "$0"; render_userdata "$1"' "$RUFN" "$SEED" env "$@" bash -c 'die() { echo "box: $*" >&2; exit 1; }; . "$0"; render_userdata "$1"' "$RUFN" "$SEED"
} }
check "render_userdata: defaults pin heavy-duty/rig" 0 "githubusercontent.com/heavy-duty/rig/main/install.sh" rud check "render_userdata: defaults pin heavy-duty/rig" 0 "githubusercontent.com/heavy-duty/rig/main/install.sh" rud
check "render_userdata: defaults feed the installer's own env too" 0 'RIG_REPO="heavy-duty/rig" RIG_REF="main"' rud check "render_userdata: defaults feed the installer's own env too" 0 'RIG_REPO="heavy-duty/rig" RIG_REF="main"' rud
check "render_userdata: the rig install pins HOME=/root (runcmd has no \$HOME)" 0 "HOME=/root " rud
check "render_userdata: RIG_REPO/RIG_REF override at mint" 0 "dan-claude-bot/rig/feat/bootstrap-roles/install.sh" \ check "render_userdata: RIG_REPO/RIG_REF override at mint" 0 "dan-claude-bot/rig/feat/bootstrap-roles/install.sh" \
rud RIG_REPO=dan-claude-bot/rig RIG_REF=feat/bootstrap-roles rud RIG_REPO=dan-claude-bot/rig RIG_REF=feat/bootstrap-roles
RIG_COMMIT=89abcdef0123456789abcdef0123456789abcdef
check "render_userdata: a rig commit downloads its immutable archive" 0 "archive/$RIG_COMMIT.tar.gz" \
rud RIG_REPO=heavy-duty/rig RIG_REF="$RIG_COMMIT"
# $rig_source belongs to the rendered guest shell.
# shellcheck disable=SC2016
check "render_userdata: a rig commit feeds the archive to the installer" 0 'RIG_INSTALL_SOURCE="$rig_source"' \
rud RIG_REPO=heavy-duty/rig RIG_REF="$RIG_COMMIT"
# Expansion belongs to the bash -c fixture.
# shellcheck disable=SC2016
check "render_userdata: a named rig ref keeps the normal channel" 1 "" \
bash -c 'die() { exit 1; }; RIG_REPO=heavy-duty/rig RIG_REF=main; . "$1"; render_userdata "$2" | grep -q RIG_INSTALL_SOURCE' \
_ "$RUFN" "$SEED"
# shellcheck disable=SC2016 # $0/$1 expand in the child shells, by design # shellcheck disable=SC2016 # $0/$1 expand in the child shells, by design
check "render_userdata: no token survives the render" 1 "" \ check "render_userdata: no token survives the render" 1 "" \
bash -c 'env bash -c "die() { echo box: \$*; exit 1; }; . \"\$0\"; render_userdata \"\$1\"" "$1" "$2" | grep -q @RIG_' _ "$RUFN" "$SEED" bash -c 'env bash -c "die() { echo box: \$*; exit 1; }; . \"\$0\"; render_userdata \"\$1\"" "$1" "$2" | grep -q @RIG_' _ "$RUFN" "$SEED"
@ -326,26 +339,13 @@ for d in "$ROOT"/templates/*/; do
# The thin-template contract (#81), both halves per template: # The thin-template contract (#81), both halves per template:
# #
# THE SEED — a template that names a tenant role (BOX_BOOTSTRAP_ROLE) must # THE SEED — a template that names a tenant role (BOX_BOOTSTRAP_ROLE) must
# preinstall rig carrying BOTH pin tokens, on the installer URL and on the # delegate its rig install to render_userdata. That one renderer owns both
# installer's own env, or the pin is a half-truth: a mint would fetch one # the named-ref channel and the immutable-commit archive channel.
# ref's installer and install another ref's tree.
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
trole="$(tpl "$ROOT" "$t" | sed -n 's/.*ROLE=\([^ ]*\).*/\1/p')" trole="$(tpl "$ROOT" "$t" | sed -n 's/.*ROLE=\([^ ]*\).*/\1/p')"
if [ -n "$trole" ]; then if [ -n "$trole" ]; then
check "template '$t': the seed installs rig (role '$trole')" 0 "" \ check "template '$t': the seed installs rig through the renderer (role '$trole')" 0 "" \
grep -q 'install.sh' "$d/user-data.yaml" grep -q "^[[:space:]]*- '@RIG_INSTALL@'\$" "$d/user-data.yaml"
# shellcheck disable=SC2016 # $1 expands in the child shell, by design
check "template '$t': the rig install carries the @RIG_REPO@ pin token" 0 "" \
bash -c 'grep "install.sh" "$1" | grep -q "@RIG_REPO@/@RIG_REF@"' _ "$d/user-data.yaml"
# shellcheck disable=SC2016
check "template '$t': the pin reaches the installer's env too" 0 "" \
bash -c 'grep "install.sh" "$1" | grep -q "RIG_REPO=\"@RIG_REPO@\" RIG_REF=\"@RIG_REF@\""' _ "$d/user-data.yaml"
# HOME=/root: a scar found live — cloud-init's runcmd has no $HOME and
# rig's installer (set -u) dies on it (rig#39). The pin must survive
# every seed rewrite.
# shellcheck disable=SC2016
check "template '$t': the rig install pins HOME=/root (runcmd has no \$HOME)" 0 "" \
bash -c 'grep "install.sh" "$1" | grep -q "HOME=/root "' _ "$d/user-data.yaml"
fi fi
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
# THE ABSENCE — no tenant content in ANY template, ever again. Everything a # THE ABSENCE — no tenant content in ANY template, ever again. Everything a

View file

@ -109,6 +109,16 @@ check "dev channel: tries the tag first" 0 "refs/tags/main.tar.gz" head -1 "$L3"
check "dev channel: then downloads the branch" 0 "" \ check "dev channel: then downloads the branch" 0 "" \
grep -qF "archive/refs/heads/main.tar.gz" "$L3" grep -qF "archive/refs/heads/main.tar.gz" "$L3"
COMMIT_REF=0123456789abcdef0123456789abcdef01234567
H5="$WORK/h5"; B5="$WORK/b5"; L5="$WORK/c5.log"
check "commit channel: installs an immutable full SHA" 0 "installing box from heavy-duty/box@$COMMIT_REF" \
ninst "$H5" "$B5" BOX_REF="$COMMIT_REF" FAKE_CURL_LOG="$L5" \
FAKE_SERVE_URL="https://github.com/heavy-duty/box/archive/$COMMIT_REF.tar.gz"
check "commit channel: downloads the commit archive directly" 0 "archive/$COMMIT_REF.tar.gz" cat "$L5"
check "commit channel: never guesses tag or branch namespaces" 1 "" grep -Eq 'refs/(tags|heads)' "$L5"
check "commit channel: records the immutable source" 0 "heavy-duty/box@$COMMIT_REF" \
cat "$H5/versions/9.9.9/INSTALLED_FROM"
H4="$WORK/h4"; B4="$WORK/b4"; L4="$WORK/c4.log" H4="$WORK/h4"; B4="$WORK/b4"; L4="$WORK/c4.log"
check "resolution failure: names the latest-release probe" 1 "could not resolve the latest release" \ check "resolution failure: names the latest-release probe" 1 "could not resolve the latest release" \
ninst "$H4" "$B4" FAKE_CURL_RC=6 FAKE_CURL_LOG="$L4" ninst "$H4" "$B4" FAKE_CURL_RC=6 FAKE_CURL_LOG="$L4"
@ -122,6 +132,7 @@ check "unknown ref names both attempted channels" 1 "neither a tag nor a branch"
check "README documents the latest-release channel" 0 "" grep -qF 'latest release' "$ROOT/README.md" check "README documents the latest-release channel" 0 "" grep -qF 'latest release' "$ROOT/README.md"
check "README documents the pinned channel" 0 "" grep -qF 'BOX_REF=0.6.0' "$ROOT/README.md" check "README documents the pinned channel" 0 "" grep -qF 'BOX_REF=0.6.0' "$ROOT/README.md"
check "README documents the dev channel" 0 "" grep -qF 'BOX_REF=main' "$ROOT/README.md" check "README documents the dev channel" 0 "" grep -qF 'BOX_REF=main' "$ROOT/README.md"
check "README documents the immutable commit channel" 0 "" grep -qF 'BOX_REF=<full-commit-sha>' "$ROOT/README.md"
echo "---" echo "---"
echo "$PASS passed, $FAIL failed" echo "$PASS passed, $FAIL failed"