diff --git a/CHANGELOG.md b/CHANGELOG.md index 8715206..8af9c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ History before 0.5.0 lives in git and in [drill/RUNS.md](drill/RUNS.md), which records not just what changed but what each drill run proved. +## Unreleased + +### Fixed + +- **`box setup-host` finishes in one run** (#63). When it had to add you to + `incus-admin` it stopped there and told you to re-login and re-run — an + `exit 0` that reported success having built none of the stack: no `boxnet`, + no ACL, no `box-net` profile, no firewall. It now re-execs itself under + `sg incus-admin` and completes in that one invocation. The membership check + was also asking the wrong question: `id -nG "$USER"` reads the group + database, which lists the group the moment `usermod` returns, so a + same-session re-run passed the check with credentials that still lacked the + group and died further down on a bare permission error from `incus`. Argless + `id -nG` asks the process what it actually holds. + +### Changed + +- **`install.sh` runs the host setup itself** (#64) — it printed a warning and + left you a command to run, so the install reported success and `box new` + failed on a host with no Incus. Since `setup-host` is idempotent, doing this + on every install is also how an upgraded host picks up stack changes. + `BOX_SKIP_SETUP_HOST=1` opts out; if setup fails, the install still stands + and says what to re-run. + ## 0.5.0 — 2026-07-15 The release the project was renamed in: the repo is `heavy-duty/box`, matching diff --git a/README.md b/README.md index 94aa408..8fbe035 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,18 @@ curl -fsSL https://raw.githubusercontent.com/heavy-duty/box/main/install.sh | ba ``` Installs the tree to `~/.local/share/box` and links `box` onto your -`PATH`. Re-run any time to upgrade — upgrading from a pre-0.4.0 install also -retires the old `claudebox` symlink. (No `git clone` needed.) +`PATH`, then runs the host setup below for you (it may ask for `sudo`; set +`BOX_SKIP_SETUP_HOST=1` to opt out and run it yourself). Re-run any time to +upgrade — upgrading re-applies the host stack, and from a pre-0.4.0 install +also retires the old `claudebox` symlink. (No `git clone` needed.) ## One-time host setup (Ubuntu 24.04 / Debian 13) +The installer already does this. Run it directly to set up a host you +installed with `BOX_SKIP_SETUP_HOST=1`, or to re-apply the stack by hand: + ```sh -box setup-host # run twice if it adds you to incus-admin (re-login between) +box setup-host # one run is enough ``` Idempotent. Installs Incus and creates the isolation stack: the `boxnet` NAT diff --git a/bin/box b/bin/box index 63be4de..c04f0e0 100755 --- a/bin/box +++ b/bin/box @@ -387,10 +387,11 @@ EOF Prepare this host to mint boxes — one time. Installs Incus and builds the isolation stack: the boxnet NAT bridge (resolver pinned), the box-isolate ACL, the box-net profile, and the firewall rules, all re-applied at boot. -Idempotent — safe to re-run after a box upgrade to pick up stack changes. +Idempotent — safe to re-run after a box upgrade to pick up stack changes; +install.sh runs it for you, so this is for re-applying by hand. -If it has to add you to the incus-admin group it will say so and exit; log -back in (or 'sg incus-admin') and run it again. +One run is enough. If it has to add you to the incus-admin group it re-runs +itself under that group — no re-login, no second invocation. box setup-host EOF diff --git a/host/setup-host.sh b/host/setup-host.sh index 3e28d43..29964c9 100755 --- a/host/setup-host.sh +++ b/host/setup-host.sh @@ -3,17 +3,39 @@ # the box-net profile. Idempotent. Ubuntu 24.04 / Debian 13. set -euo pipefail -here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +self="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")" +here="$(dirname "$(dirname "$self")")" if ! command -v incus >/dev/null; then sudo apt-get update sudo apt-get install -y incus fi -if ! id -nG "$USER" | grep -qw incus-admin; then +# Group membership is a property of THIS PROCESS's credentials, not of the group +# database — and the two disagree for exactly as long as it matters here. +# 'id -nG "$USER"' names a user, so it reads /etc/group and reports incus-admin +# the instant usermod returns; the running shell's own credentials still lack +# it, because supplementary groups are fixed at login. So the old check passed +# on a same-session re-run, sailed into the incus calls below, and died on a +# permission error that named neither the group nor the re-login. Argless +# 'id -nG' asks the process what it actually holds, which is what incus checks +# when it opens /var/lib/incus/unix.socket. +if ! id -nG | grep -qw incus-admin; then sudo usermod -aG incus-admin "$USER" - echo "NOTE: added $USER to incus-admin — re-login (or 'sg incus-admin') and re-run." - exit 0 + # Then finish the job rather than adjourning it. Exiting 0 here was a + # success-shaped no-op: no boxnet, no ACL, no box-net profile, no firewall — + # and the burden of knowing that on the reader of a NOTE (#63). 'sg' runs us + # again with the new group in our credentials, no re-login, one invocation. + # The guard makes that at most one hop: if sg somehow lands without the + # group, we fail loudly instead of forking forever. + if [ -z "${BOX_SETUP_HOST_REEXEC:-}" ]; then + echo "added $USER to incus-admin — re-running under the new group (no re-login needed)" + export BOX_SETUP_HOST_REEXEC=1 + exec sg incus-admin -c "$(printf '%q ' bash "$self" "$@")" + fi + echo "ERROR: still not in incus-admin after usermod + sg." >&2 + echo " log out and back in, then re-run: box setup-host" >&2 + exit 1 fi # Storage pool + base config (safe to re-run: skipped once the pool exists). diff --git a/install.sh b/install.sh index 1d269e0..fae5cc6 100755 --- a/install.sh +++ b/install.sh @@ -84,16 +84,44 @@ case ":$PATH:" in ;; esac -# --- environment check ----------------------------------------------------- -if ! command -v incus >/dev/null 2>&1; then - warn "incus was not found. box needs Incus on the host." - warn " run the one-time host setup: $DEST/host/setup-host.sh" -fi - # Record WHAT was installed, so a caller can assert it got what it asked for. # Without this, an installer invoked with stale env vars (the CLAUDEBOX_* names # retired in 0.5.0) silently falls back to the defaults and installs main — # and the caller drills the wrong tree, believing it drilled its branch. +# Written BEFORE host setup: this records the install, which has now happened, +# and it must not hinge on whether the host stack came up. printf '%s@%s\n' "$REPO" "$REF" > "$DEST/INSTALLED_FROM" -log "done ($REPO@$REF) — try: box new --name test" +# --- host setup ------------------------------------------------------------ +# The installer finishes the job (#64). Telling the user to go run setup-host +# was a step that read as optional and failed later as mysterious: the install +# reports success, 'box' is on PATH, and 'box new' dies on a host with no +# Incus, no boxnet, no profile. setup-host is idempotent by design, so doing +# this on EVERY install is also how an upgraded host picks up stack changes — +# the isolation fixes that ship as new firewall rules land when the tool that +# claims them lands, instead of waiting on someone to re-run a command. +# BOX_SKIP_SETUP_HOST=1 opts out: CI, image builds, a host set up by hand. +setup_ok="" +if [ -n "${BOX_SKIP_SETUP_HOST:-}" ]; then + log "skipping host setup (BOX_SKIP_SETUP_HOST is set) — run it yourself: box setup-host" +elif [ "$(id -u)" -ne 0 ] && ! command -v sudo >/dev/null 2>&1; then + warn "host setup needs root and sudo was not found." + warn " run this as root to finish: $DEST/host/setup-host.sh" +else + log "running one-time host setup (installs Incus + the isolation stack; may ask for sudo)" + #