Net-new review findings from grok and kimi on !110. Their items 1-3 were codex's, already fixed in 1933b07; these are the ones only they raised. grok #4 — the two downloaders would drift. docs/templates/ci-box/install.sh and the download block in forgejo-runner-install.sh were near-copies, and grok named the exact consequence with the exact evidence: fail-open survived in BOTH while a grep for "checksum mismatch" passed against both, because the string it looked for sat right beside the branch it could not see. The whole policy — fetch, unreadable, mismatch — is now fetch_and_verify_sha256, byte-identical in both files and diffed by test/cli.sh. They cannot share a lib: the command sources commands/lib/, and the template is a registry definition that runs standalone inside a mint with rig's tree nowhere in reach, which is the same situation valid_version faces between bin/rig and install.sh. Mutation-checked by drifting one copy's message and confirming the diff goes red. kimi #2 — the labeler could not see this family. scope:runner matched commands/runner-*.sh only, so forgejo-runner-*.sh and the staged ci-box definition scored no scope at all. Globs extended and the label's description now says either forge rather than GitHub. kimi #4 — remove stranded a unit whose user was gone. The missing-user check exited 0 before the unit was ever looked at, so a deleted account with a leftover forgejo-runner.service reported "nothing to remove" while the absence-assert that never ran implied the opposite. The unit is now checked independently. Auditing that fix surfaced a hazard kimi did not mention: with the user gone RUNNER_DIR is "", and the later unguarded "$RUNNER_DIR/.rig-labels" would have expanded to "/.rig-labels" — an rm at the filesystem root, as root. Every RUNNER_DIR path is now gated, and a test pins that none is unguarded. kimi #1 — the README handed out a config that breaks rig's own gates. DEFAULT_ACTIONS_URL is a single fallback and rig's workflows need two origins; measured: code.forgejo.org serves actions/checkout (200) but not heavy-duty/ceremony (404), which lives on the Forgejo instance. With the value the README recommended, all eight ceremony references fail to resolve. The section now states the conflict with the counts, says which references would break, and explicitly does NOT pick a side — that is an infra decision, and rig's CI running on Forgejo is not something rig forgejo-runner depends on. Asked the maintainer for direction. 746/31/43 pass, shellcheck clean, labeler.yml parses. forgejo#109 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
139 lines
6 KiB
Bash
Executable file
139 lines
6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# rig forgejo-runner remove — take the service down and wipe this box's
|
|
# registration. Convergent: a box with nothing installed exits 0.
|
|
#
|
|
# There is no --local flag here, and its absence is the design. The GitHub
|
|
# sibling offers --local as an ESCAPE HATCH from a real deregistration
|
|
# handshake (config.sh remove --token, against an endpoint that mints removal
|
|
# tokens). Forgejo has no such handshake and no such endpoint: local is the
|
|
# only thing removal can ever be. Shipping the flag would advertise a
|
|
# server-side alternative that does not exist, and an operator would spend the
|
|
# afternoon hunting for the token that turns it off.
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
|
|
# shellcheck source=SCRIPTDIR/lib/forgejo-runner-config.sh
|
|
. "$HERE/lib/forgejo-runner-config.sh"
|
|
|
|
log() { printf 'rig-forgejo-runner: %s\n' "$*"; }
|
|
warn() { printf 'rig-forgejo-runner: WARNING: %s\n' "$*" >&2; }
|
|
die() { printf 'rig-forgejo-runner: ERROR: %s\n' "$1" >&2; exit "${2:-1}"; }
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
usage: rig forgejo-runner remove [--user <name>]
|
|
|
|
--user <name> unprivileged service user (default: the tenant user `ci`
|
|
when it exists, else forgejo-runner)
|
|
|
|
Stops and disables the systemd service, then wipes this box's registration.
|
|
The binary and the user stay put, so a later `rig forgejo-runner install`
|
|
re-registers without downloading anything.
|
|
|
|
Forgejo has no runner deregistration endpoint, so this is always local-only:
|
|
the box is cleaned, and the runner stays listed as offline in the instance
|
|
until you delete it under Actions > Runners. No token is needed or asked for.
|
|
|
|
Convergent: safe to re-run; a box with no runner installed exits 0.
|
|
EOF
|
|
}
|
|
|
|
# --- args (validated before the root check, so errors are testable) ---------
|
|
RUNNER_USER=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--user)
|
|
[ $# -ge 2 ] || die "--user needs a value" 2
|
|
RUNNER_USER="$2"; shift 2 ;;
|
|
--local)
|
|
# Named rather than "unknown flag": it is the GitHub sibling's spelling,
|
|
# and the answer is that removal here is ALWAYS what --local means.
|
|
die "--local is not a flag here: Forgejo has no deregistration endpoint, so 'rig forgejo-runner remove' is always local-only. Re-run it without the flag, then delete the offline runner in the instance's Actions > Runners." 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "unknown flag: $1" 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$RUNNER_USER" ]; then
|
|
if id -u ci >/dev/null 2>&1; then RUNNER_USER="ci"; else RUNNER_USER="forgejo-runner"; fi
|
|
fi
|
|
|
|
# --- validation ------------------------------------------------------------
|
|
[ "$RUNNER_USER" != "root" ] || die "runner user must not be root" 2
|
|
|
|
# --- guards ----------------------------------------------------------------
|
|
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
|
|
|
UNIT=/etc/systemd/system/forgejo-runner.service
|
|
|
|
# --- nothing to remove? -----------------------------------------------------
|
|
# The unit is checked INDEPENDENTLY of the user, and that ordering is the whole
|
|
# point. A missing user used to exit 0 here before the unit was ever looked at,
|
|
# so a deleted account with a leftover forgejo-runner.service reported "nothing
|
|
# to remove" and left the unit behind — while the absence-assert at the end,
|
|
# which never ran, implied removal had been complete. `bootstrap --undo`'s own
|
|
# unit check would still have caught it, but a verb that claims to have removed
|
|
# everything must not be the thing that lies about it.
|
|
RUNNER_DIR=""
|
|
if id -u "$RUNNER_USER" >/dev/null 2>&1; then
|
|
USER_HOME="$(getent passwd "$RUNNER_USER" | cut -d: -f6)"
|
|
RUNNER_DIR="$USER_HOME/forgejo-runner"
|
|
else
|
|
log "no ${RUNNER_USER} user on this box"
|
|
fi
|
|
if [ -z "$RUNNER_DIR" ] && [ ! -e "$UNIT" ]; then
|
|
log "no runner user and no unit on this box; nothing to remove"
|
|
exit 0
|
|
fi
|
|
if [ -n "$RUNNER_DIR" ] && [ ! -e "$RUNNER_DIR/.runner" ] && [ ! -e "$UNIT" ]; then
|
|
log "no runner registered in ${RUNNER_DIR}; nothing to remove"
|
|
exit 0
|
|
fi
|
|
[ -n "$RUNNER_DIR" ] || warn "the ${RUNNER_USER} user is gone but ${UNIT} is still here — removing the orphaned unit"
|
|
|
|
INSTANCE=""
|
|
RUNNER_NAME=""
|
|
if [ -n "$RUNNER_DIR" ]; then
|
|
INSTANCE="$(forgejo_runner_instance "$RUNNER_DIR")"
|
|
RUNNER_NAME="$(forgejo_runner_name "$RUNNER_DIR")"
|
|
fi
|
|
|
|
# --- service ---------------------------------------------------------------
|
|
# First, in both paths: stopping after the registration is wiped would strand a
|
|
# running daemon polling with credentials that no longer exist on disk.
|
|
if [ -e "$UNIT" ]; then
|
|
log "stopping and disabling forgejo-runner.service"
|
|
systemctl stop forgejo-runner >/dev/null 2>&1 || true
|
|
systemctl disable forgejo-runner >/dev/null 2>&1 || true
|
|
rm -f "$UNIT"
|
|
systemctl daemon-reload
|
|
else
|
|
log "no service installed; skipping"
|
|
fi
|
|
|
|
# --- registration -----------------------------------------------------------
|
|
# Every path below is gated on RUNNER_DIR being non-empty. With the user gone
|
|
# it is "", and an unguarded "$RUNNER_DIR/.rig-labels" would expand to
|
|
# "/.rig-labels" — an rm at the filesystem root, as root. The repo already
|
|
# treats this class of expansion as a hazard worth spelling out (`rm -rf
|
|
# "${ir:?}/versions/$ver"` in bin/rig); same discipline here.
|
|
if [ -n "$RUNNER_DIR" ]; then
|
|
if [ -e "$RUNNER_DIR/.runner" ]; then
|
|
rm -f "$RUNNER_DIR/.runner"
|
|
log "wiped the local registration"
|
|
fi
|
|
rm -f "$RUNNER_DIR/.rig-labels"
|
|
fi
|
|
|
|
# END WITH THE ABSENCE ASSERT: "removed" is a claim, and claims get verified
|
|
# (the `rig uninstall` precedent).
|
|
leftover=""
|
|
[ -n "$RUNNER_DIR" ] && [ -e "$RUNNER_DIR/.runner" ] && leftover="$leftover $RUNNER_DIR/.runner"
|
|
[ -e "$UNIT" ] && leftover="$leftover $UNIT"
|
|
if [ -n "$leftover" ]; then
|
|
printf 'rig-forgejo-runner: remove INCOMPLETE — still present:%s\n' "$leftover" >&2
|
|
exit 1
|
|
fi
|
|
|
|
log "runner removed; the binary stays for a future rig forgejo-runner install"
|
|
warn "the runner${RUNNER_NAME:+ ${RUNNER_NAME}} is still listed as offline in ${INSTANCE:-the instance} — delete it under Actions > Runners. Forgejo has no deregistration endpoint, so rig cannot do this for you."
|