diff --git a/setup/configure_runners.sh b/setup/configure_runners.sh index eadfab2..147f5ee 100755 --- a/setup/configure_runners.sh +++ b/setup/configure_runners.sh @@ -4,7 +4,7 @@ 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). +# Persists answers to runners.conf as each prompt is completed. # Defaults are pulled from .env — nothing is hardcoded. # ============================================================================= @@ -32,15 +32,36 @@ fi # If runners.conf already exists and is INI format, we read existing values # to pre-fill prompts when re-running the wizard. # --------------------------------------------------------------------------- +DEFAULTS_CONF="" +cleanup_defaults_conf() { + if [[ -n "$DEFAULTS_CONF" ]] && [[ -f "$DEFAULTS_CONF" ]]; then + rm -f "$DEFAULTS_CONF" + fi +} +trap cleanup_defaults_conf EXIT + EXISTING_SECTIONS=() if [[ -f "$RUNNERS_CONF" ]]; then + # Keep a snapshot for defaults so we can rewrite runners.conf incrementally + # without losing pre-filled values when the wizard is re-run. + DEFAULTS_CONF=$(mktemp) + cp "$RUNNERS_CONF" "$DEFAULTS_CONF" while IFS= read -r sec; do [[ -z "$sec" ]] && continue EXISTING_SECTIONS+=("$sec") - done < <(ini_list_sections "$RUNNERS_CONF") + done < <(ini_list_sections "$DEFAULTS_CONF") fi EXISTING_COUNT=${#EXISTING_SECTIONS[@]} +ini_default_get() { + local section="$1" key="$2" default="${3:-}" + if [[ -z "$DEFAULTS_CONF" ]]; then + printf '%s' "$default" + return 0 + fi + ini_get "$DEFAULTS_CONF" "$section" "$key" "$default" +} + # --------------------------------------------------------------------------- # Validation helpers # --------------------------------------------------------------------------- @@ -180,6 +201,31 @@ prompt_field() { PROMPT_RESULT="$value" } +init_runners_conf() { + cat > "$RUNNERS_CONF" <<'HEADER' +# ============================================================================= +# 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. +# ============================================================================= + +HEADER +} + +ensure_runner_section() { + local section="$1" + if ! grep -Fqx "[$section]" "$RUNNERS_CONF" 2>/dev/null; then + printf '[%s]\n\n' "$section" >> "$RUNNERS_CONF" + fi +} + +save_runner_field() { + local section="$1" key="$2" value="$3" + ini_set "$RUNNERS_CONF" "$section" "$key" "$value" + COLLECTED_DATA["${section}:${key}"]="$value" +} + # =========================================================================== # Main # =========================================================================== @@ -218,12 +264,14 @@ fi TOTAL_PROMPTS=$((runner_count * 10)) # --------------------------------------------------------------------------- -# Collect runner definitions +# Configure runner definitions # --------------------------------------------------------------------------- -# Storage arrays for collected INI data +# Keep arrays for end-of-run summary; values are persisted immediately. COLLECTED_NAMES=() declare -A COLLECTED_DATA # COLLECTED_DATA["name:key"] = value +init_runners_conf + 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" @@ -244,11 +292,12 @@ for ((i = 0; i < runner_count; i++)); do prompt_field "name" "runner display name in Gitea" "runner_name" "$name_default" r_name="$PROMPT_RESULT" COLLECTED_NAMES+=("$r_name") + ensure_runner_section "$r_name" # --- host --- host_default="" if [[ -n "$ex_name" ]]; then - host_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "host" "") + host_default=$(ini_default_get "$ex_name" "host" "") fi if [[ -z "$host_default" ]]; then case $i in @@ -260,7 +309,7 @@ for ((i = 0; i < runner_count; i++)); do fi prompt_field "host" "unraid, fedora, local, or custom" "runner_host" "$host_default" r_host="$PROMPT_RESULT" - COLLECTED_DATA["${r_name}:host"]="$r_host" + save_runner_field "$r_name" "host" "$r_host" # Show resolved SSH details for known hosts case "$r_host" in @@ -286,19 +335,19 @@ for ((i = 0; i < runner_count; i++)); do 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" "") + ex_ssh_host=$(ini_default_get "$ex_name" "ssh_host" "") + ex_ssh_user=$(ini_default_get "$ex_name" "ssh_user" "") + ex_ssh_port=$(ini_default_get "$ex_name" "ssh_port" "22") + ex_ssh_key=$(ini_default_get "$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" + save_runner_field "$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" + save_runner_field "$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" + save_runner_field "$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" + save_runner_field "$r_name" "ssh_key" "$PROMPT_RESULT" # Adjust total prompts (added 4 custom fields, but we already allocated 10) ;; esac @@ -306,19 +355,19 @@ for ((i = 0; i < runner_count; i++)); do # --- type --- type_default="" if [[ -n "$ex_name" ]]; then - type_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "type" "") + type_default=$(ini_default_get "$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" + save_runner_field "$r_name" "type" "$r_type" # --- data_path --- path_default="" if [[ -n "$ex_name" ]]; then - path_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "data_path" "") + path_default=$(ini_default_get "$ex_name" "data_path" "") fi if [[ -z "$path_default" ]]; then if [[ "$r_type" == "native" ]]; then @@ -328,39 +377,39 @@ for ((i = 0; i < runner_count; i++)); do fi fi prompt_field "data_path" "absolute path for runner data" "runner_path" "$path_default" - COLLECTED_DATA["${r_name}:data_path"]="$PROMPT_RESULT" + save_runner_field "$r_name" "data_path" "$PROMPT_RESULT" # --- labels --- labels_default="" if [[ -n "$ex_name" ]]; then - labels_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "labels" "") + labels_default=$(ini_default_get "$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 value" "nonempty" "$labels_default" - COLLECTED_DATA["${r_name}:labels"]="$PROMPT_RESULT" + save_runner_field "$r_name" "labels" "$PROMPT_RESULT" # --- 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" "") + image_default=$(ini_default_get "$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" + save_runner_field "$r_name" "default_image" "$PROMPT_RESULT" else - COLLECTED_DATA["${r_name}:default_image"]="" + save_runner_field "$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") + repos_default=$(ini_default_get "$ex_name" "repos" "all") fi if [[ -z "$repos_default" ]]; then repos_default="all"; fi # Build hint with known repo names @@ -371,29 +420,29 @@ for ((i = 0; i < runner_count; i++)); do fi done prompt_field "repos" "$repos_hint" "runner_repos" "$repos_default" - COLLECTED_DATA["${r_name}:repos"]="$PROMPT_RESULT" + save_runner_field "$r_name" "repos" "$PROMPT_RESULT" # --- capacity --- cap_default="" if [[ -n "$ex_name" ]]; then - cap_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "capacity" "") + cap_default=$(ini_default_get "$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" + save_runner_field "$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" "") + cpu_default=$(ini_default_get "$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" + save_runner_field "$r_name" "cpu" "$PROMPT_RESULT" else - COLLECTED_DATA["${r_name}:cpu"]="" + save_runner_field "$r_name" "cpu" "" CURRENT_PROMPT=$((CURRENT_PROMPT + 1)) fi @@ -401,49 +450,16 @@ for ((i = 0; i < runner_count; i++)); do if [[ "$r_type" == "docker" ]]; then mem_default="" if [[ -n "$ex_name" ]]; then - mem_default=$(ini_get "$RUNNERS_CONF" "$ex_name" "memory" "") + mem_default=$(ini_default_get "$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" + save_runner_field "$r_name" "memory" "$PROMPT_RESULT" else - COLLECTED_DATA["${r_name}:memory"]="" + save_runner_field "$r_name" "memory" "" CURRENT_PROMPT=$((CURRENT_PROMPT + 1)) fi done -# --------------------------------------------------------------------------- -# Write runners.conf — INI format with header comment block -# --------------------------------------------------------------------------- -cat > "$RUNNERS_CONF" <<'HEADER' -# ============================================================================= -# 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. -# ============================================================================= - -HEADER - -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 # ===========================================================================