Files
gitea-migration/post-migration-check.sh

664 lines
24 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# =============================================================================
# post-migration-check.sh — Read-only infrastructure state check
# Probes live infrastructure and reports the state of every migration phase.
# No mutations — purely diagnostic. Safe to run at any time.
#
# Three states:
# [DONE] — already exists/running, phase would skip this step
# [TODO] — not done yet, phase would execute this step
# [ERROR] — something is broken (unreachable, invalid token, misconfigured)
#
# Only [ERROR] means something is wrong. [TODO] is normal for phases not yet run.
# Exit code: 0 if no errors, 1 if any [ERROR] found.
# =============================================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "${SCRIPT_DIR}/lib/common.sh"
load_env
# ---------------------------------------------------------------------------
# Counters
# ---------------------------------------------------------------------------
TOTAL_DONE=0
TOTAL_TODO=0
TOTAL_ERROR=0
# Per-phase counters (parallel arrays indexed by phase number 0-9, where 0=connectivity)
declare -a PHASE_DONE PHASE_TODO PHASE_ERROR PHASE_TOTAL
for i in $(seq 0 9); do
PHASE_DONE[$i]=0
PHASE_TODO[$i]=0
PHASE_ERROR[$i]=0
PHASE_TOTAL[$i]=0
done
# Current phase being checked (set by section_header)
CURRENT_PHASE=0
# ---------------------------------------------------------------------------
# Output helpers
# ---------------------------------------------------------------------------
check_done() {
local msg="$1"
printf ' %b[DONE]%b %s\n' "$_C_GREEN" "$_C_RESET" "$msg" >&2
TOTAL_DONE=$((TOTAL_DONE + 1))
PHASE_DONE[$CURRENT_PHASE]=$((PHASE_DONE[CURRENT_PHASE] + 1))
PHASE_TOTAL[$CURRENT_PHASE]=$((PHASE_TOTAL[CURRENT_PHASE] + 1))
}
check_todo() {
local msg="$1"
printf ' %b[TODO]%b %s\n' "$_C_YELLOW" "$_C_RESET" "$msg" >&2
TOTAL_TODO=$((TOTAL_TODO + 1))
PHASE_TODO[$CURRENT_PHASE]=$((PHASE_TODO[CURRENT_PHASE] + 1))
PHASE_TOTAL[$CURRENT_PHASE]=$((PHASE_TOTAL[CURRENT_PHASE] + 1))
}
check_error() {
local msg="$1"
printf ' %b[ERROR]%b %s\n' "$_C_RED" "$_C_RESET" "$msg" >&2
TOTAL_ERROR=$((TOTAL_ERROR + 1))
PHASE_ERROR[$CURRENT_PHASE]=$((PHASE_ERROR[CURRENT_PHASE] + 1))
PHASE_TOTAL[$CURRENT_PHASE]=$((PHASE_TOTAL[CURRENT_PHASE] + 1))
}
section_header() {
local phase_num="$1" title="$2"
CURRENT_PHASE="$phase_num"
printf '\n%b--- %s ---%b\n' "$_C_BOLD" "$title" "$_C_RESET" >&2
}
# ---------------------------------------------------------------------------
# Connectivity gates — track which hosts are reachable
# Later sections skip checks for unreachable hosts rather than spamming errors.
# ---------------------------------------------------------------------------
UNRAID_SSH_OK=false
FEDORA_SSH_OK=false
UNRAID_DOCKER_OK=false
FEDORA_DOCKER_OK=false
GITHUB_API_OK=false
GITEA_API_OK=false
GITEA_BACKUP_API_OK=false
printf '\n%b=== Post-Migration Check ===%b\n' "$_C_BOLD" "$_C_RESET" >&2
# ---------------------------------------------------------------------------
# Connectivity
# ---------------------------------------------------------------------------
section_header 0 "Connectivity"
# SSH to Unraid
if ssh_exec UNRAID "true" 2>/dev/null; then
check_done "SSH to Unraid (${UNRAID_IP:-<not set>})"
UNRAID_SSH_OK=true
else
check_error "SSH to Unraid (${UNRAID_IP:-<not set>}) — connection failed"
fi
# SSH to Fedora
if ssh_exec FEDORA "true" 2>/dev/null; then
check_done "SSH to Fedora (${FEDORA_IP:-<not set>})"
FEDORA_SSH_OK=true
else
check_error "SSH to Fedora (${FEDORA_IP:-<not set>}) — connection failed"
fi
# Docker on Unraid
if $UNRAID_SSH_OK; then
if ssh_exec UNRAID "docker info" &>/dev/null; then
check_done "Docker daemon on Unraid"
UNRAID_DOCKER_OK=true
else
check_error "Docker daemon on Unraid — not running"
fi
fi
# Docker on Fedora
if $FEDORA_SSH_OK; then
if ssh_exec FEDORA "docker info" &>/dev/null; then
check_done "Docker daemon on Fedora"
FEDORA_DOCKER_OK=true
else
check_error "Docker daemon on Fedora — not running"
fi
fi
# GitHub API
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
GH_USER_RESPONSE=$(curl -sf -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/user" 2>/dev/null || echo "")
if [[ -n "$GH_USER_RESPONSE" ]]; then
GH_ACTUAL_USER=$(printf '%s' "$GH_USER_RESPONSE" | jq -r '.login // ""' 2>/dev/null)
if [[ "$GH_ACTUAL_USER" == "${GITHUB_USERNAME:-}" ]]; then
check_done "GitHub API — token valid for ${GITHUB_USERNAME}"
GITHUB_API_OK=true
elif [[ -n "$GH_ACTUAL_USER" ]]; then
check_error "GitHub API — token belongs to '${GH_ACTUAL_USER}', expected '${GITHUB_USERNAME:-}'"
else
check_error "GitHub API — token returned unexpected response"
fi
else
check_error "GitHub API — request failed (bad token or no internet)"
fi
else
check_error "GitHub API — GITHUB_TOKEN not set"
fi
# ---------------------------------------------------------------------------
# Phase 1: Gitea on Unraid
# ---------------------------------------------------------------------------
section_header 1 "Phase 1: Gitea on Unraid"
if ! $UNRAID_DOCKER_OK; then
check_error "Cannot check — Unraid Docker unreachable"
else
# Docker network
if ssh_exec UNRAID "docker network inspect br0" &>/dev/null; then
check_done "Docker network br0 exists"
else
check_todo "Docker network br0 — would create"
fi
# docker-compose.yml
COMPOSE_DIR="${UNRAID_COMPOSE_DIR:-}/gitea"
if ssh_exec UNRAID "test -f '${COMPOSE_DIR}/docker-compose.yml'" 2>/dev/null; then
check_done "docker-compose.yml deployed at ${COMPOSE_DIR}"
else
check_todo "docker-compose.yml — would deploy to ${COMPOSE_DIR}"
fi
# app.ini
DATA_PATH="${UNRAID_GITEA_DATA_PATH:-}"
if ssh_exec UNRAID "test -f '${DATA_PATH}/config/app.ini'" 2>/dev/null; then
check_done "app.ini deployed"
else
check_todo "app.ini — would deploy"
fi
# Gitea container running + healthy
CONTAINER_STATUS=$(ssh_exec UNRAID "docker ps --filter name='^/gitea$' --format '{{.Status}}'" 2>/dev/null || true)
if [[ "$CONTAINER_STATUS" == *"Up"* ]]; then
if [[ "$CONTAINER_STATUS" == *"healthy"* ]]; then
check_done "Gitea container running (healthy)"
else
check_done "Gitea container running (${CONTAINER_STATUS})"
fi
else
check_todo "Gitea container — would start"
fi
# Gitea HTTP responds
if [[ -n "${GITEA_INTERNAL_URL:-}" ]]; then
VERSION_RESPONSE=$(curl -sf "${GITEA_INTERNAL_URL}/api/v1/version" 2>/dev/null || echo "")
if [[ -n "$VERSION_RESPONSE" ]]; then
ACTUAL_VERSION=$(printf '%s' "$VERSION_RESPONSE" | jq -r '.version // ""' 2>/dev/null)
if [[ -n "$ACTUAL_VERSION" ]]; then
check_done "Gitea API responds (v${ACTUAL_VERSION})"
GITEA_API_OK=true
else
check_error "Gitea API — unexpected response format"
fi
else
check_todo "Gitea API — not responding (container not started?)"
fi
fi
# Admin auth
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_USER:-}" ]] && [[ -n "${GITEA_ADMIN_PASSWORD:-}" ]]; then
ADMIN_RESPONSE=$(curl -sf -u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASSWORD}" \
"${GITEA_INTERNAL_URL}/api/v1/user" 2>/dev/null || echo "")
if [[ -n "$ADMIN_RESPONSE" ]]; then
ADMIN_LOGIN=$(printf '%s' "$ADMIN_RESPONSE" | jq -r '.login // ""' 2>/dev/null)
if [[ "$ADMIN_LOGIN" == "${GITEA_ADMIN_USER}" ]]; then
check_done "Admin user '${GITEA_ADMIN_USER}' credentials valid"
else
check_error "Admin auth — returned user '${ADMIN_LOGIN}', expected '${GITEA_ADMIN_USER}'"
fi
else
check_todo "Admin user — would create"
fi
fi
# API token
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
TOKEN_RESPONSE=$(curl -sf -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \
"${GITEA_INTERNAL_URL}/api/v1/user" 2>/dev/null || echo "")
if [[ -n "$TOKEN_RESPONSE" ]]; then
TOKEN_USER=$(printf '%s' "$TOKEN_RESPONSE" | jq -r '.login // ""' 2>/dev/null)
if [[ "$TOKEN_USER" == "${GITEA_ADMIN_USER:-}" ]]; then
check_done "API token valid for '${GITEA_ADMIN_USER}'"
else
check_error "API token — returned user '${TOKEN_USER}', expected '${GITEA_ADMIN_USER:-}'"
fi
else
check_error "API token — rejected by Gitea"
fi
elif $GITEA_API_OK; then
check_todo "API token — would generate"
fi
# Organization
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
if gitea_api GET "/orgs/${GITEA_ORG_NAME:-}" >/dev/null 2>&1; then
check_done "Organization '${GITEA_ORG_NAME}' exists"
else
check_todo "Organization '${GITEA_ORG_NAME}' — would create"
fi
fi
fi
# ---------------------------------------------------------------------------
# Phase 2: Gitea on Fedora
# ---------------------------------------------------------------------------
section_header 2 "Phase 2: Gitea on Fedora"
if ! $FEDORA_DOCKER_OK; then
check_error "Cannot check — Fedora Docker unreachable"
else
# Docker network
FEDORA_NETWORK="${FEDORA_MACVLAN_PARENT:-gitea_net}"
if ssh_exec FEDORA "docker network inspect gitea_net" &>/dev/null; then
check_done "Docker network gitea_net exists"
else
check_todo "Docker network gitea_net — would create"
fi
# docker-compose.yml
FEDORA_COMPOSE="${FEDORA_COMPOSE_DIR:-}/gitea"
if ssh_exec FEDORA "test -f '${FEDORA_COMPOSE}/docker-compose.yml'" 2>/dev/null; then
check_done "docker-compose.yml deployed at ${FEDORA_COMPOSE}"
else
check_todo "docker-compose.yml — would deploy to ${FEDORA_COMPOSE}"
fi
# app.ini
FEDORA_DATA="${FEDORA_GITEA_DATA_PATH:-}"
if ssh_exec FEDORA "test -f '${FEDORA_DATA}/config/app.ini'" 2>/dev/null; then
check_done "app.ini deployed"
else
check_todo "app.ini — would deploy"
fi
# Gitea container running + healthy
FEDORA_CONTAINER=$(ssh_exec FEDORA "docker ps --filter name='^/gitea$' --format '{{.Status}}'" 2>/dev/null || true)
if [[ "$FEDORA_CONTAINER" == *"Up"* ]]; then
if [[ "$FEDORA_CONTAINER" == *"healthy"* ]]; then
check_done "Gitea container running (healthy)"
else
check_done "Gitea container running (${FEDORA_CONTAINER})"
fi
else
check_todo "Gitea container — would start"
fi
# Gitea HTTP responds
if [[ -n "${GITEA_BACKUP_INTERNAL_URL:-}" ]]; then
BACKUP_VERSION=$(curl -sf "${GITEA_BACKUP_INTERNAL_URL}/api/v1/version" 2>/dev/null || echo "")
if [[ -n "$BACKUP_VERSION" ]]; then
BACKUP_VER=$(printf '%s' "$BACKUP_VERSION" | jq -r '.version // ""' 2>/dev/null)
check_done "Gitea backup API responds (v${BACKUP_VER})"
GITEA_BACKUP_API_OK=true
else
check_todo "Gitea backup API — not responding"
fi
fi
# Backup API token
if $GITEA_BACKUP_API_OK && [[ -n "${GITEA_BACKUP_ADMIN_TOKEN:-}" ]]; then
BACKUP_TOKEN_RESP=$(curl -sf -H "Authorization: token ${GITEA_BACKUP_ADMIN_TOKEN}" \
"${GITEA_BACKUP_INTERNAL_URL}/api/v1/user" 2>/dev/null || echo "")
if [[ -n "$BACKUP_TOKEN_RESP" ]]; then
check_done "Backup API token valid"
else
check_error "Backup API token — rejected by Gitea"
fi
elif $GITEA_BACKUP_API_OK; then
check_todo "Backup API token — would generate"
fi
# Cross-host connectivity: Fedora → Unraid
if $FEDORA_SSH_OK && [[ -n "${UNRAID_GITEA_IP:-}" ]]; then
if ssh_exec FEDORA "curl -sf -o /dev/null http://${UNRAID_GITEA_IP}:3000/api/v1/version" 2>/dev/null; then
check_done "Fedora can reach Unraid Gitea (${UNRAID_GITEA_IP}:3000)"
else
if $GITEA_API_OK; then
check_error "Fedora cannot reach Unraid Gitea at ${UNRAID_GITEA_IP}:3000"
else
check_todo "Fedora → Unraid connectivity (Unraid Gitea not running)"
fi
fi
fi
fi
# ---------------------------------------------------------------------------
# Phase 3: Runners
# ---------------------------------------------------------------------------
section_header 3 "Phase 3: Runners"
RUNNERS_CONF="$(_project_root)/runners.conf"
if [[ -f "$RUNNERS_CONF" ]]; then
RUNNER_COUNT=$(ini_list_sections "$RUNNERS_CONF" | wc -l | xargs)
check_done "runners.conf exists (${RUNNER_COUNT} runner(s) defined)"
else
check_todo "runners.conf — not found"
fi
# Registration token
if [[ -n "${GITEA_RUNNER_REGISTRATION_TOKEN:-}" ]]; then
check_done "Runner registration token in .env"
else
if $GITEA_API_OK; then
check_todo "Runner registration token — would fetch from Gitea"
else
check_todo "Runner registration token — Gitea not available to fetch"
fi
fi
# Check each runner's status via Gitea admin API
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]] && [[ -f "$RUNNERS_CONF" ]]; then
# Get all registered runners from Gitea
API_RUNNERS=$(gitea_api GET "/admin/runners" 2>/dev/null || echo "[]")
while IFS= read -r runner_name; do
[[ -z "$runner_name" ]] && continue
RUNNER_HOST=$(ini_get "$RUNNERS_CONF" "$runner_name" "host" "")
# Search for this runner in API response
RUNNER_STATUS=$(printf '%s' "$API_RUNNERS" | jq -r --arg n "$runner_name" \
'.[] | select(.name == $n) | .status // "unknown"' 2>/dev/null || echo "")
if [[ -z "$RUNNER_STATUS" ]]; then
# Try matching by label substring if exact name match fails
check_todo "Runner '${runner_name}' (${RUNNER_HOST:-local}) — not registered"
elif [[ "$RUNNER_STATUS" == "online" ]]; then
check_done "Runner '${runner_name}' (${RUNNER_HOST:-local}) — online"
else
check_error "Runner '${runner_name}' (${RUNNER_HOST:-local}) — ${RUNNER_STATUS}"
fi
done < <(ini_list_sections "$RUNNERS_CONF")
fi
# ---------------------------------------------------------------------------
# Phase 4: Migrate Repos
# ---------------------------------------------------------------------------
section_header 4 "Phase 4: Migrate Repos"
read -ra REPOS <<< "${REPO_NAMES:-}"
if [[ ${#REPOS[@]} -eq 0 ]]; then
check_error "REPO_NAMES is empty — no repos to migrate"
else
for repo in "${REPOS[@]}"; do
# GitHub source accessible
if $GITHUB_API_OK; then
GH_REPO_RESP=$(curl -sf -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null || echo "")
if [[ -n "$GH_REPO_RESP" ]]; then
check_done "GitHub source: ${GITHUB_USERNAME}/${repo} accessible"
else
check_error "GitHub source: ${GITHUB_USERNAME}/${repo} — not found or no access"
fi
fi
# Gitea primary
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
GITEA_REPO_RESP=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}" 2>/dev/null || echo "")
if [[ -n "$GITEA_REPO_RESP" ]]; then
check_done "Gitea primary: ${GITEA_ORG_NAME}/${repo} exists"
else
check_todo "Gitea primary: ${GITEA_ORG_NAME}/${repo} — would migrate"
fi
fi
# Fedora mirror
if $GITEA_BACKUP_API_OK && [[ -n "${GITEA_BACKUP_ADMIN_TOKEN:-}" ]]; then
BACKUP_REPO_RESP=$(gitea_backup_api GET "/repos/${GITEA_ADMIN_USER}/${repo}" 2>/dev/null || echo "")
if [[ -n "$BACKUP_REPO_RESP" ]]; then
IS_MIRROR=$(printf '%s' "$BACKUP_REPO_RESP" | jq -r '.mirror // false' 2>/dev/null)
if [[ "$IS_MIRROR" == "true" ]]; then
check_done "Fedora mirror: ${GITEA_ADMIN_USER}/${repo} (pull mirror active)"
else
check_done "Fedora mirror: ${GITEA_ADMIN_USER}/${repo} exists (not a mirror)"
fi
else
check_todo "Fedora mirror: ${GITEA_ADMIN_USER}/${repo} — would create"
fi
fi
done
fi
# ---------------------------------------------------------------------------
# Phase 5: Migrate Pipelines
# ---------------------------------------------------------------------------
section_header 5 "Phase 5: Migrate Pipelines"
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
for repo in "${REPOS[@]}"; do
WORKFLOWS_RESP=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}/contents/.gitea/workflows" 2>/dev/null || echo "")
if [[ -n "$WORKFLOWS_RESP" ]] && [[ "$WORKFLOWS_RESP" != "null" ]]; then
WORKFLOW_COUNT=$(printf '%s' "$WORKFLOWS_RESP" | jq 'length' 2>/dev/null || echo 0)
check_done "${repo}: .gitea/workflows/ exists (${WORKFLOW_COUNT} file(s))"
else
# Check if source repo has GitHub Actions workflows
if $GITHUB_API_OK; then
GH_WORKFLOWS=$(curl -sf -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${GITHUB_USERNAME}/${repo}/contents/.github/workflows" 2>/dev/null || echo "")
if [[ -n "$GH_WORKFLOWS" ]] && [[ "$GH_WORKFLOWS" != *"Not Found"* ]]; then
check_todo "${repo}: has GitHub workflows — would migrate"
else
check_done "${repo}: no workflows in source — nothing to migrate"
fi
else
check_todo "${repo}: .gitea/workflows/ — would check"
fi
fi
done
else
check_todo "Pipeline checks — Gitea API not available"
fi
# ---------------------------------------------------------------------------
# Phase 6: GitHub Mirrors
# ---------------------------------------------------------------------------
section_header 6 "Phase 6: GitHub Mirrors"
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
for repo in "${REPOS[@]}"; do
# Push mirror configured
MIRROR_RESP=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}/push_mirrors" 2>/dev/null || echo "[]")
MIRROR_COUNT=$(printf '%s' "$MIRROR_RESP" | jq 'length' 2>/dev/null || echo 0)
if [[ "$MIRROR_COUNT" -gt 0 ]]; then
check_done "${repo}: push mirror configured"
else
check_todo "${repo}: push mirror — would configure"
fi
# GitHub Actions disabled
if $GITHUB_API_OK; then
ACTIONS_RESP=$(curl -sf -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${GITHUB_USERNAME}/${repo}/actions/permissions" 2>/dev/null || echo "")
if [[ -n "$ACTIONS_RESP" ]]; then
ACTIONS_ENABLED=$(printf '%s' "$ACTIONS_RESP" | jq -r '.enabled // true' 2>/dev/null)
if [[ "$ACTIONS_ENABLED" == "false" ]]; then
check_done "${repo}: GitHub Actions disabled"
else
check_todo "${repo}: GitHub Actions — would disable"
fi
fi
fi
done
else
check_todo "Mirror checks — Gitea API not available"
fi
# ---------------------------------------------------------------------------
# Phase 7: Branch Protection
# ---------------------------------------------------------------------------
section_header 7 "Phase 7: Branch Protection"
BRANCH="${PROTECTED_BRANCH:-main}"
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
for repo in "${REPOS[@]}"; do
BP_RESP=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}/branch_protections/${BRANCH}" 2>/dev/null || echo "")
if [[ -n "$BP_RESP" ]]; then
APPROVALS=$(printf '%s' "$BP_RESP" | jq -r '.required_approvals // 0' 2>/dev/null)
check_done "${repo}: branch protection on '${BRANCH}' (${APPROVALS} approval(s) required)"
else
check_todo "${repo}: branch protection on '${BRANCH}' — would create"
fi
done
else
check_todo "Branch protection checks — Gitea API not available"
fi
# ---------------------------------------------------------------------------
# Phase 8: Cutover (HTTPS + GitHub mirror marking)
# ---------------------------------------------------------------------------
section_header 8 "Phase 8: Cutover"
# DNS resolution
if command -v dig &>/dev/null; then
DNS_RESULT=$(dig +short "${GITEA_DOMAIN:-}" 2>/dev/null | head -1)
elif command -v host &>/dev/null; then
DNS_RESULT=$(host "${GITEA_DOMAIN:-}" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
else
DNS_RESULT=""
fi
if [[ -n "$DNS_RESULT" ]]; then
check_done "DNS: ${GITEA_DOMAIN} resolves to ${DNS_RESULT}"
else
check_todo "DNS: ${GITEA_DOMAIN} — does not resolve"
fi
# Caddy container
if $UNRAID_DOCKER_OK; then
CADDY_STATUS=$(ssh_exec UNRAID "docker ps --filter name=caddy --format '{{.Status}}'" 2>/dev/null || true)
if [[ "$CADDY_STATUS" == *"Up"* ]]; then
check_done "Caddy container running"
else
check_todo "Caddy container — would start"
fi
fi
# HTTPS end-to-end
HTTPS_RESP=$(curl -sf -o /dev/null -w "%{http_code}" "https://${GITEA_DOMAIN:-}/api/v1/version" 2>/dev/null || echo "000")
if [[ "$HTTPS_RESP" == "200" ]]; then
check_done "HTTPS end-to-end: https://${GITEA_DOMAIN} works"
else
check_todo "HTTPS: https://${GITEA_DOMAIN} — not responding (HTTP ${HTTPS_RESP})"
fi
# TLS certificate validity
TLS_INFO=$(curl -vI "https://${GITEA_DOMAIN:-}/" 2>&1 || true)
if printf '%s' "$TLS_INFO" | grep -q "SSL certificate verify ok" 2>/dev/null; then
check_done "TLS certificate valid"
elif printf '%s' "$TLS_INFO" | grep -q "SSL certificate" 2>/dev/null; then
check_error "TLS certificate — verification failed"
else
if [[ "$HTTPS_RESP" == "200" ]]; then
check_done "TLS certificate present (HTTPS working)"
else
check_todo "TLS certificate — HTTPS not active yet"
fi
fi
# Cloudflare token (if applicable)
if [[ "${TLS_MODE:-}" == "cloudflare" ]] && [[ -n "${CLOUDFLARE_API_TOKEN:-}" ]]; then
CF_VERIFY=$(curl -sf -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
"https://api.cloudflare.com/client/v4/user/tokens/verify" 2>/dev/null || echo "")
if printf '%s' "$CF_VERIFY" | jq -e '.success == true' &>/dev/null; then
check_done "Cloudflare API token valid"
else
check_error "Cloudflare API token — verification failed"
fi
elif [[ "${TLS_MODE:-}" == "cloudflare" ]]; then
check_error "Cloudflare API token — CLOUDFLARE_API_TOKEN not set"
fi
# GitHub repos marked as mirror
if $GITHUB_API_OK; then
for repo in "${REPOS[@]}"; do
GH_DESC=$(curl -sf -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null \
| jq -r '.description // ""' 2>/dev/null || echo "")
if [[ "$GH_DESC" == "[MIRROR]"* ]]; then
check_done "${repo}: GitHub description marked as [MIRROR]"
else
check_todo "${repo}: GitHub description — would mark as [MIRROR]"
fi
done
fi
# ---------------------------------------------------------------------------
# Phase 9: Security
# ---------------------------------------------------------------------------
section_header 9 "Phase 9: Security"
if $GITEA_API_OK && [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
for repo in "${REPOS[@]}"; do
SEC_RESP=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}/contents/.gitea/workflows/security-scan.yml" 2>/dev/null || echo "")
if [[ -n "$SEC_RESP" ]] && [[ "$SEC_RESP" != "null" ]]; then
check_done "${repo}: security-scan.yml exists"
else
check_todo "${repo}: security-scan.yml — would deploy"
fi
done
else
check_todo "Security checks — Gitea API not available"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
PHASE_NAMES=(
"Connectivity"
"Phase 1: Gitea on Unraid"
"Phase 2: Gitea on Fedora"
"Phase 3: Runners"
"Phase 4: Migrate Repos"
"Phase 5: Migrate Pipelines"
"Phase 6: GitHub Mirrors"
"Phase 7: Branch Protection"
"Phase 8: Cutover"
"Phase 9: Security"
)
printf '\n%b=== Summary ===%b\n' "$_C_BOLD" "$_C_RESET" >&2
printf ' %s error(s), %s todo(s), %s done\n\n' "$TOTAL_ERROR" "$TOTAL_TODO" "$TOTAL_DONE" >&2
for i in $(seq 0 9); do
DONE="${PHASE_DONE[$i]}"
TODO="${PHASE_TODO[$i]}"
ERRORS="${PHASE_ERROR[$i]}"
TOTAL="${PHASE_TOTAL[$i]}"
NAME="${PHASE_NAMES[$i]}"
if [[ "$TOTAL" -eq 0 ]]; then
continue
fi
if [[ "$ERRORS" -gt 0 ]]; then
printf ' %b%-30s%b %s/%s done, %s error(s)\n' "$_C_RED" "$NAME" "$_C_RESET" "$DONE" "$TOTAL" "$ERRORS" >&2
elif [[ "$TODO" -gt 0 ]]; then
printf ' %b%-30s%b %s/%s done, %s todo\n' "$_C_YELLOW" "$NAME" "$_C_RESET" "$DONE" "$TOTAL" "$TODO" >&2
else
printf ' %b%-30s%b %s/%s done\n' "$_C_GREEN" "$NAME" "$_C_RESET" "$DONE" "$TOTAL" >&2
fi
done
printf '\n' >&2
if [[ "$TOTAL_ERROR" -gt 0 ]]; then
log_error "${TOTAL_ERROR} error(s) found — see above for details"
exit 1
else
log_success "No errors found"
exit 0
fi