#!/usr/bin/env bash # Shared forgejo-runner install/register/persist helpers for the # per-OS dev-setup scripts. Sourced, never executed directly. # # Callers provide: # - RUNNER_OS (darwin | linux) # - RUNNER_ARCH (arm64 | amd64) # - RUNNER_LABELS (comma-separated, e.g. "self-hosted,macos,arm64") # - RUNNER_NAME (display name, e.g. $(hostname -s)) # Env required (load via scripts/run/common.sh cascade): # - FORGEJO_HOST e.g. http://forge.black.local # - FORGEJO_ORG org the runner registers against # - FORGEJO_RUNNER_TOKEN org-scoped registration token # # Callers (osx.sh / bluefin.sh / linux.sh) also decide the persistence # layer — these helpers only handle the binary + registration step; # each OS file installs its own launchd plist or systemd user unit. set -euo pipefail RUNNER_BIN="$HOME/.local/bin/forgejo-runner" RUNNER_DIR="$HOME/.local/share/forgejo-runner" RUNNER_URL_BASE="https://code.forgejo.org/forgejo/runner/releases/download/nightly" runner_require_env() { local missing=() for v in FORGEJO_HOST FORGEJO_ORG FORGEJO_RUNNER_TOKEN; do [[ -z "${!v:-}" ]] && missing+=("$v") done if (( ${#missing[@]} > 0 )); then echo " runner: missing required env: ${missing[*]}" >&2 echo " runner: set in .env.local — see .env.example" >&2 return 2 fi } runner_install_binary() { mkdir -p "$(dirname "$RUNNER_BIN")" "$RUNNER_DIR" if [[ -x "$RUNNER_BIN" ]]; then echo " runner: binary already present at $RUNNER_BIN" return 0 fi local url="$RUNNER_URL_BASE/forgejo-runner-nightly-$RUNNER_OS-$RUNNER_ARCH" echo " runner: downloading $url" curl -fsSL -o "$RUNNER_BIN.tmp" "$url" chmod +x "$RUNNER_BIN.tmp" mv "$RUNNER_BIN.tmp" "$RUNNER_BIN" echo " runner: installed → $RUNNER_BIN" } # Register (or re-register) at org scope. Removes .runner file if the # existing registration has different labels or a different name — this # rebinds the runner to the org without manual DB intervention. runner_register() { runner_require_env || return $? cd "$RUNNER_DIR" if [[ -f .runner ]]; then local existing_name existing_labels existing_name=$(jq -r '.name // ""' .runner 2>/dev/null || echo "") existing_labels=$(jq -c '.labels // []' .runner 2>/dev/null || echo "[]") if [[ "$existing_name" == "$RUNNER_NAME" ]] && \ [[ "$existing_labels" == "[\"${RUNNER_LABELS//,/\",\"}\"]" ]]; then echo " runner: already registered as '$RUNNER_NAME' with labels [$RUNNER_LABELS]" return 0 fi echo " runner: existing registration ($existing_name, labels=$existing_labels) differs — re-registering" rm -f .runner fi "$RUNNER_BIN" register \ --no-interactive \ --instance "$FORGEJO_HOST" \ --token "$FORGEJO_RUNNER_TOKEN" \ --name "$RUNNER_NAME" \ --labels "$RUNNER_LABELS" >/dev/null echo " runner: registered as '$RUNNER_NAME' at $FORGEJO_HOST/$FORGEJO_ORG (labels: $RUNNER_LABELS)" } # Verify the runner appears online via the Forgejo API within a short # polling window. Uses FORGEJO_RUNNER_TOKEN only if also passed as a # personal access token; otherwise requires FORGEJO_ADMIN_TOKEN to read # /api/v1/user/actions/runners. runner_verify_online() { local tok="${FORGEJO_ADMIN_TOKEN:-${FORGEJO_TOKEN:-}}" if [[ -z "$tok" ]]; then echo " runner: skipping online-verify (no admin/personal token — set FORGEJO_ADMIN_TOKEN to enable)" return 0 fi local tries=0 online="" while (( tries < 12 )); do online=$(curl -fsS -H "Authorization: token $tok" \ "$FORGEJO_HOST/api/v1/orgs/$FORGEJO_ORG/actions/runners" 2>/dev/null \ | jq -r ".runners[] | select(.name==\"$RUNNER_NAME\") | .status" 2>/dev/null || echo "") [[ "$online" == "online" ]] && { echo " runner: online"; return 0; } tries=$((tries + 1)) sleep 2 done echo " runner: NOT online after 24s; check service logs" >&2 return 1 }