docs: Requirements for the project.
This commit is contained in:
242
.claude/gitea-vs-github.md
Normal file
242
.claude/gitea-vs-github.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Gitea vs GitHub: API Feature Comparison for CLI Agent Tooling
|
||||
|
||||
Focus: features relevant to `gitea-axi` — issues, pull requests, labels, reviews, comments, auth, and API mechanics.
|
||||
Sources: Gitea OpenAPI spec (docs.gitea.com/api/1.21), GitHub REST docs, Gitea source structs (`modules/structs/`).
|
||||
|
||||
---
|
||||
|
||||
## API Style
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| REST API | Yes — `/api/v1` (actually `/repos/...` at api.github.com) | Yes — `/api/v1/repos/...` | Gitea mirrors GitHub's path structure closely |
|
||||
| GraphQL API | Yes — `api.github.com/graphql` | **No** | Gitea is REST-only; no GraphQL endpoint exists |
|
||||
| OpenAPI spec | Unofficial/community | Yes — `/api/swagger` on every instance | Gitea ships a first-class Swagger UI and JSON spec |
|
||||
| TypeScript client | `@octokit/rest` (hand-written) | `gitea-js` (generated from OpenAPI) | `gitea-js` types are auto-generated and stay in sync with the spec |
|
||||
| API versioning | `api-version` header (`2026-03-10`) | Path-prefixed (`/api/v1`) | GitHub uses a header; Gitea uses path versioning |
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Personal access tokens (classic) | Yes — broad scope-based | Yes — scope-based (`read:issue`, `write:repository`, etc.) | Gitea scopes map to API route groups |
|
||||
| Fine-grained PATs | Yes — per-repo permissions, expiry required | **No** — scopes are coarser (category-level, not per-repo) | GitHub fine-grained tokens are more restrictive by default |
|
||||
| Token creation via API | No (web only for fine-grained) | **Yes** — `POST /user/tokens` | Gitea allows programmatic token creation with BasicAuth |
|
||||
| Token listing/deletion via API | No | **Yes** — `GET /user/tokens`, `DELETE /user/tokens/{id}` | Gitea exposes full token lifecycle over the API |
|
||||
| OAuth App device flow | **Yes** | **No** — only Authorization Code (+ PKCE) | GitHub device flow is essential for headless CLI auth; Gitea lacks it |
|
||||
| OAuth App web flow | Yes | Yes | Both support standard Authorization Code grant |
|
||||
| OAuth App management via API | No | **Yes** — `GET/POST/PATCH/DELETE /user/applications/oauth2` | Gitea lets users manage their own OAuth apps via API |
|
||||
| GitHub Apps / installation tokens | Yes | **No** — no GitHub Apps concept | Gitea has no equivalent of GitHub Apps or installation tokens |
|
||||
| `GITHUB_TOKEN` in Actions | Yes | No (`GITEA_TOKEN` in Gitea Actions is similar but not identical) | Gitea Actions has a built-in `GITEA_TOKEN` but it is instance-specific |
|
||||
| HTTP Signatures (SSH key auth) | No | **Yes** | Gitea accepts signatures per draft-cavage-http-signatures |
|
||||
| Basic auth | Deprecated/removed | Yes (for token creation only; not recommended for general use) | |
|
||||
| Sudo (act-as) | No | **Yes** — `?sudo=username` for admins | Admin-only; useful for automation |
|
||||
| SAML SSO token authorization | Yes (org-enforced) | No | Gitea has no SAML SSO concept |
|
||||
|
||||
---
|
||||
|
||||
## Pagination
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Offset/page pagination | Yes — `?page=N&per_page=N` (max 100) | Yes — `?page=N&limit=N` | Same concept; parameter names differ (`per_page` vs `limit`) |
|
||||
| Cursor-based pagination | Some endpoints (`before`/`after`) | **No** | Gitea is page-offset only |
|
||||
| `Link` header (RFC 5988) | **Yes** — `next`, `prev`, `last`, `first` | **Yes** — same format | Both provide `Link` headers for navigation |
|
||||
| `X-Total-Count` response header | **No** | **Yes** | Gitea returns total count in every list response; GitHub does not |
|
||||
| `x-total-count` in `gitea-js` | n/a | Available as a parsed header | The `gitea-js` client exposes this alongside the response body |
|
||||
| Maximum per-page results | 100 | Configurable (instance default ~50, max configurable) | Gitea's max is admin-controlled; no hard 100 cap in the API |
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Per-user rate limit | 5,000 req/hr (authenticated) | **No standard rate limiting** | Gitea uses QoS concurrency throttling, not per-user counters |
|
||||
| `X-RateLimit-*` headers | Yes — `Limit`, `Remaining`, `Used`, `Reset` | **No** | Gitea does not emit rate-limit headers |
|
||||
| Secondary limits | Yes (content creation, concurrency) | No | |
|
||||
| QoS / overload protection | No | **Yes** — configurable `[qos]` section | Gitea drops or queues requests under load rather than rate-limiting |
|
||||
|
||||
---
|
||||
|
||||
## Issue Management
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Create issue | Yes — `POST /repos/{owner}/{repo}/issues` | Yes — same path pattern | |
|
||||
| List issues | Yes | Yes | |
|
||||
| Get single issue | Yes | Yes | |
|
||||
| Edit issue | Yes | Yes | |
|
||||
| Delete issue | **No** (close only) | **Yes** — `DELETE /repos/{owner}/{repo}/issues/{index}` | Gitea allows hard deletion |
|
||||
| Issue state | `open` / `closed` | `open` / `closed` | Same values |
|
||||
| `state_reason` | **Yes** — `completed`, `not_planned`, `duplicate`, `reopened` | **No** | Gitea has no close-reason concept |
|
||||
| Issue type | **Yes** — first-class `type` field on create/edit | **No** | GitHub supports issue type classification; Gitea does not |
|
||||
| `duplicate_issue_id` | **Yes** | **No** | |
|
||||
| Issue dependencies (blocking) | No (sub-issue relationships only) | **Yes** — `POST /repos/{owner}/{repo}/issues/{index}/dependencies` | Gitea has explicit blocking/blocked-by relationships between issues |
|
||||
| Issue blocking list | No | **Yes** — `GET /repos/{owner}/{repo}/issues/{index}/blocked-by` | |
|
||||
| Pin issue | Partial (read-only in API response) | **Yes** — `POST/DELETE /repos/{owner}/{repo}/issues/{index}/pin`, position reorder | Gitea has full pin CRUD including ordering |
|
||||
| Issue timeline | Yes — 30+ event types (labels, assignments, reviews, etc.) | Yes — `GET /repos/{owner}/{repo}/issues/{index}/timeline` (comments + events) | GitHub's timeline is richer in event type variety |
|
||||
| Reactions on issues | Yes — 8 types | Yes — same types | |
|
||||
| Reactions on issue comments | Yes | Yes | |
|
||||
| Lock/unlock conversation | Yes — with `lock_reason` (`off-topic`, `too heated`, `resolved`, `spam`) | **No** — issues have `IsLocked` field but no dedicated lock endpoint visible | |
|
||||
| Subscriptions (watching) | Via notifications API | **Yes** — explicit `PUT/DELETE /repos/{owner}/{repo}/issues/{index}/subscriptions` | Gitea exposes subscribe/unsubscribe directly on issues |
|
||||
| Issue search (cross-repo) | Yes — `GET /search/issues` with qualifiers | Yes — `GET /issues/search` | Both support cross-repo search |
|
||||
| Custom field values | **Yes** (org repos) | **No** | GitHub-specific feature |
|
||||
| Time tracking (estimate) | No | **Yes** — `TimeEstimate` field on Issue struct | Gitea has built-in time tracking |
|
||||
| Content versioning (conflict detect) | No | **Yes** — `ContentVersion` field on create/edit | Gitea supports optimistic locking for concurrent edits |
|
||||
| Original author tracking (migrations) | No | **Yes** — `OriginalAuthor` / `OriginalAuthorID` fields | Preserved when importing from other platforms |
|
||||
| Issue `Ref` field | No | **Yes** — git ref associated with issue | |
|
||||
|
||||
---
|
||||
|
||||
## Milestones
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Milestone CRUD | Yes — `GET/POST/PATCH/DELETE /repos/{owner}/{repo}/milestones` | Yes — same path pattern | |
|
||||
| Milestone on issues | Yes | Yes | |
|
||||
| Milestone on PRs | Yes | Yes — `milestone` field in `CreatePullRequestOption` and `EditPullRequestOption` | |
|
||||
| Milestone properties | `title`, `description`, `state`, `due_on` | Same | |
|
||||
| Org-level milestones | No | No | Neither supports org-level milestones |
|
||||
|
||||
---
|
||||
|
||||
## Label Management
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Repo label CRUD | Yes — `GET/POST/PATCH/DELETE /repos/{owner}/{repo}/labels` | Yes — same path pattern | |
|
||||
| Org-level labels | No | **Yes** — `GET/POST/PATCH/DELETE /orgs/{org}/labels` | Gitea supports organization-scoped labels |
|
||||
| Label properties | `name`, `color`, `description` | `name`, `color`, `description` | |
|
||||
| Add/remove labels on issue | Yes | Yes | |
|
||||
| Add/remove labels on PR | Yes | Yes | |
|
||||
| Replace all labels | Yes | Yes | |
|
||||
|
||||
---
|
||||
|
||||
## Pull Request Management
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| Create PR | Yes | Yes | |
|
||||
| List PRs | Yes | Yes | |
|
||||
| Get PR | Yes | Yes | |
|
||||
| Edit PR | Yes | Yes | |
|
||||
| Delete PR | No | No | Neither allows hard deletion |
|
||||
| Draft PRs | **Yes** — `draft: true` on create | **Yes** — `Draft bool` field | Both support drafts |
|
||||
| Draft PR filter in list | Yes | Yes — `draft` query param available | |
|
||||
| `maintainer_can_modify` | Yes | **Yes** — `AllowMaintainerEdit` field | Same concept, different name |
|
||||
| `ContentVersion` conflict detection | No | **Yes** | Same optimistic-locking field as issues |
|
||||
| Pin PR | No | **Yes** — same pin endpoints as issues, `PinOrder` field | |
|
||||
| Get PR diff | Yes — `Accept: application/vnd.github.diff` | **Yes** — `GET /repos/{owner}/{repo}/pulls/{index}/diff` | Gitea returns raw diff text |
|
||||
| Get PR patch | Yes — `Accept: application/vnd.github.patch` | Yes — `GET /repos/{owner}/{repo}/pulls/{index}/diff` with format param | |
|
||||
| List PR files (structured) | Yes — `GET /pulls/{number}/files` | **Yes** — `GET /repos/{owner}/{repo}/pulls/{index}/files` | Returns file list with additions/deletions |
|
||||
| List PR commits | Yes | **Yes** — `GET /repos/{owner}/{repo}/pulls/{index}/commits` | |
|
||||
| Mergeable status | Yes — `mergeable` field | **Yes** — `Mergeable bool` field on PR | |
|
||||
| Merge strategies | merge, squash, rebase | merge, squash, rebase, **rebase-merge** (rebase + no-ff), **fast-forward-only** | **Gitea has more merge strategies** |
|
||||
| Squash merge | Yes | Yes | |
|
||||
| Rebase merge | Yes | Yes | |
|
||||
| Fast-forward-only | No | **Yes** | |
|
||||
| Rebase + explicit merge commit | No | **Yes** — `rebase-merge` style | |
|
||||
| Auto-merge (merge when checks pass) | Yes | **Yes** — `MergeWhenChecksSucceed` field | Both support scheduling a merge after CI passes |
|
||||
| Cancel auto-merge | Yes | Yes — `DELETE /repos/{owner}/{repo}/pulls/{index}/merge` | |
|
||||
| Delete branch after merge | Yes | **Yes** — `DeleteBranchAfterMerge` field on merge request | |
|
||||
| Force merge | Limited (bypass protections with permissions) | **Yes** — `ForceMerge bool` field | Gitea has an explicit force-merge flag |
|
||||
| Manually merged (mark as merged) | No | **Yes** — `Do: "manually-merged"` + `MergeCommitID` field | Gitea allows recording a manual merge after the fact |
|
||||
| `HeadCommitSHA` validation on merge | No | **Yes** — `HeadCommitID` field for merge safety | |
|
||||
| PR merge commit message/title | Yes — `commit_title`, `commit_message` | **Yes** — `MergeTitleField`, `MergeMessageField` | |
|
||||
| Pinned PRs list | No | **Yes** — `GET /repos/{owner}/{repo}/pulls/pinned` | |
|
||||
|
||||
---
|
||||
|
||||
## Review Management
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| List reviews | Yes | Yes | |
|
||||
| Create review | Yes | Yes | |
|
||||
| Submit pending review | Yes | Yes | |
|
||||
| Delete pending review | Yes | Yes | |
|
||||
| Dismiss review | Yes — `POST /pulls/{number}/reviews/{id}/dismissals` | **Yes** — `POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismiss` | |
|
||||
| Un-dismiss review | No | **Yes** — `POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/undismiss` | Gitea allows undoing a dismissal |
|
||||
| Review states | `APPROVED`, `REQUEST_CHANGES`, `COMMENT`, `PENDING`, `DISMISSED` | `APPROVED`, `REQUEST_CHANGES`, `COMMENT`, `PENDING`, (+ `REQUEST_REVIEW`) | States match; Gitea adds `REQUEST_REVIEW` as a state type |
|
||||
| `DISMISSED` state | Yes — set by dismiss action | Yes — `Dismissed bool` + `Stale bool` fields | |
|
||||
| Aggregated `reviewDecision` field | **Yes (GraphQL only)** — `APPROVED`, `CHANGES_REQUESTED`, `REVIEW_REQUIRED` | **No** | Gitea has no single aggregated review decision; must be computed from individual reviews |
|
||||
| `reviewDecision` via REST | No (GitHub REST also lacks this) | No | Both REST APIs require client-side aggregation |
|
||||
| Official/required review flag | No | **Yes** — `Official bool` field on review | Gitea marks reviews from required reviewers as official |
|
||||
| `Stale` review flag | No | **Yes** — `Stale bool` field | Gitea marks reviews stale when new commits are pushed |
|
||||
| Review request (users) | Yes — `POST /pulls/{number}/requested_reviewers` | **Yes** — `POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers` | |
|
||||
| Review request (teams) | Yes — `team_reviewers` param | **Yes** — `TeamReviewers` param | |
|
||||
| Remove review request | Yes | Yes | |
|
||||
| List eligible reviewers | No (must list repo collaborators separately) | **Yes** — `GET /repos/{owner}/{repo}/pulls/{index}/requested_reviewers` returns all eligible users | Gitea returns who *can* be requested, not just who has been |
|
||||
| Inline code comments on review | Yes — with line, side, path, start_line | **Yes** — `CreatePullReviewComment` struct with position/path | |
|
||||
| Multi-line comment range | Yes — `start_line`/`start_side` + `line`/`side` | Partial — position-based rather than line-range | GitHub's multi-line range is more explicit |
|
||||
| Reply to review comment | Yes — `POST /pulls/{number}/comments/{id}/replies` | Yes — reply via in_reply_to reference | |
|
||||
| List review comments (inline) | Yes | Yes | |
|
||||
| Edit/delete review comment | Yes | Yes | |
|
||||
| Reactions on review comments | Yes | **No** — reactions are on issue comments only, not PR review comments | Gitea's reaction support does not extend to PR review comments |
|
||||
| `commitId` on review | Yes | **Yes** — `CommitID` field | |
|
||||
|
||||
---
|
||||
|
||||
## Issue & PR Comments
|
||||
|
||||
| Feature | GitHub | Gitea | Notes |
|
||||
|---|---|---|---|
|
||||
| List issue comments | Yes | Yes | |
|
||||
| Create issue comment | Yes | Yes | |
|
||||
| Edit comment | Yes | Yes | |
|
||||
| Delete comment | Yes | Yes | |
|
||||
| Pin/unpin comment | **Yes** — `PUT/DELETE /issues/comments/{id}/pin` | No | GitHub supports pinned comments on issues |
|
||||
| Reactions on issue comments | Yes — 8 types | **Yes** — same 8 types | |
|
||||
| Reactions on PR review comments | Yes | **No** | Gap in Gitea: review comment reactions not supported |
|
||||
| `body_html` / `body_text` fields | Yes | No — body is Markdown only | GitHub returns rendered HTML variants |
|
||||
| `author_association` field | Yes | No | GitHub classifies commenter relationship to repo |
|
||||
|
||||
---
|
||||
|
||||
## Summary: Key Gaps and Gitea Advantages
|
||||
|
||||
### Gitea lacks (compared to GitHub)
|
||||
|
||||
| Missing in Gitea | Impact on CLI tooling |
|
||||
|---|---|
|
||||
| No GraphQL API | Cannot get aggregated `reviewDecision`; must compute from review list |
|
||||
| No `reviewDecision` enum (REST or GraphQL) | CLI must fold `APPROVED`/`REQUEST_CHANGES` reviews client-side |
|
||||
| No device flow OAuth | Headless login requires PAT or BasicAuth; cannot do browser-less OAuth |
|
||||
| No `state_reason` on issues | Cannot distinguish "completed" vs "not planned" close reasons |
|
||||
| No issue type (bug/feature classification) | Labels must substitute for issue type |
|
||||
| No fine-grained per-repo PATs | Tokens are category-scoped, not repo-scoped |
|
||||
| No `X-RateLimit-*` headers | Cannot back off gracefully on rate limits (no limit signal) |
|
||||
| No cursor-based pagination | Must use offset pagination; large result sets may drift |
|
||||
| No `body_html` on comments | Must render Markdown client-side if HTML is needed |
|
||||
| No `author_association` | Cannot determine contributor relationship without separate lookup |
|
||||
| No reactions on PR review comments | Review comment reactions not possible |
|
||||
| No lock/unlock issue endpoint | Issue locking is not exposed as an API operation |
|
||||
| No GitHub Apps / installation tokens | Cannot use app-level auth scoping |
|
||||
|
||||
### Gitea advantages (compared to GitHub REST)
|
||||
|
||||
| Gitea-only capability | Impact on CLI tooling |
|
||||
|---|---|
|
||||
| `X-Total-Count` response header | Accurate totals on every list response; no extra count query needed |
|
||||
| Token CRUD via API | Programmatic token management without web UI |
|
||||
| OAuth app management via API | Automation-friendly app registration |
|
||||
| Hard delete issues | Useful for cleanup automation |
|
||||
| Issue dependencies (blocking/blocked-by) | Richer workflow modelling |
|
||||
| Issue pinning with position reorder | Full pin management via API |
|
||||
| Issue subscriptions endpoint | Subscribe/unsubscribe without using notifications API |
|
||||
| Org-level labels | Labels shared across all org repos |
|
||||
| `Official` + `Stale` flags on reviews | Richer review state without client-side inference |
|
||||
| `Undismiss` review endpoint | Reversible review dismissals |
|
||||
| Eligible reviewer list on PR | Know who *can* review before requesting |
|
||||
| `ForceMerge` flag | Explicit force-merge without separate branch protection bypass |
|
||||
| `manually-merged` merge style | Record out-of-band merges |
|
||||
| `rebase-merge` + `fast-forward-only` styles | More granular merge strategy control |
|
||||
| `ContentVersion` on issues and PRs | Optimistic locking for concurrent edits |
|
||||
| Time tracking (`TimeEstimate`) | Built-in estimation without third-party integrations |
|
||||
| `Sudo` param (admin) | Act-as for admin automation |
|
||||
| HTTP Signature auth | SSH key based API auth |
|
||||
| `PinOrder` on PRs | PRs can also be pinned, not just issues |
|
||||
Reference in New Issue
Block a user