From 229fcd2bf08bec9ee73b8a9b4075b0520e3bcc9c Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Sat, 18 Jul 2026 22:36:05 +0000 Subject: [PATCH] install: derive $HOME from getent when the environment has none (#39) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cloud-init's runcmd runs the installer with no $HOME, and under set -u the first expansion died with an unbound-variable stack instead of an install — found live by box#88's seed, which pins HOME=/root as its own scar. Derive the home from getent for the effective user (root included) before any path comes from $HOME; when getent has no answer either, refuse by name instead of a bash stack. Driven with a shim getent: the derived-home install lands, and the no-answer refusal is pinned. Co-Authored-By: Claude Fable 5 --- install.sh | 17 +++++++++++++++++ test/cli.sh | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/install.sh b/install.sh index 6e97110..8dd3ed4 100644 --- a/install.sh +++ b/install.sh @@ -35,6 +35,23 @@ set -euo pipefail REPO="${RIG_REPO:-heavy-duty/rig}" REF="${RIG_REF:-}" # empty = the latest release, resolved below + +# cloud-init's runcmd runs with NO $HOME in the environment, and under set -u +# the expansions just below turned that into a death instead of an install — +# found live by box#88's seed, which pins HOME=/root as its own scar (rig#39). +# Derive it from the effective user instead: getent knows every user's home, +# root included. (Inline error: die() is not defined this early on purpose — +# this guard must run before any path is derived from $HOME.) +if [ -z "${HOME:-}" ]; then + # '|| true': under pipefail a no-answer getent would kill the script here + # with ITS exit code, instead of falling through to the named refusal. + HOME="$(getent passwd "$(id -u)" | cut -d: -f6 || true)"; export HOME + if [ -z "$HOME" ]; then + printf "rig-install: ERROR: \$HOME is unset and getent knows no home for uid %s — set HOME and re-run\n" "$(id -u)" >&2 + exit 1 + fi +fi + DEST="${RIG_HOME:-$HOME/.local/share/rig}" if [ "$(id -u)" -eq 0 ]; then BINDIR="${RIG_BIN:-/usr/local/bin}" diff --git a/test/cli.sh b/test/cli.sh index e438b3c..105f1c9 100644 --- a/test/cli.sh +++ b/test/cli.sh @@ -970,6 +970,25 @@ check "install: the PATH symlink rides the chain" 0 "$H1/current/bin/rig" readli check "install: rig --version answers through the whole chain" 0 "rig $VER" irig "$B1/rig" --version check "install: INSTALLED_FROM records the local source" 0 "local:" cat "$H1/versions/$VER/INSTALLED_FROM" +# --- rig#39: no $HOME in the environment (cloud-init's runcmd) --------------- +# The box#88 seed runs install.sh from runcmd, which carries NO $HOME; under +# set -u the first $HOME expansion was a death instead of an install. The +# installer now derives a home from getent — driven here with a shim getent +# so the derived home is a throwaway root, and proven fatal-BY-NAME when +# getent has no answer either (never a bare unbound-variable stack). +GESHIM="$WORK/geshim"; GEHOME="$WORK/gehome"; mkdir -p "$GESHIM" "$GEHOME" +printf '#!/bin/sh\necho "u:x:0:0::%s:/bin/sh"\n' "$GEHOME" > "$GESHIM/getent" +chmod +x "$GESHIM/getent" +check "install: no \$HOME derives one from getent (rig#39)" 0 "done" \ + env -u HOME PATH="$GESHIM:$PATH" RIG_ROLE_MARKER="$WORK/no-marker" \ + RIG_INSTALL_SOURCE="$ROOT" bash "$ROOT/install.sh" +check "install: ...and the tree landed under the derived home" 0 "" \ + test -x "$GEHOME/.local/share/rig/versions/$VER/bin/rig" +printf '#!/bin/sh\nexit 2\n' > "$GESHIM/getent" +check "install: no \$HOME and no getent answer refuses by name" 1 "set HOME and re-run" \ + env -u HOME PATH="$GESHIM:$PATH" RIG_ROLE_MARKER="$WORK/no-marker" \ + RIG_INSTALL_SOURCE="$ROOT" bash "$ROOT/install.sh" + # --- converge, don't clobber ------------------------------------------------ touch "$H1/versions/$VER/CANARY" check "install: a same-version re-run is a no-op that says so" 0 "already installed" inst "$H1" "$B1" -- 2.45.2