# Gitea → OneDev migration plan Executes ADR-0029 (supersedes ADR-0007). Scope: this repo's own self-hosting only — git history, issues, milestones, wiki (already mirrored), and this repo's own tooling config. `plugins/gitea/` ships unchanged as a marketplace product. Historical PR objects and Gitea's exact issue/PR numbering are explicitly not migrated (see ADR-0029's "Considered and rejected"). Source: `Defame1297/holocron` on `git.dev.rkdr.net`. Target: `Holocron` (project id 1, currently empty) on `onedev.dev.rkdr.net`. ## Prerequisites - [x] `tod` installed and configured — `~/.config/tod/config` already has `server-url` and `access-token`; `~/.bashrc` sources `~/.config/tod/env` automatically (`set -a; . env; set +a`), but non-interactive shells (scripts, CI, this tool) must source it explicitly per invocation. - [x] OneDev project `Holocron` exists (`tod project get Holocron`), `codeManagement` / `issueManagement` enabled, currently no `defaultBranch` (empty repo). - [ ] **Gotcha to build scripts around:** `tod issue`/`tod pr` subcommands resolve their target project from the working directory's git remote, not from `--project` (verified — `--project` is accepted by the flag parser but ignored; commands fail outside a repo with a OneDev remote). Every migration script step below must run from inside a local clone with a remote pointing at `onedev.dev.rkdr.net/Holocron`. - [ ] Create the 7 OneDev **Iterations** (milestone equivalent) manually via the OneDev web UI — `tod` has no iteration-create command. Names, verbatim, to match Gitea milestones for clean `--iteration` references on migrated issues: `Governance: enforcement`, `Kyberforge basics`, `Legacy / Triage`, `Road to homelab - prep`, `Skills & Agents`, `The great refactoring`, `Tooling`. - [ ] Freeze Gitea: stop merging PRs there once Phase 1 starts. Solo-maintainer repo, so this is just "don't push to `git.dev.rkdr.net` after the mirror point." ## Phase 1 — Mirror git history (lossless, zero risk) 1. `git remote add onedev https://onedev.dev.rkdr.net/Holocron` in the local clone. 2. `git push onedev refs/heads/*:refs/heads/* refs/tags/*:refs/tags/*` (explicit branch+tag push, not `--mirror` — avoids touching any Gitea-internal refs that aren't real branches). 3. Verify: `tod project get Holocron` shows `defaultBranch` populated; HEAD of `main` on OneDev matches Gitea `main` HEAD (`d654dca...` as of this plan). 4. This alone carries every commit, author, message, and merge losslessly — nothing else in this plan is required for code-level fidelity. ## Phase 2 — Issues (~95 total: 81 closed + 14 open across 7 milestones, per Gitea milestone counts) Field mapping (OneDev's out-of-the-box `Type`/`Priority` fields, no new labels created): | Gitea label | OneDev field | |---|---| | `Kind/Bug` | `Type: Bug` | | `Kind/Feature` | `Type: New Feature` | | `Kind/Enhancement` | `Type: Improvement` | | `Kind/Documentation`, `Kind/Testing` | `Type: Task` | | `Kind/Security` | `Type: Bug` | | `Priority/Critical` | `Priority: Critical` | | `Priority/High` | `Priority: Major` | | `Priority/Medium` | `Priority: Normal` | | `Priority/Low` | `Priority: Minor` | | `Reviewed/*`, `Status/*`, `Compat/Breaking` | no field/state equivalent — fold into a one-line note in the migrated body | State mapping: Gitea `open` → OneDev `Open` (default, no action); Gitea `closed` → `tod issue change-state Closed`. OneDev's out-of-the-box workflow only has Open/In Progress/Closed — no disposition states, confirmed by probing the live server. Per issue: 5. `tod issue create "" --field Type=<mapped> --field Priority=<mapped> --iteration "<milestone>" --description "<body>\n\n---\nMigrated from git.dev.rkdr.net/Defame1297/holocron/issues/<N>.[\nGitea disposition: <label>.]"` 6. Replay comments via `tod issue add-comment` (low effort, worth doing for continuity). 7. `tod issue change-state <new-ref> Closed` for originally-closed issues. 8. Spot-check a sample (e.g. 5 issues across different milestones) against the Gitea source. Numbers will not match Gitea's (accepted — ADR-0029). Author/submitter on migrated issues will be the migration token's own OneDev account, not the original Gitea author — no CLI-exposed way to override this (the `onBehalfOf` field exists in OneDev's issue schema but isn't exposed through `tod`; using it would mean raw, unverified REST calls, not worth it for this scope). ## Phase 3 — Pull requests - **Currently-open PRs only** (check at execution time: `list_pull_requests state=open`): migrate for real via `tod pr create`, since the source branch still exists. Add description, reviewers. - **Closed/merged historical PRs (~140 of them): explicitly skipped.** Per ADR-0029, their content survives losslessly in the Phase 1 git mirror; the archived Gitea instance remains the record for anyone who wants the original review thread. ## Phase 4 — Releases Git tags (`v1.0.0`, `v2.0.0`, `v2.0.1`) carry over automatically in Phase 1. OneDev has no confirmed first-class "Release" object with a rendered markdown body the way Gitea does — this needs a quick check against the live server before deciding further (not yet verified in this session). Fallback if none exists: leave the 3 release-note bodies in the archived Gitea (read-only) and optionally fold them into a `CHANGELOG.md` in the repo for local discoverability. **Flag this to the user before executing Phase 4** — not fully resolved. ## Phase 5 — Wiki Nothing to do. `docs/wiki/HUMANS.md` and `docs/wiki/Home.md` already mirror the two Gitea wiki pages in-repo (confirmed identical), and OneDev has no separate wiki feature to migrate into (confirmed: no wiki flag on the project object, no wiki REST resource). They travel with Phase 1 automatically. ## Phase 6 — Repo self-reference updates (code changes) 9. Repoint the six first-party `apm.yml` plugin dependencies (`git`, `gitea`, `kyberforge`, `lint`, `core`, `bin`) from `git@git.dev.rkdr.net:Defame1297/holocron.git` to the OneDev remote. 10. Update `AGENTS.md`'s routing table (currently: *"Issues, PRs, labels, milestones → `gitea-issues`, `gitea-prs`, `gitea-labels-milestones`..."*) to route this repo's own issue/PR operations to the OneDev/tod skills instead (`using-tod` as the catch-all, plus `work-on-issue`, `work-on-pull-request`, `submit-issue-work`, `submit-pull-request-work`). This needs a deliberate mapping pass, not a mechanical find-replace — the tod skillset is workflow-shaped, not CRUD-shaped like the gitea skills it replaces. 11. Update local `origin` remote to point at OneDev; rename the old one (e.g. `git remote rename origin gitea-archive`) rather than deleting it. 12. Run `apm install` against the repointed remote and verify it resolves cleanly. 13. Sweep `README.md` and any other doc prose that names Gitea as *this repo's own* host (separate from `plugins/gitea/`'s own product documentation, which is unaffected). ## Phase 7 — Freeze and archive Gitea 14. Set the Gitea repository to read-only/archived via the Gitea web UI (no MCP tool exposes this — manual step). ## Verification checklist - [ ] `tod project get Holocron` → `defaultBranch: main`, HEAD SHA matches Gitea's `main`. - [ ] Issue count on OneDev matches Gitea's ~95 (open + closed). - [ ] `apm install` succeeds from a fresh clone against the new remote. - [ ] Pre-commit hooks (`pre-commit run --all-files`) pass in a fresh OneDev clone. - [ ] Gitea repo is read-only; a test push to it fails as expected. ## Explicitly out of scope (deferred, per earlier decisions) - `.onedev-buildspec.yml` / CI setup — no Gitea Actions exist today to migrate; separate follow-up task via the `edit-build-spec` skill. - Sunsetting `plugins/gitea/` as a marketplace product — agreed as a *later* phase, not part of this migration.