feat: add Phase 1 — Gitea on Unraid

- phase1_gitea_unraid.sh: 9-step deploy (dirs, docker-compose, app.ini,
  container start, wait, admin user, API token, save to .env, create org).
  Every step has idempotency check — running twice changes nothing.
- phase1_post_check.sh: 5 independent verification checks
- phase1_teardown.sh: stop container + optionally remove data, with prompts

Also adds inline comments to lib/common.sh and preflight.sh explaining
WHY decisions were made (SSH flags, API tmpfile pattern, port checks, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
S
2026-02-26 15:12:02 -06:00
parent da9f56cce9
commit 63f708e556
5 changed files with 333 additions and 3 deletions

View File

@@ -69,6 +69,9 @@ _project_root() {
cd "$(dirname "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")/.." && pwd
}
# Source .env and export all variables.
# Uses set -a/+a to auto-export every variable defined in the file,
# making them available to child processes (envsubst, ssh, etc.).
load_env() {
local env_file
env_file="$(_project_root)/.env"
@@ -77,10 +80,10 @@ load_env() {
log_error "Copy .env.example to .env and populate values."
return 1
fi
set -a
set -a # auto-export all vars defined below
# shellcheck source=/dev/null
source "$env_file"
set +a
set +a # stop auto-exporting
}
save_env_var() {
@@ -124,10 +127,15 @@ require_vars() {
# SSH
# ---------------------------------------------------------------------------
# Execute a command on a remote host via SSH.
# Uses indirect variable expansion: ssh_exec "UNRAID" "ls" reads
# UNRAID_IP, UNRAID_SSH_USER, UNRAID_SSH_PORT from the environment.
# This pattern avoids passing connection details to every function call.
ssh_exec() {
local host_key="$1"; shift
local cmd="$*"
# Indirect expansion: ${!ip_var} dereferences the variable named by $ip_var
local ip_var="${host_key}_IP"
local user_var="${host_key}_SSH_USER"
local port_var="${host_key}_SSH_PORT"
@@ -141,6 +149,9 @@ ssh_exec() {
return 1
fi
# ConnectTimeout: fail fast if host is unreachable (don't hang for 60s)
# StrictHostKeyChecking=accept-new: auto-accept new hosts but reject changed keys
# BatchMode=yes: never prompt for password (fail if key auth doesn't work)
ssh -o ConnectTimeout=10 \
-o StrictHostKeyChecking=accept-new \
-o BatchMode=yes \
@@ -181,6 +192,10 @@ scp_to() {
# API wrappers — return JSON on stdout, logs go to stderr
# ---------------------------------------------------------------------------
# Internal API call helper. Writes response to a tmpfile so we can separate
# the HTTP status code (via curl -w) from the response body. This ensures
# JSON output goes to stdout and error messages go to stderr — callers can
# pipe JSON through jq without log noise contaminating the output.
_api_call() {
local base_url="$1" token="$2" method="$3" path="$4" data="${5:-}"