forked from heavy-duty/rig
Merge pull request #43 from dan-claude-bot/fix/silent-prompts
fix: headless credential prompts refuse loudly, naming their variable
This commit is contained in:
commit
525e24acd6
6 changed files with 60 additions and 6 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -6,6 +6,22 @@ on the way to cutting its first release, and this file starts there.
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Headless credential prompts refuse loudly instead of dying silently**
|
||||
(#42) — the interactive credential prompts (`TS_AUTHKEY` in `bootstrap`,
|
||||
`RUNNER_TOKEN` in `runner install`, `RUNNER_REMOVE_TOKEN` in
|
||||
`runner remove`, and both tokens in `runner repoint` — a site the new
|
||||
no-bare-read test caught after the issue counted three) were bare
|
||||
`read -rsp`: with stdin not a tty (CI,
|
||||
`box exec`, any script), `read` fails, `set -e` ends the run, and the
|
||||
log just *stops* — exit 1, no last word, measured live in the
|
||||
2026-07-19 release drill. Each prompt now checks for a tty first and
|
||||
dies naming the variable that unblocks an unattended run (`runner
|
||||
remove` also names `--local`), and every `read` is `|| die`-guarded so
|
||||
EOF at a real prompt gets the same courtesy. `db.sh` already held the
|
||||
line here; now all of rig does.
|
||||
|
||||
### Added
|
||||
|
||||
- **Tagged releases, and an installer that installs them** (#32) — the rig
|
||||
|
|
|
|||
|
|
@ -396,9 +396,15 @@ elif [ "$JOIN" = "login" ]; then
|
|||
tailscale up --hostname="$TS_HOSTNAME"
|
||||
verify_user_owned back-out
|
||||
else
|
||||
# env override, else prompt; never touches disk
|
||||
# env override, else prompt; never touches disk. The prompt only fires on a
|
||||
# tty: with no terminal, a bare `read` exits non-zero and `set -e` would end
|
||||
# the whole bootstrap with NO last word — the 2026-07-19 drill met exactly
|
||||
# that, a log that stops mid-converge with exit 1 and nothing to grep. An
|
||||
# unattended run gets the same refusal every other guard gives: loud, and
|
||||
# naming the variable that unblocks it.
|
||||
if [ -z "${TS_AUTHKEY:-}" ]; then
|
||||
read -rsp "tailscale pre-auth key (single-use, tagged, <=1h expiry): " TS_AUTHKEY
|
||||
[ -t 0 ] || die "TS_AUTHKEY is unset and stdin is not a tty — set TS_AUTHKEY to run unattended"
|
||||
read -rsp "tailscale pre-auth key (single-use, tagged, <=1h expiry): " TS_AUTHKEY || { echo; die "no pre-auth key read (EOF) — set TS_AUTHKEY to run unattended"; }
|
||||
echo
|
||||
fi
|
||||
[ -n "${TS_AUTHKEY:-}" ] || die "empty pre-auth key"
|
||||
|
|
|
|||
|
|
@ -120,8 +120,11 @@ fi
|
|||
# --- registration token — only when registration is actually pending -------
|
||||
if [ "$REG_PENDING" -eq 1 ]; then
|
||||
RUNNER_TOKEN="${RUNNER_TOKEN:-}"
|
||||
# Prompt only on a tty: headless, a bare `read` dies under set -e with no
|
||||
# message at all (drill-measured). Refuse loudly, naming the variable.
|
||||
if [ -z "$RUNNER_TOKEN" ]; then
|
||||
read -rsp "runner registration token (short-lived): " RUNNER_TOKEN
|
||||
[ -t 0 ] || die "RUNNER_TOKEN is unset and stdin is not a tty — set RUNNER_TOKEN to run unattended"
|
||||
read -rsp "runner registration token (short-lived): " RUNNER_TOKEN || { echo; die "no registration token read (EOF) — set RUNNER_TOKEN to run unattended"; }
|
||||
echo
|
||||
fi
|
||||
[ -n "$RUNNER_TOKEN" ] || die "empty registration token"
|
||||
|
|
|
|||
|
|
@ -70,8 +70,12 @@ fi
|
|||
REMOVE_TOKEN=""
|
||||
if [ -e "$RUNNER_DIR/.runner" ] && [ "$LOCAL" -eq 0 ]; then
|
||||
REMOVE_TOKEN="${RUNNER_REMOVE_TOKEN:-}"
|
||||
# Prompt only on a tty: headless, a bare `read` dies under set -e with no
|
||||
# message at all — the drill hit exactly this (wrong env var, exit 1, zero
|
||||
# output). Refuse loudly, naming both the variable and the tokenless out.
|
||||
if [ -z "$REMOVE_TOKEN" ]; then
|
||||
read -rsp "runner removal token (short-lived): " REMOVE_TOKEN
|
||||
[ -t 0 ] || die "RUNNER_REMOVE_TOKEN is unset and stdin is not a tty — set RUNNER_REMOVE_TOKEN to run unattended, or use --local"
|
||||
read -rsp "runner removal token (short-lived): " REMOVE_TOKEN || { echo; die "no removal token read (EOF) — set RUNNER_REMOVE_TOKEN to run unattended, or use --local"; }
|
||||
echo
|
||||
fi
|
||||
[ -n "$REMOVE_TOKEN" ] || die "empty removal token"
|
||||
|
|
|
|||
|
|
@ -128,8 +128,11 @@ log "labels: ${LABELS}"
|
|||
# while the runner is still registered and working.
|
||||
if [ "$LOCAL" -eq 0 ]; then
|
||||
RUNNER_REMOVE_TOKEN="${RUNNER_REMOVE_TOKEN:-}"
|
||||
# Prompt only on a tty — headless, a bare `read` dies under set -e with no
|
||||
# message (issue #42; same cure as runner-install/remove).
|
||||
if [ -z "$RUNNER_REMOVE_TOKEN" ]; then
|
||||
read -rsp "removal token for ${CURRENT_URL} (short-lived): " RUNNER_REMOVE_TOKEN
|
||||
[ -t 0 ] || die "RUNNER_REMOVE_TOKEN is unset and stdin is not a tty — set RUNNER_REMOVE_TOKEN to run unattended, or use --local"
|
||||
read -rsp "removal token for ${CURRENT_URL} (short-lived): " RUNNER_REMOVE_TOKEN || { echo; die "no removal token read (EOF) — set RUNNER_REMOVE_TOKEN to run unattended, or use --local"; }
|
||||
echo
|
||||
fi
|
||||
[ -n "$RUNNER_REMOVE_TOKEN" ] || die "empty removal token"
|
||||
|
|
@ -138,7 +141,8 @@ fi
|
|||
|
||||
RUNNER_TOKEN="${RUNNER_TOKEN:-}"
|
||||
if [ -z "$RUNNER_TOKEN" ]; then
|
||||
read -rsp "registration token for ${TARGET_URL} (short-lived): " RUNNER_TOKEN
|
||||
[ -t 0 ] || die "RUNNER_TOKEN is unset and stdin is not a tty — set RUNNER_TOKEN to run unattended"
|
||||
read -rsp "registration token for ${TARGET_URL} (short-lived): " RUNNER_TOKEN || { echo; die "no registration token read (EOF) — set RUNNER_TOKEN to run unattended"; }
|
||||
echo
|
||||
fi
|
||||
[ -n "$RUNNER_TOKEN" ] || die "empty registration token"
|
||||
|
|
|
|||
21
test/cli.sh
21
test/cli.sh
|
|
@ -461,6 +461,27 @@ fi
|
|||
|
||||
check "runner: bad subcommand exits 2" 2 "usage:" "$ROOT/bin/rig" runner frobnicate
|
||||
|
||||
# --- headless prompts refuse loudly (issue #42) ------------------------------
|
||||
# The three credential prompts (TS_AUTHKEY, RUNNER_TOKEN, RUNNER_REMOVE_TOKEN)
|
||||
# used to be bare `read -rsp`: with no tty, read exits non-zero and `set -e`
|
||||
# ends the script with NO output at all — the drill watched a bootstrap die
|
||||
# mid-converge with exit 1 and nothing to grep. Each prompt now refuses first,
|
||||
# naming its variable. The prompts live behind the root check (and, for the
|
||||
# runner pair, behind a real registration), so the harness cannot reach them
|
||||
# non-root; grep the guards so a deleted one cannot ship green (repo
|
||||
# precedent: the login-path tag refusals above).
|
||||
check "bootstrap: headless TS_AUTHKEY prompt refuses loudly" 0 "" \
|
||||
grep -q 'TS_AUTHKEY is unset and stdin is not a tty' "$ROOT/commands/bootstrap.sh"
|
||||
check "runner install: headless token prompt refuses loudly" 0 "" \
|
||||
grep -q 'RUNNER_TOKEN is unset and stdin is not a tty' "$ROOT/commands/runner-install.sh"
|
||||
check "runner remove: headless token prompt refuses loudly" 0 "" \
|
||||
grep -q 'RUNNER_REMOVE_TOKEN is unset and stdin is not a tty' "$ROOT/commands/runner-remove.sh"
|
||||
# The EOF-at-the-prompt path (Ctrl-D on a real tty) must also die with a last
|
||||
# word rather than ride set -e into silence: every read is `|| die`-guarded,
|
||||
# so a bare `read -rsp` (no `||` on its line) must not exist anywhere.
|
||||
check "prompts: no bare read -rsp remains" 1 "" \
|
||||
grep -RE 'read -rsp[^|]*$' "$ROOT/commands/"
|
||||
|
||||
# --- runner install: --repo must agree with what the box is already on -------
|
||||
# The bug: `install --repo B` on a box registered to repo A skipped configure,
|
||||
# restarted the service on A, and reported success — --repo accepted, validated,
|
||||
|
|
|
|||
Loading…
Reference in a new issue