返回 CodeWhale
setup-mac-runner.sh
根目录 / scripts / ci / setup-mac-runner.sh
1 #!/usr/bin/env bash
2 # Register this Mac as a self-hosted GitHub Actions runner for the macOS legs.
3 #
4 # WHY: GitHub's hosted macOS queue routinely takes 50+ minutes, which is the
5 # single slowest gate on every PR. This machine finishes the same work in a
6 # fraction of that.
7 #
8 # SAFETY — read this before running it.
9 #
10 # Hmbown/CodeWhale is a PUBLIC repository with thousands of forks. A
11 # self-hosted runner that accepts pull requests from forks lets anyone who
12 # opens a PR execute arbitrary code on this machine, as this user. That is the
13 # one CI configuration GitHub explicitly warns against, and on this machine it
14 # would expose ~/.codewhale/secrets.json, the gh token, and the whole CW
15 # workspace.
16 #
17 # The companion change in .github/workflows/ci.yml therefore routes jobs to
18 # this runner ONLY for same-repository events (pushes and PRs whose head repo
19 # is Hmbown/CodeWhale). Fork PRs keep running on GitHub-hosted runners. Do not
20 # remove that guard; without it this script is a remote-code-execution hole.
21 #
22 # Each job gets a freshly registered --ephemeral runner: the registration is
23 # consumed by one job and then discarded, so a job cannot persist a runner
24 # registration for later use. Note this is NOT filesystem isolation — an
25 # ephemeral runner still runs as this user on this disk. For real isolation,
26 # run this inside a throwaway VM (Tart, UTM, or a dedicated macOS user with no
27 # access to the secrets above).
28 #
29 # Usage: scripts/ci/setup-mac-runner.sh [--once]
30 # Requires: gh, authenticated with admin rights on the repo.
31
32 set -euo pipefail
33
34 REPO="${RUNNER_REPO:-Hmbown/CodeWhale}"
35 RUNNER_VERSION="${RUNNER_VERSION:-2.337.0}"
36 RUNNER_HOME="${RUNNER_HOME:-$HOME/actions-runner}"
37 LABELS="${RUNNER_LABELS:-self-hosted,macOS,ARM64,codewhale-mac}"
38 RUNNER_NAME="${RUNNER_NAME:-$(scutil --get LocalHostName 2>/dev/null || hostname -s)-cw}"
39
40 case "$(uname -m)" in
41 arm64) ARCH="osx-arm64" ;;
42 x86_64) ARCH="osx-x64" ;;
43 *) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;;
44 esac
45
46 command -v gh >/dev/null || { echo "gh is required" >&2; exit 1; }
47 gh auth status >/dev/null 2>&1 || { echo "run: gh auth login" >&2; exit 1; }
48
49 if [ ! -x "$RUNNER_HOME/run.sh" ]; then
50 echo "==> installing actions-runner $RUNNER_VERSION ($ARCH) into $RUNNER_HOME"
51 mkdir -p "$RUNNER_HOME"
52 TARBALL="actions-runner-${ARCH}-${RUNNER_VERSION}.tar.gz"
53 curl -fsSL -o "$RUNNER_HOME/$TARBALL" \
54 "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/${TARBALL}"
55 tar -xzf "$RUNNER_HOME/$TARBALL" -C "$RUNNER_HOME"
56 rm -f "$RUNNER_HOME/$TARBALL"
57 fi
58
59 register_and_run() {
60 # Registration tokens are single-use and expire in ~1 hour, so mint a fresh
61 # one per job rather than caching it.
62 local token
63 token="$(gh api -X POST "repos/${REPO}/actions/runners/registration-token" --jq .token)"
64 [ -n "$token" ] || { echo "failed to mint a registration token" >&2; return 1; }
65
66 ( cd "$RUNNER_HOME" && ./config.sh \
67 --url "https://github.com/${REPO}" \
68 --token "$token" \
69 --name "$RUNNER_NAME" \
70 --labels "$LABELS" \
71 --work _work \
72 --ephemeral \
73 --unattended \
74 --replace >/dev/null )
75
76 # --ephemeral: run.sh exits after exactly one job, leaving no registration.
77 ( cd "$RUNNER_HOME" && ./run.sh )
78 }
79
80 if [ "${1:-}" = "--once" ]; then
81 register_and_run
82 exit 0
83 fi
84
85 echo "==> serving jobs for ${REPO} as '${RUNNER_NAME}' [${LABELS}]"
86 echo " one ephemeral registration per job; Ctrl-C to stop"
87 while true; do
88 register_and_run || { echo "runner exited abnormally; retrying in 15s" >&2; sleep 15; }
89 done
90
90 lines BASH