#!/usr/bin/env bash set -euo pipefail # ── setup_runner_mac.sh ─────────────────────────────────────────────────────── # Sets up act_runner as a host-mode runner on macOS for iOS CI/CD. # Installs the binary, generates a config, registers against Gitea, # and installs a LaunchDaemon so the runner starts at boot. # # Usage: sudo ./setup_runner_mac.sh # Example: sudo ./setup_runner_mac.sh mac-runner-1 # ───────────────────────────────────────────────────────────────────────────── usage() { echo "Usage: sudo $0 " exit 1 } [[ $# -ne 1 ]] && usage [[ "$EUID" -ne 0 ]] && { echo "ERROR: run with sudo"; exit 1; } RUNNER_NAME="$1" GITEA_URL="https://gitea.kalekber.cc/" REGISTRATION_TOKEN="AboxpDKWx7gizwJ9xeheHVqKjj9J9N9BgyX96wvu" CACHE_PORT=8088 INSTALL_DIR="/usr/local/bin" WORK_DIR="/var/lib/act_runner" CONFIG_PATH="/etc/act_runner/config.yaml" LAUNCHDAEMON_PLIST="/Library/LaunchDaemons/com.gitea.act_runner.plist" # ── detect Mac LAN IP ───────────────────────────────────────────────────────── HOST_IP=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo "") if [[ -z "$HOST_IP" ]]; then echo "ERROR: could not detect LAN IP via en0/en1. Set cache.host manually in $CONFIG_PATH" HOST_IP="127.0.0.1" fi echo "Host LAN IP: $HOST_IP" # ── download act_runner binary ──────────────────────────────────────────────── ARCH=$(uname -m) if [[ "$ARCH" == "arm64" ]]; then BINARY_URL="https://dl.gitea.com/act_runner/latest/act_runner-darwin-arm64" else BINARY_URL="https://dl.gitea.com/act_runner/latest/act_runner-darwin-amd64" fi echo "Downloading act_runner for $ARCH..." curl -fsSL "$BINARY_URL" -o "$INSTALL_DIR/act_runner" chmod +x "$INSTALL_DIR/act_runner" echo "Installed: $("$INSTALL_DIR/act_runner" --version)" # ── create working directory ────────────────────────────────────────────────── mkdir -p "$WORK_DIR" mkdir -p "$(dirname "$CONFIG_PATH")" # ── generate and patch config ───────────────────────────────────────────────── "$INSTALL_DIR/act_runner" generate-config > "$CONFIG_PATH" # Patch labels, cache host/port, and capacity python3 - "$CONFIG_PATH" "$HOST_IP" "$CACHE_PORT" <<'PYEOF' import sys, re path, host_ip, port = sys.argv[1], sys.argv[2], sys.argv[3] with open(path) as f: content = f.read() # labels content = re.sub( r'(labels:\s*\n)(\s+-.*\n)+', 'labels:\n - "macos-latest:host"\n - "macos-14:host"\n', content ) # cache enabled content = re.sub(r'(cache:.*?\n\s+enabled:\s*)false', r'\g<1>true', content, flags=re.DOTALL) # cache host content = re.sub(r'( host:\s*)""', f' host: "{host_ip}"', content) # cache port content = re.sub(r'( port:\s*)\d+', f' port: {port}', content) # capacity content = re.sub(r'( capacity:\s*)\d+', r' capacity: 1', content) with open(path, 'w') as f: f.write(content) print(f"Config patched: labels=macos-latest:host, cache={host_ip}:{port}") PYEOF # ── register runner ─────────────────────────────────────────────────────────── echo "Registering runner '$RUNNER_NAME'..." "$INSTALL_DIR/act_runner" register \ --no-interactive \ --config "$CONFIG_PATH" \ --instance "$GITEA_URL" \ --token "$REGISTRATION_TOKEN" \ --name "$RUNNER_NAME" \ --labels "macos-latest:host,macos-14:host" # Copy .runner file to work dir if it was created in cwd [[ -f ".runner" ]] && cp .runner "$WORK_DIR/.runner" # ── install LaunchDaemon ────────────────────────────────────────────────────── # PATH must include Homebrew + Xcode tools so xcodebuild, xcrun, npm, etc. are found. HOMEBREW_PREFIX=$([ "$ARCH" = "arm64" ] && echo "/opt/homebrew" || echo "/usr/local") cat > "$LAUNCHDAEMON_PLIST" < Label com.gitea.act_runner ProgramArguments ${INSTALL_DIR}/act_runner daemon --config ${CONFIG_PATH} RunAtLoad KeepAlive WorkingDirectory ${WORK_DIR} StandardOutPath ${WORK_DIR}/act_runner.log StandardErrorPath ${WORK_DIR}/act_runner.err EnvironmentVariables PATH ${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/Xcode.app/Contents/Developer/usr/bin HOME ${WORK_DIR} PLIST echo "LaunchDaemon written to $LAUNCHDAEMON_PLIST" # ── load the daemon ─────────────────────────────────────────────────────────── launchctl unload "$LAUNCHDAEMON_PLIST" 2>/dev/null || true launchctl load "$LAUNCHDAEMON_PLIST" echo "Runner '$RUNNER_NAME' started via LaunchDaemon" echo "" echo "Useful commands:" echo " View logs: tail -f $WORK_DIR/act_runner.log" echo " Stop runner: sudo launchctl unload $LAUNCHDAEMON_PLIST" echo " Start runner: sudo launchctl load $LAUNCHDAEMON_PLIST"