feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: add Gitea source-control provider - #8232

Closed
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider
Closed

feat: add Gitea source-control provider#8232
MDragonryu wants to merge 15 commits into
pingdotgg:mainfrom
MDragonryu:feat/gitea-source-control-provider

Conversation

@MDragonryu

@MDragonryuMDragonryu commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • add Gitea discovery and authentication through tea
  • implement repository, branch, pull-request, and provider operations
  • wire Gitea through server, web, mobile, contracts, Git actions, and documentation
  • add focused coverage for CLI parsing, provider behavior, registry discovery, and source-control contracts

Testing

  • CI=true pnpm exec vp test run apps/server/src/sourceControl/GiteaCli.test.ts apps/server/src/sourceControl/GiteaSourceControlProvider.test.ts apps/server/src/sourceControl/giteaLogins.test.ts apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts packages/shared/src/sourceControl.test.ts packages/contracts/src/sourceControl.test.ts
  • 6 test files passed, 109 tests passed
  • live validation completed against a fresh Gitea container with repository, commit/push, and three pull-request scenarios

Notes

Gitea support uses the authenticated tea CLI and preserves the existing provider architecture across server and clients.


Note

Medium Risk
Changes git stacked actions, PR creation/deduping, and remote provider resolution; behavior is heavily tested but errors could still mis-route remotes or mishandle tea HTTP responses.

Overview
Adds first-class Gitea support end-to-end so remotes no longer resolve to unknown and Commit, push & create PR works against Gitea hosts.

On the server, a new GiteaCli layer wraps the tea tool (API calls with HTTP status parsing because tea api exits 0 on errors, client-side PR list filtering/pagination, repo publish routing to user/repos vs orgs/.../repos). GiteaSourceControlProvider plugs into the existing registry with CLI discovery from tea logins list and host-based refinement of otherwise-unknown self-hosted remotes when tea is logged into that host.

Contracts, shared remote detection (gitea.com / *gitea* hosts), web and mobile add-project/publish/settings flows, and docs are extended for gitea. Publish UI uses discovery for hostname when there is no canonical host. In-app deep links still skip Gitea (parseChangeRequestUrl does not claim /pulls/{n}), while checkout/reference parsing accepts Gitea URLs and tea pulls checkout.

VcsProcess now treats tea’s “no available login” stderr as an authentication failure.

Reviewed by Cursor Bugbot for commit d1b9069. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Gitea source-control provider across backend, web, and mobile

  • Implements GiteaCli wrapper around the tea CLI in GiteaCli.ts, covering PR list/get/create, repo creation, clone URLs, default branch, and checkout, with typed errors and HTTP status mapping.
  • Adds GiteaSourceControlProvider in GiteaSourceControlProvider.ts implementing the provider contract, plus discovery and auth parsing based on tea logins list output.
  • Wires Gitea into the server provider registry and WebSocket RPC layers in server.ts and ws.ts.
  • Updates web, mobile, and shared packages: command palette, publish dialog, PR reference parsing, settings UI, icons, and provider detection.
  • Risk: new 'gitea' literal added to SourceControlProviderKind schema in sourceControl.ts; out-of-tree contract consumers must regenerate schemas to decode the new kind.

Macroscope summarized d1b9069.

MDragonryuand others added 7 commits August 24, 2026 03:36
Adds `gitea` to `SourceControlProviderKind` and gives it provider-neutral
presentation metadata (PR / pull request terminology, `tea pulls checkout`
example). Static remote detection matches only obvious installations —
`gitea.com` and hosts carrying a `gitea` DNS label — because Gitea is usually
self-hosted on a hostname that says nothing about it. Arbitrary hosts stay
`unknown` here and are refined later from `tea`'s authenticated logins.
No provider is registered yet, so behavior is unchanged for every existing host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tea logins list --output json` is how the server learns which Gitea instances
it can talk to. This parses that output into a typed list and adds host lookup
used both by the Source Control settings card and by unknown-remote refinement.
Two details verified against tea 0.15.1 rather than assumed:
- `default` is reported as the string "true"/"false", not a boolean.
- No token appears in the output, so it is safe to parse and log around.
Host matching compares hostnames with ports stripped, because a Gitea instance
is routinely reached over HTTPS on one port and SSH on another; an SSH remote
would otherwise never match its own login. Matching stays exact per DNS label,
so a suffix like evil-git.example.com cannot impersonate git.example.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wraps the official Gitea CLI for the operations SourceControlProvider needs,
going through `tea api` rather than `tea pulls`. Two verified gaps forced that
choice: `tea pulls list` cannot filter by head branch, and its --state accepts
only all/open/closed, while T3 needs head filtering and a distinct merged state.
`tea api` is the same escape hatch GitLabCli uses with `glab api`.
The important detail is error handling. `tea api` exits 0 even for HTTP 401,
403, 404 and 429, so exit-code classification silently reports failures as
success — a 404 would look like "no pull request exists" and T3 would open a
duplicate. Every call therefore passes -i, which puts the status line on stderr
and leaves clean JSON on stdout, and failures are classified from that status.
Gitea has no head filter on its list endpoint, so pages are walked and matched
locally, bounded to 5 pages of 50 and exiting early. The common case is one
request. Merged is read off the `merged` flag, since Gitea models a merged PR as
closed. PR bodies are passed as `-F body=@file`, which was verified to encode
file contents as a JSON string even when they begin with `{`, keeping bodies out
of argv.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds GiteaSourceControlProvider and wires it into the registry, so a Gitea
remote now resolves to a real provider instead of the `unknown` stub whose every
method fails.
Detection works in two stages. Obvious hosts are matched statically, and any
other host stays `unknown` until discovery asks `tea logins list` whether it is
an instance the server is authenticated against. That keeps arbitrary Git hosts
untouched and avoids probing unknown remotes over the network. Host comparison
ignores ports, since a Gitea instance is commonly reached over HTTPS and SSH on
different ones.
The settings card reports the default `tea` login; additional instances are
named in the detail rather than dropped, since the discovery contract holds a
single account but refinement still consults every login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drives the whole stacked action through the real Gitea provider, which is the
workflow the original report failed on: a Gitea remote resolved to `unknown`,
whose stub failed every call, so the action died with "No unknown source control
provider is registered."
Also covers the duplicate case. GitManager looks for an existing PR before
creating one, and swallowing a provider error there would open a second PR, so
the test asserts create is never called when one is already open.
makeManager gained an optional sourceControlProvider override; it defaults to
the GitHub provider, so every existing test is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Gitea to the Add Project source list and the publish provider options, and
documents setup in the user guide.
The mobile and web clients both draw Gitea with the neutral pull-request mark:
no Gitea logo is bundled here yet, and borrowing another host's brand would be
wrong. A real icon can drop in later without touching this wiring.
One bounded limitation is documented rather than designed around: a short
owner/repository path resolves against tea's default login, so cloning from a
second Gitea instance needs a full Git URL. Representing per-instance selection
would mean a new account-selection contract, which this change does not add.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ish the UI surfaces
Review pass over the Gitea provider. The important fix is repository creation.
Gitea splits creation in two: POST /user/repos creates under the authenticated
user, while POST /orgs/{org}/repos requires a real organization and 404s for a
plain user. Every owner was being sent to the orgs endpoint. Because the publish
dialog prefills the signed-in account as the owner and refuses to submit without
one, the ordinary input is `<you>/name` — so publishing to Gitea would have
failed for essentially every user. Creation now resolves the authenticated
account first and picks the endpoint accordingly.
The rest is reach. Gitea was in the Add Project list but missing from four
surfaces that each keep their own provider enumeration:
- The publish dialog had a separate PublishProviderKind that never included it,
so the previous commit's message overstated what worked. Its host label reads
the authenticated instance from discovery, since a self-hosted Gitea has no
canonical host to hardcode.
- CommandPalette kept private copies of the Add Project types, which is why it
drifted. It now imports the canonical ones, which turned the gap into three
compiler errors and one silent one: an icon switch returning ReactNode, whose
missing arm type-checks fine and renders nothing.
- Two mobile guards narrowed by string equality and dropped gitea, sending the
Gitea entry to the URL flow.
- The settings icon map and the PR link context menu are Partial records, so
neither complained about the missing key.
Gitea PR URLs (/{owner}/{repo}/pulls/{n}) are now recognized by the in-app link
handler. The plural path is Gitea's own; the GitHub-ish hosts return earlier, so
the singular /pull/ shape is untouched.
`tea` reports an unconfigured instance as "no available login" and exits 1, which
classified as a generic command failure. It is now an authentication failure, so
the user is told to run `tea login add`.
Deliberately not changed: PROVIDER_REQUIREMENT in the pullRequest contract. Gitea
is not registered in the PR dashboard registry, so its reason is
provider-unsupported, which returns null before that table is consulted. An entry
would be dead code implying dashboard support that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dce5a6-b2ec-4eb6-81ff-3ae86dc8f69e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 25, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Structure, namespace imports, Context.Service + inline interface, make/layer, and environment-based dependency acquisition all look right, and the test-only service-instance injection in GitManager.test.ts is a legitimate seam. Three findings on error modeling in apps/server/src/sourceControl/GiteaCli.ts.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaSourceControlProvider.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Consistency: 2 findings

Provider plumbing for the web client is otherwise complete and consistent (icon maps, labels, path hints, readiness, publish host, "Open on Gitea"). Two web surfaces that key off provider identity were not extended with the new kind, so Gitea users get different behaviour from every other host:

  1. apps/web/src/lib/openPullRequestLink.ts (inline comment) — Gitea PR URLs are now readable for opening links, but apps/web/src/pullRequestReference.ts still rejects them for input, while the input placeholder promises "PR URL, checkout command, or #42".

  2. apps/web/src/components/pullRequest/pullRequestDetail.logic.ts:855TOOL_NOISE replaces content-free host errors with an actionable hint for github|gitlab|bitbucket|azure devops, but the new provider's GiteaCliCommandError.detail is "Gitea CLI command failed." (apps/server/src/sourceControl/GiteaCli.ts:121), which no pattern matches. A failed Gitea action therefore surfaces the bare "Gitea CLI command failed." string where the other hosts surface the hint. Smallest fix is to include gitea in the alternation:

- /^(github|gitlab|bitbucket|azure devops)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,+ /^(github|gitlab|bitbucket|azure devops|gitea)?\s*(cli|api)?\s*(command\s*)?failed\.?$/iu,

Minor, optional: the "Add project" command item's searchTerms (apps/web/src/components/CommandPalette.tsx:1547) lists github/gitlab/bitbucket/azure/devops but not gitea, so typing "gitea" no longer reaches the flow that now offers a Gitea source.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@macroscopeapp

macroscopeappBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a large, cross-cutting Gitea integration with new authenticated repository, pull-request, checkout, and publishing workflows across production server and client paths. A remaining error-classification concern may cause missing pull requests to surface as generic command failures, so the runtime behavior warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in :\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout , and required a real NAME:
tea - command line tool to interact with Gitea

USAGE:
tea [global options] [command [command options]]

VERSION:
Version: �[1m0.15.1�[0m golang: 1.26.5 go-sdk: v1.2.0

DESCRIPTION:
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
help, h Shows a list of commands or help for one command

ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Manage repositories
branches, branch, b Consult branches
actions, action Manage repository actions
wiki Manage repository wiki pages
webhooks, webhook, hooks, hook Manage webhooks
comments, comment, c Manage comments on issues and pull requests

HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
api Make an authenticated API request

MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance

SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
ssh-keys, ssh-key Manage SSH public keys

GLOBAL OPTIONS:
--debug, --vvv Enable debug mode
--help, -h show help
--version, -v print the version login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@MDragonryu

Copy link
Copy Markdown
Author

Addressed the review findings in commit 3e5118d:\n\n- tightened Gitea execution error structure and HTTP status handling\n- classified only missing tea executables as unavailable\n- handled malformed PR URLs and preserved raw pagination counts\n- matched fork source owners, honored checkout --force, and required a real tea login user\n- added Gitea pull-request URL/CLI reference recognition and UI noise/search coverage\n- preserved public GitHub/Bitbucket URL behavior while allowing self-hosted lookalike hosts to fall through to Gitea parsing\n\nVerification in the Linux devcontainer: 8 focused test files, 161 tests passed; server and web TypeScript checks completed with only existing repository suggestions.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Gitea service modules against the Effect service conventions. Service definition, dependency acquisition (GiteaCli/GiteaSourceControlProvider both acquire their deps with yield* and expose make/layer), namespace imports, and the registry/layer wiring all look consistent with the sibling providers. Two error-modelling points below.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
Comment threadapps/web/src/pullRequestReference.ts Outdated
Comment threadapps/server/src/sourceControl/GiteaCli.ts
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit 3f98c41:\n\n- createPullRequest no longer supplies a PR reference to HTTP error mapping, so a create-time 404 is a GiteaCliCommandError; existing get/checkout PR 404s remain not-found\n- listPullRequests now requests sort=recentupdate, keeping current-branch PR discovery within the bounded page window\n\nThe focused Linux devcontainer suite now passes 8 files and 164 tests.

Comment threadapps/web/src/pullRequestReference.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up Effect cleanup pushed in commit c0599af:\n\n- removed the redundant single-valued list decode operation field\n- removed the manufactured non-array JSON Error; decoder failures now carry the real schema failure while malformed JSON retains the real parse exception\n\nThe focused Linux devcontainer suite remains green: 8 files, 164 tests.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new tea branch in classifyNonZeroExit changes backend error classification but has no focused test, and no tea rule produces not-found, so GiteaCli.checkoutPullRequest's not-found mapping is unreachable in production. Everything else in the new Gitea service modules follows the conventions (subpath namespace imports, inline Context.Service interface, make acquiring VcsProcess/GiteaCli from the environment, Schema.TaggedErrorClass errors with structural attributes and preserved cause, exported Schema.is predicate, layer at the bottom).

Posted via Macroscope — Effect Service Conventions

Comment on lines +68 to +70
// `tea` reports an unconfigured or unmatched instance this way, and it is by far the most
// common Gitea setup mistake. Scoped to tea so the phrase cannot misclassify another CLI.
(command === "tea" && normalized.includes("no available login"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes backend failure classification (a tea exit mentioning "no available login" now surfaces as GiteaCliAuthenticationError rather than GiteaCliCommandError), but VcsProcess.test.ts has no case for it even though it already covers the analogous authentication and rate-limited phrases. Consider adding a focused case asserting failureKind: "authentication" for command: "tea" with that stderr, plus one asserting another command with the same phrase stays command-failed (the reason the rule is scoped to tea).

Related: no tea branch exists in the not-found block below, so classifyNonZeroExit never returns not-found for tea. GiteaCli.checkoutPullRequest's GiteaPullRequestNotFoundError.fromVcsError path is therefore only reachable in the unit test that hand-builds failureKind: "not-found"; a real tea pulls checkout 9999 failure maps to GiteaCliCommandError. Worth either adding the tea not-found phrases here or dropping the unreachable branch.

Posted via Macroscope — Effect Service Conventions

@MDragonryu

Copy link
Copy Markdown
Author

Follow-up fixes pushed in commit d257b11:\n\n- full PR URL checkout now verifies the URL repository against the current Gitea repository before invoking tea, preserving same-repository and numeric checkout behavior\n- public github.com and bitbucket.org /pulls/ URLs are excluded from the generic Gitea reference parser while self-hosted lookalikes remain supported\n- added regression coverage for the repository guard and URL parsing\n\nVerification: 8 focused Linux devcontainer test files, 168 tests passed; targeted server/web TypeScript checks completed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new finding on the Gitea CLI error model, plus one earlier finding that is still open (see below). Everything else in the Gitea service/provider pair follows the conventions: namespaced effect/* imports, Context.Service with an inline interface, exported make/layer, dependencies acquired via yield* VcsProcess.VcsProcess / yield* GiteaCli.GiteaCli, structured Schema.TaggedErrorClass failures with real causes preserved and messages derived from stable attributes, and Schema.Union + Schema.is for the error predicate.

Still open from a previous run (not re-commented): apps/server/src/vcs/VcsProcess.ts changes non-zero-exit classification so a tea failure mentioning "no available login" is now categorized as authentication, and apps/server/src/vcs/VcsProcess.test.ts still has no case covering it (nor a case proving another command with the same phrase stays command-failed). Backend behavior changes should carry focused tests.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/sourceControl/GiteaCli.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Follow-up test/convention fixes pushed in commit 556ce2d:\n\n- removed the redundant single-value Gitea pull-request decode operation field\n- added focused coverage for tea no-available-login authentication classification and the non-tea negative case\n\nVerification: 9 focused Linux devcontainer test files, 182 tests passed.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces for the Gitea provider addition (CommandPalette.tsx, GitActionsControl.tsx, SourceControlSettings.tsx, sourceControlPresentation.ts, pullRequestLinkContextMenu.ts, pullRequestDetail.logic.ts, openPullRequestLink.ts, pullRequestReference.ts). Provider enumerations, labels, icons and readiness maps are consistent, and the reference parser gap flagged on the earlier revision is now closed.

Two findings, one behavioral:

  1. openPullRequestLink.ts now claims Gitea /pulls/{n} URLs, which makes those links open the in-app change-request surfaces even though this build registers no Gitea pull-request provider — the reader lands on the "Could not load pull requests" empty state instead of the host page they previously got in the browser.
  2. GitActionsControl.tsx uses the literal "gitea" as the publish-dialog host, which is rendered as a hostname prefix when discovery reports no host.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/GitActionsControl.tsx Outdated
Comment threadapps/web/src/lib/openPullRequestLink.ts Outdated
@MDragonryu

Copy link
Copy Markdown
Author

Final review cycle for 556ce2d is complete. Cursor Bugbot, Macroscope Correctness, Effect Service Conventions, and UI Consistency are green; the Approvability check completed neutral with correctness checked and eligibility unchecked. No new inline comments were posted on the current head. The worktree is clean and the PR is ready for human review.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16d32bc. Configure here.

Comment threadapps/web/src/components/settings/SourceControlSettings.logic.ts Outdated

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings, both in the Source Control settings copy that this PR reworked to make room for Gitea's tea login add hint. The rest of the Gitea UI wiring (neutral GitPullRequestIcon for the unbranded provider, resolvePublishHost host fallback, /pulls/ link handling) looks consistent with the existing provider patterns.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/components/settings/SourceControlSettings.tsx Outdated
Comment on lines +22 to +23
if (executable !== null) {
return `${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login add is a tea-only subcommand, but this helper renders the unauthenticated row for every provider, so GitHub now reads "Run gh login add", GitLab "Run glab login add" and Azure DevOps "Run az login add" — none of those commands exist (gh auth login, glab auth login, az login). The copy that was replaced was deliberately executable-agnostic.

Consider keeping the neutral phrasing here (or threading a per-provider auth command through the discovery spec) so only Gitea gets the tea login add wording:

Suggested change
if(executable!==null){
return`${label} is not authenticated on this server. Run \`${executable} login add\` on the server host to enable change request features.`;
return`${label} is not authenticated on this server. Sign in or configure credentials using the \`${executable}\` tool on the server host to enable change request features.`;

Posted via Macroscope — UI Consistency

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We do not have a current support commitment for Gitea. This 39-file branch would add a source control CLI, discovery, repository operations, settings, contracts, icons, and a permanent compatibility obligation.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotggt3dotgg closed this Aug 28, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MDragonryu@t3dotgg