feat: add Gitea/Forgejo integration for pull request management#3332
feat: add Gitea/Forgejo integration for pull request management#3332gabrielbelli wants to merge 5 commits into
Conversation
Add a new Gitea provider to the git-host crate, enabling PR creation, status tracking, branch listing, and comment retrieval for Gitea and Forgejo instances (including Codeberg). Unlike the GitHub and Azure providers which shell out to CLI tools, this uses the Gitea REST API v1 directly via reqwest — no external binary needed. Authentication is via GITEA_TOKEN env var with tea CLI config as fallback. Self-hosted instances with custom domains are supported via the GITEA_URL env var. Tested end-to-end against a live Gitea 1.25.5 instance.
- Fetch review comments per-review via /reviews/{id}/comments instead
of expecting inline comments in the reviews list response (Gitea API
does not embed comments in the reviews endpoint)
- Separate 401 (AuthFailed) from 403 (InsufficientPermissions) in
check_response_status so users with wrong token scopes get the
correct error message
- Add InsufficientPermissions variant to GiteaApiError
- Remove unreachable 403 string-matching in From<GiteaApiError> impl
- Add empty-string guard to GITEA_URL detection: str::contains("")
always returns true in Rust, so an empty/scheme-only GITEA_URL would
match every URL as Gitea
- Only use GITEA_URL env var in gitea_base_url() when the input URL
actually matches the configured instance, so interactions with other
Gitea instances (e.g. Codeberg) derive the correct base URL
- Add 4 edge-case tests covering both bugs
Addressed Bugbot findingsRound 1 (commit 6940051)
Round 2 (commit 08edc93)
TestingAll fixes verified end-to-end against a live Gitea 1.25.5 instance through the full VK server stack:
|
- Read response body on error so Gitea's error messages (e.g. "The base branch does not exist") are included in error output instead of just the HTTP status code - Remove unused probe_instance() and GiteaVersionResponse dead code - Remove unreliable tea CLI config parser that ignored which instance a token belonged to — GITEA_TOKEN env var is the only auth method - Update docs to remove tea CLI fallback reference
Security: remove standalone /pulls/ URL pattern detection — it would send GITEA_TOKEN to any server with /pulls/ in the path. Custom-domain Gitea instances must set GITEA_URL for detection. Well-known hostnames (gitea.*, forgejo.*, codeberg.org) still auto-detect. Also fix gitea_base_url() to force HTTPS scheme for ssh:// and git:// remote URLs, which would otherwise produce unreachable base URLs like ssh://host for API calls.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 37acb7f. Configure here.
| head_branch: Option<&str>, | ||
| ) -> Result<Vec<PullRequestDetail>, GiteaApiError> { | ||
| let mut url = self.api_url(&format!("/repos/{}/{}/pulls", info.owner, info.repo)); | ||
| url.push_str(&format!("?state={state}&limit=50")); |
There was a problem hiding this comment.
No pagination in list_prs silently drops results
Medium Severity
list_prs fetches only the first page of results (limit=50) without iterating through subsequent pages. The Gitea API supports page and limit pagination parameters, and returns at most 50 items per page. For repos that accumulate more than 50 closed PRs over time, list_prs_for_branch will silently miss matching PRs since it filters by head branch client-side after fetching. Notably, the Gitea API also offers a GET /repos/{owner}/{repo}/pulls/{base}/{head} endpoint that could replace client-side filtering entirely.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 37acb7f. Configure here.
Aggressive review summary — PR #33321207-line feature: adds Gitea / Forgejo / Codeberg as a third PR-hosting backend alongside GitHub and Azure DevOps. New module What it doesThree detection paths:
The new Findings
NITs
VerdictApprove. — Reviewed by automated single-pass review (new third-party integration; full 4-tool battery skipped — implementation parallels existing providers). |


Summary
Adds a new Gitea/Forgejo provider to the
git-hostcrate, enabling pull request management for Gitea, Forgejo, and Codeberg instances directly from Vibe Kanban.GitHostProvidertrait methods: create PR, get PR status, list PRs by branch, list open PRs, and get PR commentsreqwest— no external CLI binary needed (unlike thegh/azCLI approach used by GitHub/Azure providers)GITEA_TOKENenv var, withteaCLI config as fallbackGITEA_URLenv vargitea.*,forgejo.*,codeberg.org) and the Gitea PR URL pattern (/pulls/)Architecture
The implementation follows the existing provider pattern:
crates/git-host/src/gitea/api.rs— HTTP client wrapping the Gitea REST API v1 withreqwest. Handles token auth, response parsing, and error mapping.crates/git-host/src/gitea/mod.rs—GiteaProviderstruct implementing theGitHostProvidertrait with exponential backoff retry (matching GitHub/Azure providers).crates/git-host/src/detection.rs— URL-based provider detection extended with Gitea patterns (GITEA_URLenv var,/pulls/path, well-known hostnames). Also addsgitea_base_url()helper to extract instance URL from remotes.crates/git-host/src/types.rs—Giteavariant added toProviderKindenum.crates/git-host/src/lib.rs—Gitea(GiteaProvider)variant added toGitHostServiceenum_dispatch.Why direct API instead of a CLI?
The GitHub and Azure providers shell out to
ghandazCLIs respectively, which manage their own authentication. For Gitea:teaCLI has much lower adoption thangh/azreqwest(already a workspace dependency) is simpler and has zero external dependenciesFiles changed
crates/git-host/src/gitea/api.rscrates/git-host/src/gitea/mod.rsGiteaProvidertrait implementationcrates/git-host/src/detection.rscrates/git-host/src/lib.rsgiteamodule, enum variant,from_urlmatch armcrates/git-host/src/types.rsGiteatoProviderKindcrates/git-host/Cargo.tomlreqwestworkspace dependencyshared/types.ts"gitea"added toProviderKinddocs/integrations/gitea-integration.mdxdocs/docs.jsonCargo.lockTesting
Unit tests (33 total, all pass)
/pulls/pattern)gitea_base_url()helperEnd-to-end validation
Manually tested against a live Gitea 1.25.5 instance through the full VK server stack:
POST /workspaces/{id}/pull-requestsGET /repos/pr-info?url=...GET /repos/{id}/prsGET /workspaces/{id}/pull-requests/commentslist_prs_for_branchtrait methodKnown limitations
create_workspace_from_prcurrently hardcodesGhClifor PR checkout — this is a pre-existing issue that also affects Azure DevOps, and should be addressed in a separate PRTest plan
cargo test -p git-host— 33 tests passcargo clippy -p git-host -p server— zero warningscargo fmt -p git-host -- --check— formattedpnpm run generate-types:check— TS types up to datecargo check -p git-host -p server— compiles cleanNote
Medium Risk
Adds a new networked
reqwest-based provider that usesGITEA_TOKENand new URL-detection logic, which could affect provider selection and introduces new auth/error-handling paths. Existing GitHub/Azure flows are mostly untouched but share theGitHostService::from_urldispatch that now includes Gitea.Overview
Adds Gitea/Forgejo (incl. Codeberg) pull request support to the
git-hostcrate by introducing a newGiteaProviderbacked by areqwestREST API client (create PR, fetch PR status, list PRs, and aggregate PR comments).Extends provider detection to recognize Gitea instances via
GITEA_URLand well-known hostnames, plus a newgitea_base_url()helper to derive the correct instance base URL from remote/PR URLs (with added unit tests to avoid false positives). Updates shared types/docs to expose the newProviderKind::Giteaand adds setup documentation forGITEA_TOKEN/GITEA_URL.Reviewed by Cursor Bugbot for commit 37acb7f. Bugbot is set up for automated code reviews on this repo. Configure here.