status reports `systemctl is-active` as the service line, and that is the strongest health signal it has — the command reads only on-disk config, by contract. But a poller can go quiet while the process stays up: measured 2026-07-30 while probing for #129, a daemon logged "[poller] launched" and never fetched a job dispatched four minutes later, while a daemon started fresh claimed that same queued task in one second. Both times it read as a label-mapping bug on the forge, which is the wrong place to look. Says so where an operator already looks when nothing is obviously wrong, and names the remedy. log, not warn: an idle-but-healthy runner is silent in exactly the same way a stalled one is, so there is no signal separating them and a warning on every run would be crying wolf. No network call and no token read — the header contract, and test/cli.sh's existing guard, both stand. Refs #133 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
108 lines
4.6 KiB
Bash
Executable file
108 lines
4.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# rig forgejo-runner status — what is this box's Forgejo runner registered to?
|
|
# Read-only: reports what is already on the box. No credential, no network call.
|
|
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 status [--user <name>]
|
|
|
|
--user <name> unprivileged service user (default: the tenant user `ci`
|
|
when it exists, else forgejo-runner)
|
|
|
|
Prints the Forgejo instance this box's runner is registered to, its runner
|
|
name, the labels rig recorded when it registered, the install directory, and
|
|
the systemd unit and its state.
|
|
|
|
Reads only the runner's own on-disk config — no token, no network call. The
|
|
registration secret that config holds is never printed. Exits 1 when no runner
|
|
is installed.
|
|
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 ;;
|
|
-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"
|
|
|
|
id -u "$RUNNER_USER" >/dev/null 2>&1 \
|
|
|| die "no runner installed (no ${RUNNER_USER} user on this box)"
|
|
USER_HOME="$(getent passwd "$RUNNER_USER" | cut -d: -f6)"
|
|
RUNNER_DIR="$USER_HOME/forgejo-runner"
|
|
[ -e "$RUNNER_DIR/.runner" ] \
|
|
|| die "no runner registered in ${RUNNER_DIR}"
|
|
|
|
# --- read the runner's own config -------------------------------------------
|
|
INSTANCE="$(forgejo_runner_instance "$RUNNER_DIR")"
|
|
RUNNER_NAME="$(forgejo_runner_name "$RUNNER_DIR")"
|
|
|
|
if [ -r "$RUNNER_DIR/.rig-labels" ]; then
|
|
LABELS="$(cat "$RUNNER_DIR/.rig-labels")"
|
|
else
|
|
LABELS="(not recorded on this box — see the instance's Actions > Runners)"
|
|
fi
|
|
|
|
UNIT=/etc/systemd/system/forgejo-runner.service
|
|
if [ -e "$UNIT" ]; then
|
|
STATE="$(systemctl is-active forgejo-runner 2>/dev/null || true)"
|
|
SERVICE="forgejo-runner.service (${STATE:-unknown})"
|
|
else
|
|
SERVICE="(not installed as a service)"
|
|
fi
|
|
|
|
log "instance: ${INSTANCE:-unknown}"
|
|
log "name: ${RUNNER_NAME:-unknown}"
|
|
log "labels: ${LABELS}"
|
|
log "dir: ${RUNNER_DIR}"
|
|
log "service: ${SERVICE}"
|
|
# `active` is the strongest health signal this command has, and it proves only
|
|
# that a process exists — not that the runner is still asking Forgejo for work.
|
|
# A poller can go quiet while the daemon stays up: measured 2026-07-30 (#129,
|
|
# #133), a daemon logged "[poller] launched" and never fetched a job dispatched
|
|
# four minutes later, while a daemon started fresh claimed that same queued
|
|
# task in one second. Both times it read as a label-mapping bug on the forge.
|
|
#
|
|
# log, not warn: nothing has been DETECTED here. An idle runner with no queued
|
|
# jobs is silent in exactly the same way a stalled one is, so there is no
|
|
# signal separating them — a warning on every status run would be crying wolf,
|
|
# and warn in this file means a drift actually measured (the .runner mode
|
|
# below). Saying what the signal does not cover is the honest middle.
|
|
if [ "${STATE:-}" = active ]; then
|
|
log " note: 'active' is not proof the runner is fetching jobs — only that the process is up."
|
|
log " If a job stays queued and its run page says it never started, run"
|
|
log " 'systemctl restart forgejo-runner' and re-read before suspecting the labels."
|
|
fi
|
|
|
|
# status is the only command an operator runs when nothing is obviously wrong,
|
|
# which makes it the right place to notice a mode that drifted. It reports and
|
|
# does not fix: converging state is `install`'s job, and a read-only verb that
|
|
# quietly writes is a worse surprise than a loud warning.
|
|
MODE="$(stat -c '%a' "$RUNNER_DIR/.runner" 2>/dev/null || true)"
|
|
if [ -n "$MODE" ] && [ "$MODE" != "$FORGEJO_RUNNER_FILE_MODE" ]; then
|
|
warn ".runner is mode ${MODE}, not ${FORGEJO_RUNNER_FILE_MODE} — it holds this runner's registration secret, and every account on this box can read it. Re-run 'rig forgejo-runner install --instance ${INSTANCE:-<url>}' to converge the mode."
|
|
fi
|