#!/usr/bin/env bash # # register-runner.sh — Reproducibly register (or re-register) a Gitea Actions # runner against the local/VM Gitea instance. # # Why this exists: the runner was originally registered by hand, which is not # reproducible for the next operator. This script captures the canonical flow: # fetch a registration token from Gitea, register act_runner with the agreed # labels + capacity, and externalise the npm token into a gitignored env_file # (never inline in config.yaml). # # Usage: # GITEA_ADMIN_USER=gitea-admin GITEA_ADMIN_PASS=... bash register-runner.sh # bash register-runner.sh --instance http://localhost:3300 --name bytelyst-mac # bash register-runner.sh --mode docker # containerized labels # bash register-runner.sh --capacity 2 # parallel jobs # # Idempotent: if a runner is already registered (.runner exists), it prints the # current registration and exits unless --force is passed. set -euo pipefail # ── defaults ─────────────────────────────────────────────────────────────── INSTANCE="${GITEA_INSTANCE:-http://localhost:3300}" RUNNER_NAME="${RUNNER_NAME:-$(hostname -s)}" CAPACITY="${RUNNER_CAPACITY:-2}" MODE="host" # host | docker FORCE=0 CONFIG_DIR="${ACT_RUNNER_CONFIG_DIR:-/opt/homebrew/etc/act_runner}" RUNNER_FILE="${ACT_RUNNER_FILE:-/opt/homebrew/var/lib/act_runner/.runner}" ENV_FILE="${ACT_RUNNER_ENV_FILE:-$CONFIG_DIR/runner.env}" # Docker image used when MODE=docker (mirrors GitHub's ubuntu-latest closely). DOCKER_IMAGE="${RUNNER_DOCKER_IMAGE:-catthehacker/ubuntu:act-latest}" while [ $# -gt 0 ]; do case "$1" in --instance) INSTANCE="$2"; shift 2 ;; --name) RUNNER_NAME="$2"; shift 2 ;; --capacity) CAPACITY="$2"; shift 2 ;; --mode) MODE="$2"; shift 2 ;; --force) FORCE=1; shift ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "unknown arg: $1" >&2; exit 2 ;; esac done command -v act_runner >/dev/null 2>&1 || { echo "✗ act_runner not on PATH. Install: brew install act_runner" >&2 exit 1 } # ── labels per mode ────────────────────────────────────────────────────────── if [ "$MODE" = "docker" ]; then LABELS="ubuntu-latest:docker://$DOCKER_IMAGE,ubuntu-22.04:docker://$DOCKER_IMAGE,self-hosted:host" else LABELS="ubuntu-latest:host,macos-latest:host,macos-15:host,self-hosted:host" fi echo "── Gitea runner registration ──" echo " instance : $INSTANCE" echo " name : $RUNNER_NAME" echo " mode : $MODE" echo " capacity : $CAPACITY" echo " labels : $LABELS" # ── already registered? ────────────────────────────────────────────────────── if [ -f "$RUNNER_FILE" ] && [ "$FORCE" -ne 1 ]; then echo "" echo "✓ runner already registered ($RUNNER_FILE). Current identity:" python3 - "$RUNNER_FILE" <<'PY' 2>/dev/null || cat "$RUNNER_FILE" import json, sys d = json.load(open(sys.argv[1])) for k in ("id", "uuid", "name", "address", "labels", "ephemeral"): print(f" {k}: {d.get(k)}") PY echo "" echo " Pass --force to re-register (this invalidates the old runner)." exit 0 fi # ── fetch a registration token ─────────────────────────────────────────────── # Admin-scoped: GET /admin/runners/registration-token (Gitea >= 1.20). : "${GITEA_ADMIN_USER:?set GITEA_ADMIN_USER}" : "${GITEA_ADMIN_PASS:?set GITEA_ADMIN_PASS}" REG_TOKEN=$(curl -fsS -u "$GITEA_ADMIN_USER:$GITEA_ADMIN_PASS" \ "$INSTANCE/api/v1/admin/runners/registration-token" \ | python3 -c "import json,sys; print(json.load(sys.stdin)['token'])") [ -n "$REG_TOKEN" ] || { echo "✗ could not fetch registration token" >&2; exit 1; } echo "✓ registration token obtained" # ── register ──────────────────────────────────────────────────────────────── [ "$FORCE" -eq 1 ] && rm -f "$RUNNER_FILE" act_runner register \ --no-interactive \ --instance "$INSTANCE" \ --token "$REG_TOKEN" \ --name "$RUNNER_NAME" \ --labels "$LABELS" \ --config "$CONFIG_DIR/config.yaml" echo "✓ runner registered" echo "" echo "Next: externalise secrets into $ENV_FILE (see GITEA_VM_SETUP.md §11)," echo "set runner.capacity: $CAPACITY in config.yaml, then:" echo " brew services restart act_runner"