Make the REST API Gitea-compatible (single API, enables Renovate) #20
Make /api/v1 Gitea-compatible (single API)
Problem
We want automated dependency updates (Renovate) on our own git-shark-hosted code. Renovate has no git-shark platform driver. Supported platforms: github, gitlab, bitbucket, gitea/forgejo, azure, gerrit, codecommit, local.
The GitHub-mirror route is a dead end: the mirror is a one-way backup push (git-shark → GitHub, overwrites), so PRs Renovate opens on GitHub never flow back and get clobbered on the next sync. git-shark is the source of truth.
Decision: migrate, don't add a parallel API
Rather than run a Gitea-compatible shim alongside the existing bespoke /api/v1, migrate /api/v1 to be Gitea-native and drop the bespoke shapes.
Rationale
- No consumers yet. The REST API has no external users, so this is a rename/reshape, not a breaking change. Now is the cheapest possible moment.
- One schema, not two. Avoids a permanent path-collision problem (native
GET /api/v1/repos/{owner}/{name}and the Gitea equivalent share a path but want different JSON). - Broad ecosystem compat, not just Renovate. A Gitea-native API also works with
teaCLI, act_runner, and other Gitea tooling/bots. - Net effort ≈ the same as a separate shim, better result.
MCP tools keep working against the same domain services (see caveat below).
Spike: how big is the gap?
Small-to-medium. git-shark already has ~80% of the machinery. Renovate's Gitea client clones/pushes repos via the git CLI and uses REST only for PR/issue/label/status metadata — so most file access needs no API.
Already present (reuse, no change)
- PAT auth (
gs_-prefixed tokens, SHA-256 stored) works on git smart-HTTP (GitBasicAuthFilter, password = token) and REST (ApiTokenAuthFilter,Authorization: Bearer). Renovate pushes branches over HTTPS with the token → works today, zero change. /api/v1JSON REST backed by domain services (MergeRequestService,IssueService,GitRepositoryService,GitBrowseService). Migration is a reshape of the resource/DTO layer, not new domain logic.MergeRequestentity maps cleanly to a Gitea pull request: number, title, description, sourceBranch, targetBranch, status {OPEN, MERGED, CLOSED}, mergedAt, assignee, reviewer.
Gap table (Gitea contract → git-shark today → work)
| Gitea endpoint | git-shark today | Work |
|---|---|---|
GET /version |
none | stub; return a version matching implemented features |
GET /user |
/api/v1/user exists |
reshape to {id, login, email, full_name} — easy |
GET /repos/{o}/{r} |
/api/v1/repos/{owner}/{name} exists |
reshape + add default_branch (from git), archived/empty/mirror, clone_url, merge flags — medium |
GET /repos/{o}/{r}/branches/{b} |
UI-only, no REST | new: read git ref via GitBrowseService → {name, commit.id, protected} — medium |
pulls (create/get/list, GET pulls/{base}/{head}, POST .../merge) |
MR create/get/list/merge/close exist under /merge-requests/{number} |
rename merge-requests→pulls, reshape; find-by-branch = filter list — medium |
PATCH pulls/{index} (title/body/state) |
no REST update (UI only) | wire MR update into API — medium |
GET /repos/{o}/{r}/contents/{path} |
UI raw only | small git-backed endpoint; Renovate mostly clones — low |
GET labels, PUT/DELETE issue labels |
no Label model | stub GET→[]; skip if labels config empty — low |
POST /statuses/{sha}, GET /commits/{b}/statuses |
no status store (CI runners exist, unwired) | stub empty combined status → Renovate proceeds — low |
| Issues + comments (dependency dashboard) | Issue exists but status enum {PLANNED, IN_DEVELOPMENT, DONE} ≠ open/closed; issue-comments MCP-only | defer — set dependencyDashboard: false — medium-large if wanted |
Authorization: token <PAT> scheme |
only Bearer/Basic parsed |
add token scheme to ApiTokenAuthFilter — trivial |
autodiscover (/repos/search, /orgs/{o}/repos) |
list exists | defer — pin repos in repositories: config — optional |
(Authoritative endpoint list taken from Renovate's Gitea platform client: gitea-helper.ts + index.ts.)
Plan
MVP — Renovate opens / updates / merges dependency PRs
- Add
token <PAT>scheme toApiTokenAuthFilter. - Reshape existing
/api/v1resources + DTOs to the Gitea contract:GET /version,GET /user,GET /repos/{o}/{r},GET /repos/{o}/{r}/branches/{b}, fullpulls(create/get/list/find-by-branch/patch/merge),GET labels→[], commit-status stubs. - Rename
merge-requestsresource path →pulls; expose per-reponumberas the Giteaindexand add an int64id. - Wire an MR update path (title/description/state) into the API.
- Config: pin repos in
repositories:, setdependencyDashboard: false. - Estimate: ~4–6 dev-days incl. test-first + a real Renovate
LOG_LEVEL=debugintegration run.
Phase 2
- Dependency dashboard (Issue open/closed mapping + issue-comment REST endpoints).
- Real Label model.
- Wire CI runner status into the commit-status API.
- Estimate: +3–5 dev-days.
Risks / caveats
- MCP shares DTOs. MCP tools return
ApiModelsDTOs directly (shared with REST). Reshaping REST → Gitea shape changes MCP output too. Two handlings: (a) accept it — Gitea-shaped JSON is fine, arguably richer, for MCP's AI-agent consumers (zero extra work); (b) decouple — give MCP its own thin DTOs (small). Recommend (a) now, decouple only if it chafes. - Vocab split. The Gitea contract says "pull request"; the domain/UI say "merge request". Keep the domain naming (
MergeRequest*) internal and only speak Gitea at the wire. Cosmetic but permanent. - JSON fidelity. Renovate deserializes exact Gitea field names; a mismatch fails silently. Validate against a real Renovate debug run, not just unit tests.
- Version string. Report the version that matches what is implemented:
>= 1.24.0unlocksdelete_branch_after_merge;>= 1.14.0makes Renovate callrequested_reviewers— report lower until those are implemented so Renovate does not call unimplemented endpoints. - Surrogate
id. Gitea needs an int64id; git-shark uses UUID PK + per-reponumber. Exposenumberas the index and add a stable intid.
Docs to update on implementation
docs/admins/— the reshaped/api/v1contract, config/reverse-proxy notes, Renovate setup, theGITSHARK_*config-reference table if any new property is added.docs/maintainers/— Gitea-compatibility scope, component map, "works today / still needed" lists.docs/users/— how to enable Renovate against a git-shark repo.- Root
README.md— feature list.
Acceptance
- A real Renovate run (
platform: "gitea",endpoint: https://gitshark.de/api/v1) against a git-shark repo opens a dependency-update PR, updates it on a new version, and merges it — end to end.
Comments 0
No comments yet.
Log in to comment.