repos: Gitea provider (first-class) — sync + wizard default
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 42s
ci / rust (push) Successful in 2m47s
ci / e2e (push) Has been skipped
ci / publish (push) Successful in 2m30s

Fleet's Gitea (git.redclaw.dev) hosts most of this workspace's repos,
so Gitea gets the same inline sync treatment GitHub already had.

Backend sync_gitea:
- base_url is required — Gitea has no shared 'gitea.com'; we accept
  either the instance root (auto-appends /api/v1) or the fully-formed
  API base if the user already included the suffix
- /orgs/{owner}/repos when owner set, /repos/search when not (with the
  {data: [...], ok: bool} envelope Gitea wraps that endpoint in)
- 404 with an owner surfaces as 'org not found or PAT lacks access',
  same UX as GitHub
- 50/page, capped at 20 pages (~1000 repos); short page terminates
- upsert_gitea_repo tolerates the small field-name differences
  (stars_count vs stargazers_count, owner.login vs owner.username on
  older versions)

Frontend wizard:
- Gitea listed first — matches the workspace's actual usage
- Default provider selection is now gitea
- Token-input placeholder tailored per provider (Gitea's is
  'Settings → Applications → Generate New Token (repo)')

GitLab still returns 'not yet supported' — that's the next follow-up.
This commit is contained in:
Omar Sobh
2026-07-07 15:11:30 -07:00
parent 9cb14ddd89
commit e858a7f92f
2 changed files with 196 additions and 4 deletions
@@ -18,7 +18,7 @@ import { GitBranch, KeyRound, Link2, X } from "lucide-react";
const mono =
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
type Provider = "github" | "gitea" | "gitlab";
type Provider = "gitea" | "github" | "gitlab";
const PROVIDERS: {
key: Provider;
@@ -27,8 +27,9 @@ const PROVIDERS: {
needsBaseUrl: boolean;
defaultBase?: string;
}[] = [
{ key: "github", label: "GitHub", hint: "github.com or GHES", needsBaseUrl: false, defaultBase: "https://api.github.com" },
// Gitea first — most of this workspace's repos live on the fleet's Gitea.
{ key: "gitea", label: "Gitea", hint: "self-hosted", needsBaseUrl: true, defaultBase: "https://git.example.com" },
{ key: "github", label: "GitHub", hint: "github.com or GHES", needsBaseUrl: false, defaultBase: "https://api.github.com" },
{ key: "gitlab", label: "GitLab", hint: "gitlab.com or self-hosted", needsBaseUrl: true, defaultBase: "https://gitlab.com" },
];
@@ -47,7 +48,7 @@ export function RepoConnectionWizardStub({
onClose: () => void;
onCreated: () => void;
}) {
const [provider, setProvider] = useState<Provider>("github");
const [provider, setProvider] = useState<Provider>("gitea");
const [token, setToken] = useState("");
const [owner, setOwner] = useState("");
const [baseUrl, setBaseUrl] = useState("");
@@ -199,7 +200,13 @@ export function RepoConnectionWizardStub({
type="password"
value={token}
onChange={(e) => setToken(e.target.value)}
placeholder={provider === "github" ? "ghp_… (needs `repo` scope for private)" : "PAT"}
placeholder={
provider === "github"
? "ghp_… (needs `repo` scope for private)"
: provider === "gitea"
? "Settings → Applications → Generate New Token (repo)"
: "PAT (read_api + read_repository)"
}
style={fieldStyle}
autoComplete="off"
/>