#!/usr/bin/env bash set -euo pipefail # ============================================================================= # phase4_migrate_repos.sh — Import repos from GitHub + set up Fedora mirrors # Depends on: Phase 1 + Phase 2 complete (both Gitea instances running) # For each repo: # 1. Import from GitHub → Unraid primary (under org, one-time migration) # 2. Create pull mirror on Fedora (mirrors Unraid → Fedora on interval) # Idempotent: skips repos that already exist on either instance. # ============================================================================= SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "${SCRIPT_DIR}/lib/common.sh" load_env require_vars GITEA_ADMIN_TOKEN GITEA_BACKUP_ADMIN_TOKEN \ GITEA_INTERNAL_URL GITEA_BACKUP_INTERNAL_URL \ GITEA_ORG_NAME GITEA_ADMIN_USER GITEA_ADMIN_PASSWORD \ GITHUB_USERNAME GITHUB_TOKEN \ REPO_NAMES \ MIGRATE_ISSUES MIGRATE_LABELS MIGRATE_MILESTONES MIGRATE_WIKI \ GITEA_BACKUP_MIRROR_INTERVAL # Migration polling knobs (optional in .env) MIGRATION_POLL_INTERVAL_SEC="${MIGRATION_POLL_INTERVAL_SEC:-3}" MIGRATION_POLL_TIMEOUT_SEC="${MIGRATION_POLL_TIMEOUT_SEC:-600}" if ! [[ "$MIGRATION_POLL_INTERVAL_SEC" =~ ^[1-9][0-9]*$ ]]; then log_error "MIGRATION_POLL_INTERVAL_SEC must be a positive integer (seconds)" exit 1 fi if ! [[ "$MIGRATION_POLL_TIMEOUT_SEC" =~ ^[1-9][0-9]*$ ]]; then log_error "MIGRATION_POLL_TIMEOUT_SEC must be a positive integer (seconds)" exit 1 fi if (( MIGRATION_POLL_TIMEOUT_SEC < MIGRATION_POLL_INTERVAL_SEC )); then log_error "MIGRATION_POLL_TIMEOUT_SEC (${MIGRATION_POLL_TIMEOUT_SEC}) must be >= MIGRATION_POLL_INTERVAL_SEC (${MIGRATION_POLL_INTERVAL_SEC})" exit 1 fi phase_header 4 "Migrate Repos + Fedora Mirrors" # --------------------------------------------------------------------------- # Build list of repos from REPO_N_NAME env vars # Using an array rather than hardcoding 3 loops keeps it flexible. # --------------------------------------------------------------------------- read -ra REPOS <<< "$REPO_NAMES" TOTAL=${#REPOS[@]} SUCCESS=0 FAILED=0 for repo in "${REPOS[@]}"; do log_info "--- Processing repo: ${repo} ---" # ------------------------------------------------------------------------- # Step A: Import from GitHub to Unraid primary (under org) # Uses Gitea's migration API which clones the GitHub repo server-side. # mirror=false because this is a one-time import — we don't want Gitea # to keep pulling from GitHub (GitHub becomes the push mirror target later). # ------------------------------------------------------------------------- log_step "A" "Importing ${repo} from GitHub to Unraid..." # Idempotency: check if repo already exists on primary if gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}" >/dev/null 2>&1; then log_info "Repo '${GITEA_ORG_NAME}/${repo}' already exists on primary — skipping import" else # Build migration payload — JSON with all options from .env MIGRATE_PAYLOAD=$(jq -n \ --arg clone_addr "https://github.com/${GITHUB_USERNAME}/${repo}.git" \ --arg auth_token "$GITHUB_TOKEN" \ --arg repo_owner "$GITEA_ORG_NAME" \ --arg repo_name "$repo" \ --arg service "github" \ --argjson issues "${MIGRATE_ISSUES}" \ --argjson labels "${MIGRATE_LABELS}" \ --argjson milestones "${MIGRATE_MILESTONES}" \ --argjson wiki "${MIGRATE_WIKI}" \ '{ clone_addr: $clone_addr, auth_token: $auth_token, repo_owner: $repo_owner, repo_name: $repo_name, service: $service, mirror: false, issues: $issues, labels: $labels, milestones: $milestones, wiki: $wiki }') if gitea_api POST "/repos/migrate" "$MIGRATE_PAYLOAD" >/dev/null; then log_success "Migration started for ${repo}" else log_error "Failed to start migration for ${repo}" FAILED=$((FAILED + 1)) continue fi # Wait for migration to complete — poll until repo has content # The migration API returns immediately but cloning happens async. # We check for commits to confirm the repo has actual content. log_info "Waiting for migration to complete (timeout: ${MIGRATION_POLL_TIMEOUT_SEC}s, interval: ${MIGRATION_POLL_INTERVAL_SEC}s)..." local_elapsed=0 while [[ $local_elapsed -lt $MIGRATION_POLL_TIMEOUT_SEC ]]; do COMMITS=$(gitea_api GET "/repos/${GITEA_ORG_NAME}/${repo}/commits?limit=1" 2>/dev/null || echo "[]") COMMIT_COUNT=$(printf '%s' "$COMMITS" | jq 'length' 2>/dev/null || echo 0) if [[ "$COMMIT_COUNT" -gt 0 ]]; then log_success "Repo ${repo} migrated with commits" break fi sleep "$MIGRATION_POLL_INTERVAL_SEC" local_elapsed=$((local_elapsed + MIGRATION_POLL_INTERVAL_SEC)) done if [[ $local_elapsed -ge $MIGRATION_POLL_TIMEOUT_SEC ]]; then log_error "Timeout waiting for ${repo} migration to complete" FAILED=$((FAILED + 1)) continue fi fi # ------------------------------------------------------------------------- # Step B: Create pull mirror on Fedora # The Fedora instance mirrors the Unraid primary on a schedule. This uses # Gitea's migration API with mirror=true, which creates a mirror repo that # periodically pulls from the source URL. # The mirror authenticates to the primary using admin credentials since # the primary's repos may be private. # ------------------------------------------------------------------------- log_step "B" "Creating pull mirror for ${repo} on Fedora..." # Idempotency: check if mirror repo already exists on Fedora if gitea_backup_api GET "/repos/${GITEA_ADMIN_USER}/${repo}" >/dev/null 2>&1; then log_info "Mirror '${GITEA_ADMIN_USER}/${repo}' already exists on Fedora — skipping" else # The clone URL points to the primary Gitea instance (Unraid) MIRROR_PAYLOAD=$(jq -n \ --arg clone_addr "${GITEA_INTERNAL_URL}/${GITEA_ORG_NAME}/${repo}.git" \ --arg auth_username "$GITEA_ADMIN_USER" \ --arg auth_password "$GITEA_ADMIN_PASSWORD" \ --arg repo_owner "$GITEA_ADMIN_USER" \ --arg repo_name "$repo" \ --arg mirror_interval "$GITEA_BACKUP_MIRROR_INTERVAL" \ '{ clone_addr: $clone_addr, auth_username: $auth_username, auth_password: $auth_password, repo_owner: $repo_owner, repo_name: $repo_name, service: "gitea", mirror: true, mirror_interval: $mirror_interval }') if gitea_backup_api POST "/repos/migrate" "$MIRROR_PAYLOAD" >/dev/null; then log_success "Pull mirror created for ${repo} on Fedora" else log_error "Failed to create pull mirror for ${repo}" FAILED=$((FAILED + 1)) continue fi fi SUCCESS=$((SUCCESS + 1)) done # --------------------------------------------------------------------------- # Summary # --------------------------------------------------------------------------- printf '\n' log_info "Results: ${SUCCESS} succeeded, ${FAILED} failed (out of ${TOTAL})" if [[ $FAILED -gt 0 ]]; then log_error "Some repos failed — check logs above" exit 1 fi log_success "Phase 4 complete — all repos migrated and mirrored"