Ruling 2: every tracked text mention of the login's pre-rename spelling becomes igneum-labs (29 files); the commit identity in the scripts becomes igneum-labs <337424239+igneum-labs@users.noreply.github.com> (the same noreply id); the three scripts that hand the stored gh keyring name to gh (register.sh, fetch-ci-artifacts.sh, ship-app.mjs) read it from ~/.config/igneum/gh-user (never in the repository), default igneum-labs; fresh-repo.sh reads the login to rewrite from the encoded list's row 10; the old spelling joins the founder-strings list, so no tracked file may spell it; CLAUDE.md's GitHub paragraph rewritten. The red watcher's self-test fixtures use a neutral author. Ruling 4: docs/ledger-public.md, one row per ledger item (id, the claim in one line, status, what was done in one line, the evidence link or the evidence in words), generated by tools/ledger/export-public.mjs from docs/fud-ledger.md: no round or status-update sections, no commit ids, no time of day, no lane, agent or branch names. 190 items. The gate runs its self-test (a fixture with a commit id, a time, a lane and a branch name comes out clean; --check fails on drift) and its --check on every merge; the founder check reads the output like every tracked file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
73 lines
5.4 KiB
Bash
Executable file
73 lines
5.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Register (or re-register) the GitHub Actions self-hosted runner on igneum-build-1 from this Mac.
|
|
# infra/build-server/runner/register.sh fetch a registration token with gh, run provision.sh on the box with it
|
|
# infra/build-server/runner/register.sh --status list the repository's runners (name, status, labels) and the box's unit
|
|
# infra/build-server/runner/register.sh --host <ip> another box (7 October 2026, igneum-build-2): the same, against that ip;
|
|
# BOX_HOSTNAME, RUNNER_NAME, RUNNER_LABELS, RUNNER_CPUS and RUNNER_JOBS
|
|
# from this shell's environment travel with it (provision.sh would
|
|
# otherwise rename the box igneum-build-1), e.g.
|
|
# BOX_HOSTNAME=igneum-build-2 RUNNER_LABELS=igneum-build-1,igneum-build-2 RUNNER_CPUS=0-31 RUNNER_JOBS=32 \
|
|
# infra/build-server/runner/register.sh --host 142.132.249.238
|
|
#
|
|
# The token: `gh api -X POST repos/igneum-network/igneum/actions/runners/registration-token` as igneum-labs (the CLAUDE.md gh
|
|
# rule: that account must be ACTIVE; any other active account fails here before anything is fetched). It is a one-hour
|
|
# registration token, not a credential the runner keeps (config.sh writes its own into /opt/actions-runner/.credentials,
|
|
# owner runner, mode 600). It travels to the box on ssh stdin as the first line, followed by provision.sh itself; it is
|
|
# never an argument of ssh, never written to a file on the Mac, and provision.sh never logs it. The whole of provision.sh
|
|
# runs (idempotent, every other step says ok), so the box is also brought up to date.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_SLUG="${IGNEUM_GH_REPO:-igneum-network/igneum}"
|
|
KEY="${IGNEUM_BUILD_KEY:-$HOME/.ssh/igneum_ed25519}"
|
|
HOST_LINE="$(head -1 "${IGNEUM_BUILD_HOST_FILE:-$HOME/.config/igneum/build-server}" | tr -d '[:space:]')"
|
|
IP="${HOST_LINE#*@}"
|
|
if [ "${1:-}" = --host ]; then
|
|
IP="${2:-}"; [ -n "$IP" ] || { echo "--host needs an ip" >&2; exit 1; }; shift 2
|
|
[ -n "${BOX_HOSTNAME:-}" ] || { echo "--host: set BOX_HOSTNAME (provision.sh would otherwise rename the box igneum-build-1)" >&2; exit 1; }
|
|
fi
|
|
[ -n "$IP" ] || { echo "no build server in ~/.config/igneum/build-server (infra/build-server/run-from-mac.sh writes it)" >&2; exit 1; }
|
|
# the provisioning variables a second box needs, forwarded as assignments in front of the remote shell (values are plain
|
|
# words: a hostname, a label list, a cpu range, a number; anything else is refused)
|
|
FWD=""
|
|
for v in BOX_HOSTNAME RUNNER_NAME RUNNER_LABELS RUNNER_CPUS RUNNER_JOBS; do
|
|
val="${!v:-}"; [ -n "$val" ] || continue
|
|
printf '%s' "$val" | grep -qE '^[A-Za-z0-9,._-]+$' || { echo "$v='$val' is not a plain word" >&2; exit 1; }
|
|
FWD="$FWD $v=$val"
|
|
done
|
|
SSH=(ssh -i "$KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 "root@$IP")
|
|
|
|
gh_owner() {
|
|
local active
|
|
active=$(gh auth status 2>/dev/null | awk '/Logged in to github.com account/ { acct=$7 } /Active account: true/ { print acct; exit }')
|
|
stored="$(cat "$HOME/.config/igneum/gh-user" 2>/dev/null | tr -d '[:space:]')"; stored="${stored:-igneum-labs}" # the keyring entry's name (the login's pre-rename spelling until a re-login)
|
|
if [ "$active" != "$stored" ]; then
|
|
gh auth switch --user "$stored" >/dev/null 2>&1 || { echo "gh: cannot switch to the stored login (gh auth status: ${active:-no active account}; the entry's name is in ~/.config/igneum/gh-user)" >&2; exit 1; }
|
|
fi
|
|
[ "$(gh api user --jq .login 2>/dev/null)" = igneum-labs ] || { echo "gh: the active token is not the igneum-labs login; refusing" >&2; exit 1; }
|
|
}
|
|
|
|
if [ "${1:-}" = --status ]; then
|
|
gh_owner
|
|
gh api "repos/$REPO_SLUG/actions/runners" --jq '.runners[] | "\(.name)\t\(.status)\tbusy=\(.busy)\t\(([.labels[].name]) | join(","))"' || echo "(no runners or no access)"
|
|
"${SSH[@]}" 'systemctl list-units --type=service --no-legend "actions.runner.*" ; ls -la /opt/actions-runner/.runner 2>/dev/null || echo "not registered on the box"'
|
|
exit 0
|
|
fi
|
|
|
|
gh_owner
|
|
echo "fetching a registration token for $REPO_SLUG as igneum-labs ..."
|
|
TOKEN=$(gh api -X POST "repos/$REPO_SLUG/actions/runners/registration-token" --jq .token 2>/dev/null) || { echo "gh refused the registration token: the account needs admin on $REPO_SLUG (gh api repos/$REPO_SLUG --jq .permissions)" >&2; exit 1; }
|
|
[ -n "$TOKEN" ] || { echo "empty token from gh" >&2; exit 1; }
|
|
echo "token received (not shown); running provision.sh on root@$IP with it (first line of stdin, then the script)"
|
|
# the first stdin line is the token, read by the remote shell before bash -s takes the rest as the script; the output is
|
|
# kept in a temp file so the ssh exit code is read (a filter in the pipe would hide it) and any line carrying the token is
|
|
# dropped before it is shown (provision.sh never prints it; this is the belt)
|
|
OUT=$(mktemp); trap 'rm -f "$OUT"' EXIT
|
|
set +e
|
|
{ printf '%s\n' "$TOKEN"; cat "$HERE/../provision.sh"; } | "${SSH[@]}" "IFS= read -r RUNNER_TOKEN; export RUNNER_TOKEN; MODE=provision$FWD bash -s" > "$OUT" 2>&1
|
|
RC=$?
|
|
set -e
|
|
grep -v -F "$TOKEN" "$OUT" || true
|
|
unset TOKEN
|
|
[ "$RC" = 0 ] || { echo "provision.sh exited $RC on the box" >&2; exit "$RC"; }
|
|
echo "runners now registered on $REPO_SLUG:"
|
|
gh api "repos/$REPO_SLUG/actions/runners" --jq '.runners[] | " \(.name)\t\(.status)\t\(([.labels[].name]) | join(","))"'
|