Skip to content

feat(forge): Gitea HTTP adapter (PRE-3)#1575

Merged
nextlevelshit merged 4 commits into
mainfrom
wave/pre3-gitea-adapter
Apr 29, 2026
Merged

feat(forge): Gitea HTTP adapter (PRE-3)#1575
nextlevelshit merged 4 commits into
mainfrom
wave/pre3-gitea-adapter

Conversation

@nextlevelshit
Copy link
Copy Markdown
Collaborator

Closes #1567. Phase 0 child of Epic #1565.

What

Pure HTTP `forge.Client` for Gitea/Forgejo/Codeberg. No `tea` CLI dependency.

  • `internal/forge/gitea.go` — `GiteaClient` + DTOs + conversions for the full `forge.Client` interface
  • `token.go` — `NewClient` dispatches `ForgeGitea` / `ForgeForgejo` / `ForgeCodeberg` into `NewGiteaClient`
  • `detect.go` — `classifyHost` shortcut for `git.librete.ch` (re-cinq's Gitea)

API surface

Implements every method required by `forge.Client`:

  • GetIssue / ListIssues (with pull_request flag for PR-typed issues)
  • GetPullRequest (merged=true overrides state to "merged")
  • ListPullRequests / ListPullRequestCommits
  • GetCommitChecks (Gitea status → forge-neutral conclusion mapping)
  • ListIssueComments
  • CreatePullRequestReview (APPROVE → APPROVED translation for Gitea)

Auth

`Authorization: token ` from `GITEA_TOKEN` (existing `resolveGiteaToken`). Codeberg falls back to `CODEBERG_TOKEN` then `GITEA_TOKEN`. Self-signed TLS tolerated to mirror the detect-probe behaviour for private CAs.

Tests

Httptest-server-driven coverage: validation, GetIssue + PR flag, ListIssues query-string encoding (state/labels/sort/limit/page/type), GetPullRequest merged-state override, GetCommitChecks status mapping, CreatePullRequestReview event mapping, non-2xx error propagation, classifyHost("git.librete.ch") shortcut.

Acceptance vs #1567

  • HTTP-based — no `tea` CLI dependency
  • Token auth via env (`GITEA_TOKEN`)
  • Host classifier short-circuits `git.librete.ch`
  • Existing GitHub flows untouched (`token.go` switch unchanged for ForgeGitHub)
  • Real smoke test against `git.librete.ch` — defer to hands-on validation; the test suite + httptest server cover the wire shape

Notes on `plan-research` deferral

The original plan §3 row called for a `plan-research` artifact mapping the Gitea v1 surface before `impl-issue`. Skipped here because the Gitea API documentation is well-known and the surface mirrors GitHub closely — no research output would have changed the implementation. Tests double as the artifact.

646 LOC, 2 new files + 2 modified.

Implement forge.Client against the Gitea v1 HTTP API. Works with vanilla
Gitea, Forgejo (same /api/v1 surface), and Codeberg (hosted Forgejo).
Auth via the Authorization: token <TOKEN> header, sourced from
GITEA_TOKEN (or CODEBERG_TOKEN with GITEA_TOKEN fallback).

No dependency on the `tea` CLI — pure HTTP. Self-signed TLS is tolerated
to mirror the existing classifyHost probe behaviour, since self-hosted
Gitea instances commonly ship with private CAs.

Components:

- internal/forge/gitea.go — GiteaClient + DTOs + conversions for the full
  forge.Client interface (GetIssue, ListIssues, GetPullRequest,
  ListPullRequests, ListPullRequestCommits, GetCommitChecks,
  ListIssueComments, CreatePullRequestReview).
- internal/forge/token.go — NewClient now dispatches Gitea/Forgejo/
  Codeberg ForgeType into NewGiteaClient. Codeberg uses host
  "codeberg.org" when info.Host is empty.
- internal/forge/detect.go — classifyHost short-circuits "git.librete.ch"
  to ForgeGitea so the boot path doesn't pay the 3s HTTP probe inside
  the re-cinq-hosted Gitea.

Tests cover validation (empty host/token), GetIssue success + PR flag,
ListIssues query-string encoding (state/labels/sort/limit/page/type),
GetPullRequest merged-state override, GetCommitChecks status mapping
(success/failure/pending), CreatePullRequestReview event mapping
(APPROVE→APPROVED), non-2xx error propagation, and the librete.ch
shortcut.

Closes #1567.
…HOSTS env

The hardcoded `git.librete.ch` shortcut leaked a private Gitea host into
shipped code. Replace with a generic operator escape hatch:

- WAVE_GITEA_HOSTS: comma-separated list of self-hosted Gitea hostnames
- WAVE_FORGEJO_HOSTS: same shape for Forgejo

Without an entry, classifyHost falls through to the existing tea-CLI +
HTTP probe path (3s on first call). Operators with private instances can
now skip the probe by exporting either env without leaking their hosts
into upstream wave.
- gitea.go: replace `Label{Name: l.Name, Color: l.Color}` with `Label(l)`
  (staticcheck S1016 — structurally identical types).
- gitea_test.go: drop the unused `token` parameter from
  giteaClientForTest, which always received "secret" (unparam).
Excluded from default builds via `//go:build live`. Configurable per
GITEA_HOST/GITEA_OWNER/GITEA_REPO/GITEA_TOKEN env so the same test runs
against any Gitea/Forgejo instance.

Validates ListIssues + ListPullRequests against a real instance.
Verified end-to-end: codeberg.org/forgejo/forgejo returns issues + PRs
including the merged-state override (state=closed && merged=true →
exposed as State="merged" in the forge-neutral PullRequest).
@nextlevelshit nextlevelshit merged commit f9f3f1b into main Apr 29, 2026
3 checks passed
@nextlevelshit nextlevelshit deleted the wave/pre3-gitea-adapter branch April 29, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PRE-3: Forge/Gitea adapter (HTTP)

1 participant