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:
@@ -4,6 +4,8 @@ set -euo pipefail
|
||||
# =============================================================================
|
||||
# setup/configure_runners.sh — Interactive runners.conf configuration wizard
|
||||
# Prompts for each runner's fields with validation and progress tracking.
|
||||
# Writes INI-style runners.conf (see runners.conf.example for format).
|
||||
# Defaults are pulled from .env — nothing is hardcoded.
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
@@ -14,6 +16,9 @@ RUNNERS_CONF="${PROJECT_ROOT}/runners.conf"
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/lib/common.sh"
|
||||
|
||||
# Load .env for defaults (RUNNER_DEFAULT_IMAGE, RUNNER_DEFAULT_CAPACITY, etc.)
|
||||
load_env
|
||||
|
||||
# Colors — only emit ANSI escapes when stdout is a terminal (not piped/redirected)
|
||||
if [[ -t 1 ]]; then
|
||||
C_RESET='\033[0m'; C_BOLD='\033[1m'; C_GREEN='\033[0;32m'
|
||||
@@ -24,62 +29,56 @@ fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Load existing runner entries as defaults (idempotent re-runs).
|
||||
# If runners.conf already exists, parse each non-comment line into parallel
|
||||
# arrays so we can pre-fill prompts with previous values.
|
||||
# If runners.conf already exists and is INI format, we read existing values
|
||||
# to pre-fill prompts when re-running the wizard.
|
||||
# ---------------------------------------------------------------------------
|
||||
EXISTING_NAMES=()
|
||||
EXISTING_HOSTS=()
|
||||
EXISTING_USERS=()
|
||||
EXISTING_PORTS=()
|
||||
EXISTING_PATHS=()
|
||||
EXISTING_LABELS=()
|
||||
EXISTING_TYPES=()
|
||||
|
||||
EXISTING_SECTIONS=()
|
||||
if [[ -f "$RUNNERS_CONF" ]]; then
|
||||
while IFS='|' read -r name host user port path labels type; do
|
||||
[[ "$name" =~ ^[[:space:]]*# ]] && continue # skip comments
|
||||
[[ -z "$name" ]] && continue # skip blank lines
|
||||
# Trim whitespace from each field (xargs strips leading/trailing spaces)
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_NAMES+=("$(echo "$name" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_HOSTS+=("$(echo "$host" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_USERS+=("$(echo "$user" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_PORTS+=("$(echo "$port" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_PATHS+=("$(echo "$path" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_LABELS+=("$(echo "$labels" | xargs)")
|
||||
# shellcheck disable=SC2005
|
||||
EXISTING_TYPES+=("$(echo "$type" | xargs)")
|
||||
done < "$RUNNERS_CONF"
|
||||
while IFS= read -r sec; do
|
||||
[[ -z "$sec" ]] && continue
|
||||
EXISTING_SECTIONS+=("$sec")
|
||||
done < <(ini_list_sections "$RUNNERS_CONF")
|
||||
fi
|
||||
|
||||
EXISTING_COUNT=${#EXISTING_NAMES[@]}
|
||||
EXISTING_COUNT=${#EXISTING_SECTIONS[@]}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validation helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
# Must start with alphanumeric; rest can include hyphens and underscores.
|
||||
# Matches the naming convention Gitea uses in its admin panel.
|
||||
validate_runner_name() {
|
||||
[[ "$1" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]
|
||||
}
|
||||
|
||||
# Only two runner types exist: docker (Linux containers) and native (macOS binary)
|
||||
validate_runner_type() {
|
||||
[[ "$1" == "docker" || "$1" == "native" ]]
|
||||
}
|
||||
|
||||
# Absolute paths (/) for remote hosts, or ~/ for native macOS runners.
|
||||
# Tilde is stored literally; manage_runner.sh expands it at runtime.
|
||||
validate_runner_host() {
|
||||
[[ "$1" == "unraid" || "$1" == "fedora" || "$1" == "local" || "$1" == "custom" ]]
|
||||
}
|
||||
|
||||
validate_runner_path() {
|
||||
# shellcheck disable=SC2088 # tilde intentionally stored as literal string
|
||||
[[ "$1" == /* || "$1" == "~/"* || "$1" == "~" ]]
|
||||
}
|
||||
|
||||
validate_runner_repos() {
|
||||
if [[ "$1" == "all" ]]; then return 0; fi
|
||||
# Check against known REPO_*_NAME values
|
||||
[[ "$1" == "${REPO_1_NAME:-}" ]] || [[ "$1" == "${REPO_2_NAME:-}" ]] || [[ "$1" == "${REPO_3_NAME:-}" ]]
|
||||
}
|
||||
|
||||
validate_capacity() {
|
||||
[[ "$1" =~ ^[1-9][0-9]*$ ]]
|
||||
}
|
||||
|
||||
validate_docker_cpu() {
|
||||
[[ -z "$1" ]] || [[ "$1" =~ ^[0-9]+(\.[0-9]+)?$ ]]
|
||||
}
|
||||
|
||||
validate_docker_memory() {
|
||||
[[ -z "$1" ]] || [[ "$1" =~ ^[0-9]+[kmgKMG]?$ ]]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Prompt function — matches configure_env.sh UX (progress counter, default
|
||||
# values in yellow brackets, validation loop with red error hints).
|
||||
@@ -91,16 +90,15 @@ TOTAL_PROMPTS=0
|
||||
prompt_field() {
|
||||
local field_name="$1" # e.g. "name", "ssh_host"
|
||||
local description="$2" # human-readable hint shown in parentheses
|
||||
local validation="$3" # validator key: runner_type, runner_name, ip, port, etc.
|
||||
local validation="$3" # validator key
|
||||
local default="${4:-}" # pre-filled value shown in [brackets]; Enter accepts it
|
||||
local allow_empty="${5:-false}" # true if empty is a valid answer
|
||||
|
||||
CURRENT_PROMPT=$((CURRENT_PROMPT + 1))
|
||||
|
||||
# Progress indicator in dim grey: [3/21]
|
||||
local progress
|
||||
progress=$(printf '[%d/%d]' "$CURRENT_PROMPT" "$TOTAL_PROMPTS")
|
||||
|
||||
# Format: [3/21] field_name (description) [default]: _
|
||||
local prompt_text
|
||||
if [[ -n "$default" ]]; then
|
||||
prompt_text=$(printf '%b%s%b %s (%s) %b[%s]%b: ' "$C_DIM" "$progress" "$C_RESET" "$field_name" "$description" "$C_YELLOW" "$default" "$C_RESET")
|
||||
@@ -108,32 +106,57 @@ prompt_field() {
|
||||
prompt_text=$(printf '%b%s%b %s (%s): ' "$C_DIM" "$progress" "$C_RESET" "$field_name" "$description")
|
||||
fi
|
||||
|
||||
# Validation loop — re-prompts on invalid input until the value passes
|
||||
local value
|
||||
while true; do
|
||||
printf '%b' "$prompt_text"
|
||||
read -r value
|
||||
|
||||
# Empty input → accept the default (if one exists)
|
||||
if [[ -z "$value" ]]; then
|
||||
value="$default"
|
||||
fi
|
||||
|
||||
# Reject empty when no default was available
|
||||
# Allow empty when explicitly permitted (optional fields)
|
||||
if [[ -z "$value" ]] && [[ "$allow_empty" == "true" ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ -z "$value" ]]; then
|
||||
printf '%b Invalid: value cannot be empty%b\n' "$C_RED" "$C_RESET"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Dispatch to the appropriate validator
|
||||
case "$validation" in
|
||||
runner_name)
|
||||
if validate_runner_name "$value"; then break; fi
|
||||
printf '%b Invalid: alphanumeric, hyphens, and underscores only%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
runner_type)
|
||||
if validate_runner_type "$value"; then break; fi
|
||||
printf '%b Invalid: must be "docker" or "native"%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
runner_name)
|
||||
if validate_runner_name "$value"; then break; fi
|
||||
printf '%b Invalid: alphanumeric, hyphens, and underscores only%b\n' "$C_RED" "$C_RESET"
|
||||
runner_host)
|
||||
if validate_runner_host "$value"; then break; fi
|
||||
printf '%b Invalid: must be unraid, fedora, local, or custom%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
runner_path)
|
||||
if validate_runner_path "$value"; then break; fi
|
||||
printf '%b Invalid path (must start with / or ~/)%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
runner_repos)
|
||||
if validate_runner_repos "$value"; then break; fi
|
||||
printf '%b Invalid: must be "all" or a known repo name%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
capacity)
|
||||
if validate_capacity "$value"; then break; fi
|
||||
printf '%b Invalid: must be a positive integer (>= 1)%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
docker_cpu)
|
||||
if validate_docker_cpu "$value"; then break; fi
|
||||
printf '%b Invalid: must be a positive number (e.g. 2.0, 0.5) or empty%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
docker_memory)
|
||||
if validate_docker_memory "$value"; then break; fi
|
||||
printf '%b Invalid: must be Docker memory format (e.g. 2g, 512m) or empty%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
ip)
|
||||
if validate_ip "$value"; then break; fi
|
||||
@@ -143,9 +166,9 @@ prompt_field() {
|
||||
if validate_port "$value"; then break; fi
|
||||
printf '%b Invalid port (expected: 1-65535)%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
runner_path)
|
||||
if validate_runner_path "$value"; then break; fi
|
||||
printf '%b Invalid path (must start with / or ~/)%b\n' "$C_RED" "$C_RESET"
|
||||
optional_path)
|
||||
if validate_optional_path "$value"; then break; fi
|
||||
printf '%b Invalid path (must start with / or ~/ or be empty)%b\n' "$C_RED" "$C_RESET"
|
||||
;;
|
||||
nonempty|*)
|
||||
if validate_nonempty "$value"; then break; fi
|
||||
@@ -154,7 +177,6 @@ prompt_field() {
|
||||
esac
|
||||
done
|
||||
|
||||
# Return the validated value via global (bash has no return-by-value)
|
||||
PROMPT_RESULT="$value"
|
||||
}
|
||||
|
||||
@@ -166,6 +188,7 @@ printf '\n%b╔═════════════════════
|
||||
printf '%b║ Gitea Migration — Runner Setup ║%b\n' "$C_BOLD" "$C_RESET"
|
||||
printf '%b╚══════════════════════════════════════════════════════════╝%b\n\n' "$C_BOLD" "$C_RESET"
|
||||
printf 'Press Enter to keep the current value shown in [brackets].\n'
|
||||
printf 'Defaults are pulled from .env variables.\n'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ask how many runners to configure.
|
||||
@@ -187,47 +210,28 @@ if ! [[ "$runner_count" =~ ^[0-9]+$ ]] || [[ "$runner_count" -lt 1 ]]; then
|
||||
runner_count=$local_default
|
||||
fi
|
||||
|
||||
# Each runner has 7 fields. Native runners auto-fill 3 (host/user/port) but
|
||||
# we still count them toward the total so the progress bar stays consistent.
|
||||
TOTAL_PROMPTS=$((runner_count * 7))
|
||||
# Prompt count per runner:
|
||||
# name(1) + host(1) + type(1) + data_path(1) + labels(1) + default_image(1) +
|
||||
# repos(1) + capacity(1) + cpu(1) + memory(1) = 10
|
||||
# Custom host adds: ssh_host(1) + ssh_user(1) + ssh_port(1) + ssh_key(1) = 4
|
||||
# We estimate max 10 per runner for progress display
|
||||
TOTAL_PROMPTS=$((runner_count * 10))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Collect runner definitions
|
||||
# ---------------------------------------------------------------------------
|
||||
# Storage arrays for collected values
|
||||
# Storage arrays for collected INI data
|
||||
COLLECTED_NAMES=()
|
||||
COLLECTED_HOSTS=()
|
||||
COLLECTED_USERS=()
|
||||
COLLECTED_PORTS=()
|
||||
COLLECTED_PATHS=()
|
||||
COLLECTED_LABELS=()
|
||||
COLLECTED_TYPES=()
|
||||
declare -A COLLECTED_DATA # COLLECTED_DATA["name:key"] = value
|
||||
|
||||
for ((i = 0; i < runner_count; i++)); do
|
||||
runner_num=$((i + 1))
|
||||
printf '\n%b── RUNNER %d OF %d ──────────────────────────────────────────%b\n' "$C_BOLD" "$runner_num" "$runner_count" "$C_RESET"
|
||||
|
||||
# Defaults from existing config (if available)
|
||||
ex_type="${EXISTING_TYPES[$i]:-}"
|
||||
ex_name="${EXISTING_NAMES[$i]:-}"
|
||||
ex_host="${EXISTING_HOSTS[$i]:-}"
|
||||
ex_user="${EXISTING_USERS[$i]:-}"
|
||||
ex_port="${EXISTING_PORTS[$i]:-}"
|
||||
ex_path="${EXISTING_PATHS[$i]:-}"
|
||||
ex_labels="${EXISTING_LABELS[$i]:-}"
|
||||
# Existing defaults from previous runners.conf (if available)
|
||||
ex_name="${EXISTING_SECTIONS[$i]:-}"
|
||||
|
||||
# --- type (asked first — drives conditionals below) ---
|
||||
# Like SSL_MODE in configure_env.sh, type determines which fields are prompted
|
||||
# vs auto-filled. Docker runners need SSH creds; native runners run locally.
|
||||
type_default="$ex_type"
|
||||
if [[ -z "$type_default" ]]; then
|
||||
# Smart default: first two runners are docker (Unraid/Fedora), third is native (MacBook)
|
||||
if [[ $i -lt 2 ]]; then type_default="docker"; else type_default="native"; fi
|
||||
fi
|
||||
prompt_field "type" "docker (Linux) or native (macOS)" "runner_type" "$type_default"
|
||||
r_type="$PROMPT_RESULT"
|
||||
|
||||
# --- name (shown in Gitea admin panel) ---
|
||||
# --- name ---
|
||||
name_default="$ex_name"
|
||||
if [[ -z "$name_default" ]]; then
|
||||
case $i in
|
||||
@@ -237,120 +241,229 @@ for ((i = 0; i < runner_count; i++)); do
|
||||
*) name_default="runner-${runner_num}" ;;
|
||||
esac
|
||||
fi
|
||||
prompt_field "name" "display name in Gitea admin" "runner_name" "$name_default"
|
||||
prompt_field "name" "runner display name in Gitea" "runner_name" "$name_default"
|
||||
r_name="$PROMPT_RESULT"
|
||||
COLLECTED_NAMES+=("$r_name")
|
||||
|
||||
# --- Conditional SSH fields ---
|
||||
# Native runners execute on the local macOS machine — no SSH needed.
|
||||
# Docker runners deploy to a remote Linux host via SSH.
|
||||
if [[ "$r_type" == "native" ]]; then
|
||||
r_host="local"
|
||||
r_user="_"
|
||||
r_port="_"
|
||||
printf '%b → ssh_host=local, ssh_user=_, ssh_port=_ (native runner)%b\n' "$C_DIM" "$C_RESET"
|
||||
# Increment counter for the 3 skipped prompts to keep progress accurate
|
||||
CURRENT_PROMPT=$((CURRENT_PROMPT + 3))
|
||||
else
|
||||
# --- ssh_host (IP of remote Linux host) ---
|
||||
prompt_field "ssh_host" "IP address of remote host" "ip" "${ex_host:-}"
|
||||
r_host="$PROMPT_RESULT"
|
||||
|
||||
# --- ssh_user ---
|
||||
user_default="$ex_user"
|
||||
if [[ -z "$user_default" ]]; then user_default="root"; fi
|
||||
prompt_field "ssh_user" "SSH username" "nonempty" "$user_default"
|
||||
r_user="$PROMPT_RESULT"
|
||||
|
||||
# --- ssh_port ---
|
||||
port_default="$ex_port"
|
||||
if [[ -z "$port_default" ]]; then port_default="22"; fi
|
||||
prompt_field "ssh_port" "SSH port" "port" "$port_default"
|
||||
r_port="$PROMPT_RESULT"
|
||||
# --- host ---
|
||||
host_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
host_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "host" "")
|
||||
fi
|
||||
if [[ -z "$host_default" ]]; then
|
||||
case $i in
|
||||
0) host_default="unraid" ;;
|
||||
1) host_default="fedora" ;;
|
||||
2) host_default="local" ;;
|
||||
*) host_default="unraid" ;;
|
||||
esac
|
||||
fi
|
||||
prompt_field "host" "unraid, fedora, local, or custom" "runner_host" "$host_default"
|
||||
r_host="$PROMPT_RESULT"
|
||||
COLLECTED_DATA["${r_name}:host"]="$r_host"
|
||||
|
||||
# --- data_path (where runner binary + config + data live) ---
|
||||
path_default="$ex_path"
|
||||
# Show resolved SSH details for known hosts
|
||||
case "$r_host" in
|
||||
unraid)
|
||||
printf '%b → SSH: %s@%s:%s%b\n' "$C_DIM" "${UNRAID_SSH_USER:-<not set>}" "${UNRAID_IP:-<not set>}" "${UNRAID_SSH_PORT:-22}" "$C_RESET"
|
||||
if [[ -n "${UNRAID_SSH_KEY:-}" ]]; then
|
||||
printf '%b → SSH key: %s%b\n' "$C_DIM" "${UNRAID_SSH_KEY}" "$C_RESET"
|
||||
fi
|
||||
;;
|
||||
fedora)
|
||||
printf '%b → SSH: %s@%s:%s%b\n' "$C_DIM" "${FEDORA_SSH_USER:-<not set>}" "${FEDORA_IP:-<not set>}" "${FEDORA_SSH_PORT:-22}" "$C_RESET"
|
||||
if [[ -n "${FEDORA_SSH_KEY:-}" ]]; then
|
||||
printf '%b → SSH key: %s%b\n' "$C_DIM" "${FEDORA_SSH_KEY}" "$C_RESET"
|
||||
fi
|
||||
;;
|
||||
local)
|
||||
printf '%b → Runs on this machine (no SSH)%b\n' "$C_DIM" "$C_RESET"
|
||||
;;
|
||||
custom)
|
||||
# Prompt for custom SSH details
|
||||
ex_ssh_host=""
|
||||
ex_ssh_user=""
|
||||
ex_ssh_port=""
|
||||
ex_ssh_key=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
ex_ssh_host=$(ini_get "$RUNNERS_CONF" "$ex_name" "ssh_host" "")
|
||||
ex_ssh_user=$(ini_get "$RUNNERS_CONF" "$ex_name" "ssh_user" "")
|
||||
ex_ssh_port=$(ini_get "$RUNNERS_CONF" "$ex_name" "ssh_port" "22")
|
||||
ex_ssh_key=$(ini_get "$RUNNERS_CONF" "$ex_name" "ssh_key" "")
|
||||
fi
|
||||
prompt_field "ssh_host" "IP address of remote host" "ip" "$ex_ssh_host"
|
||||
COLLECTED_DATA["${r_name}:ssh_host"]="$PROMPT_RESULT"
|
||||
prompt_field "ssh_user" "SSH username" "nonempty" "${ex_ssh_user:-root}"
|
||||
COLLECTED_DATA["${r_name}:ssh_user"]="$PROMPT_RESULT"
|
||||
prompt_field "ssh_port" "SSH port" "port" "${ex_ssh_port:-22}"
|
||||
COLLECTED_DATA["${r_name}:ssh_port"]="$PROMPT_RESULT"
|
||||
prompt_field "ssh_key" "path to SSH key (empty = ssh-agent)" "optional_path" "$ex_ssh_key" "true"
|
||||
COLLECTED_DATA["${r_name}:ssh_key"]="$PROMPT_RESULT"
|
||||
# Adjust total prompts (added 4 custom fields, but we already allocated 10)
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- type ---
|
||||
type_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
type_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "type" "")
|
||||
fi
|
||||
if [[ -z "$type_default" ]]; then
|
||||
if [[ "$r_host" == "local" ]]; then type_default="native"; else type_default="docker"; fi
|
||||
fi
|
||||
prompt_field "type" "docker (Linux) or native (macOS)" "runner_type" "$type_default"
|
||||
r_type="$PROMPT_RESULT"
|
||||
COLLECTED_DATA["${r_name}:type"]="$r_type"
|
||||
|
||||
# --- data_path ---
|
||||
path_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
path_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "data_path" "")
|
||||
fi
|
||||
if [[ -z "$path_default" ]]; then
|
||||
if [[ "$r_type" == "native" ]]; then
|
||||
# shellcheck disable=SC2088 # literal tilde — expanded by manage_runner.sh
|
||||
path_default="~/gitea-runner"
|
||||
path_default="${LOCAL_RUNNER_DATA_PATH:-~/gitea-runner}"
|
||||
else
|
||||
path_default="/mnt/nvme/gitea-runner" # typical Unraid/Fedora NVMe mount
|
||||
path_default="${RUNNER_DEFAULT_DATA_PATH:-/mnt/nvme/gitea-runner}"
|
||||
fi
|
||||
fi
|
||||
prompt_field "data_path" "absolute path for runner data" "runner_path" "$path_default"
|
||||
r_path="$PROMPT_RESULT"
|
||||
COLLECTED_DATA["${r_name}:data_path"]="$PROMPT_RESULT"
|
||||
|
||||
# --- labels (matched by workflow `runs-on:` directives) ---
|
||||
labels_default="$ex_labels"
|
||||
# --- labels ---
|
||||
labels_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
labels_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "labels" "")
|
||||
fi
|
||||
if [[ -z "$labels_default" ]]; then
|
||||
if [[ "$r_type" == "native" ]]; then labels_default="macos"; else labels_default="linux"; fi
|
||||
fi
|
||||
prompt_field "labels" "workflow runs-on labels" "nonempty" "$labels_default"
|
||||
r_labels="$PROMPT_RESULT"
|
||||
prompt_field "labels" "workflow runs-on value" "nonempty" "$labels_default"
|
||||
COLLECTED_DATA["${r_name}:labels"]="$PROMPT_RESULT"
|
||||
|
||||
# Append this runner's values to the collection arrays
|
||||
COLLECTED_NAMES+=("$r_name")
|
||||
COLLECTED_HOSTS+=("$r_host")
|
||||
COLLECTED_USERS+=("$r_user")
|
||||
COLLECTED_PORTS+=("$r_port")
|
||||
COLLECTED_PATHS+=("$r_path")
|
||||
COLLECTED_LABELS+=("$r_labels")
|
||||
COLLECTED_TYPES+=("$r_type")
|
||||
# --- default_image (skip for native) ---
|
||||
if [[ "$r_type" == "docker" ]]; then
|
||||
image_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
image_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "default_image" "")
|
||||
fi
|
||||
if [[ -z "$image_default" ]]; then
|
||||
image_default="${RUNNER_DEFAULT_IMAGE:-catthehacker/ubuntu:act-latest}"
|
||||
fi
|
||||
prompt_field "default_image" "Docker image for job execution" "nonempty" "$image_default"
|
||||
COLLECTED_DATA["${r_name}:default_image"]="$PROMPT_RESULT"
|
||||
else
|
||||
COLLECTED_DATA["${r_name}:default_image"]=""
|
||||
CURRENT_PROMPT=$((CURRENT_PROMPT + 1)) # skip but count for progress
|
||||
fi
|
||||
|
||||
# --- repos ---
|
||||
repos_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
repos_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "repos" "all")
|
||||
fi
|
||||
if [[ -z "$repos_default" ]]; then repos_default="all"; fi
|
||||
# Build hint with known repo names
|
||||
repos_hint="token scope: all"
|
||||
for var in REPO_1_NAME REPO_2_NAME REPO_3_NAME; do
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
repos_hint="${repos_hint}, ${!var}"
|
||||
fi
|
||||
done
|
||||
prompt_field "repos" "$repos_hint" "runner_repos" "$repos_default"
|
||||
COLLECTED_DATA["${r_name}:repos"]="$PROMPT_RESULT"
|
||||
|
||||
# --- capacity ---
|
||||
cap_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
cap_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "capacity" "")
|
||||
fi
|
||||
if [[ -z "$cap_default" ]]; then
|
||||
cap_default="${RUNNER_DEFAULT_CAPACITY:-1}"
|
||||
fi
|
||||
prompt_field "capacity" "max concurrent jobs (>= 1)" "capacity" "$cap_default"
|
||||
COLLECTED_DATA["${r_name}:capacity"]="$PROMPT_RESULT"
|
||||
|
||||
# --- cpu (skip for native) ---
|
||||
if [[ "$r_type" == "docker" ]]; then
|
||||
cpu_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
cpu_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "cpu" "")
|
||||
fi
|
||||
prompt_field "cpu" "Docker CPU limit (e.g. 2.0, empty = no limit)" "docker_cpu" "$cpu_default" "true"
|
||||
COLLECTED_DATA["${r_name}:cpu"]="$PROMPT_RESULT"
|
||||
else
|
||||
COLLECTED_DATA["${r_name}:cpu"]=""
|
||||
CURRENT_PROMPT=$((CURRENT_PROMPT + 1))
|
||||
fi
|
||||
|
||||
# --- memory (skip for native) ---
|
||||
if [[ "$r_type" == "docker" ]]; then
|
||||
mem_default=""
|
||||
if [[ -n "$ex_name" ]]; then
|
||||
mem_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "memory" "")
|
||||
fi
|
||||
prompt_field "memory" "Docker memory limit (e.g. 2g, empty = no limit)" "docker_memory" "$mem_default" "true"
|
||||
COLLECTED_DATA["${r_name}:memory"]="$PROMPT_RESULT"
|
||||
else
|
||||
COLLECTED_DATA["${r_name}:memory"]=""
|
||||
CURRENT_PROMPT=$((CURRENT_PROMPT + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Write runners.conf — overwrites any existing file with the header comment
|
||||
# block and one pipe-delimited line per runner.
|
||||
# Write runners.conf — INI format with header comment block
|
||||
# ---------------------------------------------------------------------------
|
||||
cat > "$RUNNERS_CONF" <<'HEADER'
|
||||
# =============================================================================
|
||||
# runners.conf — Gitea Actions Runner Definitions
|
||||
# runners.conf — Gitea Actions Runner Definitions (INI format)
|
||||
# Generated by setup/configure_runners.sh — edit manually or re-run wizard.
|
||||
# Use manage_runner.sh to add/remove runners dynamically.
|
||||
# See runners.conf.example for field reference.
|
||||
# =============================================================================
|
||||
#
|
||||
# FORMAT: name|ssh_host|ssh_user|ssh_port|data_path|labels|type
|
||||
#
|
||||
# name — Display name in Gitea admin panel
|
||||
# ssh_host — IP address or hostname (for local machine, use "local")
|
||||
# ssh_user — SSH username (ignored if ssh_host is "local")
|
||||
# ssh_port — SSH port (ignored if ssh_host is "local")
|
||||
# data_path — Absolute path for runner binary + data on that machine
|
||||
# labels — Comma-separated runner labels (used in workflow runs-on)
|
||||
# type — "docker" (Linux, runs jobs in containers) or "native" (macOS, runs jobs on host)
|
||||
#
|
||||
|
||||
HEADER
|
||||
|
||||
for ((i = 0; i < ${#COLLECTED_NAMES[@]}; i++)); do
|
||||
printf '%s|%s|%s|%s|%s|%s|%s\n' \
|
||||
"${COLLECTED_NAMES[$i]}" \
|
||||
"${COLLECTED_HOSTS[$i]}" \
|
||||
"${COLLECTED_USERS[$i]}" \
|
||||
"${COLLECTED_PORTS[$i]}" \
|
||||
"${COLLECTED_PATHS[$i]}" \
|
||||
"${COLLECTED_LABELS[$i]}" \
|
||||
"${COLLECTED_TYPES[$i]}" >> "$RUNNERS_CONF"
|
||||
for r_name in "${COLLECTED_NAMES[@]}"; do
|
||||
printf '[%s]\n' "$r_name" >> "$RUNNERS_CONF"
|
||||
|
||||
# Write fields in canonical order
|
||||
for key in host type data_path labels default_image repos capacity cpu memory; do
|
||||
local_val="${COLLECTED_DATA["${r_name}:${key}"]:-}"
|
||||
printf '%-14s= %s\n' "$key" "$local_val" >> "$RUNNERS_CONF"
|
||||
done
|
||||
|
||||
# Write custom SSH fields if host=custom
|
||||
if [[ "${COLLECTED_DATA["${r_name}:host"]:-}" == "custom" ]]; then
|
||||
for key in ssh_host ssh_user ssh_port ssh_key; do
|
||||
local_val="${COLLECTED_DATA["${r_name}:${key}"]:-}"
|
||||
printf '%-14s= %s\n' "$key" "$local_val" >> "$RUNNERS_CONF"
|
||||
done
|
||||
fi
|
||||
|
||||
printf '\n' >> "$RUNNERS_CONF"
|
||||
done
|
||||
|
||||
# ===========================================================================
|
||||
# Summary — green border box + table matching manage_runner.sh list format
|
||||
# Summary — green border box + table
|
||||
# ===========================================================================
|
||||
printf '\n%b╔══════════════════════════════════════════════════════════╗%b\n' "$C_GREEN" "$C_RESET"
|
||||
printf '%b║ Runner Configuration Complete ║%b\n' "$C_GREEN" "$C_RESET"
|
||||
printf '%b╚══════════════════════════════════════════════════════════╝%b\n\n' "$C_GREEN" "$C_RESET"
|
||||
|
||||
printf '%bRunners configured:%b %d\n\n' "$C_BOLD" "$C_RESET" "${#COLLECTED_NAMES[@]}"
|
||||
printf '%-20s %-16s %-10s %-8s %-24s\n' "NAME" "HOST" "LABELS" "TYPE" "DATA PATH"
|
||||
printf '%-20s %-16s %-10s %-8s %-24s\n' "----" "----" "------" "----" "---------"
|
||||
printf '%-20s %-10s %-8s %-10s %-6s %-10s %-24s\n' "NAME" "HOST" "TYPE" "LABELS" "CAP" "REPOS" "DATA PATH"
|
||||
printf '%-20s %-10s %-8s %-10s %-6s %-10s %-24s\n' "----" "----" "----" "------" "---" "-----" "---------"
|
||||
|
||||
for ((i = 0; i < ${#COLLECTED_NAMES[@]}; i++)); do
|
||||
printf '%-20s %-16s %-10s %-8s %-24s\n' \
|
||||
"${COLLECTED_NAMES[$i]}" \
|
||||
"${COLLECTED_HOSTS[$i]}" \
|
||||
"${COLLECTED_LABELS[$i]}" \
|
||||
"${COLLECTED_TYPES[$i]}" \
|
||||
"${COLLECTED_PATHS[$i]}"
|
||||
for r_name in "${COLLECTED_NAMES[@]}"; do
|
||||
printf '%-20s %-10s %-8s %-10s %-6s %-10s %-24s\n' \
|
||||
"$r_name" \
|
||||
"${COLLECTED_DATA["${r_name}:host"]:-}" \
|
||||
"${COLLECTED_DATA["${r_name}:type"]:-}" \
|
||||
"${COLLECTED_DATA["${r_name}:labels"]:-}" \
|
||||
"${COLLECTED_DATA["${r_name}:capacity"]:-}" \
|
||||
"${COLLECTED_DATA["${r_name}:repos"]:-}" \
|
||||
"${COLLECTED_DATA["${r_name}:data_path"]:-}"
|
||||
done
|
||||
|
||||
printf '\n Saved to: %s\n\n' "$RUNNERS_CONF"
|
||||
|
||||
Reference in New Issue
Block a user