install.sh resolves releases and archive URLs through RIG_HOST (default GitHub), using Forgejo's single /archive/<ref>.tar.gz form and the same /releases/latest redirect grammar. Bootstrap's box fetch gets BOX_HOST with forge-aware raw-file URLs. Refusal hints and coolify Documentation= stop pointing only at GitHub. Closes #111
555 lines
23 KiB
Bash
Executable file
555 lines
23 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)"
|
|
|
|
log() { printf 'rig: %s\n' "$*"; }
|
|
warn() { printf 'rig: WARNING: %s\n' "$*" >&2; }
|
|
die() { printf 'rig: ERROR: %s\n' "$*" >&2; exit 1; }
|
|
version() { echo "rig $(cat "$ROOT/VERSION" 2>/dev/null || echo unknown) ($ROOT)"; }
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
usage: rig <command> [args]
|
|
|
|
commands:
|
|
bootstrap <control-plane-server|workload-server|runner-server|staging-server|
|
|
dev-server|workstation|custom>
|
|
(--users <path> | --no-users)
|
|
[--hostname <name>] [--class <human|server>] [--host <yes|no>]
|
|
[--join <authkey|login>]
|
|
OS plumbing on a pristine Debian box: hardening, unattended-upgrades,
|
|
tailscale join, then this box's operators. --users is REQUIRED on
|
|
every role — it converges the users file as bootstrap's last phase
|
|
(identical to 'rig users apply --file <path>', marker written first)
|
|
so one command leaves a box with its people on it; --no-users is the
|
|
deliberate opt-out and leaves root as the only door. The tenant roles
|
|
below take neither. Roles are presets over the three traits; any flag
|
|
overrides its trait, and custom states all of them. Prompts for a
|
|
single-use TAGGED tailnet pre-auth key (TS_AUTHKEY env overrides the
|
|
prompt); the key's tags are the tailnet tag, verified after join —
|
|
only control-plane-server and workload-server may carry tag:server.
|
|
The '-server' suffix names the FAMILY (a fleet machine, vs the '-box'
|
|
tenant roles); custom and workstation take none. join=login
|
|
(workstation) needs no key: interactive login, node must come up
|
|
untagged. Run as root.
|
|
bootstrap --undo
|
|
Leave the tailnet only when /etc/rig/role proves rig performed the
|
|
join, then remove that marker. Refuses for pre-existing or old unknown
|
|
joins, and while a GitHub runner is installed. Run as root.
|
|
bootstrap <role>-box [--user <name>]
|
|
The box TENANT roles: converge a box-minted guest. The '-box' suffix
|
|
names the family (a guest, vs the '-server' machine roles above).
|
|
The agent tenants (claude-box, codex-box, grok-box, kimi-box, …) are
|
|
DEFINED in the heavy-duty/rig-templates registry — resolved via
|
|
RIG_TEMPLATES_DIR > RIG_TEMPLATES_REF > the in-tree pin — and land
|
|
the agent toolbelt (git, gh, tmux, docker), the agent's CLI on the
|
|
system PATH, and the agent-context file (with the box#80 guard:
|
|
never run box setup-host inside a box). staging-box, in rig's own
|
|
tree, lands box#69's server posture — docker + sshd hardening; its
|
|
tailnet join stays operator-run via 'rig bootstrap workload-server'.
|
|
Creds-free and non-interactive: box auto-runs these at mint. Run as
|
|
root, inside the box.
|
|
coolify install --version <pin>
|
|
Pinned Coolify install (AUTOUPDATE=false). Control-plane box only.
|
|
coolify backup install [options]
|
|
Nightly age-encrypted dump of the control-plane database, as a
|
|
systemd timer. rig installs the machinery and templates an empty
|
|
0600 bindings file; you fill in the age recipient and S3 details.
|
|
Control-plane box only. Run as root.
|
|
db <dump|restore> ...
|
|
Ad-hoc PostgreSQL dump/restore for a container on this box. `dump`
|
|
writes a gzipped SQL artifact (--no-owner --no-acl, so it restores
|
|
onto a different instance); `restore` loads one back, connecting as
|
|
the container's own superuser, behind a confirm gate. Run as root.
|
|
platform
|
|
What is this machine: hostname, OS, kernel, CPU, memory, disk and
|
|
virtualization, then rig's own provenance (which rig, when, and the
|
|
role marker bootstrap wrote). Computed at run time from /proc, uname,
|
|
/etc/os-release, df and systemd-detect-virt and stored NOWHERE — a
|
|
spec goes stale the moment someone adds RAM, so there is nothing to
|
|
go stale here. Writes nothing, needs no root, makes no network call,
|
|
and therefore also runs on a pristine Debian box rig has never
|
|
bootstrapped, where the provenance block reads 'not bootstrapped'.
|
|
runner install --repo <owner/repo> [options]
|
|
GitHub Actions runner as a systemd service under an unprivileged
|
|
user — outbound-only, no Docker. Prompts for the short-lived
|
|
registration token (RUNNER_TOKEN env overrides). Run as root.
|
|
runner status [--user <name>]
|
|
What this box's runner is registered to: repo, name, labels, unit.
|
|
Reads the box only — no token, no network call. Run as root.
|
|
runner remove [--local] [--user <name>]
|
|
Take the service down and deregister the runner. Prompts for the
|
|
short-lived removal token (RUNNER_REMOVE_TOKEN env overrides).
|
|
Run as root.
|
|
runner repoint --repo <owner/repo> [options]
|
|
Move an installed runner to another repository — deregister, then
|
|
re-register, reusing the binary already on the box. Needs a removal
|
|
token for the old repo and a registration token for the new one.
|
|
Run as root.
|
|
forgejo-runner install --instance <url> [options]
|
|
Forgejo Actions runner as a systemd service under an unprivileged
|
|
user — outbound-only, jobs in containers on this box's own dockerd.
|
|
Its home is a ci-box tenant, where 'rig bootstrap ci-box' already
|
|
installed that daemon. Registers to an INSTANCE: whether the runner
|
|
serves that instance, one org or one repo is a property of the
|
|
registration token, so there is no --repo. Prompts for the token
|
|
(FORGEJO_RUNNER_TOKEN env overrides). Run as root.
|
|
forgejo-runner status [--user <name>]
|
|
Which instance this box's Forgejo runner is registered to: instance,
|
|
name, labels, unit. Reads the box only — no token, no network call,
|
|
and never prints the registration secret .runner holds. Run as root.
|
|
forgejo-runner remove [--user <name>]
|
|
Take the service down and wipe this box's registration. Always
|
|
local-only: Forgejo has no deregistration endpoint, so the runner
|
|
stays listed offline until you delete it in the instance. Needs no
|
|
token. Run as root.
|
|
users apply --file <path>
|
|
Converge named operator accounts from a declarative users file, on
|
|
every class: groups by role (admin/rig/box), passwords locked always,
|
|
authorized_keys made exact, visudo-gated sudoers rules. Users dropped
|
|
from the file are locked, never deleted. '-' reads stdin. Run as root.
|
|
users status
|
|
Roles (derived from actual group membership), key counts and lock
|
|
state for the rig-managed users. Reads the box only. Run as root.
|
|
users close-root
|
|
Shut root SSH on a class=human box once an admin key works. Refuses
|
|
on class=server — root there is the control plane's automation door —
|
|
and while no admin holds a key. Run as root.
|
|
template-lint <role-dir>...
|
|
Validate tenant-role definitions (the heavy-duty/rig-templates
|
|
shape): template.env against the allowlist (data, never sourced),
|
|
install.sh present with a shebang, creds.md non-blank. Every refusal
|
|
names the failing key. The registry repo's CI runs this on every PR;
|
|
no root, no network, no writes.
|
|
manifest [<key>]
|
|
Print /etc/rig/manifest — which rig converged this machine and when
|
|
(bootstrapped_by/_at pin the FIRST convergence forever; converged_by/_at
|
|
name the latest). With a key, print that value alone for shell callers.
|
|
Written by 'rig bootstrap' only; read-only here, and no root needed.
|
|
Provenance is DECIDED, so it is stored; the machine's specs are
|
|
OBSERVED, so they are not — those are 'rig platform'.
|
|
versions
|
|
List the installed rig versions — install.sh lands each one side by
|
|
side at <root>/versions/<v>, a 'current' symlink tracks the default
|
|
(what the rig on your PATH runs). The default is marked (current);
|
|
the tree answering this command is marked (running).
|
|
use <version>
|
|
Switch the default rig version — repoint 'current' (and the PATH
|
|
symlink riding it) at an installed version, atomically, then assert
|
|
the flip took. WARNS on a bootstrapped host (/etc/rig/role exists):
|
|
switching the rig under a converged host changes what a re-converge
|
|
would do.
|
|
uninstall [<version>|--all] [--force]
|
|
Remove one NON-current installed version, or --all: every version,
|
|
'current', and the PATH symlinks. Asks first (--force or RIG_YES=1
|
|
skips the prompt) and ENDS with an absence assert — every removed
|
|
path is re-checked, and any survivor makes it exit 1 naming the
|
|
leftovers instead of reporting a clean uninstall that wasn't.
|
|
--version
|
|
Print the running rig's version (its tree's own VERSION file) and
|
|
where it runs from.
|
|
|
|
install/upgrade:
|
|
curl -fsSL https://raw.githubusercontent.com/heavy-duty/rig/main/install.sh | bash
|
|
Installs the latest RELEASE from GitHub (the default RIG_HOST).
|
|
RIG_REF=<tag> pins one, RIG_REF=main tracks the development tree.
|
|
From a Forgejo host: set RIG_HOST to that origin and fetch install.sh
|
|
from <host>/heavy-duty/rig/raw/branch/main/install.sh (#111).
|
|
Re-run any time: an installed version converges (no-op), a new one
|
|
installs side by side at <root>/versions/<v> and becomes the default.
|
|
EOF
|
|
}
|
|
|
|
# --- the versioned install (box#79's layout, ported — #35) -------------------
|
|
# install.sh lands each version at <install-root>/versions/<v>, with a
|
|
# 'current' symlink naming the default and $BINDIR/rig pointing through it.
|
|
# $ROOT (readlink -f, line 4) already resolved the whole chain, so a versioned
|
|
# install always runs from .../versions/<v> — and a git checkout does not,
|
|
# which is how these verbs know to refuse instead of uninstalling somebody's
|
|
# working copy.
|
|
install_root() {
|
|
local vdir; vdir="$(dirname "$ROOT")"
|
|
[ "$(basename "$vdir")" = versions ] || return 1
|
|
dirname "$vdir"
|
|
}
|
|
|
|
# A version is a DIRECTORY NAME under versions/ — nothing else. One strict
|
|
# gate for every caller that builds a path from one (the installer's new_ver,
|
|
# migration's flat_ver, and bin/rig's 'use'/single-version uninstall): only
|
|
# [A-Za-z0-9._+-], no leading '.' or '-'. That forbids '/', '..'-escapes,
|
|
# spaces and option-lookalikes by construction — a crafted version dies HERE,
|
|
# never in an rm -rf or an ln. install.sh carries a byte-identical copy;
|
|
# test/cli.sh diffs the two so the gates cannot drift.
|
|
valid_version() {
|
|
case "$1" in
|
|
''|.*|-*) return 1 ;;
|
|
*[!A-Za-z0-9._+-]*) return 1 ;;
|
|
esac
|
|
return 0
|
|
}
|
|
|
|
# The flip gate, rig's shape (#35): box refuses version flips under existing
|
|
# boxes; rig's stake is the converged HOST — /etc/rig/role marks a box that
|
|
# bootstrap has made into something. Switching the default rig under it
|
|
# changes what a re-converge would do, which is worth a warning, not a
|
|
# refusal: there is no user state a flip can strand, and flipping versions on
|
|
# a bootstrapped host is the normal upgrade. RIG_ROLE_MARKER overrides the
|
|
# path so tests point it at fixtures (repo precedent: the coolify marker
|
|
# gate). install.sh carries a byte-identical copy; test/cli.sh diffs the two.
|
|
warn_bootstrapped() { # $1 = what is about to happen
|
|
local marker="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
|
[ -e "$marker" ] || return 0
|
|
warn "this host is bootstrapped ($(head -n1 "$marker" 2>/dev/null || echo "role marker at $marker"))"
|
|
warn "$1 changes what a re-converge (rig bootstrap, users apply) would do — proceeding."
|
|
}
|
|
|
|
# The PATH symlinks that could ride this install: the one this invocation came
|
|
# in on, RIG_BIN's, and the tier default's. Candidates only — every consumer
|
|
# checks where a link actually points before touching it, so a symlink that is
|
|
# somebody else's (another install root, a hand-rolled wrapper) is never moved.
|
|
bin_links() {
|
|
local c=()
|
|
[ -L "${BASH_SOURCE[0]}" ] && c+=("${BASH_SOURCE[0]}")
|
|
[ -n "${RIG_BIN:-}" ] && c+=("$RIG_BIN/rig")
|
|
if [ "$(id -u)" -eq 0 ]; then c+=(/usr/local/bin/rig); else c+=("$HOME/.local/bin/rig"); fi
|
|
printf '%s\n' "${c[@]}" | awk '!seen[$0]++'
|
|
}
|
|
|
|
converge_bin_links() { # $1 = install root: point our PATH symlinks through current
|
|
local ir="$1" p t
|
|
while IFS= read -r p; do
|
|
[ -L "$p" ] || continue
|
|
t="$(readlink -f "$p" 2>/dev/null || true)"
|
|
[ -n "$t" ] || t="$(readlink "$p" 2>/dev/null || true)"
|
|
case "$t" in
|
|
"$ir"/*) ln -sfn "$ir/current/bin/rig" "$p" ;;
|
|
esac
|
|
done < <(bin_links)
|
|
}
|
|
|
|
cmd_versions() {
|
|
local ir cur d v mark
|
|
ir="$(install_root)" || die "this rig runs from a working tree ($ROOT), not a versioned install — nothing to list"
|
|
cur="$(readlink -f "$ir/current" 2>/dev/null || true)"
|
|
echo "VERSIONS ($ir)"
|
|
for d in "$ir/versions"/*/; do
|
|
[ -d "$d" ] || continue
|
|
v="$(basename "$d")"
|
|
mark=""
|
|
[ "$(readlink -f "$d")" = "$cur" ] && mark=" (current)"
|
|
[ "$(readlink -f "$d")" = "$ROOT" ] && mark="$mark (running)"
|
|
printf ' %s%s\n' "$v" "$mark"
|
|
done
|
|
echo
|
|
echo "switch the default: rig use <version>"
|
|
echo "install another: re-run install.sh (versions land side by side)"
|
|
}
|
|
|
|
cmd_use() {
|
|
local v="${1:-}" ir eff expect out
|
|
if [ -z "$v" ]; then
|
|
printf 'rig: use needs a version (see: rig versions)\n' >&2
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
ir="$(install_root)" || die "this rig runs from a working tree ($ROOT), not a versioned install — nothing to switch"
|
|
valid_version "$v" || die "not a sane version name: '$v' (a version is a directory name under versions/ — see 'rig versions')"
|
|
[ -d "$ir/versions/$v" ] || die "no such version: $v (see 'rig versions')"
|
|
warn_bootstrapped "switching the default rig version to $v"
|
|
# An atomic flip, not unlink+create: ln -sfn leaves a window where current
|
|
# is missing; a rename over it does not.
|
|
ln -sfn "versions/$v" "$ir/current.new.$$" && mv -Tf "$ir/current.new.$$" "$ir/current"
|
|
converge_bin_links "$ir"
|
|
# Assert the EFFECTIVE result, not the intent: current must resolve to the
|
|
# version asked for, and the chain's own binary must answer that version —
|
|
# a flip that "worked" while the operator's rig still runs the old tree is
|
|
# exactly the flakiness this verb exists to end.
|
|
eff="$(basename "$(readlink -f "$ir/current" 2>/dev/null || true)")"
|
|
[ "$eff" = "$v" ] || die "the flip did not take — current resolves to '${eff:-nothing}', not $v"
|
|
expect="$(cat "$ir/versions/$v/VERSION" 2>/dev/null || true)"
|
|
if [ -n "$expect" ]; then
|
|
out="$("$ir/current/bin/rig" --version 2>&1 || true)"
|
|
case "$out" in
|
|
*"$expect"*) : ;;
|
|
*) die "current/bin/rig answers '$out', not version $expect — the symlink chain is broken" ;;
|
|
esac
|
|
fi
|
|
log "switched to $v (current -> versions/$v)"
|
|
}
|
|
|
|
# The uninstall's own confirmation: --force, or RIG_YES=1, or a TTY. RIG_YES
|
|
# is the installer-family consent contract — how automation says yes without
|
|
# a terminal; without any of the three we refuse rather than assume consent.
|
|
uninstall_confirm() { # $1 = question
|
|
[ "$force" -eq 1 ] && return 0
|
|
[ -n "${RIG_YES:-}" ] && return 0
|
|
if [ ! -t 0 ]; then
|
|
printf 'rig: refusing to %s without --force (no terminal to confirm on; RIG_YES=1 also means yes)\n' "$1" >&2
|
|
exit 2
|
|
fi
|
|
local reply
|
|
printf 'rig: %s? [y/N] ' "$1"
|
|
# `|| reply=""` is load-bearing under `set -e`: Ctrl-D makes read return
|
|
# non-zero, and as a plain-statement call site this function would die HERE,
|
|
# before the case — the abort would be real but completely silent. Empty
|
|
# falls through to the `*)` arm, so EOF aborts with exactly one message,
|
|
# the same spelling commands/db.sh uses for the same [y/N] shape.
|
|
read -r reply || reply=""
|
|
case "$reply" in y|Y|yes|YES|Yes) return 0 ;; *) die "aborted." ;; esac
|
|
}
|
|
|
|
# 'rig uninstall' — the real uninstall, replacing the undocumented rm -rf
|
|
# prose. Trees and symlinks, and it ENDS by PROVING the absence — the last
|
|
# word is a re-check, not a hope.
|
|
cmd_uninstall() {
|
|
local ir a ver="" all=0 force=0 cur p t leftover=""
|
|
local targets=()
|
|
for a in "$@"; do
|
|
case "$a" in
|
|
--all) all=1 ;;
|
|
--force) force=1 ;;
|
|
-*)
|
|
printf 'rig: unknown option: %s\n' "$a" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
if [ -n "$ver" ]; then
|
|
printf 'rig: uninstall takes one version, or --all\n' >&2
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
ver="$a"
|
|
;;
|
|
esac
|
|
done
|
|
ir="$(install_root)" || die "this rig runs from a working tree ($ROOT), not a versioned install — nothing to uninstall (a checkout is removed with plain rm)"
|
|
[ -w "$ir" ] || die "cannot write $ir — uninstall as the user that installed it (or root: sudo rig uninstall)"
|
|
|
|
# -- one version -----------------------------------------------------------
|
|
if [ -n "$ver" ] && [ "$all" -eq 0 ]; then
|
|
valid_version "$ver" || die "not a sane version name: '$ver' (a version is a directory name under versions/ — see 'rig versions')"
|
|
[ -d "$ir/versions/$ver" ] || die "no such version: $ver (see 'rig versions')"
|
|
cur="$(basename "$(readlink -f "$ir/current" 2>/dev/null || true)")"
|
|
# A broken current makes the CURRENT guard below unfireable (cur empty
|
|
# when the link is missing; cur naming a non-directory when it dangles —
|
|
# readlink -f resolves a link whose last component does not exist). Heal
|
|
# first, then decide; never delete around a broken default.
|
|
{ [ -n "$cur" ] && [ -d "$ir/versions/$cur" ]; } \
|
|
|| die "current is dangling — 'rig use <version>' to repoint the default first (refusing to remove versions while it is broken)"
|
|
[ "$ver" != "$cur" ] || die "$ver is the CURRENT version — 'rig use <other>' first, or 'rig uninstall --all' for everything"
|
|
uninstall_confirm "remove rig version $ver from $ir"
|
|
# rm's exit code is not the verdict — the absence re-check below is (a
|
|
# half-removed tree must be reported as INCOMPLETE, not as a crash).
|
|
rm -rf "${ir:?}/versions/$ver" || true
|
|
if [ -e "$ir/versions/$ver" ] || [ -L "$ir/versions/$ver" ]; then
|
|
echo "rig: uninstall INCOMPLETE — still present: $ir/versions/$ver" >&2
|
|
exit 1
|
|
fi
|
|
log "removed version $ver (the default stays $cur)"
|
|
return 0
|
|
fi
|
|
if [ -n "$ver" ]; then
|
|
printf 'rig: a version and --all together is ambiguous\n' >&2
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
|
|
# -- everything (bare 'rig uninstall' and '--all' both mean all of it) -----
|
|
warn_bootstrapped "removing rig entirely"
|
|
uninstall_confirm "remove the ENTIRE rig install at $ir (every version)"
|
|
|
|
# The removal set, gathered BEFORE anything is deleted, so the absence
|
|
# assert below re-checks exactly what was promised gone. PATH symlinks are
|
|
# removed only when they resolve into (or dangle at) THIS install root.
|
|
targets+=("$ir")
|
|
while IFS= read -r p; do
|
|
[ -L "$p" ] || continue
|
|
t="$(readlink -f "$p" 2>/dev/null || true)"
|
|
[ -n "$t" ] || t="$(readlink "$p" 2>/dev/null || true)"
|
|
case "$t" in "$ir"/*) targets+=("$p") ;; esac
|
|
done < <(bin_links)
|
|
mapfile -t targets < <(printf '%s\n' "${targets[@]}" | awk '!seen[$0]++')
|
|
|
|
# rm's exit code is not the verdict — the absence assert below is (a
|
|
# half-removed tree must be reported as INCOMPLETE by name, not as a crash).
|
|
for p in "${targets[@]}"; do rm -rf "$p" || true; done
|
|
|
|
# END WITH THE ABSENCE ASSERT: every path re-checked — file, dir OR symlink.
|
|
# A leftover makes this exit 1 by name; "uninstalled" is a claim, and claims
|
|
# get verified.
|
|
for p in "${targets[@]}"; do
|
|
if [ -e "$p" ] || [ -L "$p" ]; then leftover="$leftover $p"; fi
|
|
done
|
|
if [ -n "$leftover" ]; then
|
|
echo "rig: uninstall INCOMPLETE — still present:$leftover" >&2
|
|
echo "rig: remove them by hand, and re-check each path is really gone." >&2
|
|
exit 1
|
|
fi
|
|
echo "rig: uninstalled — removed:"
|
|
for p in "${targets[@]}"; do echo "rig: · $p"; done
|
|
}
|
|
|
|
cmd="${1:-}"
|
|
case "$cmd" in
|
|
bootstrap)
|
|
shift
|
|
exec "$ROOT/commands/bootstrap.sh" "$@"
|
|
;;
|
|
coolify)
|
|
shift
|
|
sub="${1:-}"
|
|
case "$sub" in
|
|
install)
|
|
shift
|
|
exec "$ROOT/commands/coolify-install.sh" "$@"
|
|
;;
|
|
backup)
|
|
shift
|
|
if [ "${1:-}" != "install" ]; then
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
shift
|
|
exec "$ROOT/commands/coolify-backup-install.sh" "$@"
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
;;
|
|
db)
|
|
shift
|
|
case "${1:-}" in
|
|
dump|restore|-h|--help)
|
|
exec "$ROOT/commands/db.sh" "$@"
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
;;
|
|
platform)
|
|
shift
|
|
exec "$ROOT/commands/platform.sh" "$@"
|
|
;;
|
|
runner)
|
|
shift
|
|
sub="${1:-}"
|
|
case "$sub" in
|
|
install)
|
|
shift
|
|
exec "$ROOT/commands/runner-install.sh" "$@"
|
|
;;
|
|
status)
|
|
shift
|
|
exec "$ROOT/commands/runner-status.sh" "$@"
|
|
;;
|
|
remove)
|
|
shift
|
|
exec "$ROOT/commands/runner-remove.sh" "$@"
|
|
;;
|
|
repoint)
|
|
shift
|
|
exec "$ROOT/commands/runner-repoint.sh" "$@"
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
;;
|
|
forgejo-runner)
|
|
shift
|
|
sub="${1:-}"
|
|
case "$sub" in
|
|
install)
|
|
shift
|
|
exec "$ROOT/commands/forgejo-runner-install.sh" "$@"
|
|
;;
|
|
status)
|
|
shift
|
|
exec "$ROOT/commands/forgejo-runner-status.sh" "$@"
|
|
;;
|
|
remove)
|
|
shift
|
|
exec "$ROOT/commands/forgejo-runner-remove.sh" "$@"
|
|
;;
|
|
repoint)
|
|
# The GitHub sibling HAS this verb, so an operator will try it. Say why
|
|
# it cannot exist here rather than printing usage and leaving them to
|
|
# infer it: 'repoint' is atomic because GitHub lets rig deregister from
|
|
# the old repo; Forgejo has no such endpoint, so the honest shape is
|
|
# two acts, and one of them leaves a stale entry behind.
|
|
printf 'rig: forgejo-runner has no repoint: Forgejo has no deregistration endpoint, so a move cannot be one atomic act. Use "rig forgejo-runner remove" then "rig forgejo-runner install --instance <url>", and delete the stale runner in the old instance under Actions > Runners.\n' >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
;;
|
|
users)
|
|
shift
|
|
sub="${1:-}"
|
|
case "$sub" in
|
|
apply)
|
|
shift
|
|
exec "$ROOT/commands/users-apply.sh" "$@"
|
|
;;
|
|
status)
|
|
shift
|
|
exec "$ROOT/commands/users-status.sh" "$@"
|
|
;;
|
|
close-root)
|
|
shift
|
|
exec "$ROOT/commands/users-close-root.sh" "$@"
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
;;
|
|
template-lint)
|
|
shift
|
|
exec "$ROOT/commands/template-lint.sh" "$@"
|
|
;;
|
|
manifest)
|
|
shift
|
|
exec "$ROOT/commands/manifest.sh" "$@"
|
|
;;
|
|
versions)
|
|
shift
|
|
cmd_versions "$@"
|
|
;;
|
|
use)
|
|
shift
|
|
cmd_use "$@"
|
|
;;
|
|
uninstall)
|
|
shift
|
|
cmd_uninstall "$@"
|
|
;;
|
|
-V|--version)
|
|
version
|
|
exit 0
|
|
;;
|
|
-h|--help|help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
"")
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
printf 'rig: unknown command: %s\n' "$cmd" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|