feat: add Phase 8 — Cutover (HTTPS + Archive GitHub)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
309
phase8_cutover.sh
Executable file
309
phase8_cutover.sh
Executable file
@@ -0,0 +1,309 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# phase8_cutover.sh — HTTPS via Nginx + Archive GitHub repos
|
||||||
|
# Depends on: All prior phases complete, Nginx running on Unraid
|
||||||
|
# This is the "go live" script — after this, Gitea is the primary git host.
|
||||||
|
#
|
||||||
|
# Two-stage Nginx deploy:
|
||||||
|
# Stage 1: HTTP-only reverse proxy (needed for ACME challenge if letsencrypt)
|
||||||
|
# Stage 2: Full HTTPS with SSL cert + HTTP→HTTPS redirect
|
||||||
|
#
|
||||||
|
# SSL_MODE from .env controls certificate handling:
|
||||||
|
# "letsencrypt" → auto-provision via Certbot container
|
||||||
|
# "existing" → user provides cert paths (SSL_CERT_PATH, SSL_KEY_PATH)
|
||||||
|
#
|
||||||
|
# After HTTPS is live, GitHub repos are archived with a "[MOVED]" description.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
load_env
|
||||||
|
require_vars UNRAID_IP UNRAID_SSH_USER UNRAID_GITEA_PORT \
|
||||||
|
GITEA_INTERNAL_URL GITEA_DOMAIN GITEA_ADMIN_TOKEN \
|
||||||
|
GITEA_ORG_NAME NGINX_CONTAINER_NAME NGINX_CONF_PATH \
|
||||||
|
SSL_MODE GITHUB_USERNAME GITHUB_TOKEN \
|
||||||
|
REPO_1_NAME REPO_2_NAME REPO_3_NAME
|
||||||
|
|
||||||
|
phase_header 8 "Cutover (HTTPS + Archive GitHub)"
|
||||||
|
|
||||||
|
REPOS=("$REPO_1_NAME" "$REPO_2_NAME" "$REPO_3_NAME")
|
||||||
|
|
||||||
|
# Validate SSL_MODE
|
||||||
|
if [[ "$SSL_MODE" != "letsencrypt" ]] && [[ "$SSL_MODE" != "existing" ]]; then
|
||||||
|
log_error "Invalid SSL_MODE='${SSL_MODE}' — must be 'letsencrypt' or 'existing'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$SSL_MODE" == "letsencrypt" ]]; then
|
||||||
|
require_vars SSL_EMAIL
|
||||||
|
elif [[ "$SSL_MODE" == "existing" ]]; then
|
||||||
|
require_vars SSL_CERT_PATH SSL_KEY_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helper: render Nginx config in HTTP-only or HTTPS mode
|
||||||
|
# Uses the template + sed to strip/modify marker blocks.
|
||||||
|
# Writes result to a local tmpfile and returns its path via stdout.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
render_nginx_http_only() {
|
||||||
|
local tmpfile
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
# Render the template with envsubst first
|
||||||
|
local rendered
|
||||||
|
rendered=$(mktemp)
|
||||||
|
export GITEA_DOMAIN UNRAID_IP UNRAID_GITEA_PORT
|
||||||
|
# Set dummy cert paths (not used in HTTP-only mode)
|
||||||
|
export SSL_CERT_FULLPATH="/dev/null"
|
||||||
|
export SSL_KEY_FULLPATH="/dev/null"
|
||||||
|
render_template "${SCRIPT_DIR}/templates/nginx-gitea.conf.tpl" "$rendered"
|
||||||
|
|
||||||
|
# Strip the HTTPS server block (everything between markers inclusive)
|
||||||
|
sed '/# SSL_HTTPS_BLOCK_START/,/# SSL_HTTPS_BLOCK_END/d' "$rendered" > "$tmpfile"
|
||||||
|
rm -f "$rendered"
|
||||||
|
echo "$tmpfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
render_nginx_https() {
|
||||||
|
local cert_path="$1" key_path="$2"
|
||||||
|
local tmpfile
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
local rendered
|
||||||
|
rendered=$(mktemp)
|
||||||
|
export GITEA_DOMAIN UNRAID_IP UNRAID_GITEA_PORT
|
||||||
|
export SSL_CERT_FULLPATH="$cert_path"
|
||||||
|
export SSL_KEY_FULLPATH="$key_path"
|
||||||
|
render_template "${SCRIPT_DIR}/templates/nginx-gitea.conf.tpl" "$rendered"
|
||||||
|
|
||||||
|
# Replace the redirect block content with a 301 redirect to HTTPS
|
||||||
|
# The block between markers gets replaced with just the redirect
|
||||||
|
sed '/# SSL_REDIRECT_BLOCK_START/,/# SSL_REDIRECT_BLOCK_END/{
|
||||||
|
/# SSL_REDIRECT_BLOCK_START/!{/# SSL_REDIRECT_BLOCK_END/!d;}
|
||||||
|
/# SSL_REDIRECT_BLOCK_START/a\
|
||||||
|
return 301 https://\$host\$request_uri;
|
||||||
|
}' "$rendered" > "$tmpfile"
|
||||||
|
rm -f "$rendered"
|
||||||
|
echo "$tmpfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 1: Deploy HTTP-only Nginx config
|
||||||
|
# This enables the reverse proxy and the ACME challenge location needed
|
||||||
|
# by Certbot for domain validation (letsencrypt mode).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 1 "Deploying HTTP-only Nginx config..."
|
||||||
|
if ssh_exec UNRAID "test -f '${NGINX_CONF_PATH}/gitea.conf'" 2>/dev/null; then
|
||||||
|
log_info "gitea.conf already exists — checking if SSL is already configured"
|
||||||
|
# If HTTPS block is present in the existing config, we're already past HTTP-only stage
|
||||||
|
if ssh_exec UNRAID "grep -q 'listen 443 ssl' '${NGINX_CONF_PATH}/gitea.conf'" 2>/dev/null; then
|
||||||
|
log_info "HTTPS already configured — skipping HTTP-only stage"
|
||||||
|
else
|
||||||
|
log_info "HTTP-only config exists — skipping"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
HTTP_CONF=$(render_nginx_http_only)
|
||||||
|
scp_to UNRAID "$HTTP_CONF" "${NGINX_CONF_PATH}/gitea.conf"
|
||||||
|
rm -f "$HTTP_CONF"
|
||||||
|
log_success "HTTP-only config deployed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 2: Test Nginx config
|
||||||
|
# Always test before reloading — never push a broken config to a live server.
|
||||||
|
# If test fails, remove the config and exit.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 2 "Testing Nginx config..."
|
||||||
|
if ! ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -t" 2>/dev/null; then
|
||||||
|
log_error "Nginx config test failed — removing gitea.conf and aborting"
|
||||||
|
ssh_exec UNRAID "rm -f '${NGINX_CONF_PATH}/gitea.conf'" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Nginx config test passed"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 3: Reload Nginx (HTTP)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 3 "Reloading Nginx..."
|
||||||
|
ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -s reload"
|
||||||
|
log_success "Nginx reloaded"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 4: Verify HTTP proxy works
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 4 "Verifying HTTP proxy..."
|
||||||
|
if curl -sf -o /dev/null "http://${GITEA_DOMAIN}/api/v1/version" 2>/dev/null; then
|
||||||
|
log_success "HTTP proxy verified — http://${GITEA_DOMAIN} works"
|
||||||
|
else
|
||||||
|
log_error "HTTP proxy failed — http://${GITEA_DOMAIN} not responding"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 5: Obtain or verify SSL certificate
|
||||||
|
# For letsencrypt: run Certbot in a Docker container using webroot validation.
|
||||||
|
# For existing: verify cert files exist on Unraid.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 5 "Obtaining/verifying SSL certificate..."
|
||||||
|
|
||||||
|
if [[ "$SSL_MODE" == "letsencrypt" ]]; then
|
||||||
|
# Check if cert already exists
|
||||||
|
if ssh_exec UNRAID "test -f '/etc/letsencrypt/live/${GITEA_DOMAIN}/fullchain.pem'" 2>/dev/null; then
|
||||||
|
log_info "Let's Encrypt cert already exists for ${GITEA_DOMAIN} — skipping"
|
||||||
|
else
|
||||||
|
# Verify Nginx container has required volume mounts for Certbot
|
||||||
|
# /etc/letsencrypt must be mounted for cert storage
|
||||||
|
# /var/www/html must be mounted for ACME challenge serving
|
||||||
|
MOUNTS=$(ssh_exec UNRAID "docker inspect ${NGINX_CONTAINER_NAME} --format '{{json .Mounts}}'" 2>/dev/null)
|
||||||
|
if ! echo "$MOUNTS" | grep -q "letsencrypt"; then
|
||||||
|
log_error "Nginx container '${NGINX_CONTAINER_NAME}' does not mount /etc/letsencrypt"
|
||||||
|
log_error "Add the volume mount to your Nginx container config and restart it"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Running Certbot for ${GITEA_DOMAIN}..."
|
||||||
|
ssh_exec UNRAID "docker run --rm \
|
||||||
|
-v /etc/letsencrypt:/etc/letsencrypt \
|
||||||
|
-v /var/www/html:/var/www/html \
|
||||||
|
certbot/certbot certonly \
|
||||||
|
--webroot -w /var/www/html \
|
||||||
|
-d '${GITEA_DOMAIN}' \
|
||||||
|
--email '${SSL_EMAIL}' \
|
||||||
|
--agree-tos \
|
||||||
|
--non-interactive"
|
||||||
|
log_success "SSL certificate obtained from Let's Encrypt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SSL_CERT_FULLPATH="/etc/letsencrypt/live/${GITEA_DOMAIN}/fullchain.pem"
|
||||||
|
SSL_KEY_FULLPATH="/etc/letsencrypt/live/${GITEA_DOMAIN}/privkey.pem"
|
||||||
|
|
||||||
|
elif [[ "$SSL_MODE" == "existing" ]]; then
|
||||||
|
# Verify cert files exist on Unraid
|
||||||
|
if ! ssh_exec UNRAID "test -f '${SSL_CERT_PATH}'" 2>/dev/null; then
|
||||||
|
log_error "SSL cert not found at ${SSL_CERT_PATH} on Unraid"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! ssh_exec UNRAID "test -f '${SSL_KEY_PATH}'" 2>/dev/null; then
|
||||||
|
log_error "SSL key not found at ${SSL_KEY_PATH} on Unraid"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Existing SSL cert verified"
|
||||||
|
|
||||||
|
SSL_CERT_FULLPATH="$SSL_CERT_PATH"
|
||||||
|
SSL_KEY_FULLPATH="$SSL_KEY_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 6: Deploy HTTPS Nginx config
|
||||||
|
# Overwrites the HTTP-only config with the full HTTPS version.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 6 "Deploying HTTPS Nginx config..."
|
||||||
|
HTTPS_CONF=$(render_nginx_https "$SSL_CERT_FULLPATH" "$SSL_KEY_FULLPATH")
|
||||||
|
scp_to UNRAID "$HTTPS_CONF" "${NGINX_CONF_PATH}/gitea.conf"
|
||||||
|
rm -f "$HTTPS_CONF"
|
||||||
|
log_success "HTTPS config deployed"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 7: Test Nginx config (HTTPS)
|
||||||
|
# If the HTTPS config fails, revert to HTTP-only as a safety measure.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 7 "Testing HTTPS Nginx config..."
|
||||||
|
if ! ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -t" 2>/dev/null; then
|
||||||
|
log_error "HTTPS Nginx config test failed — reverting to HTTP-only"
|
||||||
|
HTTP_CONF=$(render_nginx_http_only)
|
||||||
|
scp_to UNRAID "$HTTP_CONF" "${NGINX_CONF_PATH}/gitea.conf"
|
||||||
|
rm -f "$HTTP_CONF"
|
||||||
|
ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -s reload" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "HTTPS Nginx config test passed"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 8: Reload Nginx (HTTPS)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 8 "Reloading Nginx with HTTPS..."
|
||||||
|
ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -s reload"
|
||||||
|
log_success "Nginx reloaded with HTTPS"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 9: Verify HTTPS works
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 9 "Verifying HTTPS..."
|
||||||
|
sleep 2 # Brief pause for Nginx to apply the new config
|
||||||
|
if curl -sf -o /dev/null "https://${GITEA_DOMAIN}/api/v1/version" 2>/dev/null; then
|
||||||
|
log_success "HTTPS verified — https://${GITEA_DOMAIN} works"
|
||||||
|
else
|
||||||
|
log_error "HTTPS verification failed — https://${GITEA_DOMAIN} not responding"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify HTTP redirects to HTTPS
|
||||||
|
HTTP_STATUS=$(curl -sI -o /dev/null -w "%{http_code}" "http://${GITEA_DOMAIN}/" 2>/dev/null || true)
|
||||||
|
if [[ "$HTTP_STATUS" == "301" ]]; then
|
||||||
|
log_success "HTTP → HTTPS redirect working (301)"
|
||||||
|
else
|
||||||
|
log_warn "HTTP redirect returned ${HTTP_STATUS} instead of 301"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 10: Set up cert auto-renewal cron (letsencrypt only)
|
||||||
|
# Runs daily at 3 AM — Certbot only renews when cert is within 30 days of expiry.
|
||||||
|
# After renewal, Nginx is reloaded to pick up the new cert.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 10 "Setting up cert auto-renewal..."
|
||||||
|
if [[ "$SSL_MODE" == "letsencrypt" ]]; then
|
||||||
|
# Check if cron already exists
|
||||||
|
if ssh_exec UNRAID "crontab -l 2>/dev/null | grep -q certbot" 2>/dev/null; then
|
||||||
|
log_info "Certbot renewal cron already installed — skipping"
|
||||||
|
else
|
||||||
|
# Append certbot cron to existing crontab
|
||||||
|
ssh_exec UNRAID "(crontab -l 2>/dev/null; echo '0 3 * * * docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -v /var/www/html:/var/www/html certbot/certbot renew --quiet && docker exec ${NGINX_CONTAINER_NAME} nginx -s reload') | crontab -"
|
||||||
|
log_success "Certbot renewal cron installed (daily at 3 AM)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "SSL_MODE=existing — skipping auto-renewal (user manages certs)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 11: Archive GitHub repos
|
||||||
|
# Marks repos as archived with a "[MOVED]" description pointing to Gitea.
|
||||||
|
# Preserves the original description by appending it after "— was: ".
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log_step 11 "Archiving GitHub repos..."
|
||||||
|
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
# Check if already archived
|
||||||
|
IS_ARCHIVED=$(github_api GET "/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null | jq -r '.archived' || echo "false")
|
||||||
|
if [[ "$IS_ARCHIVED" == "true" ]]; then
|
||||||
|
log_info "GitHub repo ${repo} already archived — skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get original description to preserve it
|
||||||
|
ORIGINAL_DESC=$(github_api GET "/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null | jq -r '.description // ""' || echo "")
|
||||||
|
|
||||||
|
# Build new description with moved notice
|
||||||
|
NEW_DESC="[MOVED] Now at https://${GITEA_DOMAIN}/${GITEA_ORG_NAME}/${repo}"
|
||||||
|
if [[ -n "$ORIGINAL_DESC" ]]; then
|
||||||
|
NEW_DESC="${NEW_DESC} — was: ${ORIGINAL_DESC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Archive the repo with the new description
|
||||||
|
ARCHIVE_PAYLOAD=$(jq -n \
|
||||||
|
--arg description "$NEW_DESC" \
|
||||||
|
'{archived: true, description: $description}')
|
||||||
|
|
||||||
|
if github_api PATCH "/repos/${GITHUB_USERNAME}/${repo}" "$ARCHIVE_PAYLOAD" >/dev/null 2>&1; then
|
||||||
|
log_success "Archived GitHub repo: ${repo}"
|
||||||
|
else
|
||||||
|
log_error "Failed to archive GitHub repo: ${repo}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
printf '\n'
|
||||||
|
log_success "Phase 8 complete — Gitea is live at https://${GITEA_DOMAIN}"
|
||||||
|
log_info "GitHub repos have been archived. Gitea is now the primary git host."
|
||||||
87
phase8_post_check.sh
Executable file
87
phase8_post_check.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# phase8_post_check.sh — Verify Phase 8 (Cutover) succeeded
|
||||||
|
# Checks:
|
||||||
|
# 1. HTTPS works with valid cert
|
||||||
|
# 2. HTTP redirects to HTTPS
|
||||||
|
# 3. All repos accessible via HTTPS
|
||||||
|
# 4. GitHub repos are archived
|
||||||
|
# Exits 0 only if ALL checks pass.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
load_env
|
||||||
|
require_vars GITEA_DOMAIN GITEA_ADMIN_TOKEN GITEA_ORG_NAME \
|
||||||
|
GITHUB_USERNAME GITHUB_TOKEN \
|
||||||
|
REPO_1_NAME REPO_2_NAME REPO_3_NAME
|
||||||
|
|
||||||
|
log_info "=== Phase 8 Post-Check ==="
|
||||||
|
|
||||||
|
REPOS=("$REPO_1_NAME" "$REPO_2_NAME" "$REPO_3_NAME")
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
run_check() {
|
||||||
|
local description="$1"; shift
|
||||||
|
if "$@" 2>/dev/null; then
|
||||||
|
log_success "$description"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
log_error "FAIL: $description"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check 1: HTTPS works
|
||||||
|
run_check "HTTPS returns 200 at https://${GITEA_DOMAIN}" \
|
||||||
|
curl -sf -o /dev/null "https://${GITEA_DOMAIN}/api/v1/version"
|
||||||
|
|
||||||
|
# Check 2: HTTP redirects to HTTPS (returns 301)
|
||||||
|
check_redirect() {
|
||||||
|
local http_code
|
||||||
|
http_code=$(curl -sI -o /dev/null -w "%{http_code}" "http://${GITEA_DOMAIN}/")
|
||||||
|
[[ "$http_code" == "301" ]]
|
||||||
|
}
|
||||||
|
run_check "HTTP → HTTPS redirect (301)" check_redirect
|
||||||
|
|
||||||
|
# Check 3: SSL certificate is valid (not self-signed)
|
||||||
|
check_ssl_cert() {
|
||||||
|
# Verify openssl can connect and the cert is issued by a recognized CA
|
||||||
|
local issuer
|
||||||
|
issuer=$(echo | openssl s_client -connect "${GITEA_DOMAIN}:443" -servername "${GITEA_DOMAIN}" 2>/dev/null | openssl x509 -noout -issuer 2>/dev/null || echo "")
|
||||||
|
# Check that the issuer is not empty (meaning cert is valid)
|
||||||
|
[[ -n "$issuer" ]]
|
||||||
|
}
|
||||||
|
run_check "SSL certificate is valid" check_ssl_cert
|
||||||
|
|
||||||
|
# Check 4: All repos accessible via HTTPS
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
run_check "Repo ${repo} accessible at https://${GITEA_DOMAIN}/${GITEA_ORG_NAME}/${repo}" \
|
||||||
|
curl -sf -o /dev/null -H "Authorization: token ${GITEA_ADMIN_TOKEN}" "https://${GITEA_DOMAIN}/api/v1/repos/${GITEA_ORG_NAME}/${repo}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check 5: GitHub repos are archived
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
check_archived() {
|
||||||
|
local is_archived
|
||||||
|
is_archived=$(github_api GET "/repos/${GITHUB_USERNAME}/$1" | jq -r '.archived')
|
||||||
|
[[ "$is_archived" == "true" ]]
|
||||||
|
}
|
||||||
|
run_check "GitHub repo ${repo} is archived" check_archived "$repo"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
printf '\n'
|
||||||
|
log_info "Results: ${PASS} passed, ${FAIL} failed"
|
||||||
|
|
||||||
|
if [[ $FAIL -gt 0 ]]; then
|
||||||
|
log_error "Phase 8 post-check FAILED"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
log_success "Phase 8 post-check PASSED — Gitea is live with HTTPS"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
107
phase8_teardown.sh
Executable file
107
phase8_teardown.sh
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# phase8_teardown.sh — Reverse the cutover: remove HTTPS, un-archive GitHub
|
||||||
|
# Steps:
|
||||||
|
# 1. Remove Nginx gitea.conf + reload
|
||||||
|
# 2. Remove cert renewal cron
|
||||||
|
# 3. Optionally remove SSL certificates
|
||||||
|
# 4. Un-archive GitHub repos + restore original descriptions
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
load_env
|
||||||
|
require_vars UNRAID_IP UNRAID_SSH_USER \
|
||||||
|
GITEA_DOMAIN NGINX_CONTAINER_NAME NGINX_CONF_PATH \
|
||||||
|
SSL_MODE GITHUB_USERNAME GITHUB_TOKEN \
|
||||||
|
REPO_1_NAME REPO_2_NAME REPO_3_NAME
|
||||||
|
|
||||||
|
log_warn "=== Phase 8 Teardown: Cutover ==="
|
||||||
|
|
||||||
|
REPOS=("$REPO_1_NAME" "$REPO_2_NAME" "$REPO_3_NAME")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 1: Remove Nginx config and reload
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if ssh_exec UNRAID "test -f '${NGINX_CONF_PATH}/gitea.conf'" 2>/dev/null; then
|
||||||
|
printf 'Remove Nginx config for %s? [y/N] ' "$GITEA_DOMAIN"
|
||||||
|
read -r confirm
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
ssh_exec UNRAID "rm -f '${NGINX_CONF_PATH}/gitea.conf'"
|
||||||
|
ssh_exec UNRAID "docker exec ${NGINX_CONTAINER_NAME} nginx -s reload" || true
|
||||||
|
log_success "Nginx config removed and reloaded"
|
||||||
|
else
|
||||||
|
log_info "Nginx config preserved"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Nginx config already removed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 2: Remove cert renewal cron
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if ssh_exec UNRAID "crontab -l 2>/dev/null | grep -q certbot" 2>/dev/null; then
|
||||||
|
ssh_exec UNRAID "crontab -l 2>/dev/null | grep -v certbot | crontab -"
|
||||||
|
log_success "Certbot renewal cron removed"
|
||||||
|
else
|
||||||
|
log_info "No certbot cron found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 3: Optionally remove SSL certificates (letsencrypt only)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "$SSL_MODE" == "letsencrypt" ]]; then
|
||||||
|
if ssh_exec UNRAID "test -d '/etc/letsencrypt/live/${GITEA_DOMAIN}'" 2>/dev/null; then
|
||||||
|
printf 'Remove SSL certificates for %s? [y/N] ' "$GITEA_DOMAIN"
|
||||||
|
read -r confirm
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
ssh_exec UNRAID "rm -rf '/etc/letsencrypt/live/${GITEA_DOMAIN}' '/etc/letsencrypt/archive/${GITEA_DOMAIN}' '/etc/letsencrypt/renewal/${GITEA_DOMAIN}.conf'"
|
||||||
|
log_success "SSL certificates removed"
|
||||||
|
else
|
||||||
|
log_info "SSL certificates preserved"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Step 4: Un-archive GitHub repos + restore original descriptions
|
||||||
|
# The archive description format is: "[MOVED] ... — was: ORIGINAL_DESC"
|
||||||
|
# We parse the original description from after "— was: " to restore it.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
printf 'Un-archive GitHub repos and restore descriptions? [y/N] '
|
||||||
|
read -r confirm
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
IS_ARCHIVED=$(github_api GET "/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null | jq -r '.archived' || echo "false")
|
||||||
|
if [[ "$IS_ARCHIVED" != "true" ]]; then
|
||||||
|
log_info "GitHub repo ${repo} not archived — skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract original description from the archived description
|
||||||
|
CURRENT_DESC=$(github_api GET "/repos/${GITHUB_USERNAME}/${repo}" 2>/dev/null | jq -r '.description // ""')
|
||||||
|
ORIGINAL_DESC=""
|
||||||
|
if [[ "$CURRENT_DESC" == *" — was: "* ]]; then
|
||||||
|
# Extract everything after "— was: "
|
||||||
|
ORIGINAL_DESC="${CURRENT_DESC##* — was: }"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Un-archive and restore description
|
||||||
|
RESTORE_PAYLOAD=$(jq -n \
|
||||||
|
--arg description "$ORIGINAL_DESC" \
|
||||||
|
'{archived: false, description: $description}')
|
||||||
|
|
||||||
|
if github_api PATCH "/repos/${GITHUB_USERNAME}/${repo}" "$RESTORE_PAYLOAD" >/dev/null 2>&1; then
|
||||||
|
log_success "Un-archived GitHub repo: ${repo}"
|
||||||
|
else
|
||||||
|
log_error "Failed to un-archive GitHub repo: ${repo}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
log_info "GitHub repos left as-is"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Phase 8 teardown complete"
|
||||||
Reference in New Issue
Block a user