forked from heavy-duty/rig
107 lines
5 KiB
Bash
107 lines
5 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# Shared reader for the Forgejo runner's own on-disk config ($RUNNER_DIR/.runner).
|
||
|
|
# Sourced by the forgejo-runner-* commands; never executed on its own.
|
||
|
|
#
|
||
|
|
# WHY A SECOND LIB, not an arm inside lib/runner-config.sh: the two files are
|
||
|
|
# different documents making different claims, and the sibling's helpers answer
|
||
|
|
# questions this one cannot ask. GitHub's .runner names a REPOSITORY
|
||
|
|
# (gitHubUrl), so `runner install` converges toward --repo. Forgejo's names an
|
||
|
|
# INSTANCE (address) and nothing else about scope — whether a registration is
|
||
|
|
# instance-wide, org, or single-repo is a property of the TOKEN, decided in
|
||
|
|
# Forgejo's UI before rig ever sees it. There is no repo here to converge
|
||
|
|
# toward, and no way to read one back. Sharing a reader would mean a
|
||
|
|
# gitHubUrl accessor that returns empty forever on one of the two forges.
|
||
|
|
#
|
||
|
|
# json_field is deliberately re-used FROM the sibling rather than copied: a
|
||
|
|
# rig-bootstrapped box has no jq, both files are flat JSON, and one grep/sed
|
||
|
|
# reader for both is the same trade lib/runner-config.sh already argued.
|
||
|
|
|
||
|
|
HERE_FJ="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
# shellcheck source=SCRIPTDIR/runner-config.sh
|
||
|
|
. "$HERE_FJ/runner-config.sh" # json_field
|
||
|
|
|
||
|
|
# THE CREDENTIAL FACT that shapes this whole family: Forgejo's .runner holds
|
||
|
|
# the runner's own long-lived token — the secret it authenticates every poll
|
||
|
|
# with — alongside address/name/labels. GitHub's holds no such thing.
|
||
|
|
#
|
||
|
|
# So the mode is part of the contract, not hygiene: a registration secret
|
||
|
|
# readable by every account on the box is a quiet, permanent credential leak,
|
||
|
|
# and it leaks silently — nothing fails, the runner keeps working. Converge is
|
||
|
|
# the only moment rig can notice a mode that drifted (an operator's editor, a
|
||
|
|
# restore from a tarball that lost modes, a hand-edit to add a label).
|
||
|
|
FORGEJO_RUNNER_FILE_MODE=600
|
||
|
|
|
||
|
|
# forgejo_runner_instance <runner_dir> — the Forgejo instance this box's runner
|
||
|
|
# is registered to, empty when nothing is registered there.
|
||
|
|
forgejo_runner_instance() {
|
||
|
|
[ -e "$1/.runner" ] || return 0
|
||
|
|
json_field "$1/.runner" address
|
||
|
|
}
|
||
|
|
|
||
|
|
# forgejo_runner_name <runner_dir> — the runner's name, empty when unregistered.
|
||
|
|
forgejo_runner_name() {
|
||
|
|
[ -e "$1/.runner" ] || return 0
|
||
|
|
json_field "$1/.runner" name
|
||
|
|
}
|
||
|
|
|
||
|
|
# forgejo_runner_secure <runner_dir> <user> <group> — converge .runner to 0600
|
||
|
|
# owned by the runner user. Called on every install, not only at registration.
|
||
|
|
# Silent on success: this is a mode that should always already be right, and a
|
||
|
|
# line saying so on every converge would train the reader to skip it.
|
||
|
|
forgejo_runner_secure() {
|
||
|
|
local dir="$1" user="$2" group="$3"
|
||
|
|
[ -e "$dir/.runner" ] || return 0
|
||
|
|
chmod "$FORGEJO_RUNNER_FILE_MODE" "$dir/.runner"
|
||
|
|
chown "$user:$group" "$dir/.runner"
|
||
|
|
}
|
||
|
|
|
||
|
|
# assert_runner_instance <runner_dir> <instance-url>
|
||
|
|
#
|
||
|
|
# Returns 0 when the box has no runner, or has one already registered to
|
||
|
|
# <instance-url>: re-running `install` against the instance the box is already
|
||
|
|
# on is real convergence — it re-uses the binary, skips registration, exits 0.
|
||
|
|
#
|
||
|
|
# Returns 1, explaining itself on stderr, when the runner is registered to a
|
||
|
|
# DIFFERENT instance. Skipping *that* is not convergence, it is ignoring the
|
||
|
|
# argument: `install` would skip its registration step, restart the service
|
||
|
|
# against the OLD instance, and report success — leaving the instance you asked
|
||
|
|
# for with no runner and its jobs queued against one that will never come.
|
||
|
|
#
|
||
|
|
# This is assert_runner_repo's reasoning, asked about the axis Forgejo actually
|
||
|
|
# has. There is deliberately no `repoint` sibling: Forgejo has no
|
||
|
|
# deregistration handshake to perform against the old instance, so moving a
|
||
|
|
# runner is `remove` then `install` — two acts that are already honest about
|
||
|
|
# leaving a stale entry behind, rather than one verb pretending to be atomic.
|
||
|
|
assert_runner_instance() {
|
||
|
|
local dir="$1" wanted="$2" current
|
||
|
|
[ -e "$dir/.runner" ] || return 0
|
||
|
|
|
||
|
|
current="$(forgejo_runner_instance "$dir")"
|
||
|
|
|
||
|
|
if [ -z "$current" ]; then
|
||
|
|
printf 'rig-forgejo-runner: ERROR: %s\n' \
|
||
|
|
"${dir}/.runner exists but names no instance — this box's registration cannot
|
||
|
|
be read, so rig cannot tell whether it is already on ${wanted}.
|
||
|
|
Wipe the local registration and install again:
|
||
|
|
rig forgejo-runner remove" >&2
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Trailing slashes are a spelling difference, not a different instance:
|
||
|
|
# forgejo-runner records the URL as given, so `--instance https://f.example/`
|
||
|
|
# and `--instance https://f.example` would otherwise read as a move.
|
||
|
|
if [ "${current%/}" = "${wanted%/}" ]; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf 'rig-forgejo-runner: ERROR: %s\n' \
|
||
|
|
"this box's runner is already registered to ${current}, not ${wanted}.
|
||
|
|
install will not move a runner between instances: it would leave the service
|
||
|
|
running against the OLD instance and report success. To move it, take it off
|
||
|
|
the old instance first:
|
||
|
|
rig forgejo-runner remove
|
||
|
|
then install against the new one. Forgejo has no deregistration handshake, so
|
||
|
|
the old entry stays listed until you delete it in that instance's admin UI." >&2
|
||
|
|
return 1
|
||
|
|
}
|