#!/usr/bin/env bash # provision-runner.sh — VPS provisioning script for Gitea act_runner # Refs: #1097 (POKA-YOKE: Make unregistered runners impossible to miss) # # Usage (on Bezalel VPS as root): # bash provision-runner.sh --gitea-url --token # # This script: # 1. Downloads and installs act_runner binary # 2. Registers the runner with the Gitea instance # 3. Creates and enables systemd service for act_runner # 4. Installs the runner-health-probe timer (poka-yoke detection layer) # # POKA-YOKE principles applied: # Prevention: runner registration is mandatory — script exits non-zero if registration fails # Detection: runner-health-probe.sh installed and enabled as part of this script # Correction: health probe auto-restarts act_runner on zero-runner detection set -euo pipefail # ── Configuration defaults (override via env or flags) ─────────────────────── GITEA_URL="${GITEA_URL:-https://forge.alexanderwhitestone.com}" RUNNER_TOKEN="${RUNNER_TOKEN:-}" RUNNER_NAME="${RUNNER_NAME:-$(hostname)-runner}" RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest,linux,x86_64}" ACT_RUNNER_VERSION="${ACT_RUNNER_VERSION:-0.2.10}" INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}" CONFIG_DIR="${CONFIG_DIR:-/etc/act_runner}" DATA_DIR="${DATA_DIR:-/var/lib/act_runner}" NEXUS_DIR="${NEXUS_DIR:-/root/wizards/the-nexus}" PROBE_SCRIPT="${NEXUS_DIR}/scripts/runner-health-probe.sh" # ── Helpers ─────────────────────────────────────────────────────────────────── log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] PROVISION: $*"; } fail() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] PROVISION ERROR: $*" >&2; exit 1; } usage() { cat < Gitea base URL (default: $GITEA_URL) --token Runner registration token (required) --name Runner name (default: hostname-runner) --labels Comma-separated labels (default: $RUNNER_LABELS) --version act_runner version to install (default: $ACT_RUNNER_VERSION) --nexus-dir Path to the-nexus checkout (default: $NEXUS_DIR) --help Show this help Environment variables: GITEA_URL, RUNNER_TOKEN, RUNNER_NAME, RUNNER_LABELS, ACT_RUNNER_VERSION, NEXUS_DIR POKA-YOKE CHECKLIST (enforced by this script): [1] act_runner binary installed and executable [2] Runner registered with Gitea (non-zero runner count verified) [3] act_runner systemd service enabled and running [4] runner-health-probe timer installed and enabled EOF } # ── Argument parsing ────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case "$1" in --gitea-url) GITEA_URL="$2"; shift 2 ;; --token) RUNNER_TOKEN="$2"; shift 2 ;; --name) RUNNER_NAME="$2"; shift 2 ;; --labels) RUNNER_LABELS="$2"; shift 2 ;; --version) ACT_RUNNER_VERSION="$2"; shift 2 ;; --nexus-dir) NEXUS_DIR="$2"; PROBE_SCRIPT="${NEXUS_DIR}/scripts/runner-health-probe.sh"; shift 2 ;; --help) usage; exit 0 ;; *) fail "Unknown argument: $1. Use --help for usage." ;; esac done [[ -z "$RUNNER_TOKEN" ]] && fail "Runner registration token required. Pass --token or set RUNNER_TOKEN env var." # ── Step 1: Install act_runner binary ───────────────────────────────────────── log "Step 1/4: Installing act_runner v${ACT_RUNNER_VERSION}..." ARCH=$(uname -m) case "$ARCH" in x86_64) ARCH_SUFFIX="amd64" ;; aarch64) ARCH_SUFFIX="arm64" ;; *) fail "Unsupported architecture: $ARCH" ;; esac BINARY_URL="https://gitea.com/gitea/act_runner/releases/download/v${ACT_RUNNER_VERSION}/act_runner-${ACT_RUNNER_VERSION}-linux-${ARCH_SUFFIX}" BINARY_PATH="${INSTALL_DIR}/act_runner" if [[ -f "$BINARY_PATH" ]]; then CURRENT_VER=$("$BINARY_PATH" --version 2>/dev/null | grep -oP '\d+\.\d+\.\d+' || echo "unknown") if [[ "$CURRENT_VER" == "$ACT_RUNNER_VERSION" ]]; then log "act_runner v${ACT_RUNNER_VERSION} already installed — skipping download." else log "Upgrading act_runner from v${CURRENT_VER} to v${ACT_RUNNER_VERSION}..." curl -fsSL "$BINARY_URL" -o "$BINARY_PATH" chmod +x "$BINARY_PATH" fi else curl -fsSL "$BINARY_URL" -o "$BINARY_PATH" chmod +x "$BINARY_PATH" fi "$BINARY_PATH" --version >/dev/null 2>&1 || fail "act_runner binary not functional after install." log "act_runner binary OK: $($BINARY_PATH --version 2>/dev/null || echo 'installed')" # ── Step 2: Register runner with Gitea ──────────────────────────────────────── log "Step 2/4: Registering runner with Gitea at ${GITEA_URL}..." mkdir -p "$CONFIG_DIR" "$DATA_DIR" CONFIG_FILE="${CONFIG_DIR}/config.yaml" # Generate config and register "$BINARY_PATH" register \ --no-interactive \ --instance "$GITEA_URL" \ --token "$RUNNER_TOKEN" \ --name "$RUNNER_NAME" \ --labels "$RUNNER_LABELS" \ --config "$CONFIG_FILE" \ 2>&1 | tee /tmp/act_runner_register.log if ! grep -q "Runner registered" /tmp/act_runner_register.log 2>/dev/null && \ ! grep -q "registered" /tmp/act_runner_register.log 2>/dev/null; then # Registration output varies — check if config was written as a fallback signal if [[ ! -f "$CONFIG_FILE" ]]; then fail "Runner registration failed. Check token and Gitea URL. Log: /tmp/act_runner_register.log" fi fi log "Runner registered. Config written to ${CONFIG_FILE}" # ── Step 3: Create and enable systemd service ───────────────────────────────── log "Step 3/4: Installing act_runner systemd service..." cat > /etc/systemd/system/act_runner.service < /etc/systemd/system/runner-health-probe.service < /etc/systemd/system/runner-health-probe.timer </dev/null 2>&1 && echo "OK" || echo "FAIL" printf " [2] act_runner registered : " [[ -f "$CONFIG_FILE" ]] && echo "OK (config exists)" || echo "FAIL (no config)" printf " [3] act_runner service : " systemctl is-active --quiet act_runner && echo "RUNNING" || echo "FAIL" printf " [4] health-probe timer : " systemctl is-active --quiet runner-health-probe.timer 2>/dev/null && echo "ACTIVE" || echo "NOT INSTALLED (re-run after nexus checkout)" echo "══════════════════════════════════════════════════════════" echo "" log "Provisioning complete. Runner '${RUNNER_NAME}' registered at ${GITEA_URL}"