Skip to content

Commit dd4a254

Browse files
authored
ci(runner): add macOS Intel runner auto-reregister watchdog (#332)
1 parent e7567e4 commit dd4a254

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env bash
2+
#
3+
# runner-watchdog-mac-intel.sh
4+
#
5+
# Auto-reregister watchdog for the self-hosted macOS Intel CI runner
6+
# (macpro-intel-204, labels: self-hosted,macOS,X64,c2pool-mac-intel).
7+
#
8+
# WHERE THIS RUNS: the CI bridge / workstation -- NOT on the Mac node.
9+
# The node's launchd service already has KeepAlive(SuccessfulExit=false),
10+
# which restarts a *crashed runner process*. That alone cannot recover a
11+
# runner that GitHub has *deregistered* server-side (registration removed
12+
# or its auth token revoked): re-registration needs a fresh registration
13+
# token minted with repo-admin creds, which live here on the bridge, not
14+
# on the Mac node. This watchdog closes that gap.
15+
#
16+
# WHAT IT DOES (idempotent, non-destructive):
17+
# 1. Polls the repo's runners API for macpro-intel-204.
18+
# 2. If the runner is absent or "offline" for >= THRESHOLD consecutive
19+
# checks, mints a fresh registration token and re-registers the runner
20+
# over SSH using `config.sh --replace` (no delete; --replace reuses the
21+
# same name), then reloads the launchd service.
22+
# 3. Resets the failure counter as soon as the runner is seen online.
23+
#
24+
# REVERT: delete this file + remove its launchd/cron entry on the bridge.
25+
# It never deletes the runner; --replace is the only mutation it makes.
26+
#
27+
set -euo pipefail
28+
29+
REPO="${C2POOL_REPO:-frstrtr/c2pool}"
30+
RUNNER_NAME="${RUNNER_NAME:-macpro-intel-204}"
31+
NODE_SSH="${NODE_SSH:-user0@192.168.86.204}"
32+
NODE_KEY="${NODE_KEY:-$HOME/.ssh/agentmail_deploy}"
33+
RUNNER_DIR="${RUNNER_DIR:-/Users/user0/actions-runner}"
34+
RUNNER_LABELS="${RUNNER_LABELS:-self-hosted,macOS,X64,c2pool-mac-intel}"
35+
SVC_LABEL="actions.runner.frstrtr-c2pool.${RUNNER_NAME}"
36+
37+
# Re-register only after this many consecutive offline observations, so a
38+
# single transient blip (job restart, brief network drop) does not trigger
39+
# a churn. With a 5-min poll, 3 == ~15 min sustained offline.
40+
THRESHOLD="${THRESHOLD:-3}"
41+
STATE_FILE="${STATE_FILE:-$HOME/.cache/c2pool-runner-watchdog/${RUNNER_NAME}.fails}"
42+
43+
log() { printf '%s [runner-watchdog:%s] %s\n' "$(date -u +%FT%TZ)" "$RUNNER_NAME" "$*"; }
44+
45+
mkdir -p "$(dirname "$STATE_FILE")"
46+
fails=$(cat "$STATE_FILE" 2>/dev/null || echo 0)
47+
48+
# Status as GitHub sees it: "online" | "offline" | "absent".
49+
status=$(gh api "repos/${REPO}/actions/runners" \
50+
--jq ".runners[] | select(.name==\"${RUNNER_NAME}\") | .status" 2>/dev/null || true)
51+
[ -z "$status" ] && status="absent"
52+
53+
if [ "$status" = "online" ]; then
54+
[ "$fails" -ne 0 ] && log "back online; clearing fail counter ($fails -> 0)"
55+
echo 0 > "$STATE_FILE"
56+
exit 0
57+
fi
58+
59+
fails=$((fails + 1))
60+
echo "$fails" > "$STATE_FILE"
61+
log "status=${status} consecutive_fails=${fails}/${THRESHOLD}"
62+
[ "$fails" -lt "$THRESHOLD" ] && exit 0
63+
64+
log "threshold reached -> re-registering"
65+
TOKEN=$(gh api --method POST "repos/${REPO}/actions/runners/registration-token" --jq .token)
66+
[ -z "$TOKEN" ] && { log "ERROR: empty registration token"; exit 1; }
67+
68+
ssh -i "$NODE_KEY" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no "$NODE_SSH" bash -s <<EOF
69+
set -e
70+
cd "${RUNNER_DIR}"
71+
./svc.sh stop || true
72+
./config.sh remove --token "${TOKEN}" || true
73+
./config.sh --unattended --replace \
74+
--url "https://github.com/${REPO}" \
75+
--token "${TOKEN}" \
76+
--name "${RUNNER_NAME}" \
77+
--labels "${RUNNER_LABELS}"
78+
./svc.sh start
79+
EOF
80+
81+
log "re-registration issued; clearing fail counter"
82+
echo 0 > "$STATE_FILE"

0 commit comments

Comments
 (0)