rig's CI story was GitHub-shaped end to end. This makes it work against a self-hosted Forgejo, in three pieces. The registry fetch becomes forge-aware. templates_resolve hardcoded three github.com archive URLs; RIG_TEMPLATES_HOST now selects the grammar, because the forges genuinely differ — GitHub serves refs/tags, refs/heads and bare paths, Forgejo serves exactly one, and emitting the other two there would mean two guaranteed 404s per fetch and a failure message listing URLs that never could have worked. Measured against forgejo.heavyduty.builders, not inferred. The default stays GitHub, so every existing caller is unchanged. install.sh's snapshot reads the same variable through a byte-identical copy of the builder, diffed by the tests: a snapshot cached from a forge converge would never fetch from is worse than no snapshot, and the pin-in-the-name staleness guard cannot catch a wrong-ORIGIN snapshot, only an old one. ci-box is a tenant, not a machine role. The topology is a fleet machine hosting boxes, one of which runs CI — a '-box' guest by rig's own family rule. That also deletes the docker-in-docker layer the usual setup needs: bootstrap-tenant.sh already installs Docker and adds the tenant user to the group, and the isolation a privileged dind sidecar buys is already paid for by a box that is network-isolated, inbound-less and disposable. rig runner install refuses Docker for good reason — it converges a MACHINE, where the blast radius is the machine. Here it is a guest that gets thrown away. rig forgejo-runner is a new family beside rig runner, which is untouched. Forgejo registers against an INSTANCE and the token carries the scope, so there is no --repo to converge toward and nothing to compare; folding that into one command would make every guard bimodal to share a flag name while the contract underneath differs. assert_runner_instance asks the same trust-boundary question about the axis Forgejo actually has. There is no repoint and no --local, and both absences are explained where an operator arriving from the GitHub sibling will hit them. Forgejo's .runner holds the runner's own long-lived token, unlike GitHub's, so it is installed 0600 and the mode is re-asserted on every converge — a mode that drifted leaks the secret silently, since nothing fails and the runner keeps working. status reports it and never prints the token. Both downloads verify the published .sha256 before installing: this binary lands as root and is executed by a systemd unit. bootstrap --undo learns the guard for the same hazard on the other forge, and it matters more here — Forgejo has no deregistration endpoint, so the ghost it would strand has to be deleted by hand. Known prerequisite, documented rather than assumed: the fetch is unauthenticated by contract, and a Forgejo with REQUIRE_SIGNIN_VIEW=true answers 404 for repos it reports as public. Hosting a registry there needs FORGEJO__service__REQUIRE_SIGNIN_VIEW=false. The refusal names that case, because it is indistinguishable from a wrong ref. forgejo#109 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
74 lines
3.2 KiB
Bash
Executable file
74 lines
3.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# rig bootstrap --undo — remove only off-box state rig can prove it created.
|
|
set -euo pipefail
|
|
|
|
log() { printf 'rig-bootstrap: %s\n' "$*"; }
|
|
die() { printf 'rig-bootstrap: ERROR: %s\n' "$*" >&2; exit 1; }
|
|
|
|
MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
|
[ -e "$MARKER" ] || die "no /etc/rig/role marker — refusing to touch the tailnet"
|
|
|
|
runner_installed=0
|
|
if [ -n "${RIG_RUNNER_DIR:-}" ]; then
|
|
[ -e "$RIG_RUNNER_DIR/.runner" ] && runner_installed=1
|
|
else
|
|
for runner_config in /home/*/actions-runner/.runner /root/actions-runner/.runner; do
|
|
[ -e "$runner_config" ] && runner_installed=1
|
|
done
|
|
compgen -G '/etc/systemd/system/actions.runner.*.service' >/dev/null \
|
|
&& runner_installed=1
|
|
fi
|
|
if [ "$runner_installed" -eq 1 ]; then
|
|
die "a GitHub runner is installed — run 'rig runner remove' first so undo does not leave a ghost runner in the repository"
|
|
fi
|
|
|
|
# The same hazard, the other forge (#109): leaving the tailnet under a live
|
|
# Forgejo runner strands a registration this box can no longer serve, and
|
|
# Forgejo has no deregistration endpoint — so the ghost it leaves is one
|
|
# somebody has to delete BY HAND in the instance's admin UI. That makes the
|
|
# refusal more load-bearing here than for GitHub, not less.
|
|
#
|
|
# RIG_FORGEJO_RUNNER_DIR mirrors RIG_RUNNER_DIR above so tests can point this
|
|
# at a fixture. The glob covers the tenant default (`ci`) and the dedicated
|
|
# account alike, because both are reachable defaults of `install --user`.
|
|
forgejo_runner_installed=0
|
|
if [ -n "${RIG_FORGEJO_RUNNER_DIR:-}" ]; then
|
|
[ -e "$RIG_FORGEJO_RUNNER_DIR/.runner" ] && forgejo_runner_installed=1
|
|
else
|
|
for runner_config in /home/*/forgejo-runner/.runner /root/forgejo-runner/.runner; do
|
|
[ -e "$runner_config" ] && forgejo_runner_installed=1
|
|
done
|
|
[ -e /etc/systemd/system/forgejo-runner.service ] && forgejo_runner_installed=1
|
|
fi
|
|
if [ "$forgejo_runner_installed" -eq 1 ]; then
|
|
die "a Forgejo runner is installed — run 'rig forgejo-runner remove' first so undo does not leave a ghost runner in the instance"
|
|
fi
|
|
|
|
join_by=""
|
|
while IFS= read -r field; do
|
|
case "$field" in
|
|
join-by=*) join_by="${field#join-by=}" ;;
|
|
esac
|
|
done < <(tr '[:space:]' '\n' < "$MARKER")
|
|
|
|
case "$join_by" in
|
|
rig) ;;
|
|
preexisting)
|
|
die "the tailnet join predates this bootstrap run (join-by=preexisting), so rig will not remove state it did not create; run 'tailscale logout' by hand if that is intended" ;;
|
|
"")
|
|
die "the role marker predates join-by provenance, so rig cannot prove it made this tailnet join and will not remove it; re-run bootstrap to write a current marker, or run 'tailscale logout' by hand" ;;
|
|
*)
|
|
die "the role marker has unknown join-by=$join_by, so rig cannot prove it made this tailnet join and will not remove it; run 'tailscale logout' by hand if that is intended" ;;
|
|
esac
|
|
|
|
# The same back-out/keep law as first-join verification: logout is earned only
|
|
# when the marker proves rig performed the join. Preserve the marker on failure
|
|
# so the operation remains retryable and never reports a half-undone machine.
|
|
if ! tailscale logout; then
|
|
die "tailscale logout failed; role marker kept so 'rig bootstrap --undo' can be retried"
|
|
fi
|
|
|
|
rm -f -- "$MARKER"
|
|
log "tailnet join removed; role marker removed"
|