From 13f73b850fabd357f2860f5184769ba6e8f939a9 Mon Sep 17 00:00:00 2001 From: S Date: Sun, 1 Mar 2026 13:21:40 -0500 Subject: [PATCH] docs: fix stale references in PLAN, contracts, README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: update prompt count ~65 to ~70 - contracts/gitea-api.md: fix "Used in" annotations — POST branch_protections is Phase 7 only (not Phase 9), DELETE branch_protections is Phase 7 teardown only (Phase 9 uses PATCH), add missing used-in entries for GET commits and GET contents - PLAN.md: replace stale UNRAID_GITEA_PORT/FEDORA_GITEA_PORT with macvlan vars, update REPO_N_NAME to REPO_NAMES, fix version defaults (1.23->1.25, 0.2.11->0.3.0), add missing setup/ files to tree, fix prompt table to match current ~70 variables, update require_vars lists to match actual scripts Co-Authored-By: Claude Opus 4.6 --- PLAN.md | 102 +++++++++++++++-------------------------- README.md | 2 +- contracts/gitea-api.md | 10 ++-- 3 files changed, 44 insertions(+), 70 deletions(-) diff --git a/PLAN.md b/PLAN.md index c1e33b0..459ba1d 100644 --- a/PLAN.md +++ b/PLAN.md @@ -36,9 +36,14 @@ gitea-migration/ │ └── common.sh ├── setup/ │ ├── configure_env.sh +│ ├── configure_runners.sh │ ├── macbook.sh │ ├── unraid.sh -│ └── fedora.sh +│ ├── fedora.sh +│ ├── cross_host_ssh.sh +│ ├── env_to_bitwarden.sh +│ ├── bitwarden_to_env.sh +│ └── cleanup.sh ├── templates/ │ ├── docker-compose-gitea.yml.tpl │ ├── docker-compose-runner.yml.tpl @@ -330,12 +335,12 @@ gitea-migration/ - Gitea image pinned to `$GITEA_VERSION` - Container name: `gitea` - Volumes: `$DATA_PATH/data:/data`, `$DATA_PATH/config:/data/gitea/conf` -- Ports: `$GITEA_PORT:3000`, `$GITEA_SSH_PORT:22` (add `GITEA_SSH_PORT` to .env if missing) +- Networks: macvlan with `$GITEA_CONTAINER_IP` (dedicated LAN IP, no port mapping) - Environment: `USER_UID=1000`, `USER_GID=1000` - Restart policy: `unless-stopped` -- No database service (SQLite is file-based) +- No database service for sqlite3; conditional DB service block for external DBs -**Variables used** (must all be in .env): `GITEA_VERSION`, `DATA_PATH`, `GITEA_PORT` +**Variables used**: `GITEA_VERSION`, `DATA_PATH`, `GITEA_CONTAINER_IP` (+ DB vars if external DB) **Done when**: - [ ] `render_template` produces valid YAML (test with `python3 -c "import yaml; yaml.safe_load(open('output.yml'))"` or `yq`) @@ -502,56 +507,25 @@ gitea-migration/ | # | Variable | Validation | Default | |---|----------|------------|---------| -| 1 | `UNRAID_IP` | IP address | — | -| 2 | `UNRAID_SSH_USER` | Non-empty | — | -| 3 | `UNRAID_SSH_PORT` | Port | 22 | -| 4 | `UNRAID_GITEA_PORT` | Port | 3000 | -| 5 | `UNRAID_GITEA_SSH_PORT` | Port | 2222 | -| 6 | `UNRAID_GITEA_DATA_PATH` | Absolute path | — | -| 7 | `FEDORA_IP` | IP address | — | -| 8 | `FEDORA_SSH_USER` | Non-empty | — | -| 9 | `FEDORA_SSH_PORT` | Port | 22 | -| 10 | `FEDORA_GITEA_PORT` | Port | 3000 | -| 11 | `FEDORA_GITEA_SSH_PORT` | Port | 2222 | -| 12 | `FEDORA_GITEA_DATA_PATH` | Absolute path | — | -| 13 | `GITEA_ADMIN_USER` | Non-empty | — | -| 14 | `GITEA_ADMIN_PASSWORD` | Min 8 chars | — | -| 15 | `GITEA_ADMIN_EMAIL` | Email | — | -| 16 | `GITEA_ORG_NAME` | Non-empty | — | -| 17 | `GITEA_INSTANCE_NAME` | Non-empty | — | -| 18 | `GITEA_DB_TYPE` | Non-empty | sqlite3 | -| 19 | `GITEA_VERSION` | Non-empty | 1.23 | -| 20 | `ACT_RUNNER_VERSION` | Non-empty | 0.2.11 | -| 21 | `GITEA_DOMAIN` | Non-empty | — | -| 22 | `GITEA_INTERNAL_URL` | URL | — | -| 23 | `GITEA_BACKUP_INTERNAL_URL` | URL | — | -| 24 | `GITEA_BACKUP_MIRROR_INTERVAL` | Non-empty | 8h | -| 25 | `BACKUP_STORAGE_PATH` | Absolute path | — | -| 26 | `BACKUP_RETENTION_COUNT` | Integer | 5 | -| 27 | `GITHUB_USERNAME` | Non-empty | — | -| 28 | `GITHUB_TOKEN` | Non-empty | — | -| 29 | `REPO_NAMES` | Non-empty | — | -| 30 | `MIGRATE_ISSUES` | true/false | false | -| 33 | `MIGRATE_LABELS` | true/false | true | -| 34 | `MIGRATE_MILESTONES` | true/false | false | -| 35 | `MIGRATE_WIKI` | true/false | false | -| 36 | `GITHUB_MIRROR_INTERVAL` | Non-empty | 8h | -| 37 | `TLS_MODE` | `cloudflare` or `existing` | cloudflare | -| 38 | `CADDY_DOMAIN` | Non-empty | — | -| 39 | `CADDY_DATA_PATH` | Absolute path | — | -| 40 | `CLOUDFLARE_API_TOKEN` | Non-empty *(only if TLS_MODE=cloudflare)* | — | -| 41 | `SSL_CERT_PATH` | Absolute path *(only if TLS_MODE=existing)* | — | -| 42 | `SSL_KEY_PATH` | Absolute path *(only if TLS_MODE=existing)* | — | -| 44 | `PROTECTED_BRANCH` | Non-empty | main | -| 45 | `REQUIRE_PR_REVIEW` | true/false | false | -| 46 | `REQUIRED_APPROVALS` | Integer | 1 | -| 47 | `SEMGREP_VERSION` | Non-empty | latest | -| 48 | `TRIVY_VERSION` | Non-empty | latest | -| 49 | `GITLEAKS_VERSION` | Non-empty | latest | -| 50 | `SECURITY_FAIL_ON_ERROR` | true/false | true | +| 1-5 | `UNRAID_IP`, `UNRAID_SSH_USER`, `UNRAID_SSH_PORT`, `UNRAID_GITEA_DATA_PATH`, `UNRAID_SSH_KEY` | IP, non-empty, port, path, optional | —, —, 22, —, — | +| 6-10 | `FEDORA_IP`, `FEDORA_SSH_USER`, `FEDORA_SSH_PORT`, `FEDORA_GITEA_DATA_PATH`, `FEDORA_SSH_KEY` | IP, non-empty, port, path, optional | —, —, 22, —, — | +| 11-24 | Macvlan networking: `*_MACVLAN_PARENT`, `*_MACVLAN_SUBNET`, `*_MACVLAN_GATEWAY`, `*_MACVLAN_IP_RANGE`, `*_GITEA_IP`, `*_DB_IP`, `UNRAID_CADDY_IP` (per host) | non-empty, non-empty, IP, non-empty, IP, optional, IP | — | +| 25-30 | `GITEA_ADMIN_USER`, `GITEA_ADMIN_PASSWORD`, `GITEA_ADMIN_EMAIL`, `GITEA_ORG_NAME`, `GITEA_INSTANCE_NAME`, `GITEA_DB_TYPE` | non-empty, password, email, non-empty, non-empty, db_type | —, —, —, —, —, sqlite3 | +| 31-35 | *(conditional, only if DB_TYPE != sqlite3)* `GITEA_DB_HOST`, `GITEA_DB_PORT`, `GITEA_DB_NAME`, `GITEA_DB_USER`, `GITEA_DB_PASSWD` | non-empty, port, non-empty, non-empty, password | —, auto, gitea, gitea, — | +| 36-37 | `GITEA_VERSION`, `ACT_RUNNER_VERSION` | Non-empty | 1.25, 0.3.0 | +| 38-39 | `GITEA_DOMAIN`, `GITEA_INTERNAL_URL` | Non-empty, URL | — | +| 40-43 | `GITEA_BACKUP_INTERNAL_URL`, `GITEA_BACKUP_MIRROR_INTERVAL`, `BACKUP_STORAGE_PATH`, `BACKUP_RETENTION_COUNT` | URL, non-empty, path, integer | —, 8h, —, 5 | +| 44-45 | `GITHUB_USERNAME`, `GITHUB_TOKEN` | Non-empty | — | +| 46 | "How many repos?" + N × repo names → `REPO_NAMES` | positive integer, non-empty | — | +| 47-53 | `MIGRATE_ISSUES`, `MIGRATE_LABELS`, `MIGRATE_MILESTONES`, `MIGRATE_WIKI`, `MIGRATION_POLL_INTERVAL_SEC`, `MIGRATION_POLL_TIMEOUT_SEC`, `GITHUB_MIRROR_INTERVAL` | bool, bool, bool, bool, positive_integer, positive_integer, non-empty | false, true, false, false, 3, 600, 8h | +| 54-57 | `RUNNER_DEFAULT_IMAGE`, `RUNNER_DATA_BASE_PATH`, `LOCAL_RUNNER_DATA_BASE_PATH`, `LOCAL_REGISTRY` | non-empty, non-empty, non-empty, optional | catthehacker/ubuntu:act-latest, /mnt/nvme/gitea-runner, ~/gitea-runner, — | +| 58-60 | `TLS_MODE`, `CADDY_DOMAIN`, `CADDY_DATA_PATH` | tls_mode, non-empty, path | cloudflare, —, — | +| 61-63 | *(conditional)* `CLOUDFLARE_API_TOKEN` or `SSL_CERT_PATH` + `SSL_KEY_PATH` | non-empty / path | — | +| 64-66 | `PROTECTED_BRANCH`, `REQUIRE_PR_REVIEW`, `REQUIRED_APPROVALS` | Non-empty, bool, integer | main, false, 1 | +| 67-70 | `SEMGREP_VERSION`, `TRIVY_VERSION`, `GITLEAKS_VERSION`, `SECURITY_FAIL_ON_ERROR` | Non-empty, non-empty, non-empty, bool | latest, latest, latest, true | **Done when**: -- [ ] Each prompt shows progress: `[N/~62]` with section header when entering a new section +- [x] Each prompt shows progress: `[N/~70]` with section header when entering a new section - [ ] Running with no existing `.env` walks through all prompts and produces a valid `.env` - [ ] TLS prompts are conditional: if `TLS_MODE=cloudflare`, prompt for `CLOUDFLARE_API_TOKEN` only; if `TLS_MODE=existing`, prompt for `SSL_CERT_PATH` and `SSL_KEY_PATH` only - [ ] DB prompts are conditional: if `GITEA_DB_TYPE` is not `sqlite3`, prompt for host/port/name/user/password @@ -709,7 +683,7 @@ gitea-migration/ **Depends on**: preflight passed, templates exist **Produces**: Running Gitea instance on Unraid, admin user, API token in .env, org created -**`require_vars`**: `UNRAID_IP`, `UNRAID_SSH_USER`, `UNRAID_SSH_PORT`, `UNRAID_GITEA_PORT`, `UNRAID_GITEA_SSH_PORT`, `UNRAID_GITEA_DATA_PATH`, `GITEA_ADMIN_USER`, `GITEA_ADMIN_PASSWORD`, `GITEA_ADMIN_EMAIL`, `GITEA_ORG_NAME`, `GITEA_INSTANCE_NAME`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `GITEA_INTERNAL_URL`, `GITEA_DOMAIN` +**`require_vars`**: `UNRAID_IP`, `UNRAID_SSH_USER`, `UNRAID_SSH_PORT`, `UNRAID_GITEA_DATA_PATH`, `UNRAID_MACVLAN_PARENT`, `UNRAID_MACVLAN_SUBNET`, `UNRAID_MACVLAN_GATEWAY`, `UNRAID_MACVLAN_IP_RANGE`, `UNRAID_GITEA_IP`, `GITEA_ADMIN_USER`, `GITEA_ADMIN_PASSWORD`, `GITEA_ADMIN_EMAIL`, `GITEA_ORG_NAME`, `GITEA_INSTANCE_NAME`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `GITEA_INTERNAL_URL`, `GITEA_DOMAIN` (+ `UNRAID_DB_IP` + DB vars if DB_TYPE != sqlite3) **Steps with idempotency**: @@ -777,10 +751,10 @@ gitea-migration/ **Depends on**: preflight passed, templates exist **Produces**: Running Gitea instance on Fedora, admin user, API token in .env -**`require_vars`**: `FEDORA_IP`, `FEDORA_SSH_USER`, `FEDORA_SSH_PORT`, `FEDORA_GITEA_PORT`, `FEDORA_GITEA_SSH_PORT`, `FEDORA_GITEA_DATA_PATH`, `GITEA_ADMIN_USER`, `GITEA_ADMIN_PASSWORD`, `GITEA_ADMIN_EMAIL`, `GITEA_INSTANCE_NAME`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `GITEA_BACKUP_INTERNAL_URL` +**`require_vars`**: `FEDORA_IP`, `FEDORA_SSH_USER`, `FEDORA_SSH_PORT`, `FEDORA_GITEA_DATA_PATH`, `FEDORA_MACVLAN_PARENT`, `FEDORA_MACVLAN_SUBNET`, `FEDORA_MACVLAN_GATEWAY`, `FEDORA_MACVLAN_IP_RANGE`, `FEDORA_GITEA_IP`, `GITEA_ADMIN_USER`, `GITEA_ADMIN_PASSWORD`, `GITEA_ADMIN_EMAIL`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `GITEA_BACKUP_INTERNAL_URL` (+ `FEDORA_DB_IP` + DB vars if DB_TYPE != sqlite3) **Identical to phase1 except**: -- Target: Fedora (uses `FEDORA_IP`, `FEDORA_SSH_USER`, `FEDORA_SSH_PORT`, `FEDORA_GITEA_DATA_PATH`, `FEDORA_GITEA_PORT`) +- Target: Fedora (uses `FEDORA_IP`, `FEDORA_SSH_USER`, `FEDORA_SSH_PORT`, `FEDORA_GITEA_DATA_PATH`, `FEDORA_GITEA_IP`) - Uses `GITEA_BACKUP_INTERNAL_URL` for API calls - Saves token as `GITEA_BACKUP_ADMIN_TOKEN` - Uses same admin credentials (`GITEA_ADMIN_USER`/`GITEA_ADMIN_PASSWORD`/`GITEA_ADMIN_EMAIL`) @@ -884,23 +858,23 @@ For each runner in `runners.conf`: `manage_runner.sh remove --name `. Clea ### 8.1 — `phase4_migrate_repos.sh` **Depends on**: Phase 1 + Phase 2 completed (both Gitea instances running) -**Produces**: All 3 repos on Unraid primary under org + pull mirrors on Fedora +**Produces**: All repos on Unraid primary under org + pull mirrors on Fedora **`require_vars`**: `GITEA_ADMIN_TOKEN` *(auto)*, `GITEA_BACKUP_ADMIN_TOKEN` *(auto)*, `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` **Steps**: -For each `REPO_N_NAME` (N=1,2,3): +For each repo in `REPO_NAMES`: | # | Action | API call | Idempotency check | |---|--------|----------|-------------------| -| 1 | Import repo from GitHub to Unraid | `gitea_api POST /repos/migrate` with `clone_addr=https://github.com/$GITHUB_USERNAME/$REPO_N_NAME.git`, `auth_token=$GITHUB_TOKEN`, `repo_owner=$GITEA_ORG_NAME`, `repo_name=$REPO_N_NAME`, `mirror=false`, `issues=$MIGRATE_ISSUES`, `labels=$MIGRATE_LABELS`, `milestones=$MIGRATE_MILESTONES`, `wiki=$MIGRATE_WIKI` | `gitea_api GET /repos/$GITEA_ORG_NAME/$REPO_N_NAME` returns 200 | -| 2 | Wait for migration | Poll `gitea_api GET /repos/$GITEA_ORG_NAME/$REPO_N_NAME` until `empty=false` (repo has content) | — | -| 3 | Create pull mirror on Fedora | `gitea_backup_api POST /repos/migrate` with `clone_addr=$GITEA_INTERNAL_URL/$GITEA_ORG_NAME/$REPO_N_NAME.git`, `auth_username=$GITEA_ADMIN_USER`, `auth_password=$GITEA_ADMIN_PASSWORD`, `repo_owner=$GITEA_BACKUP_ADMIN_USER`, `mirror=true`, `mirror_interval=$GITEA_BACKUP_MIRROR_INTERVAL` | `gitea_backup_api GET /repos/$GITEA_BACKUP_ADMIN_USER/$REPO_N_NAME` returns 200 | +| 1 | Import repo from GitHub to Unraid | `gitea_api POST /repos/migrate` with `clone_addr=https://github.com/$GITHUB_USERNAME/$REPO.git`, `auth_token=$GITHUB_TOKEN`, `repo_owner=$GITEA_ORG_NAME`, `repo_name=$REPO`, `mirror=false`, `issues=$MIGRATE_ISSUES`, `labels=$MIGRATE_LABELS`, `milestones=$MIGRATE_MILESTONES`, `wiki=$MIGRATE_WIKI` | `gitea_api GET /repos/$GITEA_ORG_NAME/$REPO` returns 200 | +| 2 | Wait for migration | Poll `gitea_api GET /repos/$GITEA_ORG_NAME/$REPO` until `empty=false` (repo has content) | — | +| 3 | Create pull mirror on Fedora | `gitea_backup_api POST /repos/migrate` with `clone_addr=$GITEA_INTERNAL_URL/$GITEA_ORG_NAME/$REPO.git`, `auth_username=$GITEA_ADMIN_USER`, `auth_password=$GITEA_ADMIN_PASSWORD`, `repo_owner=$GITEA_ADMIN_USER`, `mirror=true`, `mirror_interval=$GITEA_BACKUP_MIRROR_INTERVAL` | `gitea_backup_api GET /repos/$GITEA_ADMIN_USER/$REPO` returns 200 | **Done when**: -- [ ] All 3 repos exist under `$GITEA_ORG_NAME` on Unraid with commits +- [ ] All repos exist under `$GITEA_ORG_NAME` on Unraid with commits - [ ] Each repo's default branch matches the GitHub source -- [ ] All 3 mirror repos exist on Fedora under admin user +- [ ] All mirror repos exist on Fedora under admin user - [ ] Fedora mirrors show `mirror=true` in API response - [ ] Fedora mirrors have synced at least once (has commits) - [ ] Running again skips all existing repos @@ -920,8 +894,8 @@ For each `REPO_N_NAME` (N=1,2,3): ### 8.3 — `phase4_teardown.sh` -1. For each repo: `gitea_api DELETE /repos/$GITEA_ORG_NAME/$REPO_N_NAME` -2. For each mirror: `gitea_backup_api DELETE /repos/$ADMIN/$REPO_N_NAME` +1. For each repo in `REPO_NAMES`: `gitea_api DELETE /repos/$GITEA_ORG_NAME/$REPO` +2. For each mirror: `gitea_backup_api DELETE /repos/$ADMIN/$REPO` 3. Prompt before each deletion --- diff --git a/README.md b/README.md index e2dac22..a558e8e 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ gitea-migration/ ├── runners.conf.example # Runner definitions template ├── lib/common.sh # Shared functions + .env validators ├── setup/ -│ ├── configure_env.sh # Interactive .env wizard (~65 prompts) +│ ├── configure_env.sh # Interactive .env wizard (~70 prompts) │ ├── configure_runners.sh # Interactive runner definition wizard │ ├── macbook.sh # Local prerequisites (brew packages) │ ├── unraid.sh # Remote prerequisites (static binaries) diff --git a/contracts/gitea-api.md b/contracts/gitea-api.md index a1f3ee2..4184b12 100644 --- a/contracts/gitea-api.md +++ b/contracts/gitea-api.md @@ -206,7 +206,7 @@ Auth: `Authorization: token ${GITEA_ADMIN_TOKEN}` (or `GITEA_BACKUP_ADMIN_TOKEN` ### GET /repos/{owner}/{repo}/commits -**Used in**: Phase 4 post-check +**Used in**: Phase 4 (migration polling), Phase 4 post-check, Phase 6 post-check (compare HEAD SHAs) **Purpose**: Verify repo has commits **Query params**: `?limit=1` @@ -217,7 +217,7 @@ Auth: `Authorization: token ${GITEA_ADMIN_TOKEN}` (or `GITEA_BACKUP_ADMIN_TOKEN` ### GET /repos/{owner}/{repo}/contents/{filepath} -**Used in**: Phase 5 post-check (check `.gitea/workflows/` exists), Phase 9 (check security workflow exists) +**Used in**: Phase 5 (idempotency check), Phase 5 post-check, Phase 5 teardown, Phase 9 (idempotency check), Phase 9 post-check, Phase 9 teardown **Purpose**: Get file or directory contents **Response** (200): File/directory metadata @@ -341,7 +341,7 @@ Auth: `Authorization: token ${GITEA_ADMIN_TOKEN}` (or `GITEA_BACKUP_ADMIN_TOKEN` ### POST /repos/{owner}/{repo}/branch_protections -**Used in**: Phase 7, Phase 9 (update with status checks) +**Used in**: Phase 7 **Purpose**: Create branch protection rule **Request**: @@ -383,7 +383,7 @@ Auth: `Authorization: token ${GITEA_ADMIN_TOKEN}` (or `GITEA_BACKUP_ADMIN_TOKEN` ### PATCH /repos/{owner}/{repo}/branch_protections/{name} -**Used in**: Phase 9 (add status check contexts) +**Used in**: Phase 9 (add status check contexts), Phase 9 teardown (clear status check contexts) **Purpose**: Update branch protection rule **Request** (partial update): @@ -400,7 +400,7 @@ Auth: `Authorization: token ${GITEA_ADMIN_TOKEN}` (or `GITEA_BACKUP_ADMIN_TOKEN` ### DELETE /repos/{owner}/{repo}/branch_protections/{name} -**Used in**: Phase 7 teardown, Phase 9 teardown +**Used in**: Phase 7 teardown **Purpose**: Delete branch protection rule **Response**: 204 No Content