feat: rework runner config to INI format with full field support
Replace pipe-delimited runners.conf with INI-style sections supporting host resolution, container images, repo-scoped tokens, resource limits, capacity, and SSH key passthrough. All defaults pulled from .env. - Add INI parsing helpers (ini_list_sections, ini_get, ini_set) to common.sh - Add SSH key support (UNRAID_SSH_KEY, FEDORA_SSH_KEY) to ssh_exec/scp_to - Add .env vars: RUNNER_DEFAULT_IMAGE, RUNNER_DEFAULT_CAPACITY, RUNNER_DEFAULT_DATA_PATH, LOCAL_RUNNER_DATA_PATH, LOCAL_REGISTRY - Rewrite manage_runner.sh with host/image/token resolution and resource limits - Rewrite configure_runners.sh wizard for INI format with all 9 fields - Update phase3 scripts to use ini_list_sections instead of pipe parsing - Add runners.conf INI validation to preflight.sh (check 5b) - Update templates to use resolved labels, capacity, and deploy resources Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
341
manage_runner.sh
341
manage_runner.sh
@@ -3,7 +3,7 @@ set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# manage_runner.sh — Add, remove, or list Gitea Actions runners
|
||||
# Reads runner definitions from runners.conf (pipe-delimited format).
|
||||
# Reads runner definitions from runners.conf (INI format).
|
||||
# Supports two runner types:
|
||||
# docker — Linux hosts, deployed as Docker container via docker-compose
|
||||
# native — macOS, deployed as binary + launchd service
|
||||
@@ -31,16 +31,19 @@ Commands:
|
||||
list Show all runners with status
|
||||
|
||||
Options:
|
||||
--name <name> Runner name as defined in runners.conf
|
||||
--name <name> Runner name (= section name in runners.conf)
|
||||
--help Show this help
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parse a runner entry from runners.conf by name
|
||||
# Sets: RUNNER_NAME, RUNNER_SSH_HOST, RUNNER_SSH_USER, RUNNER_SSH_PORT,
|
||||
# RUNNER_DATA_PATH, RUNNER_LABELS, RUNNER_TYPE
|
||||
# Parse a runner entry from runners.conf (INI format) by section name.
|
||||
# Sets globals: RUNNER_NAME, RUNNER_HOST, RUNNER_TYPE, RUNNER_DATA_PATH,
|
||||
# RUNNER_LABELS, RUNNER_DEFAULT_IMAGE, RUNNER_REPOS, RUNNER_CAPACITY,
|
||||
# RUNNER_CPU, RUNNER_MEMORY
|
||||
# Also resolves: RUNNER_SSH_HOST, RUNNER_SSH_USER, RUNNER_SSH_PORT,
|
||||
# RUNNER_SSH_KEY (from .env or custom section keys)
|
||||
# Returns 1 if not found.
|
||||
# ---------------------------------------------------------------------------
|
||||
parse_runner_entry() {
|
||||
@@ -51,63 +54,205 @@ parse_runner_entry() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS='|' read -r name host user port path labels type; do
|
||||
# Skip comments and blank lines
|
||||
[[ "$name" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
# Trim whitespace from fields
|
||||
name=$(echo "$name" | xargs)
|
||||
|
||||
if [[ "$name" == "$target_name" ]]; then
|
||||
RUNNER_NAME="$name"
|
||||
RUNNER_SSH_HOST=$(echo "$host" | xargs)
|
||||
RUNNER_SSH_USER=$(echo "$user" | xargs)
|
||||
RUNNER_SSH_PORT=$(echo "$port" | xargs)
|
||||
RUNNER_DATA_PATH=$(echo "$path" | xargs)
|
||||
RUNNER_LABELS=$(echo "$labels" | xargs)
|
||||
RUNNER_TYPE=$(echo "$type" | xargs)
|
||||
return 0
|
||||
fi
|
||||
done < "$RUNNERS_CONF"
|
||||
|
||||
log_error "Runner '$target_name' not found in runners.conf"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# List all runner entries from runners.conf (without looking up a name)
|
||||
# Outputs lines to stdout: name|host|user|port|path|labels|type
|
||||
# ---------------------------------------------------------------------------
|
||||
all_runner_entries() {
|
||||
if [[ ! -f "$RUNNERS_CONF" ]]; then
|
||||
log_error "runners.conf not found at $RUNNERS_CONF"
|
||||
# Check section exists
|
||||
if ! ini_list_sections "$RUNNERS_CONF" | grep -qx "$target_name"; then
|
||||
log_error "Runner '$target_name' not found in runners.conf"
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Skip comments and blank lines
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
echo "$line"
|
||||
done < "$RUNNERS_CONF"
|
||||
RUNNER_NAME="$target_name"
|
||||
RUNNER_HOST=$(ini_get "$RUNNERS_CONF" "$target_name" "host" "")
|
||||
RUNNER_TYPE=$(ini_get "$RUNNERS_CONF" "$target_name" "type" "")
|
||||
RUNNER_DATA_PATH=$(ini_get "$RUNNERS_CONF" "$target_name" "data_path" "")
|
||||
RUNNER_LABELS=$(ini_get "$RUNNERS_CONF" "$target_name" "labels" "")
|
||||
RUNNER_DEFAULT_IMAGE=$(ini_get "$RUNNERS_CONF" "$target_name" "default_image" "")
|
||||
RUNNER_REPOS=$(ini_get "$RUNNERS_CONF" "$target_name" "repos" "all")
|
||||
RUNNER_CAPACITY=$(ini_get "$RUNNERS_CONF" "$target_name" "capacity" "${RUNNER_DEFAULT_CAPACITY:-1}")
|
||||
RUNNER_CPU=$(ini_get "$RUNNERS_CONF" "$target_name" "cpu" "")
|
||||
RUNNER_MEMORY=$(ini_get "$RUNNERS_CONF" "$target_name" "memory" "")
|
||||
|
||||
# --- Host resolution ---
|
||||
case "$RUNNER_HOST" in
|
||||
unraid)
|
||||
RUNNER_SSH_HOST="${UNRAID_IP:-}"
|
||||
RUNNER_SSH_USER="${UNRAID_SSH_USER:-}"
|
||||
RUNNER_SSH_PORT="${UNRAID_SSH_PORT:-22}"
|
||||
RUNNER_SSH_KEY="${UNRAID_SSH_KEY:-}"
|
||||
;;
|
||||
fedora)
|
||||
RUNNER_SSH_HOST="${FEDORA_IP:-}"
|
||||
RUNNER_SSH_USER="${FEDORA_SSH_USER:-}"
|
||||
RUNNER_SSH_PORT="${FEDORA_SSH_PORT:-22}"
|
||||
RUNNER_SSH_KEY="${FEDORA_SSH_KEY:-}"
|
||||
;;
|
||||
local)
|
||||
RUNNER_SSH_HOST="local"
|
||||
RUNNER_SSH_USER=""
|
||||
RUNNER_SSH_PORT=""
|
||||
RUNNER_SSH_KEY=""
|
||||
;;
|
||||
custom)
|
||||
RUNNER_SSH_HOST=$(ini_get "$RUNNERS_CONF" "$target_name" "ssh_host" "")
|
||||
RUNNER_SSH_USER=$(ini_get "$RUNNERS_CONF" "$target_name" "ssh_user" "")
|
||||
RUNNER_SSH_PORT=$(ini_get "$RUNNERS_CONF" "$target_name" "ssh_port" "22")
|
||||
RUNNER_SSH_KEY=$(ini_get "$RUNNERS_CONF" "$target_name" "ssh_key" "")
|
||||
;;
|
||||
*)
|
||||
log_error "Runner '$target_name': unknown host '$RUNNER_HOST' (must be unraid, fedora, local, or custom)"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Validate required fields ---
|
||||
if [[ -z "$RUNNER_TYPE" ]]; then
|
||||
log_error "Runner '$target_name': type is empty (must be docker or native)"
|
||||
return 1
|
||||
fi
|
||||
if [[ "$RUNNER_TYPE" != "docker" ]] && [[ "$RUNNER_TYPE" != "native" ]]; then
|
||||
log_error "Runner '$target_name': type='$RUNNER_TYPE' (must be docker or native)"
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$RUNNER_DATA_PATH" ]]; then
|
||||
log_error "Runner '$target_name': data_path is empty"
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$RUNNER_LABELS" ]]; then
|
||||
log_error "Runner '$target_name': labels is empty"
|
||||
return 1
|
||||
fi
|
||||
if ! [[ "$RUNNER_CAPACITY" =~ ^[1-9][0-9]*$ ]]; then
|
||||
log_error "Runner '$target_name': capacity='$RUNNER_CAPACITY' (must be positive integer >= 1)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Resolve the runner's image (with LOCAL_REGISTRY prefix if set).
|
||||
# Sets RUNNER_RESOLVED_IMAGE.
|
||||
# ---------------------------------------------------------------------------
|
||||
resolve_runner_image() {
|
||||
local image="${RUNNER_DEFAULT_IMAGE:-${RUNNER_DEFAULT_IMAGE_ENV:-}}"
|
||||
if [[ -z "$image" ]] && [[ "$RUNNER_TYPE" == "docker" ]]; then
|
||||
image="${RUNNER_DEFAULT_IMAGE:-catthehacker/ubuntu:act-latest}"
|
||||
fi
|
||||
|
||||
if [[ -n "$image" ]] && [[ -n "${LOCAL_REGISTRY:-}" ]]; then
|
||||
RUNNER_RESOLVED_IMAGE="${LOCAL_REGISTRY}/${image}"
|
||||
else
|
||||
RUNNER_RESOLVED_IMAGE="${image}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build act_runner label specs from labels + default_image.
|
||||
# Docker: "linux:docker://image" | Native: "macos:host"
|
||||
# Sets RUNNER_LABELS_CSV and RUNNER_LABELS_YAML.
|
||||
# ---------------------------------------------------------------------------
|
||||
build_runner_labels() {
|
||||
resolve_runner_image
|
||||
|
||||
local labels_csv=""
|
||||
local labels_yaml=""
|
||||
local IFS=','
|
||||
# shellcheck disable=SC2206
|
||||
local -a parts=($RUNNER_LABELS)
|
||||
|
||||
local label spec
|
||||
for label in "${parts[@]}"; do
|
||||
label=$(echo "$label" | xargs)
|
||||
[[ -z "$label" ]] && continue
|
||||
|
||||
if [[ "$label" == *:* ]]; then
|
||||
# Already a full spec (e.g. linux:docker://node:20)
|
||||
spec="$label"
|
||||
elif [[ "$RUNNER_TYPE" == "docker" ]]; then
|
||||
spec="${label}:docker://${RUNNER_RESOLVED_IMAGE}"
|
||||
else
|
||||
spec="${label}:host"
|
||||
fi
|
||||
|
||||
if [[ -z "$labels_csv" ]]; then
|
||||
labels_csv="$spec"
|
||||
else
|
||||
labels_csv="${labels_csv},${spec}"
|
||||
fi
|
||||
# shellcheck disable=SC2089 # intentional — value rendered via envsubst, not shell expansion
|
||||
labels_yaml="${labels_yaml} - \"${spec}\"
|
||||
"
|
||||
done
|
||||
|
||||
export RUNNER_LABELS_CSV="$labels_csv"
|
||||
# shellcheck disable=SC2090 # intentional — value rendered via envsubst, not shell expansion
|
||||
export RUNNER_LABELS_YAML="$labels_yaml"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Resolve registration token based on repos field.
|
||||
# repos=all → instance-level token from .env
|
||||
# repos=<name> → fetch repo-level token from Gitea API
|
||||
# Sets RUNNER_REG_TOKEN.
|
||||
# ---------------------------------------------------------------------------
|
||||
resolve_registration_token() {
|
||||
if [[ "$RUNNER_REPOS" == "all" ]] || [[ -z "$RUNNER_REPOS" ]]; then
|
||||
RUNNER_REG_TOKEN="${GITEA_RUNNER_REGISTRATION_TOKEN:-}"
|
||||
else
|
||||
# Fetch repo-level registration token from Gitea API
|
||||
local owner="${GITEA_ORG_NAME:-}"
|
||||
if [[ -z "$owner" ]]; then
|
||||
log_error "GITEA_ORG_NAME is empty — cannot fetch repo-level token"
|
||||
return 1
|
||||
fi
|
||||
log_info "Fetching registration token for repo '${owner}/${RUNNER_REPOS}'..."
|
||||
local response
|
||||
response=$(gitea_api GET "/repos/${owner}/${RUNNER_REPOS}/actions/runners/registration-token" 2>/dev/null) || {
|
||||
log_error "Failed to fetch registration token for repo '${RUNNER_REPOS}'"
|
||||
return 1
|
||||
}
|
||||
RUNNER_REG_TOKEN=$(printf '%s' "$response" | jq -r '.token // empty' 2>/dev/null)
|
||||
if [[ -z "$RUNNER_REG_TOKEN" ]]; then
|
||||
log_error "Empty registration token returned for repo '${RUNNER_REPOS}'"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
export RUNNER_REG_TOKEN
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build Docker deploy.resources block (only when cpu/memory are set).
|
||||
# Sets RUNNER_DEPLOY_RESOURCES.
|
||||
# ---------------------------------------------------------------------------
|
||||
build_deploy_resources() {
|
||||
if [[ -n "$RUNNER_CPU" ]] || [[ -n "$RUNNER_MEMORY" ]]; then
|
||||
local block=" deploy:\n resources:\n limits:"
|
||||
if [[ -n "$RUNNER_CPU" ]]; then
|
||||
block="${block}\n cpus: \"${RUNNER_CPU}\""
|
||||
fi
|
||||
if [[ -n "$RUNNER_MEMORY" ]]; then
|
||||
block="${block}\n memory: \"${RUNNER_MEMORY}\""
|
||||
fi
|
||||
RUNNER_DEPLOY_RESOURCES=$(printf '%b' "$block")
|
||||
else
|
||||
RUNNER_DEPLOY_RESOURCES=""
|
||||
fi
|
||||
export RUNNER_DEPLOY_RESOURCES
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Execute a command on the runner's host.
|
||||
# For "local" hosts (macOS), runs the command directly.
|
||||
# For remote hosts, SSHs into them — uses direct ssh (not ssh_exec from
|
||||
# common.sh) because runner hosts have their own SSH creds defined in
|
||||
# runners.conf, not from the standard *_IP/*_SSH_USER env vars.
|
||||
# For remote hosts, SSHs into them using resolved SSH credentials.
|
||||
# ---------------------------------------------------------------------------
|
||||
runner_ssh() {
|
||||
local cmd="$*"
|
||||
|
||||
if [[ "$RUNNER_SSH_HOST" == "local" ]]; then
|
||||
# macOS runner — execute directly on this machine
|
||||
eval "$cmd"
|
||||
else
|
||||
ssh -o ConnectTimeout=10 \
|
||||
# shellcheck disable=SC2086
|
||||
ssh ${RUNNER_SSH_KEY:+-i "$RUNNER_SSH_KEY"} \
|
||||
-o ConnectTimeout=10 \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o BatchMode=yes \
|
||||
-p "${RUNNER_SSH_PORT}" \
|
||||
@@ -126,7 +271,9 @@ runner_scp() {
|
||||
if [[ "$RUNNER_SSH_HOST" == "local" ]]; then
|
||||
cp "$src" "$dst"
|
||||
else
|
||||
scp -o ConnectTimeout=10 \
|
||||
# shellcheck disable=SC2086
|
||||
scp ${RUNNER_SSH_KEY:+-i "$RUNNER_SSH_KEY"} \
|
||||
-o ConnectTimeout=10 \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o BatchMode=yes \
|
||||
-P "${RUNNER_SSH_PORT}" \
|
||||
@@ -136,16 +283,13 @@ runner_scp() {
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# add_docker_runner — Deploy a runner as a Docker container on a Linux host
|
||||
# Steps:
|
||||
# 1. Create data directory
|
||||
# 2. Render + SCP docker-compose.yml
|
||||
# 3. Render + SCP runner-config.yaml
|
||||
# 4. Start container
|
||||
# The act_runner Docker image auto-registers with Gitea using env vars
|
||||
# (GITEA_INSTANCE_URL + GITEA_RUNNER_REGISTRATION_TOKEN) on first boot.
|
||||
# ---------------------------------------------------------------------------
|
||||
add_docker_runner() {
|
||||
log_info "Deploying Docker runner '${RUNNER_NAME}' on ${RUNNER_SSH_HOST}..."
|
||||
log_info "Deploying Docker runner '${RUNNER_NAME}' on ${RUNNER_HOST} (${RUNNER_SSH_HOST})..."
|
||||
|
||||
build_runner_labels
|
||||
resolve_registration_token
|
||||
build_deploy_resources
|
||||
|
||||
# Check if container is already running
|
||||
local status
|
||||
@@ -158,44 +302,41 @@ add_docker_runner() {
|
||||
# Create data directory on remote host
|
||||
runner_ssh "mkdir -p '${RUNNER_DATA_PATH}'"
|
||||
|
||||
# Render docker-compose template with runner-specific vars
|
||||
# Render docker-compose template
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp)
|
||||
export RUNNER_NAME RUNNER_LABELS RUNNER_DATA_PATH
|
||||
export GITEA_RUNNER_REGISTRATION_TOKEN="${GITEA_RUNNER_REGISTRATION_TOKEN:-}"
|
||||
export RUNNER_NAME RUNNER_DATA_PATH RUNNER_LABELS_CSV RUNNER_REG_TOKEN RUNNER_DEPLOY_RESOURCES
|
||||
render_template "${SCRIPT_DIR}/templates/docker-compose-runner.yml.tpl" "$tmpfile" \
|
||||
"\${ACT_RUNNER_VERSION} \${RUNNER_NAME} \${GITEA_INTERNAL_URL} \${GITEA_RUNNER_REGISTRATION_TOKEN} \${RUNNER_LABELS} \${RUNNER_DATA_PATH}"
|
||||
"\${ACT_RUNNER_VERSION} \${RUNNER_NAME} \${GITEA_INTERNAL_URL} \${RUNNER_REG_TOKEN} \${RUNNER_LABELS_CSV} \${RUNNER_DATA_PATH} \${RUNNER_DEPLOY_RESOURCES}"
|
||||
runner_scp "$tmpfile" "${RUNNER_DATA_PATH}/docker-compose.yml"
|
||||
rm -f "$tmpfile"
|
||||
|
||||
# Render runner config
|
||||
tmpfile=$(mktemp)
|
||||
# shellcheck disable=SC2090 # intentional — RUNNER_LABELS_YAML rendered via envsubst
|
||||
export RUNNER_LABELS_YAML
|
||||
export RUNNER_CAPACITY
|
||||
render_template "${SCRIPT_DIR}/templates/runner-config.yaml.tpl" "$tmpfile" \
|
||||
"\${RUNNER_NAME} \${RUNNER_LABELS}"
|
||||
"\${RUNNER_NAME} \${RUNNER_LABELS_YAML} \${RUNNER_CAPACITY}"
|
||||
runner_scp "$tmpfile" "${RUNNER_DATA_PATH}/config.yaml"
|
||||
rm -f "$tmpfile"
|
||||
|
||||
# Start the container
|
||||
runner_ssh "cd '${RUNNER_DATA_PATH}' && docker compose up -d 2>/dev/null || docker-compose up -d"
|
||||
log_success "Docker runner '${RUNNER_NAME}' started on ${RUNNER_SSH_HOST}"
|
||||
log_success "Docker runner '${RUNNER_NAME}' started on ${RUNNER_HOST} (${RUNNER_SSH_HOST})"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# add_native_runner — Deploy a runner as a native binary on macOS
|
||||
# Steps:
|
||||
# 1. Download act_runner binary for macOS (arm64 or amd64)
|
||||
# 2. Register with Gitea (--no-interactive)
|
||||
# 3. Render launchd plist
|
||||
# 4. Load plist via launchctl
|
||||
# Native runners are used on macOS because Docker Desktop is heavyweight
|
||||
# and unreliable for long-running background services.
|
||||
# ---------------------------------------------------------------------------
|
||||
add_native_runner() {
|
||||
# Native runners use launchctl + macOS-specific paths — must be macOS
|
||||
require_local_os "Darwin" "Native runner deployment requires macOS (uses launchctl)"
|
||||
|
||||
log_info "Deploying native runner '${RUNNER_NAME}' on local machine..."
|
||||
|
||||
build_runner_labels
|
||||
resolve_registration_token
|
||||
|
||||
# Resolve ~ to actual home directory for local execution
|
||||
RUNNER_DATA_PATH="${RUNNER_DATA_PATH/#\~/$HOME}"
|
||||
export RUNNER_DATA_PATH
|
||||
@@ -214,7 +355,6 @@ add_native_runner() {
|
||||
|
||||
# Download act_runner binary if not present
|
||||
if [[ ! -x "${RUNNER_DATA_PATH}/act_runner" ]]; then
|
||||
# Detect architecture — Apple Silicon (arm64) vs Intel (x86_64)
|
||||
local arch
|
||||
arch=$(uname -m)
|
||||
case "$arch" in
|
||||
@@ -230,26 +370,26 @@ add_native_runner() {
|
||||
log_success "act_runner binary downloaded"
|
||||
fi
|
||||
|
||||
# Register the runner with Gitea (generates .runner file in data dir)
|
||||
# --no-interactive skips prompts, --config generates default config if missing
|
||||
# Register the runner with Gitea
|
||||
if [[ ! -f "${RUNNER_DATA_PATH}/.runner" ]]; then
|
||||
log_info "Registering runner with Gitea..."
|
||||
"${RUNNER_DATA_PATH}/act_runner" register \
|
||||
--no-interactive \
|
||||
--instance "${GITEA_INTERNAL_URL}" \
|
||||
--token "${GITEA_RUNNER_REGISTRATION_TOKEN:-}" \
|
||||
--token "${RUNNER_REG_TOKEN}" \
|
||||
--name "${RUNNER_NAME}" \
|
||||
--labels "${RUNNER_LABELS}" \
|
||||
--labels "${RUNNER_LABELS_CSV}" \
|
||||
--config "${RUNNER_DATA_PATH}/config.yaml"
|
||||
log_success "Runner registered"
|
||||
fi
|
||||
|
||||
# Render runner config (overwrites any default generated by register)
|
||||
# Render runner config
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp)
|
||||
export RUNNER_NAME RUNNER_LABELS RUNNER_DATA_PATH
|
||||
# shellcheck disable=SC2090 # intentional — RUNNER_LABELS_YAML rendered via envsubst
|
||||
export RUNNER_NAME RUNNER_DATA_PATH RUNNER_LABELS_YAML RUNNER_CAPACITY
|
||||
render_template "${SCRIPT_DIR}/templates/runner-config.yaml.tpl" "$tmpfile" \
|
||||
"\${RUNNER_NAME} \${RUNNER_LABELS}"
|
||||
"\${RUNNER_NAME} \${RUNNER_LABELS_YAML} \${RUNNER_CAPACITY}"
|
||||
cp "$tmpfile" "${RUNNER_DATA_PATH}/config.yaml"
|
||||
rm -f "$tmpfile"
|
||||
|
||||
@@ -270,9 +410,8 @@ add_native_runner() {
|
||||
# remove_docker_runner — Stop + remove Docker runner container
|
||||
# ---------------------------------------------------------------------------
|
||||
remove_docker_runner() {
|
||||
log_info "Removing Docker runner '${RUNNER_NAME}' from ${RUNNER_SSH_HOST}..."
|
||||
log_info "Removing Docker runner '${RUNNER_NAME}' from ${RUNNER_HOST} (${RUNNER_SSH_HOST})..."
|
||||
|
||||
# Check if docker-compose file exists
|
||||
if runner_ssh "test -f '${RUNNER_DATA_PATH}/docker-compose.yml'" 2>/dev/null; then
|
||||
runner_ssh "cd '${RUNNER_DATA_PATH}' && docker compose down 2>/dev/null || docker-compose down" || true
|
||||
log_success "Docker runner '${RUNNER_NAME}' stopped"
|
||||
@@ -285,30 +424,25 @@ remove_docker_runner() {
|
||||
# remove_native_runner — Unload launchd service + remove binary + plist
|
||||
# ---------------------------------------------------------------------------
|
||||
remove_native_runner() {
|
||||
# Native runners use launchctl + macOS-specific paths — must be macOS
|
||||
require_local_os "Darwin" "Native runner removal requires macOS (uses launchctl)"
|
||||
|
||||
log_info "Removing native runner '${RUNNER_NAME}' from local machine..."
|
||||
|
||||
# Resolve ~ to actual home directory
|
||||
RUNNER_DATA_PATH="${RUNNER_DATA_PATH/#\~/$HOME}"
|
||||
|
||||
local plist_name="com.gitea.runner.${RUNNER_NAME}.plist"
|
||||
local plist_path="$HOME/Library/LaunchAgents/${plist_name}"
|
||||
|
||||
# Unload launchd service if loaded
|
||||
if launchctl list 2>/dev/null | grep -q "com.gitea.runner.${RUNNER_NAME}"; then
|
||||
launchctl unload "$plist_path" 2>/dev/null || true
|
||||
log_success "Launchd service unloaded"
|
||||
fi
|
||||
|
||||
# Remove plist file
|
||||
if [[ -f "$plist_path" ]]; then
|
||||
rm -f "$plist_path"
|
||||
log_success "Plist removed: $plist_path"
|
||||
fi
|
||||
|
||||
# Remove runner data directory (binary, config, registration)
|
||||
if [[ -d "${RUNNER_DATA_PATH}" ]]; then
|
||||
printf 'Remove runner data at %s? [y/N] ' "${RUNNER_DATA_PATH}"
|
||||
read -r confirm
|
||||
@@ -323,41 +457,35 @@ remove_native_runner() {
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# list_runners — Print table of all runners with their Gitea status
|
||||
# Queries the Gitea admin API for registered runners and cross-references
|
||||
# with runners.conf to show which are online/offline/unregistered.
|
||||
# ---------------------------------------------------------------------------
|
||||
list_runners() {
|
||||
log_info "Listing runners..."
|
||||
|
||||
# Fetch registered runners from Gitea admin API
|
||||
if [[ ! -f "$RUNNERS_CONF" ]]; then
|
||||
log_error "runners.conf not found at $RUNNERS_CONF"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local api_runners
|
||||
api_runners=$(gitea_api GET "/admin/runners" 2>/dev/null || echo "[]")
|
||||
|
||||
# Print header
|
||||
printf '%-20s %-16s %-10s %-8s %-10s\n' "NAME" "HOST" "LABELS" "TYPE" "STATUS"
|
||||
printf '%-20s %-16s %-10s %-8s %-10s\n' "----" "----" "------" "----" "------"
|
||||
printf '%-20s %-10s %-10s %-8s %-6s %-10s\n' "NAME" "HOST" "LABELS" "TYPE" "CAP" "STATUS"
|
||||
printf '%-20s %-10s %-10s %-8s %-6s %-10s\n' "----" "----" "------" "----" "---" "------"
|
||||
|
||||
# For each entry in runners.conf, look up status in API response
|
||||
while IFS='|' read -r name host user port path labels type; do
|
||||
# Skip comments and blank lines
|
||||
[[ "$name" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
local name host labels runner_type capacity status
|
||||
while IFS= read -r name; do
|
||||
host=$(ini_get "$RUNNERS_CONF" "$name" "host" "")
|
||||
labels=$(ini_get "$RUNNERS_CONF" "$name" "labels" "")
|
||||
runner_type=$(ini_get "$RUNNERS_CONF" "$name" "type" "")
|
||||
capacity=$(ini_get "$RUNNERS_CONF" "$name" "capacity" "1")
|
||||
|
||||
name=$(echo "$name" | xargs)
|
||||
host=$(echo "$host" | xargs)
|
||||
labels=$(echo "$labels" | xargs)
|
||||
type=$(echo "$type" | xargs)
|
||||
|
||||
# Search for this runner in the API response by name
|
||||
local status
|
||||
status=$(printf '%s' "$api_runners" | jq -r --arg n "$name" '.[] | select(.name == $n) | .status' 2>/dev/null || true)
|
||||
|
||||
if [[ -z "$status" ]]; then
|
||||
status="not-found"
|
||||
fi
|
||||
|
||||
printf '%-20s %-16s %-10s %-8s %-10s\n' "$name" "$host" "$labels" "$type" "$status"
|
||||
done < "$RUNNERS_CONF"
|
||||
printf '%-20s %-10s %-10s %-8s %-6s %-10s\n' "$name" "$host" "$labels" "$runner_type" "$capacity" "$status"
|
||||
done < <(ini_list_sections "$RUNNERS_CONF")
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -388,7 +516,6 @@ case "$COMMAND" in
|
||||
log_error "add requires --name <runner_name>"
|
||||
usage
|
||||
fi
|
||||
require_vars GITEA_RUNNER_REGISTRATION_TOKEN
|
||||
parse_runner_entry "$RUNNER_ARG_NAME"
|
||||
case "$RUNNER_TYPE" in
|
||||
docker) add_docker_runner ;;
|
||||
|
||||
Reference in New Issue
Block a user