From da9f56cce9b9f28599f4353397f7630a8e33d93d Mon Sep 17 00:00:00 2001 From: S Date: Thu, 26 Feb 2026 15:08:37 -0600 Subject: [PATCH] feat: add preflight validation 16 checks: .env exists, runners.conf exists, 24 required vars non-empty, SSH to Unraid/Fedora, Docker + docker-compose on both servers, ports free, DNS resolution, GitHub token valid, GitHub repos exist, Nginx running, Nginx conf dir writable. Runs ALL checks even if earlier ones fail. Exits 0 only if all 16 pass. Co-Authored-By: Claude Opus 4.6 --- preflight.sh | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100755 preflight.sh diff --git a/preflight.sh b/preflight.sh new file mode 100755 index 0000000..323d05f --- /dev/null +++ b/preflight.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================= +# preflight.sh — Validate everything before running migration phases +# Installs nothing. Exits 0 only if ALL checks pass. +# ============================================================================= + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/lib/common.sh" + +log_info "=== Preflight Checks ===" + +PASS_COUNT=0 +FAIL_COUNT=0 + +# --------------------------------------------------------------------------- +# Check helper — runs a check, tracks pass/fail +# --------------------------------------------------------------------------- +check() { + local num="$1" description="$2" + shift 2 + + if "$@" 2>/dev/null; then + log_success "[${num}] ${description}" + PASS_COUNT=$((PASS_COUNT + 1)) + else + log_error "[${num}] FAIL: ${description}" + FAIL_COUNT=$((FAIL_COUNT + 1)) + fi +} + +# --------------------------------------------------------------------------- +# Check 1: .env exists +# --------------------------------------------------------------------------- +check_env_exists() { + [[ -f "${SCRIPT_DIR}/.env" ]] +} +check 1 ".env file exists" check_env_exists +if [[ ! -f "${SCRIPT_DIR}/.env" ]]; then + log_error " → .env not found. Copy .env.example to .env and fill in values." + log_error " → Or run: setup/configure_env.sh" + # Can't continue without .env — run remaining checks but they'll mostly fail +fi + +# --------------------------------------------------------------------------- +# Check 2: runners.conf exists +# --------------------------------------------------------------------------- +check_runners_conf() { + [[ -f "${SCRIPT_DIR}/runners.conf" ]] +} +check 2 "runners.conf file exists" check_runners_conf +if [[ ! -f "${SCRIPT_DIR}/runners.conf" ]]; then + log_error " → runners.conf not found. Copy runners.conf.example to runners.conf." +fi + +# --------------------------------------------------------------------------- +# Load env for remaining checks (may fail if .env missing) +# --------------------------------------------------------------------------- +if [[ -f "${SCRIPT_DIR}/.env" ]]; then + load_env +fi + +# --------------------------------------------------------------------------- +# Check 3: Required .env vars +# --------------------------------------------------------------------------- +REQUIRED_VARS=( + UNRAID_IP UNRAID_SSH_USER UNRAID_GITEA_DATA_PATH + FEDORA_IP FEDORA_SSH_USER FEDORA_GITEA_DATA_PATH + GITEA_ADMIN_USER GITEA_ADMIN_PASSWORD GITEA_ADMIN_EMAIL + GITEA_ORG_NAME GITEA_INSTANCE_NAME + GITEA_DOMAIN GITEA_INTERNAL_URL + GITEA_BACKUP_INTERNAL_URL BACKUP_STORAGE_PATH + GITHUB_USERNAME GITHUB_TOKEN + REPO_1_NAME REPO_2_NAME REPO_3_NAME + GITHUB_MIRROR_TOKEN + NGINX_CONTAINER_NAME NGINX_CONF_PATH SSL_EMAIL +) + +check_required_vars() { + local missing=0 + for var in "${REQUIRED_VARS[@]}"; do + if [[ -z "${!var:-}" ]]; then + log_error " → Missing required var: $var" + missing=1 + fi + done + return $missing +} +check 3 "All required .env vars are set" check_required_vars + +# --------------------------------------------------------------------------- +# Check 4: SSH to Unraid +# --------------------------------------------------------------------------- +check_ssh_unraid() { + ssh_check UNRAID +} +check 4 "SSH to Unraid (${UNRAID_IP:-})" check_ssh_unraid +if [[ $FAIL_COUNT -gt 0 ]] && ! ssh_check UNRAID 2>/dev/null; then + log_error " → Cannot SSH to Unraid. Run setup/unraid.sh or check SSH config." +fi + +# --------------------------------------------------------------------------- +# Check 5: SSH to Fedora +# --------------------------------------------------------------------------- +check_ssh_fedora() { + ssh_check FEDORA +} +check 5 "SSH to Fedora (${FEDORA_IP:-})" check_ssh_fedora +if ! ssh_check FEDORA 2>/dev/null; then + log_error " → Cannot SSH to Fedora. Run setup/fedora.sh or check SSH config." +fi + +# --------------------------------------------------------------------------- +# Check 6: Docker on Unraid +# --------------------------------------------------------------------------- +check_docker_unraid() { + ssh_exec UNRAID "docker --version" &>/dev/null +} +check 6 "Docker available on Unraid" check_docker_unraid +if ! check_docker_unraid 2>/dev/null; then + log_error " → Docker not found on Unraid. Run setup/unraid.sh." +fi + +# --------------------------------------------------------------------------- +# Check 7: Docker on Fedora +# --------------------------------------------------------------------------- +check_docker_fedora() { + ssh_exec FEDORA "docker --version" &>/dev/null +} +check 7 "Docker available on Fedora" check_docker_fedora +if ! check_docker_fedora 2>/dev/null; then + log_error " → Docker not found on Fedora. Run setup/fedora.sh." +fi + +# --------------------------------------------------------------------------- +# Check 8: docker-compose on Unraid +# --------------------------------------------------------------------------- +check_compose_unraid() { + ssh_exec UNRAID "docker compose version" &>/dev/null || ssh_exec UNRAID "docker-compose --version" &>/dev/null +} +check 8 "docker-compose available on Unraid" check_compose_unraid +if ! check_compose_unraid 2>/dev/null; then + log_error " → docker-compose not found on Unraid. Run setup/unraid.sh." +fi + +# --------------------------------------------------------------------------- +# Check 9: docker-compose on Fedora +# --------------------------------------------------------------------------- +check_compose_fedora() { + ssh_exec FEDORA "docker compose version" &>/dev/null || ssh_exec FEDORA "docker-compose --version" &>/dev/null +} +check 9 "docker-compose available on Fedora" check_compose_fedora +if ! check_compose_fedora 2>/dev/null; then + log_error " → docker-compose not found on Fedora. Run setup/fedora.sh." +fi + +# --------------------------------------------------------------------------- +# Check 10: Port free on Unraid +# --------------------------------------------------------------------------- +check_port_unraid() { + local port="${UNRAID_GITEA_PORT:-3000}" + ! ssh_exec UNRAID "ss -tlnp | grep -q ':${port} '" 2>/dev/null +} +check 10 "Port ${UNRAID_GITEA_PORT:-3000} free on Unraid" check_port_unraid +if ! check_port_unraid 2>/dev/null; then + log_error " → Port ${UNRAID_GITEA_PORT:-3000} already in use on Unraid." +fi + +# --------------------------------------------------------------------------- +# Check 11: Port free on Fedora +# --------------------------------------------------------------------------- +check_port_fedora() { + local port="${FEDORA_GITEA_PORT:-3000}" + ! ssh_exec FEDORA "ss -tlnp | grep -q ':${port} '" 2>/dev/null +} +check 11 "Port ${FEDORA_GITEA_PORT:-3000} free on Fedora" check_port_fedora +if ! check_port_fedora 2>/dev/null; then + log_error " → Port ${FEDORA_GITEA_PORT:-3000} already in use on Fedora." +fi + +# --------------------------------------------------------------------------- +# Check 12: DNS resolves +# --------------------------------------------------------------------------- +check_dns() { + local resolved + resolved=$(dig +short "${GITEA_DOMAIN:-}" 2>/dev/null | head -1) + [[ "$resolved" == "${UNRAID_IP:-}" ]] +} +check 12 "DNS: ${GITEA_DOMAIN:-} resolves to ${UNRAID_IP:-}" check_dns +if ! check_dns 2>/dev/null; then + log_error " → ${GITEA_DOMAIN:-GITEA_DOMAIN} does not resolve to ${UNRAID_IP:-UNRAID_IP}." +fi + +# --------------------------------------------------------------------------- +# Check 13: GitHub token valid +# --------------------------------------------------------------------------- +check_github_token() { + [[ -n "${GITHUB_TOKEN:-}" ]] && curl -sf -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/user -o /dev/null +} +check 13 "GitHub token valid" check_github_token +if ! check_github_token 2>/dev/null; then + log_error " → GitHub token invalid. Check GITHUB_TOKEN in .env." +fi + +# --------------------------------------------------------------------------- +# Check 14: GitHub repos exist +# --------------------------------------------------------------------------- +check_github_repos() { + local all_ok=0 + for var in REPO_1_NAME REPO_2_NAME REPO_3_NAME; do + local repo="${!var:-}" + if [[ -z "$repo" ]]; then + continue + fi + if ! curl -sf -H "Authorization: token ${GITHUB_TOKEN:-}" "https://api.github.com/repos/${GITHUB_USERNAME:-}/${repo}" -o /dev/null 2>/dev/null; then + log_error " → GitHub repo ${repo} not found under ${GITHUB_USERNAME:-}" + all_ok=1 + fi + done + return $all_ok +} +check 14 "All GitHub repos exist" check_github_repos + +# --------------------------------------------------------------------------- +# Check 15: Nginx running on Unraid +# --------------------------------------------------------------------------- +check_nginx() { + local status + status=$(ssh_exec UNRAID "docker ps --filter name=${NGINX_CONTAINER_NAME:-nginx} --format '{{.Status}}'" 2>/dev/null) + [[ "$status" == *"Up"* ]] +} +check 15 "Nginx container '${NGINX_CONTAINER_NAME:-}' running on Unraid" check_nginx +if ! check_nginx 2>/dev/null; then + log_error " → Nginx container '${NGINX_CONTAINER_NAME:-}' not running on Unraid." +fi + +# --------------------------------------------------------------------------- +# Check 16: Nginx conf dir writable +# --------------------------------------------------------------------------- +check_nginx_conf() { + ssh_exec UNRAID "test -w '${NGINX_CONF_PATH:-/nonexistent}'" 2>/dev/null +} +check 16 "Nginx config path writable (${NGINX_CONF_PATH:-})" check_nginx_conf +if ! check_nginx_conf 2>/dev/null; then + log_error " → Nginx config path ${NGINX_CONF_PATH:-} not writable on Unraid." +fi + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +printf '\n' +log_info "Results: ${PASS_COUNT} passed, ${FAIL_COUNT} failed (out of 16 checks)" + +if [[ $FAIL_COUNT -gt 0 ]]; then + log_error "Preflight FAILED — fix the issues above before proceeding." + exit 1 +else + log_success "All preflight checks passed. Ready to run migration phases." + exit 0 +fi