@@ -1,31 +1,176 @@ |
| 1 | 1 | # Repositories |
| 2 | 2 | |
| 3 | | -> **Planned.** The repository CRUD API surface is not yet |
| 4 | | -> shipped. Today the repo lifecycle is driven through the web UI |
| 5 | | -> and the `shithubd` CLI. The shape below is the **planned** v1 |
| 6 | | -> surface; it will land in a follow-up sprint. |
| 7 | | - |
| 8 | | -## Planned routes |
| 9 | | - |
| 10 | | -| Method | Path | Scope | Purpose | |
| 11 | | -|--------|---------------------------------------------|--------------|-------------------------------| |
| 12 | | -| GET | `/api/v1/repos/{owner}/{repo}` | `repo:read` | Get one repo. | |
| 13 | | -| POST | `/api/v1/user/repos` | `repo` | Create a repo under the user. | |
| 14 | | -| POST | `/api/v1/orgs/{org}/repos` | `repo,admin:org` | Create a repo under an org. | |
| 15 | | -| PATCH | `/api/v1/repos/{owner}/{repo}` | `repo` | Edit settings. | |
| 16 | | -| DELETE | `/api/v1/repos/{owner}/{repo}` | `repo` | Delete (with confirmation). | |
| 17 | | -| GET | `/api/v1/repos/{owner}/{repo}/branches` | `repo:read` | List branches. | |
| 18 | | -| GET | `/api/v1/repos/{owner}/{repo}/branches/{branch}/protection` | `repo:read` | Get branch protection rules. | |
| 19 | | -| PUT | `/api/v1/repos/{owner}/{repo}/branches/{branch}/protection` | `repo` | Replace branch protection. | |
| 20 | | - |
| 21 | | -The web UI's behavior is the de-facto specification — when these |
| 22 | | -endpoints land, they implement the same checks (visibility, name |
| 23 | | -collisions, owner-permissions, soft-delete grace) that the web |
| 24 | | -forms enforce. |
| 25 | | - |
| 26 | | -## Today |
| 27 | | - |
| 28 | | -Until the API ships, scripted repo lifecycle is best done via |
| 29 | | -the `shithubd` operator CLI on the host (operator-only) or |
| 30 | | -through HTML form submission with a session cookie (per-user but |
| 31 | | -not API-shaped). |
| 3 | +The repository core REST surface. Mirrors GitHub's shape where |
| 4 | +the model matches; deviations are called out inline. List routes |
| 5 | +emit the standard `Link:` pagination header (see [overview](overview.md)). |
| 6 | + |
| 7 | +## Repo shape |
| 8 | + |
| 9 | +Every list and single-repo response renders the same envelope: |
| 10 | + |
| 11 | +```json |
| 12 | +{ |
| 13 | + "id": 12, |
| 14 | + "name": "demo", |
| 15 | + "full_name": "alice/demo", |
| 16 | + "owner_login": "alice", |
| 17 | + "owner_type": "user", |
| 18 | + "description": "first cut", |
| 19 | + "visibility": "public", |
| 20 | + "private": false, |
| 21 | + "default_branch": "trunk", |
| 22 | + "fork": false, |
| 23 | + "archived": false, |
| 24 | + "has_issues": true, |
| 25 | + "has_pulls": true, |
| 26 | + "star_count": 0, |
| 27 | + "watcher_count": 0, |
| 28 | + "fork_count": 0, |
| 29 | + "created_at": "2026-05-12T04:00:00Z", |
| 30 | + "updated_at": "2026-05-12T04:00:00Z" |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +`owner_type` is `"user"` or `"org"`. `default_branch` is |
| 35 | +shithub's own default name (`trunk`), not GitHub's `main` — read |
| 36 | +this field; don't hardcode. |
| 37 | + |
| 38 | +## List the authenticated user's repos |
| 39 | + |
| 40 | +``` |
| 41 | +GET /api/v1/user/repos |
| 42 | +``` |
| 43 | + |
| 44 | +Required scope: `repo:read`. Returns every repo the user owns, |
| 45 | +private included. Paginated; `?per_page=` (≤100) and `?page=`. |
| 46 | + |
| 47 | +## List a user's public repos |
| 48 | + |
| 49 | +``` |
| 50 | +GET /api/v1/users/{username}/repos |
| 51 | +``` |
| 52 | + |
| 53 | +Required scope: `repo:read`. Returns the named user's public |
| 54 | +repos. When the authenticated viewer is the named user, this |
| 55 | +endpoint returns the same set as `GET /api/v1/user/repos` |
| 56 | +(private included) to match the gh-shape. |
| 57 | + |
| 58 | +## List an org's repos |
| 59 | + |
| 60 | +``` |
| 61 | +GET /api/v1/orgs/{org}/repos |
| 62 | +``` |
| 63 | + |
| 64 | +Required scope: `repo:read`. Org members see every repo |
| 65 | +(visibility-aware). Non-members see only public repos. |
| 66 | + |
| 67 | +## Get a single repo |
| 68 | + |
| 69 | +``` |
| 70 | +GET /api/v1/repos/{owner}/{repo} |
| 71 | +``` |
| 72 | + |
| 73 | +Required scope: `repo:read`. Returns the repo envelope above. |
| 74 | +`404` when the caller can't see the repo (existence-leak-safe |
| 75 | +treatment for private repos belonging to someone else). |
| 76 | + |
| 77 | +## Create a personal repo |
| 78 | + |
| 79 | +``` |
| 80 | +POST /api/v1/user/repos |
| 81 | +``` |
| 82 | + |
| 83 | +Required scope: `repo:write`. Body fields (all optional except |
| 84 | +`name`): |
| 85 | + |
| 86 | +```json |
| 87 | +{ |
| 88 | + "name": "demo", |
| 89 | + "description": "first cut", |
| 90 | + "visibility": "public", |
| 91 | + "auto_init": true, |
| 92 | + "license_template": "mit", |
| 93 | + "gitignore_template": "Go" |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +| Field | Type | Notes | |
| 98 | +|----------------------|--------|-------------------------------------------------------| |
| 99 | +| `name` | string | Lowercased, `[a-z0-9._-]`, 1–99 chars. | |
| 100 | +| `description` | string | ≤350 chars. | |
| 101 | +| `visibility` | string | `"public"` or `"private"`. Defaults to `"private"`. | |
| 102 | +| `private` | bool | gh-compatible alternative to `visibility`. | |
| 103 | +| `auto_init` | bool | Seed an initial README commit. | |
| 104 | +| `license_template` | string | License key (e.g. `mit`); requires `auto_init=true`. | |
| 105 | +| `gitignore_template` | string | Gitignore preset name; requires `auto_init=true`. | |
| 106 | + |
| 107 | +Returns `201` with the repo envelope. |
| 108 | + |
| 109 | +### Errors |
| 110 | + |
| 111 | +| Status | When | |
| 112 | +|-------:|-----------------------------------------------------------------------| |
| 113 | +| 401 | PAT missing/invalid. | |
| 114 | +| 403 | PAT lacks `repo:write` scope. | |
| 115 | +| 409 | Name already taken for this owner. | |
| 116 | +| 422 | Invalid name, description too long, unknown license/gitignore template, or actor lacks a verified primary email. | |
| 117 | + |
| 118 | +## Create an org repo |
| 119 | + |
| 120 | +``` |
| 121 | +POST /api/v1/orgs/{org}/repos |
| 122 | +``` |
| 123 | + |
| 124 | +Required scope: `repo:write`. Same body shape as the personal |
| 125 | +variant. Org members can create only when the org has |
| 126 | +`allow_member_repo_create` enabled; owners (and site admins) |
| 127 | +bypass that gate. Non-members get `404` (existence-leak-safe). |
| 128 | + |
| 129 | +## Update repo settings |
| 130 | + |
| 131 | +``` |
| 132 | +PATCH /api/v1/repos/{owner}/{repo} |
| 133 | +``` |
| 134 | + |
| 135 | +Required scope: `repo:write`. Only the fields you send are |
| 136 | +modified; everything else stays as it was. |
| 137 | + |
| 138 | +```json |
| 139 | +{ |
| 140 | + "description": "new copy", |
| 141 | + "has_issues": false, |
| 142 | + "has_pulls": true, |
| 143 | + "archived": true, |
| 144 | + "visibility": "private" |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +Setting `archived` flips the archived flag through the lifecycle |
| 149 | +orchestrator (so the on-disk repo and audit-log entry stay in |
| 150 | +sync with the HTML surface). Visibility changes also go through |
| 151 | +the orchestrator and emit the usual audit entry. |
| 152 | + |
| 153 | +## Soft-delete a repo |
| 154 | + |
| 155 | +``` |
| 156 | +DELETE /api/v1/repos/{owner}/{repo} |
| 157 | +``` |
| 158 | + |
| 159 | +Required scope: `repo:write`. The repo is soft-deleted (matches |
| 160 | +the web UI's deletion semantics — there is a grace window during |
| 161 | +which a site admin can restore it). Returns `204` on success; |
| 162 | +`404` for cross-user attempts and for already-deleted repos. |
| 163 | + |
| 164 | +## Not yet shipped |
| 165 | + |
| 166 | +The §2 batch covered core CRUD. The following routes are |
| 167 | +**planned** and will land in later batches: |
| 168 | + |
| 169 | +| Method | Path | Notes | |
| 170 | +|--------|-------------------------------------------------------|--------------------------------------| |
| 171 | +| POST | `/api/v1/repos/{owner}/{repo}/forks` | Paired with shithub-cli's fork flow. | |
| 172 | +| POST | `/api/v1/repos/{owner}/{repo}/merge-upstream` | Fork sync. | |
| 173 | +| PUT | `/api/v1/repos/{owner}/{repo}/topics` | Topic replace. | |
| 174 | +| GET | `/api/v1/repos/{owner}/{repo}/readme` | README content by ref. | |
| 175 | +| POST | `/api/v1/repos/{template_owner}/{template_repo}/generate` | Create from template. | |
| 176 | +| POST | `/api/v1/repos/{owner}/{repo}/transfer` | Owner transfer ack. | |