#!/usr/bin/env bash # Runner installer — runs inside the LXC, called by ct/runner.sh # # Allgemeiner, INSTANZWEITER Gitea-Actions-Runner ohne Deploy-Rechte: # - Node.js (für actions/checkout im Host-Mode) + git + rsync # - Docker (für Wegwerf-Test-Container in CI, z. B. pgvector) # - act_runner als unprivilegierter User "runner" (Mitglied der docker-Gruppe, # KEIN sudo, keine sudoers-Datei — bewusst, vgl. Kopfkommentar in ct/runner.sh) # - systemd-Unit act-runner.service # # Idempotent: erneuter Lauf überspringt vorhandene Registrierung/Pakete. set -euo pipefail LIB_URL="${LIB_URL:-https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/lib}" source <(curl -fsSL "$LIB_URL/install.func") [[ "$EUID" -eq 0 ]] || { msg_err "Must run as root"; exit 1; } CONF="/root/runner.deploy.env" [[ -f "$CONF" ]] || { msg_err "$CONF not found (host bootstrap incomplete)"; exit 1; } set -a; . "$CONF"; set +a : "${GITEA_INSTANCE_URL:?missing GITEA_INSTANCE_URL}" : "${RUNNER_TOKEN:?missing RUNNER_TOKEN}" RUNNER_NAME="${RUNNER_NAME:-$(hostname)}" RUNNER_LABELS="${RUNNER_LABELS:-homelab:host}" RUNNER_VERSION="${RUNNER_VERSION:-0.2.13}" NODE_MAJOR="${NODE_MAJOR:-22}" APP_USER="runner" APP_HOME="/opt/runner" RUNNER_DIR="$APP_HOME/data" run_user() { runuser -u "$APP_USER" -- env HOME="$APP_HOME" "$@"; } # ── Pakete: git/rsync, Node (checkout-Action), Docker (Test-Container) ──────── setup_base_apt git rsync ca-certificates curl NODE_HAVE="$(command -v node >/dev/null 2>&1 && node -v | sed -E 's/^v([0-9]+).*/\1/' || echo 0)" if [[ "$NODE_HAVE" != "$NODE_MAJOR" ]]; then msg_info "Installing Node.js ${NODE_MAJOR}.x (NodeSource)..." curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - >/dev/null apt-get install -y -qq nodejs >/dev/null msg_ok "Node $(node -v) installed" else msg_warn "Node $(node -v) already present, skipping" fi if ! command -v docker >/dev/null 2>&1; then msg_info "Installing Docker (get.docker.com)..." curl -fsSL https://get.docker.com | sh >/dev/null msg_ok "Docker $(docker --version | awk '{print $3}' | tr -d ',')" else msg_warn "Docker already present, skipping" fi # ── act_runner binary ───────────────────────────────────────────────────────── if [[ ! -x /usr/local/bin/act_runner ]]; then ARCH="$(dpkg --print-architecture)" case "$ARCH" in amd64|arm64) ;; *) msg_err "unsupported arch: $ARCH"; exit 1 ;; esac msg_info "Downloading act_runner $RUNNER_VERSION ($ARCH)..." curl -fsSL "https://dl.gitea.com/act_runner/${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-${ARCH}" \ -o /usr/local/bin/act_runner chmod +x /usr/local/bin/act_runner msg_ok "act_runner $(/usr/local/bin/act_runner --version 2>/dev/null | head -n1)" else msg_warn "act_runner already present, skipping download" fi # ── User (docker-Gruppe VOR dem Daemon-Start — Lesson aus nexus K-103: # Gruppenmitgliedschaften werden beim Prozessstart eingefroren) ───────────── create_system_user "$APP_USER" "$APP_HOME" usermod -aG docker "$APP_USER" mkdir -p "$RUNNER_DIR" chown -R "$APP_USER:$APP_USER" "$APP_HOME" # ── Registrierung (idempotent) ──────────────────────────────────────────────── if [[ ! -f "$RUNNER_DIR/.runner" ]]; then msg_info "Registering runner '$RUNNER_NAME' [$RUNNER_LABELS] with $GITEA_INSTANCE_URL (instance scope)..." run_user bash -c "cd '$RUNNER_DIR' && /usr/local/bin/act_runner register \ --no-interactive \ --instance '$GITEA_INSTANCE_URL' \ --token '$RUNNER_TOKEN' \ --name '$RUNNER_NAME' \ --labels '$RUNNER_LABELS'" chown -R "$APP_USER:$APP_USER" "$RUNNER_DIR" msg_ok "Runner registered" else msg_warn "Runner already registered (.runner exists), skipping" fi # ── systemd-Unit (bewusst KEINE sudoers-Datei) ──────────────────────────────── cat >/etc/systemd/system/act-runner.service <"$CRED_FILE" </dev/null || rm -f "$CONF" apt_cleanup msg_ok "runner installation finished"