Edit plans/PHASE_1_PLAN.md
This commit is contained in:
+337
-337
@@ -1,337 +1,337 @@
|
|||||||
# Gitocean Phase 1 Plan: MVP
|
# Gitocean Phase 1 Plan: MVP
|
||||||
|
|
||||||
Phase 1 focuses on building the minimum viable Git hosting platform: CLI + API only, MySQL metadata, repositories stored on disk, HTTP Smart Git transport, public/private repositories, search, forks, and pull requests.
|
Phase 1 focuses on building the minimum viable Git hosting platform: CLI + API only, MySQL metadata, repositories stored on disk, HTTP Smart Git transport, public/private repositories, search, forks, and pull requests.
|
||||||
|
|
||||||
This phase intentionally avoids a web UI, SSH Git transport, organizations, teams, CI/CD, and enterprise features. The goal is a working personal Git hosting foundation.
|
This phase intentionally avoids a web UI, SSH Git transport, organizations, teams, CI/CD, and enterprise features. The goal is a working personal Git hosting foundation.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
- Build a Go-based Git repository hosting platform.
|
- Build a Go-based Git repository hosting platform.
|
||||||
- Store bare repositories under `storage/repos/{owner}/{repo}.git`.
|
- Store bare repositories under `storage/repos/{owner}/{repo}.git`.
|
||||||
- Use MySQL for metadata.
|
- Use MySQL for metadata.
|
||||||
- Support HTTP Smart Git clone/fetch/push.
|
- Support HTTP Smart Git clone/fetch/push.
|
||||||
- Provide CLI-based registration, login, repository management, search, forks, and pull requests.
|
- Provide CLI-based registration, login, repository management, search, forks, and pull requests.
|
||||||
- Support public and private repositories.
|
- Support public and private repositories.
|
||||||
- Use temporary 7-day API/Git auth tokens.
|
- Use temporary 7-day API/Git auth tokens.
|
||||||
- Keep permissions simple: owner-only writes and private access in MVP.
|
- Keep permissions simple: owner-only writes and private access in MVP.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Product decisions
|
## 1. Product decisions
|
||||||
|
|
||||||
### Platform shape
|
### Platform shape
|
||||||
|
|
||||||
- Gitocean is a CLI + API Git hosting system.
|
- Gitocean is a CLI + API Git hosting system.
|
||||||
- No Web UI in Phase 1.
|
- No Web UI in Phase 1.
|
||||||
- No SSH Git transport in Phase 1.
|
- No SSH Git transport in Phase 1.
|
||||||
- Repositories are stored as bare Git repositories on disk.
|
- Repositories are stored as bare Git repositories on disk.
|
||||||
- Metadata is stored in MySQL.
|
- Metadata is stored in MySQL.
|
||||||
|
|
||||||
### Registration and login
|
### Registration and login
|
||||||
|
|
||||||
- Registration is open.
|
- Registration is open.
|
||||||
- Users register with:
|
- Users register with:
|
||||||
- email
|
- email
|
||||||
- username
|
- username
|
||||||
- password
|
- password
|
||||||
- Users can log in with either:
|
- Users can log in with either:
|
||||||
- email + password
|
- email + password
|
||||||
- username + password
|
- username + password
|
||||||
- Login returns a temporary token that expires after 7 days.
|
- Login returns a temporary token that expires after 7 days.
|
||||||
- The CLI stores the token locally for API calls.
|
- The CLI stores the token locally for API calls.
|
||||||
- The CLI can approve the token into Git's credential helper for HTTP Git operations.
|
- The CLI can approve the token into Git's credential helper for HTTP Git operations.
|
||||||
|
|
||||||
### Naming defaults
|
### Naming defaults
|
||||||
|
|
||||||
- CLI binary name: `gitocean`.
|
- CLI binary name: `gitocean`.
|
||||||
- Default branch: `main`.
|
- Default branch: `main`.
|
||||||
- Repository names allow lowercase letters, numbers, `.`, `_`, and `-`.
|
- Repository names allow lowercase letters, numbers, `.`, `_`, and `-`.
|
||||||
- Usernames allow lowercase letters, numbers, `_`, and `-`.
|
- Usernames allow lowercase letters, numbers, `_`, and `-`.
|
||||||
- Pull request numbers are scoped per target repository.
|
- Pull request numbers are scoped per target repository.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Permissions
|
## 2. Permissions
|
||||||
|
|
||||||
### Repository visibility
|
### Repository visibility
|
||||||
|
|
||||||
Public repositories:
|
Public repositories:
|
||||||
|
|
||||||
- Anyone can clone/fetch.
|
- Anyone can clone/fetch.
|
||||||
- Only the owner can push.
|
- Only the owner can push.
|
||||||
- Only the owner can delete or manage settings.
|
- Only the owner can delete or manage settings.
|
||||||
|
|
||||||
Private repositories:
|
Private repositories:
|
||||||
|
|
||||||
- Only the owner can clone/fetch.
|
- Only the owner can clone/fetch.
|
||||||
- Only the owner can push.
|
- Only the owner can push.
|
||||||
- Only the owner can delete or manage settings.
|
- Only the owner can delete or manage settings.
|
||||||
|
|
||||||
### Writes
|
### Writes
|
||||||
|
|
||||||
- Phase 1 uses owner-only writes.
|
- Phase 1 uses owner-only writes.
|
||||||
- No collaborators, organizations, teams, or fine-grained permissions yet.
|
- No collaborators, organizations, teams, or fine-grained permissions yet.
|
||||||
- Outside collaboration happens through pull requests against public repositories.
|
- Outside collaboration happens through pull requests against public repositories.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Storage layout
|
## 3. Storage layout
|
||||||
|
|
||||||
Repositories live under:
|
Repositories live under:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
storage/repos/{owner}/{repo}.git
|
storage/repos/{owner}/{repo}.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
storage/
|
storage/
|
||||||
repos/
|
repos/
|
||||||
alice/
|
alice/
|
||||||
demo.git/
|
demo.git/
|
||||||
bob/
|
bob/
|
||||||
project.git/
|
project.git/
|
||||||
```
|
```
|
||||||
|
|
||||||
Each repository directory is a bare Git repository created with `git init --bare`.
|
Each repository directory is a bare Git repository created with `git init --bare`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. MySQL schema
|
## 4. MySQL schema
|
||||||
|
|
||||||
Core tables:
|
Core tables:
|
||||||
|
|
||||||
### `users`
|
### `users`
|
||||||
|
|
||||||
- `id`
|
- `id`
|
||||||
- `email`
|
- `email`
|
||||||
- `username`
|
- `username`
|
||||||
- `password_hash`
|
- `password_hash`
|
||||||
- `created_at`
|
- `created_at`
|
||||||
|
|
||||||
### `auth_tokens`
|
### `auth_tokens`
|
||||||
|
|
||||||
- `id`
|
- `id`
|
||||||
- `user_id`
|
- `user_id`
|
||||||
- `token_hash`
|
- `token_hash`
|
||||||
- `expires_at`
|
- `expires_at`
|
||||||
- `revoked_at`
|
- `revoked_at`
|
||||||
- `created_at`
|
- `created_at`
|
||||||
|
|
||||||
### `repositories`
|
### `repositories`
|
||||||
|
|
||||||
- `id`
|
- `id`
|
||||||
- `owner_user_id`
|
- `owner_user_id`
|
||||||
- `name`
|
- `name`
|
||||||
- `visibility`: `public` or `private`
|
- `visibility`: `public` or `private`
|
||||||
- `forked_from_repository_id` nullable
|
- `forked_from_repository_id` nullable
|
||||||
- `created_at`
|
- `created_at`
|
||||||
- `updated_at`
|
- `updated_at`
|
||||||
|
|
||||||
### `pull_requests`
|
### `pull_requests`
|
||||||
|
|
||||||
- `id`
|
- `id`
|
||||||
- `target_repository_id`
|
- `target_repository_id`
|
||||||
- `number`
|
- `number`
|
||||||
- `author_user_id`
|
- `author_user_id`
|
||||||
- `source_repository_id`
|
- `source_repository_id`
|
||||||
- `source_branch`
|
- `source_branch`
|
||||||
- `target_branch`
|
- `target_branch`
|
||||||
- `title`
|
- `title`
|
||||||
- `description`
|
- `description`
|
||||||
- `status`: `open`, `closed`, or `merged`
|
- `status`: `open`, `closed`, or `merged`
|
||||||
- `closed_at` nullable
|
- `closed_at` nullable
|
||||||
- `merged_at` nullable
|
- `merged_at` nullable
|
||||||
- `created_at`
|
- `created_at`
|
||||||
- `updated_at`
|
- `updated_at`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. API routes
|
## 5. API routes
|
||||||
|
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
```http
|
```http
|
||||||
POST /api/register
|
POST /api/register
|
||||||
POST /api/login
|
POST /api/login
|
||||||
POST /api/logout
|
POST /api/logout
|
||||||
GET /api/me
|
GET /api/me
|
||||||
```
|
```
|
||||||
|
|
||||||
### Repositories
|
### Repositories
|
||||||
|
|
||||||
```http
|
```http
|
||||||
POST /api/repos
|
POST /api/repos
|
||||||
GET /api/repos/search?q=demo&scope=all
|
GET /api/repos/search?q=demo&scope=all
|
||||||
GET /api/repos/search?q=demo&scope=mine
|
GET /api/repos/search?q=demo&scope=mine
|
||||||
GET /api/repos/{owner}/{repo}
|
GET /api/repos/{owner}/{repo}
|
||||||
DELETE /api/repos/{owner}/{repo}?force=false
|
DELETE /api/repos/{owner}/{repo}?force=false
|
||||||
POST /api/repos/{owner}/{repo}/fork
|
POST /api/repos/{owner}/{repo}/fork
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pull requests
|
### Pull requests
|
||||||
|
|
||||||
```http
|
```http
|
||||||
POST /api/repos/{owner}/{repo}/pulls
|
POST /api/repos/{owner}/{repo}/pulls
|
||||||
GET /api/repos/{owner}/{repo}/pulls
|
GET /api/repos/{owner}/{repo}/pulls
|
||||||
GET /api/repos/{owner}/{repo}/pulls/{number}
|
GET /api/repos/{owner}/{repo}/pulls/{number}
|
||||||
POST /api/repos/{owner}/{repo}/pulls/{number}/close
|
POST /api/repos/{owner}/{repo}/pulls/{number}/close
|
||||||
POST /api/repos/{owner}/{repo}/pulls/{number}/merge
|
POST /api/repos/{owner}/{repo}/pulls/{number}/merge
|
||||||
```
|
```
|
||||||
|
|
||||||
### Git HTTP
|
### Git HTTP
|
||||||
|
|
||||||
```http
|
```http
|
||||||
/{owner}/{repo}.git/info/refs
|
/{owner}/{repo}.git/info/refs
|
||||||
/{owner}/{repo}.git/git-upload-pack
|
/{owner}/{repo}.git/git-upload-pack
|
||||||
/{owner}/{repo}.git/git-receive-pack
|
/{owner}/{repo}.git/git-receive-pack
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. CLI commands
|
## 6. CLI commands
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitocean server --addr :8080 --dsn 'user:pass@tcp(127.0.0.1:3306)/gitocean?parseTime=true' --storage storage
|
gitocean server --addr :8080 --dsn 'user:pass@tcp(127.0.0.1:3306)/gitocean?parseTime=true' --storage storage
|
||||||
```
|
```
|
||||||
|
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitocean register
|
gitocean register
|
||||||
gitocean login
|
gitocean login
|
||||||
gitocean logout
|
gitocean logout
|
||||||
gitocean whoami
|
gitocean whoami
|
||||||
```
|
```
|
||||||
|
|
||||||
### Repositories
|
### Repositories
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitocean repo create NAME --public
|
gitocean repo create NAME --public
|
||||||
gitocean repo create NAME --private
|
gitocean repo create NAME --private
|
||||||
gitocean repo delete OWNER/NAME [--force]
|
gitocean repo delete OWNER/NAME [--force]
|
||||||
gitocean repo search QUERY --all
|
gitocean repo search QUERY --all
|
||||||
gitocean repo search QUERY --mine
|
gitocean repo search QUERY --mine
|
||||||
gitocean repo fork OWNER/NAME [--name NEW_NAME]
|
gitocean repo fork OWNER/NAME [--name NEW_NAME]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Clone
|
### Clone
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitocean clone OWNER/NAME
|
gitocean clone OWNER/NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pull requests
|
### Pull requests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitocean pr create --from OWNER/REPO:BRANCH --to OWNER/REPO:BRANCH --title TITLE --description DESC
|
gitocean pr create --from OWNER/REPO:BRANCH --to OWNER/REPO:BRANCH --title TITLE --description DESC
|
||||||
gitocean pr create --repo OWNER/REPO --from BRANCH --to BRANCH --title TITLE --description DESC
|
gitocean pr create --repo OWNER/REPO --from BRANCH --to BRANCH --title TITLE --description DESC
|
||||||
gitocean pr list OWNER/REPO
|
gitocean pr list OWNER/REPO
|
||||||
gitocean pr view OWNER/REPO NUMBER
|
gitocean pr view OWNER/REPO NUMBER
|
||||||
gitocean pr close OWNER/REPO NUMBER
|
gitocean pr close OWNER/REPO NUMBER
|
||||||
gitocean pr merge OWNER/REPO NUMBER
|
gitocean pr merge OWNER/REPO NUMBER
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Git HTTP behavior
|
## 7. Git HTTP behavior
|
||||||
|
|
||||||
Use Git's built-in Smart HTTP backend:
|
Use Git's built-in Smart HTTP backend:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git http-backend
|
git http-backend
|
||||||
```
|
```
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
|
|
||||||
- Public clone/fetch works without auth.
|
- Public clone/fetch works without auth.
|
||||||
- Private clone/fetch requires owner auth.
|
- Private clone/fetch requires owner auth.
|
||||||
- Push always requires auth.
|
- Push always requires auth.
|
||||||
- Push is owner-only.
|
- Push is owner-only.
|
||||||
- Git username is the account username.
|
- Git username is the account username.
|
||||||
- Git password is the 7-day token.
|
- Git password is the 7-day token.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Fork behavior
|
## 8. Fork behavior
|
||||||
|
|
||||||
- Logged-in users can fork public repositories.
|
- Logged-in users can fork public repositories.
|
||||||
- Private repositories can only be forked by the owner in Phase 1.
|
- Private repositories can only be forked by the owner in Phase 1.
|
||||||
- Forks are copied as bare repositories under the new owner's storage path.
|
- Forks are copied as bare repositories under the new owner's storage path.
|
||||||
- Fork metadata stores `forked_from_repository_id`.
|
- Fork metadata stores `forked_from_repository_id`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Pull request rules
|
## 9. Pull request rules
|
||||||
|
|
||||||
### Same-repository PRs
|
### Same-repository PRs
|
||||||
|
|
||||||
- Source and target repository are the same.
|
- Source and target repository are the same.
|
||||||
- Only the repository owner can create same-repo PRs.
|
- Only the repository owner can create same-repo PRs.
|
||||||
- Source and target branches must both exist.
|
- Source and target branches must both exist.
|
||||||
|
|
||||||
### Cross-repository PRs
|
### Cross-repository PRs
|
||||||
|
|
||||||
- Source repository branch points to target repository branch.
|
- Source repository branch points to target repository branch.
|
||||||
- Source repository must be owned by the PR author.
|
- Source repository must be owned by the PR author.
|
||||||
- Target repository must be public, unless the author is also the target owner.
|
- Target repository must be public, unless the author is also the target owner.
|
||||||
- Source and target branches must both exist.
|
- Source and target branches must both exist.
|
||||||
- Only the target repository owner can close or merge the PR.
|
- Only the target repository owner can close or merge the PR.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Merge behavior
|
## 10. Merge behavior
|
||||||
|
|
||||||
Phase 1 merge mode is a normal merge commit:
|
Phase 1 merge mode is a normal merge commit:
|
||||||
|
|
||||||
1. Clone the target repo into a temporary worktree.
|
1. Clone the target repo into a temporary worktree.
|
||||||
2. Checkout the target branch.
|
2. Checkout the target branch.
|
||||||
3. Add/fetch the source repo branch.
|
3. Add/fetch the source repo branch.
|
||||||
4. Run `git merge --no-ff FETCH_HEAD`.
|
4. Run `git merge --no-ff FETCH_HEAD`.
|
||||||
5. Push the result back to the target branch.
|
5. Push the result back to the target branch.
|
||||||
6. Mark the PR as merged.
|
6. Mark the PR as merged.
|
||||||
|
|
||||||
If conflicts occur:
|
If conflicts occur:
|
||||||
|
|
||||||
- Return an error.
|
- Return an error.
|
||||||
- Keep the PR open.
|
- Keep the PR open.
|
||||||
- Do not modify PR status.
|
- Do not modify PR status.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Implementation milestones
|
## 11. Implementation milestones
|
||||||
|
|
||||||
1. Project scaffold and build setup.
|
1. Project scaffold and build setup.
|
||||||
2. MySQL migrations.
|
2. MySQL migrations.
|
||||||
3. User registration, login, logout, and token auth.
|
3. User registration, login, logout, and token auth.
|
||||||
4. Repository create, delete, detail, and search.
|
4. Repository create, delete, detail, and search.
|
||||||
5. HTTP Smart Git integration using `git http-backend`.
|
5. HTTP Smart Git integration using `git http-backend`.
|
||||||
6. CLI auth and repository commands.
|
6. CLI auth and repository commands.
|
||||||
7. Fork support.
|
7. Fork support.
|
||||||
8. Pull request create/list/view/close.
|
8. Pull request create/list/view/close.
|
||||||
9. Pull request merge.
|
9. Pull request merge.
|
||||||
10. Tests, hardening, and UX polish.
|
10. Tests, hardening, and UX polish.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 1 completion criteria
|
## Phase 1 completion criteria
|
||||||
|
|
||||||
Phase 1 is complete when:
|
Phase 1 is complete when:
|
||||||
|
|
||||||
- The server starts with MySQL metadata storage.
|
- The server starts with MySQL metadata storage.
|
||||||
- Missing MySQL databases can be created automatically.
|
- Missing MySQL databases can be created automatically.
|
||||||
- Users can register, login, logout, and run `whoami`.
|
- Users can register, login, logout, and run `whoami`.
|
||||||
- Login tokens expire after 7 days.
|
- Login tokens expire after 7 days.
|
||||||
- Users can create public/private repositories.
|
- Users can create public/private repositories.
|
||||||
- Public repositories can be cloned/fetched without auth.
|
- Public repositories can be cloned/fetched without auth.
|
||||||
- Private repositories require owner auth.
|
- Private repositories require owner auth.
|
||||||
- Owners can push over HTTP.
|
- Owners can push over HTTP.
|
||||||
- Non-owners cannot push.
|
- Non-owners cannot push.
|
||||||
- Users can search visible repositories.
|
- Users can search visible repositories.
|
||||||
- Users can fork allowed repositories.
|
- Users can fork allowed repositories.
|
||||||
- Users can create, list, view, close, and merge pull requests.
|
- Users can create, list, view, close, and merge pull requests.
|
||||||
- `go test ./...` passes.
|
- `go test ./...` passes.
|
||||||
- `go vet ./...` passes.
|
- `go vet ./...` passes.
|
||||||
|
|||||||
Reference in New Issue
Block a user