# Phase 3 Plan: Split the God File into an Organized Codebase ## Goal Refactor the current monolithic `cmd/gitocean/main.go` into a maintainable, testable, multi-package Go codebase without changing user-facing behavior. Phase 3 is primarily a structural refactor. The CLI commands, API routes, Git HTTP behavior, MySQL schema, storage layout, and auth rules should continue to work exactly as they do after Phase 2. Phase 3 should also finish documenting and preserving the CLI UX direction from Phase 2: human-readable CLI output by default, with JSON available only as an explicit opt-in where useful. This does **not** mean removing JSON from the HTTP API; API requests and responses should remain JSON. ## Non-goals - Do not add a Web UI. - Do not add SSH Git transport. - Do not replace MySQL. - Do not redesign API routes unless a compatibility shim is kept. - Do not introduce large frameworks. - Do not implement new product features until the refactor is stable. - Do not remove JSON from the HTTP API. Only CLI output should become human-readable by default. ## Current problem `cmd/gitocean/main.go` currently contains everything: - Type definitions - CLI command routing and command implementations - HTTP server bootstrapping - API route dispatch - Auth/token logic - User/repository/PR/collaborator handlers - Git HTTP backend integration - MySQL migrations and DB helpers - Config loading/saving - Git helper functions - Backup/restore logic - Output formatting - Generic HTTP JSON helpers This makes the code hard to test, hard to extend, and risky to modify. ## Refactor principles 1. **No behavior changes first** - Move code into packages with minimal edits. - Keep route paths, JSON payloads, CLI command names, flags, and output stable unless explicitly noted. 2. **Small, reviewable steps** - Move one domain at a time. - Run `gofmt`, `go test ./...`, and `go vet ./...` after each major move. 3. **Human-readable CLI by default** - During CLI extraction, audit commands that still print raw JSON. - Convert default CLI output to concise human-readable text/tables. - Keep `--json` as an explicit escape hatch for scripting where appropriate. - Do not change API JSON payloads. 4. **Thin `main` package** - `cmd/gitocean/main.go` should only call into an application package. 5. **Reusable packages** - Shared utilities should live in `internal/` packages, not in `cmd/`. 6. **Domain boundaries over technical dumping grounds** - Prefer packages like `repos`, `pulls`, `auth`, and `gitserver` over one huge `utils` package. 7. **Testable dependencies** - Handlers and services should accept dependencies through structs. - Avoid package-level global state except constants and regex validators. ## Target directory layout ```text cmd/ gitocean/ main.go # Tiny entrypoint only internal/ app/ app.go # Top-level CLI dispatch / application orchestration usage.go # Help text config/ client.go # CLI config: ~/.config/gitocean/config.json server.go # Server config: storage/config.json env.go # Environment defaults db/ mysql.go # MySQL open/create database logic migrate.go # Migrations model/ user.go repository.go pull_request.go collaborator.go token.go ref.go httpapi/ server.go # Server struct and ServeHTTP router.go # API route dispatch json.go # decodeJSON/writeJSON/writeError auth/ handlers.go # register/login/logout/me handlers service.go # token generation, hashing, bearer/basic auth password.go # password hashing helpers if needed tokens/ handlers.go # token list/revoke/prune handlers admin/ handlers.go # admin users/repos/storage/token routes repos/ handlers.go # repo create/get/update/delete/search/fork service.go # repo access checks and repo loading collaborators.go # collaborator handlers and role logic validate.go # repo/user name validation helpers if not shared pulls/ handlers.go # PR create/list/view/close/merge/diff/comments service.go # PR loading, scanning, merge/diff helpers gitserver/ http.go # Smart HTTP route parsing and permissions backend.go # git http-backend CGI bridge gitutil/ git.go # runGit, gitInitBare, gitBranchExists, gitRefs cli/ root.go # CLI command dispatch auth.go # register/login/logout/whoami repo.go # repo command dispatch repo_create.go repo_publish.go repo_view.go repo_collaborators.go pr.go token.go admin.go backup.go output.go # CLI formatting helpers parse.go # splitOwnerRepo, splitRepoBranch, flag parsing prompt.go api_client.go # CLI HTTP client helper backup/ backup.go # createBackup/restoreBackup/mysqlCLIArgs/copyDir validate/ names.go # username/repo/branch validation and reserved names ``` The exact final package names can change during implementation, but the direction should stay the same: small domain packages with clear responsibilities. ## Dependency direction Recommended dependency flow: ```text cmd/gitocean -> internal/app -> internal/cli -> internal/httpapi -> internal/config -> internal/db httpapi -> auth, tokens, admin, repos, pulls, gitserver -> model repos/pulls/auth/etc. -> model -> gitutil where needed -> validate where needed cli -> config -> model -> backup where needed ``` Avoid circular dependencies by keeping shared types in `internal/model` and shared helpers in targeted utility packages. ## Proposed milestones ### Milestone 1: Prepare shared model and validation packages Move pure data/types and validators first. Create: - `internal/model` - `internal/validate` Move: - `User` - `Repository` - `PullRequest` - `ServerConfig` - `RefInfo` - `Collaborator` - `PRComment` - `TokenInfo` - username/repo/branch regex validation - `isReservedName` Acceptance: - `cmd/gitocean/main.go` still builds. - All references use `model.User`, `model.Repository`, etc. - `go test ./...` passes. ### Milestone 2: Extract config and generic helpers Create: - `internal/config` - `internal/httpapi/json.go` - `internal/gitutil` Move: - CLI config path/load/save - server config path/load/save - env server URL helper - JSON request/response helpers where appropriate - `gitInitBare` - `gitBranchExists` - `gitRefs` - `runGit` Acceptance: - No behavior changes. - CLI login still saves config. - Server still reads config and env overrides. - Git helper functions are reusable outside `main`. ### Milestone 3: Extract DB connection and migrations Create: - `internal/db` Move: - `openMySQLAndCreateDatabaseIfMissing` - `quoteMySQLIdentifier` - `migrate` - `isDuplicateColumnError` Acceptance: - Server starts from empty DB. - Missing database auto-create behavior still works. - Migrations are isolated and testable. ### Milestone 4: Extract HTTP API server shell Create: - `internal/httpapi/server.go` - `internal/httpapi/router.go` Move: - `Server` struct - `ServeHTTP` - top-level API route dispatch - subroute dispatch helpers Keep domain handler method bodies temporarily in `httpapi` if needed, then move them by domain in later milestones. Acceptance: - `runServer` constructs `httpapi.Server`. - API route paths remain unchanged. - Git HTTP routes still reach the backend. ### Milestone 5: Extract auth and token domains Create: - `internal/auth` - `internal/tokens` Move: - register/login/logout/me handlers - token creation/hash helpers - bearer/basic auth helpers - token list/revoke/prune handlers - direct admin-user creation helper used by `init` Acceptance: - Registration still makes the first user admin. - Login still returns 7-day tokens. - CLI login/whoami/logout still work. - Git basic auth still works with username/token. ### Milestone 6: Extract repository domain Create: - `internal/repos` Move: - repo create/get/update/delete/search/fork handlers - repository load/scan helpers - repo access helpers: - `requireReadableRepo` - `canReadRepo` - `canWriteRepo` - `collaboratorRole` - collaborator list/add/remove handlers Acceptance: - Public/private visibility rules still work. - Private collaborators can read when allowed. - Write collaborators can push when allowed. - Archived repositories still reject pushes. - Repo CLI commands still work. ### Milestone 7: Extract pull request domain Create: - `internal/pulls` Move: - PR create/list/view/close/merge handlers - PR comments handlers - PR diff handler - `prSelectSQL` - `loadPR` - `scanOnePR` - `scanPRs` - `mergePR` - `prDiff` Acceptance: - Same-repo PRs still work. - Cross-repo PRs still work. - PR merge still uses Git commands correctly. - PR diff/comments endpoints still work. ### Milestone 8: Extract Git Smart HTTP server Create: - `internal/gitserver` Move: - `handleGitHTTP` - `parseGitPath` - `gitService` - `runGitHTTPBackend` - `writeCGIResponse` Design note: `gitserver` should depend on a small permission interface instead of importing the entire repo handler package if possible: ```go type RepoAccess interface { LoadRepo(owner, name string) (model.Repository, error) CanReadRepo(repo model.Repository, user model.User, authed bool) bool CanWriteRepo(repo model.Repository, user model.User) bool UserFromBasic(r *http.Request) (model.User, bool) } ``` Acceptance: - Public clone/fetch still works without auth. - Private clone/fetch works for owner/collaborators only. - Push requires auth. - Archived repos reject pushes. - Git HTTP backend response handling remains correct. ### Milestone 9: Extract CLI package Create: - `internal/cli` Move: - command dispatch - `usage` - `cliInit` - auth CLI commands - repo CLI commands - PR CLI commands - token/admin/backup CLI commands - CLI API request helper - output formatting helpers - prompt helpers - parsing helpers - Git credential approval helper Acceptance: - `cmd/gitocean/main.go` is reduced to roughly: ```go package main import ( "fmt" "os" "gitocean/internal/app" ) func main() { if err := app.Run(os.Args[1:]); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } ``` - Every existing CLI command still works. - Help text is unchanged except for intentional wording cleanup. ### Milestone 10: Extract backup package Create: - `internal/backup` Move: - `createBackup` - `restoreBackup` - `mysqlCLIArgs` - `copyDir` Acceptance: - `gitocean backup create FILE` still works. - `gitocean backup restore FILE` still works. - Backup logic is testable independently. ### Milestone 11: Add package-level tests Add focused unit tests where possible. Suggested tests: - `internal/validate` - username validation - repo name validation - branch validation - reserved names - `internal/cli` - `splitOwnerRepo` - `splitRepoBranch` - `parseRepoCreateArgs` - `parseRepoPublishArgs` - boolean flag removal - `internal/config` - client config load/save round trip - server config load/save round trip - env override behavior - `internal/gitserver` - `parseGitPath` - `gitService` - CGI response parsing - `internal/db` - MySQL identifier quoting Acceptance: - `go test ./...` includes meaningful tests. - Tests avoid requiring a live MySQL instance unless explicitly integration-tagged. ### Milestone 12: CLI output audit After the CLI package has been extracted, audit every CLI command for output style. Commands should default to human-readable output: - Short success messages for create/update/delete actions. - Tables for lists. - Labeled fields for detail views. - Helpful next-step commands where useful. - Clear auth/session error messages. JSON should be retained only as an explicit opt-in for scripting, for example: ```bash gitocean repo view OWNER/REPO --json gitocean repo search QUERY --json gitocean pr view OWNER/REPO NUMBER --json gitocean token list --json ``` Acceptance: - No normal CLI command prints raw JSON by default. - Commands that are useful in scripts have a documented `--json` option. - HTTP API responses remain JSON and are not changed by this milestone. ### Milestone 13: Documentation cleanup Update docs/plans after the refactor: - Update `plans/PLAN.md` if architecture descriptions changed. - Update `plans/PHASE_2_PLAN.md` if current layout references are stale. - Add a short `README.md` if missing with: - quickstart - server setup - CLI commands - storage layout - development workflow Acceptance: - New contributors can find where CLI, API, DB, Git HTTP, and models live. ## Suggested implementation order Recommended commit sequence: 1. `Add Phase 3 refactor plan` 2. `Move shared models and validators` 3. `Extract config and Git utilities` 4. `Extract database setup and migrations` 5. `Extract HTTP API server shell` 6. `Extract auth and token handlers` 7. `Extract repository handlers` 8. `Extract pull request handlers` 9. `Extract Git HTTP backend` 10. `Extract CLI commands` 11. `Extract backup utilities` 12. `Audit CLI output and JSON escape hatches` 13. `Add package-level tests and documentation` ## Compatibility checklist After each milestone, run: ```bash gofmt -w . go test ./... go vet ./... ``` Before declaring Phase 3 complete, manually verify: ```bash gitocean init gitocean server --config storage/config.json gitocean register gitocean login gitocean whoami gitocean repo create demo --public gitocean repo view USER/demo gitocean repo branches USER/demo gitocean repo tags USER/demo gitocean repo search demo --all gitocean clone USER/demo gitocean repo publish demo2 --public gitocean repo fork USER/demo gitocean pr create --from USER/demo-fork:main --to USER/demo:main --title "Test PR" gitocean pr list USER/demo gitocean pr view USER/demo 1 gitocean pr diff USER/demo 1 gitocean pr comment USER/demo 1 "Looks good" gitocean pr comments USER/demo 1 gitocean token list gitocean admin users list gitocean backup create backup.tar.gz ``` Also verify Git HTTP manually: ```bash git clone http://localhost:8080/USER/demo.git cd demo echo test >> README.md git add README.md git commit -m "test push" git push origin main ``` ## Risks and mitigations ### Risk: Circular package dependencies Mitigation: - Put shared structs in `internal/model`. - Put small interfaces between domains where needed. - Keep HTTP response helpers separate from domain services. ### Risk: Refactor changes behavior accidentally Mitigation: - Move code first, improve code second. - Keep commits small. - Add tests for parsing/routing helpers early. ### Risk: Handlers still know too much about SQL Mitigation: - Phase 3 can keep SQL in domain packages. - A later phase can introduce repository/store interfaces if needed. ### Risk: `utils` package becomes another god package Mitigation: - Only use utility packages for genuinely cross-domain helpers. - Prefer domain packages. ## Phase 3 completion criteria Phase 3 is complete when: - `cmd/gitocean/main.go` is a thin entrypoint. - No package contains unrelated CLI, API, DB, Git, and model code together. - Domain packages have clear responsibilities. - Existing CLI commands and API routes still work. - CLI output is human-readable by default, with `--json` opt-in where useful. - `go test ./...` passes. - `go vet ./...` passes. - Meaningful unit tests exist for parsing, validation, config, and Git HTTP helpers.