diff --git a/PLAN.md b/PLAN.md index 5d527ec..706b53c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -256,7 +256,7 @@ gitea-migration/ | Function | Inputs | Behavior | Returns | |----------|--------|----------|---------| -| `load_env` | none | Sources `.env`, exports all vars. Exits 1 if `.env` missing. | 0 on success | +| `load_env` | none | Sources `.env`, exports all vars, and derives `GITEA_INTERNAL_URL`/`GITEA_BACKUP_INTERNAL_URL` from `UNRAID_GITEA_IP`/`FEDORA_GITEA_IP`. Exits 1 if `.env` missing. | 0 on success | | `save_env_var KEY VALUE` | key name, value string | If KEY exists in `.env`, replaces its line. If not, appends. Must not corrupt other lines. | 0 on success | | `require_vars VAR1 VAR2...` | variable names | For each: checks if exported and non-empty. Exits 1 naming the **first** missing var. | 0 if all set | | `log_info MSG` | message string | Prints `[INFO] MSG` to stderr in blue | — | @@ -497,9 +497,9 @@ gitea-migration/ - Passwords: minimum 8 characters - Emails: contains `@` - Paths: starts with `/` - - URLs: starts with `http://` or `https://` + - Optional paths: empty or starts with `/` or `~/` 5. After all prompts, write values to `.env` preserving the file structure (comments, sections) -6. Do NOT prompt for auto-populated vars (`GITEA_ADMIN_TOKEN`, `GITEA_BACKUP_ADMIN_TOKEN`, `GITEA_RUNNER_REGISTRATION_TOKEN`) +6. Do NOT prompt for auto-populated vars (`GITEA_ADMIN_TOKEN`, `GITEA_BACKUP_ADMIN_TOKEN`, `GITEA_RUNNER_REGISTRATION_TOKEN`) or derived vars (`GITEA_INTERNAL_URL`, `GITEA_BACKUP_INTERNAL_URL`) 7. Do NOT prompt for vars with defaults unless user wants to change them — show default, press Enter to accept 8. Print summary at end showing all configured values (passwords masked) @@ -513,19 +513,19 @@ gitea-migration/ | 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-55 | `RUNNER_DEFAULT_IMAGE`, `LOCAL_REGISTRY` | non-empty, optional | catthehacker/ubuntu:act-latest, — | -| 56-58 | `TLS_MODE`, `CADDY_DOMAIN`, `CADDY_DATA_PATH` | tls_mode, non-empty, path | cloudflare, —, — | -| 59-61 | *(conditional)* `CLOUDFLARE_API_TOKEN` or `SSL_CERT_PATH` + `SSL_KEY_PATH` | non-empty / path | — | -| 62-64 | `PROTECTED_BRANCH`, `REQUIRE_PR_REVIEW`, `REQUIRED_APPROVALS` | Non-empty, bool, integer | main, false, 1 | -| 65-68 | `SEMGREP_VERSION`, `TRIVY_VERSION`, `GITLEAKS_VERSION`, `SECURITY_FAIL_ON_ERROR` | Non-empty, non-empty, non-empty, bool | latest, latest, latest, true | +| 38 | `GITEA_DOMAIN` | Non-empty | — | +| 39-41 | `GITEA_BACKUP_MIRROR_INTERVAL`, `BACKUP_STORAGE_PATH`, `BACKUP_RETENTION_COUNT` | non-empty, path, integer | 8h, —, 5 | +| 42-43 | `GITHUB_USERNAME`, `GITHUB_TOKEN` | Non-empty | — | +| 44 | "How many repos?" + N × repo names → `REPO_NAMES` | positive integer, non-empty | — | +| 45-51 | `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 | +| 52-53 | `RUNNER_DEFAULT_IMAGE`, `LOCAL_REGISTRY` | non-empty, optional | catthehacker/ubuntu:act-latest, — | +| 54-56 | `TLS_MODE`, `CADDY_DOMAIN`, `CADDY_DATA_PATH` | tls_mode, non-empty, path | cloudflare, —, — | +| 57-59 | *(conditional)* `CLOUDFLARE_API_TOKEN` or `SSL_CERT_PATH` + `SSL_KEY_PATH` | non-empty / path | — | +| 60-62 | `PROTECTED_BRANCH`, `REQUIRE_PR_REVIEW`, `REQUIRED_APPROVALS` | Non-empty, bool, integer | main, false, 1 | +| 63-66 | `SEMGREP_VERSION`, `TRIVY_VERSION`, `GITLEAKS_VERSION`, `SECURITY_FAIL_ON_ERROR` | Non-empty, non-empty, non-empty, bool | latest, latest, latest, true | **Done when**: -- [x] Each prompt shows progress: `[N/~68]` with section header when entering a new section +- [x] Each prompt shows progress: `[N/~66]` 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 @@ -641,8 +641,6 @@ gitea-migration/ | Shared creds | `GITEA_ORG_NAME` | | Shared creds | `GITEA_INSTANCE_NAME` | | Primary | `GITEA_DOMAIN` | -| Primary | `GITEA_INTERNAL_URL` | -| Backup | `GITEA_BACKUP_INTERNAL_URL` | | Backup | `BACKUP_STORAGE_PATH` | | Repos | `GITHUB_USERNAME` | | Repos | `GITHUB_TOKEN` | @@ -653,7 +651,7 @@ gitea-migration/ | TLS/Caddy | `CADDY_DATA_PATH` | **Not checked** (have defaults or auto-populated): -`UNRAID_SSH_PORT`, `FEDORA_SSH_PORT`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `ACT_RUNNER_VERSION`, `GITEA_BACKUP_MIRROR_INTERVAL`, `BACKUP_RETENTION_COUNT`, `MIGRATE_*`, `GITHUB_MIRROR_INTERVAL`, `PROTECTED_BRANCH`, `REQUIRE_PR_REVIEW`, `REQUIRED_APPROVALS`, `SEMGREP_VERSION`, `TRIVY_VERSION`, `GITLEAKS_VERSION`, `SECURITY_FAIL_ON_ERROR`, `GITEA_ADMIN_TOKEN`, `GITEA_BACKUP_ADMIN_TOKEN`, `GITEA_RUNNER_REGISTRATION_TOKEN` +`UNRAID_SSH_PORT`, `FEDORA_SSH_PORT`, `GITEA_DB_TYPE`, `GITEA_VERSION`, `ACT_RUNNER_VERSION`, `GITEA_INTERNAL_URL`, `GITEA_BACKUP_INTERNAL_URL`, `GITEA_BACKUP_MIRROR_INTERVAL`, `BACKUP_RETENTION_COUNT`, `MIGRATE_*`, `GITHUB_MIRROR_INTERVAL`, `PROTECTED_BRANCH`, `REQUIRE_PR_REVIEW`, `REQUIRED_APPROVALS`, `SEMGREP_VERSION`, `TRIVY_VERSION`, `GITLEAKS_VERSION`, `SECURITY_FAIL_ON_ERROR`, `GITEA_ADMIN_TOKEN`, `GITEA_BACKUP_ADMIN_TOKEN`, `GITEA_RUNNER_REGISTRATION_TOKEN` | 4 | SSH to Unraid | `ssh_check UNRAID` returns 0 | "Cannot SSH to Unraid at $UNRAID_IP. Run setup/unraid.sh or check SSH config." | | 5 | SSH to Fedora | `ssh_check FEDORA` returns 0 | Same pattern | | 6 | Docker on Unraid | `ssh_exec UNRAID "docker --version"` exits 0 | "Docker not found on Unraid. Run setup/unraid.sh." | @@ -683,7 +681,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_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) +**`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_DOMAIN` (+ `UNRAID_DB_IP` + DB vars if DB_TYPE != sqlite3) **Steps with idempotency**: @@ -751,11 +749,11 @@ 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_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) +**`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` (+ `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_IP`) -- Uses `GITEA_BACKUP_INTERNAL_URL` for API calls +- Uses `GITEA_BACKUP_INTERNAL_URL` (derived from `FEDORA_GITEA_IP`) for API calls - Saves token as `GITEA_BACKUP_ADMIN_TOKEN` - Uses same admin credentials (`GITEA_ADMIN_USER`/`GITEA_ADMIN_PASSWORD`/`GITEA_ADMIN_EMAIL`) - Does NOT create an org (mirrors will be under admin user's namespace)