240 lines
8.7 KiB
Bash
Executable File
240 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# =============================================================================
|
|
# phase8_teardown.sh — Reverse the cutover: remove HTTPS, restore GitHub repos
|
|
# Steps:
|
|
# 1. Stop + remove Caddy container and compose file
|
|
# 2. Optionally remove Caddy data (certs, config)
|
|
# 3. Restore GitHub repo settings from the saved Phase 8 state snapshot
|
|
# =============================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
source "${SCRIPT_DIR}/lib/common.sh"
|
|
|
|
# Parse arguments
|
|
AUTO_YES=false
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--yes|-y) AUTO_YES=true ;;
|
|
--help|-h)
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Options:
|
|
--yes, -y Skip all confirmation prompts
|
|
--help, -h Show this help
|
|
EOF
|
|
exit 0
|
|
;;
|
|
*)
|
|
log_error "Unknown argument: $arg"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
confirm_action() {
|
|
local prompt="$1"
|
|
if [[ "$AUTO_YES" == "true" ]]; then
|
|
log_info "Auto-confirmed (--yes): ${prompt}"
|
|
return 0
|
|
fi
|
|
printf '%s' "$prompt"
|
|
read -r confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]]
|
|
}
|
|
|
|
load_env
|
|
require_vars UNRAID_IP UNRAID_SSH_USER UNRAID_COMPOSE_DIR \
|
|
GITEA_DOMAIN CADDY_DATA_PATH \
|
|
GITHUB_USERNAME GITHUB_TOKEN \
|
|
REPO_NAMES
|
|
|
|
log_warn "=== Phase 8 Teardown: Cutover ==="
|
|
|
|
read -ra REPOS <<< "$REPO_NAMES"
|
|
PHASE8_STATE_FILE="$(_project_root)/.manifests/phase8_github_repo_state.json"
|
|
CADDY_COMPOSE_DIR="${UNRAID_COMPOSE_DIR}/caddy"
|
|
|
|
github_pages_http_code() {
|
|
local repo="$1"
|
|
curl -s -o /dev/null -w "%{http_code}" \
|
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
-H "Accept: application/json" \
|
|
"https://api.github.com/repos/${GITHUB_USERNAME}/${repo}/pages" 2>/dev/null || echo "000"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Step 1: Stop + remove Caddy container
|
|
# ---------------------------------------------------------------------------
|
|
CONTAINER_STATUS=$(ssh_exec UNRAID "docker ps --filter name=caddy --format '{{.Status}}'" 2>/dev/null || true)
|
|
if [[ "$CONTAINER_STATUS" == *"Up"* ]]; then
|
|
if confirm_action 'Stop and remove Caddy container? [y/N] '; then
|
|
ssh_exec UNRAID "cd '${CADDY_COMPOSE_DIR}' && docker compose down 2>/dev/null || docker-compose down"
|
|
log_success "Caddy container stopped and removed"
|
|
else
|
|
log_info "Caddy container preserved"
|
|
fi
|
|
else
|
|
log_info "Caddy container not running"
|
|
fi
|
|
|
|
# Remove Caddy compose file from compose dir; Caddyfile stays in data dir
|
|
if ssh_exec UNRAID "test -f '${CADDY_COMPOSE_DIR}/docker-compose.yml'" 2>/dev/null; then
|
|
ssh_exec UNRAID "rm -f '${CADDY_COMPOSE_DIR}/docker-compose.yml'"
|
|
log_success "Removed Caddy compose file from ${CADDY_COMPOSE_DIR}"
|
|
else
|
|
log_info "Caddy compose file already removed"
|
|
fi
|
|
if ssh_exec UNRAID "test -f '${CADDY_DATA_PATH}/Caddyfile'" 2>/dev/null; then
|
|
ssh_exec UNRAID "rm -f '${CADDY_DATA_PATH}/Caddyfile'"
|
|
log_success "Removed Caddyfile from ${CADDY_DATA_PATH}"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Step 2: Optionally remove Caddy data (certs, config)
|
|
# ---------------------------------------------------------------------------
|
|
if ssh_exec UNRAID "test -d '${CADDY_DATA_PATH}/data'" 2>/dev/null; then
|
|
if confirm_action "$(printf 'Remove Caddy TLS data (certificates) for %s? [y/N] ' "$GITEA_DOMAIN")"; then
|
|
ssh_exec UNRAID "rm -rf '${CADDY_DATA_PATH}/data' '${CADDY_DATA_PATH}/config'"
|
|
log_success "Caddy TLS data removed"
|
|
else
|
|
log_info "Caddy TLS data preserved"
|
|
fi
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Step 3: Restore GitHub repos
|
|
# Primary path: restore from state snapshot written by phase8_cutover.sh.
|
|
# Fallback path: if snapshot is missing, restore description from "— was: ..."
|
|
# and use legacy defaults for homepage/wiki/projects.
|
|
# ---------------------------------------------------------------------------
|
|
if confirm_action 'Restore GitHub repo settings (description/homepage/wiki/projects/pages)? [y/N] '; then
|
|
STATE_AVAILABLE=false
|
|
if [[ -f "$PHASE8_STATE_FILE" ]]; then
|
|
STATE_AVAILABLE=true
|
|
log_info "Using saved Phase 8 state from ${PHASE8_STATE_FILE}"
|
|
else
|
|
log_warn "No Phase 8 state file found — using fallback restore behavior"
|
|
fi
|
|
|
|
RESTORE_ERRORS=0
|
|
|
|
for repo in "${REPOS[@]}"; do
|
|
CURRENT_DESC=$(github_api GET "/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null | jq -r '.description // ""')
|
|
|
|
RESTORE_DESC=""
|
|
RESTORE_HOMEPAGE=""
|
|
RESTORE_HAS_WIKI=true
|
|
RESTORE_HAS_PROJECTS=true
|
|
RESTORE_PAGES_ENABLED=false
|
|
RESTORE_PAGES_CNAME=""
|
|
RESTORE_PAGES_BRANCH=""
|
|
RESTORE_PAGES_PATH="/"
|
|
|
|
if [[ "$STATE_AVAILABLE" == "true" ]] && jq -e --arg repo "$repo" 'has($repo)' "$PHASE8_STATE_FILE" >/dev/null 2>&1; then
|
|
REPO_STATE=$(jq -c --arg repo "$repo" '.[$repo]' "$PHASE8_STATE_FILE")
|
|
RESTORE_DESC=$(printf '%s' "$REPO_STATE" | jq -r '.description // ""')
|
|
RESTORE_HOMEPAGE=$(printf '%s' "$REPO_STATE" | jq -r '.homepage // ""')
|
|
RESTORE_HAS_WIKI=$(printf '%s' "$REPO_STATE" | jq -r '.has_wiki // true')
|
|
RESTORE_HAS_PROJECTS=$(printf '%s' "$REPO_STATE" | jq -r '.has_projects // true')
|
|
RESTORE_PAGES_ENABLED=$(printf '%s' "$REPO_STATE" | jq -r '.pages_enabled // false')
|
|
RESTORE_PAGES_CNAME=$(printf '%s' "$REPO_STATE" | jq -r '.pages_cname // ""')
|
|
RESTORE_PAGES_BRANCH=$(printf '%s' "$REPO_STATE" | jq -r '.pages_source_branch // ""')
|
|
RESTORE_PAGES_PATH=$(printf '%s' "$REPO_STATE" | jq -r '.pages_source_path // "/"')
|
|
else
|
|
if [[ "$CURRENT_DESC" != "[MIRROR]"* ]]; then
|
|
log_info "GitHub repo ${repo} not marked as mirror and no snapshot found — skipping"
|
|
continue
|
|
fi
|
|
if [[ "$CURRENT_DESC" == *" — was: "* ]]; then
|
|
RESTORE_DESC="${CURRENT_DESC##* — was: }"
|
|
fi
|
|
fi
|
|
|
|
# Restore description/homepage/wiki/projects
|
|
RESTORE_PAYLOAD=$(jq -n \
|
|
--arg description "$RESTORE_DESC" \
|
|
--arg homepage "$RESTORE_HOMEPAGE" \
|
|
--argjson has_wiki "$RESTORE_HAS_WIKI" \
|
|
--argjson has_projects "$RESTORE_HAS_PROJECTS" \
|
|
'{
|
|
description: $description,
|
|
homepage: $homepage,
|
|
has_wiki: $has_wiki,
|
|
has_projects: $has_projects
|
|
}')
|
|
|
|
if github_api PATCH "/repos/${GITHUB_USERNAME}/${repo}" "$RESTORE_PAYLOAD" >/dev/null 2>&1; then
|
|
log_success "Restored GitHub repo settings: ${repo}"
|
|
else
|
|
log_error "Failed to restore GitHub repo: ${repo}"
|
|
RESTORE_ERRORS=$((RESTORE_ERRORS + 1))
|
|
continue
|
|
fi
|
|
|
|
# Restore GitHub Pages state
|
|
CURRENT_PAGES_CODE=$(github_pages_http_code "$repo")
|
|
|
|
if [[ "$RESTORE_PAGES_ENABLED" == "true" ]]; then
|
|
if [[ -z "$RESTORE_PAGES_BRANCH" ]]; then
|
|
log_warn "Cannot restore Pages for ${repo}: missing source branch in saved state"
|
|
RESTORE_ERRORS=$((RESTORE_ERRORS + 1))
|
|
continue
|
|
fi
|
|
|
|
PAGES_PAYLOAD=$(jq -n \
|
|
--arg branch "$RESTORE_PAGES_BRANCH" \
|
|
--arg path "$RESTORE_PAGES_PATH" \
|
|
--arg cname "$RESTORE_PAGES_CNAME" \
|
|
'{
|
|
source: {
|
|
branch: $branch,
|
|
path: $path
|
|
}
|
|
} + (if $cname != "" then {cname: $cname} else {} end)')
|
|
|
|
if [[ "$CURRENT_PAGES_CODE" == "200" ]]; then
|
|
if github_api PUT "/repos/${GITHUB_USERNAME}/${repo}/pages" "$PAGES_PAYLOAD" >/dev/null 2>&1; then
|
|
log_success "Restored GitHub Pages config for ${repo}"
|
|
else
|
|
log_warn "Failed to update GitHub Pages config for ${repo}"
|
|
RESTORE_ERRORS=$((RESTORE_ERRORS + 1))
|
|
fi
|
|
else
|
|
if github_api POST "/repos/${GITHUB_USERNAME}/${repo}/pages" "$PAGES_PAYLOAD" >/dev/null 2>&1; then
|
|
log_success "Recreated GitHub Pages for ${repo}"
|
|
else
|
|
log_warn "Failed to recreate GitHub Pages for ${repo}"
|
|
RESTORE_ERRORS=$((RESTORE_ERRORS + 1))
|
|
fi
|
|
fi
|
|
else
|
|
if [[ "$CURRENT_PAGES_CODE" == "200" ]]; then
|
|
if github_api DELETE "/repos/${GITHUB_USERNAME}/${repo}/pages" >/dev/null 2>&1; then
|
|
log_info "Disabled GitHub Pages for ${repo} (matches pre-cutover state)"
|
|
else
|
|
log_warn "Failed to disable GitHub Pages for ${repo}"
|
|
RESTORE_ERRORS=$((RESTORE_ERRORS + 1))
|
|
fi
|
|
else
|
|
log_info "GitHub Pages already disabled for ${repo}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [[ "$STATE_AVAILABLE" == "true" ]]; then
|
|
if [[ "$RESTORE_ERRORS" -eq 0 ]]; then
|
|
rm -f "$PHASE8_STATE_FILE"
|
|
log_info "Removed saved Phase 8 state file after successful restore"
|
|
else
|
|
log_warn "Keeping Phase 8 state file due to restore errors: ${PHASE8_STATE_FILE}"
|
|
fi
|
|
fi
|
|
else
|
|
log_info "GitHub repos left as-is"
|
|
fi
|
|
|
|
log_success "Phase 8 teardown complete"
|