Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f62ae83
feat(contracts): add forgejo source control provider kind
pat-s Jun 9, 2026
c18706a
feat(shared): add Forgejo presentation and remote detection hint
pat-s Jun 9, 2026
fd0e36d
feat(server): add ForgejoKeyStore reading fj's keys.json
pat-s Jun 9, 2026
e3ab19c
feat(server): add Forgejo pull request schemas and normalization
pat-s Jun 9, 2026
b333336
feat(server): add per-host Forgejo REST API client
pat-s Jun 9, 2026
cf7b643
fix(server): harden Forgejo checkout remote fallback and PR branch fi…
pat-s Jun 9, 2026
16a0f01
feat(server): add Forgejo provider wrapper and fj CLI discovery
pat-s Jun 9, 2026
2f1aef5
feat(server): register Forgejo provider in the source control registry
pat-s Jun 9, 2026
46c6b97
feat(server): provide Forgejo API layer to the source control registry
pat-s Jun 9, 2026
e63be7a
feat(web): add Forgejo icon and presentation
pat-s Jun 9, 2026
2d4ed04
feat(mobile): add Forgejo source control icon
pat-s Jun 9, 2026
434a11a
feat: offer Forgejo in the add-project clone flow
pat-s Jun 9, 2026
2c9905b
test(server): cover mixed-case host in Forgejo remote refinement
pat-s Jun 9, 2026
7d7eb52
fix(web): show Forgejo icon in the source control settings list
pat-s Jun 10, 2026
b04eab9
fix(ui): use the official Forgejo logo for web and mobile icons
pat-s Jun 10, 2026
a5648bf
fix(server): refine self-hosted provider for git status terminology
pat-s Jun 10, 2026
a7852c5
fix(server): scope git-status provider refinement to Forgejo only
pat-s Jun 10, 2026
339b3ee
fix(server): harden Forgejo host resolution from review feedback
pat-s Jun 10, 2026
62d7841
Merge branch 'main' into forgejo-source-control-provider
pat-s Jun 10, 2026
f2565ce
fix(server): decouple Forgejo PR fetch size from caller limit
pat-s Jun 10, 2026
8ae9963
fix(server): match head fork owner when listing Forgejo PRs
pat-s Jun 10, 2026
a30b444
fix(server): parse clone-URL repository specs for Forgejo
pat-s Jun 10, 2026
1dfd06b
fix(server): refine the branch's own remote for git-status provider
pat-s Jun 10, 2026
f58be49
fix(server): respect the remote scheme for Forgejo API calls
pat-s Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions apps/mobile/src/components/SourceControlIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Svg, { Defs, LinearGradient, Path, Stop } from "react-native-svg";
import Svg, { Circle, Defs, LinearGradient, Path, Stop } from "react-native-svg";

export type SourceControlIconKind = "github" | "gitlab" | "bitbucket" | "azure-devops";
export type SourceControlIconKind = "github" | "gitlab" | "bitbucket" | "azure-devops" | "forgejo";

export function SourceControlIcon(props: {
readonly kind: SourceControlIconKind;
Expand Down Expand Up @@ -95,5 +95,25 @@ export function SourceControlIcon(props: {
/>
</Svg>
);
case "forgejo":
return (
<Svg width={size} height={size} viewBox="0 0 212 212" fill="none">
<Path
d="M64 174 v-98 a50 50 0 0 1 50-50 h20"
stroke="#ff6600"
strokeWidth={25}
fill="none"
/>
<Path
d="M64 174 v-30 a50 50 0 0 1 50-50 h20"
stroke="#d40000"
strokeWidth={25}
fill="none"
/>
<Circle cx={148} cy={26} r={18} stroke="#ff6600" strokeWidth={15} fill="none" />
<Circle cx={148} cy={94} r={18} stroke="#d40000" strokeWidth={15} fill="none" />
<Circle cx={64} cy={186} r={18} stroke="#d40000" strokeWidth={15} fill="none" />
</Svg>
);
}
}
3 changes: 2 additions & 1 deletion apps/mobile/src/features/projects/AddProjectScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ function sourceFromParam(value: string | string[] | undefined): AddProjectRemote
source === "github" ||
source === "gitlab" ||
source === "bitbucket" ||
source === "azure-devops"
source === "azure-devops" ||
source === "forgejo"
) {
return source;
}
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/git/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@ function makeManager(input?: {
get: () => Effect.succeed(provider),
resolveHandle: () => Effect.succeed({ provider, context: null }),
resolve: () => Effect.succeed(provider),
refineRemoteProvider: () => Effect.succeed(null),
discover: Effect.succeed([]),
}),
),
Expand Down
23 changes: 20 additions & 3 deletions apps/server/src/git/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,11 +798,28 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
branch === null
? "origin"
: ((yield* readConfigValueNullable(cwd, `branch.${branch}.remote`)) ?? "origin");
const preferredRemoteUrl = yield* readConfigValueNullable(
cwd,
`remote.${preferredRemoteName}.url`,
);
const remoteName = preferredRemoteUrl ? preferredRemoteName : "origin";
const remoteUrl =
(yield* readConfigValueNullable(cwd, `remote.${preferredRemoteName}.url`)) ??
(yield* readConfigValueNullable(cwd, "remote.origin.url"));
preferredRemoteUrl ?? (yield* readConfigValueNullable(cwd, "remote.origin.url"));
if (!remoteUrl) return null;

const detected = detectSourceControlProviderFromGitRemoteUrl(remoteUrl);
if (detected && detected.kind !== "unknown") {
return detected;
}

return remoteUrl ? detectSourceControlProviderFromGitRemoteUrl(remoteUrl) : null;
// Forgejo has no canonical hostname, so static detection returns "unknown" for its
// self-hosted instances. Refine THIS branch's remote via `fj auth list` (not origin),
// but only adopt the result when it resolves to Forgejo so other providers keep their
// existing status behavior.
const refined = yield* sourceControlProviders
.refineRemoteProvider({ cwd, remoteName, remoteUrl })
.pipe(Effect.orElseSucceed(() => null));
return refined?.kind === "forgejo" ? refined : detected;
Comment thread
cursor[bot] marked this conversation as resolved.

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.

Status provider mismatches PR lookup

Medium Severity

resolveHostingProvider can refine the branch’s tracking remote to forgejo and attach that as sourceControlProvider on local status, but findLatestPr still calls sourceControlProviders.resolve({ cwd }), which picks a provider from remotes (preferring origin) without the same branch-aware Forgejo refinement. Repos with a non-Forgejo origin and a Forgejo upstream can show Forgejo in the UI while open-PR lookup hits the wrong provider and misses the PR.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1dfd06b. Configure here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Leaving this as-is intentionally.

Git status resolves the hosting provider from the current branch's tracking remote (branch-aware, with Forgejo refinement), while PR lookup (findOpenPrresolve({ cwd })) resolves origin-first. These can only diverge in a multi-remote setup where origin points at a non-Forgejo host and the branch's upstream is a separate Forgejo remote.

In the common case — a Forgejo repo whose origin is the Forgejo instance — both paths resolve to Forgejo and agree. Fully reconciling the multi-remote case would require making the provider registry (and every PR operation) branch-aware, which changes remote selection for all providers; that's out of scope for this PR and noted as a potential follow-up.

});

const resolveRemoteRepositoryContext = Effect.fn("resolveRemoteRepositoryContext")(function* (
Expand Down
9 changes: 8 additions & 1 deletion apps/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { CheckpointDiffQueryLive } from "./checkpointing/Layers/CheckpointDiffQu
import { CheckpointStoreLive } from "./checkpointing/Layers/CheckpointStore.ts";
import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts";
import * as BitbucketApi from "./sourceControl/BitbucketApi.ts";
import * as ForgejoApi from "./sourceControl/ForgejoApi.ts";
import * as GitHubCli from "./sourceControl/GitHubCli.ts";
import * as GitLabCli from "./sourceControl/GitLabCli.ts";
import * as TextGeneration from "./textGeneration/TextGeneration.ts";
Expand Down Expand Up @@ -175,7 +176,13 @@ const VcsDriverRegistryLayerLive = VcsDriverRegistry.layer.pipe(

const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.layer.pipe(
Layer.provide(
Layer.mergeAll(AzureDevOpsCli.layer, BitbucketApi.layer, GitHubCli.layer, GitLabCli.layer),
Layer.mergeAll(
AzureDevOpsCli.layer,
BitbucketApi.layer,
ForgejoApi.layer,
GitHubCli.layer,
GitLabCli.layer,
),
),
Layer.provideMerge(GitVcsDriver.layer),
Layer.provideMerge(VcsDriverRegistryLayerLive),
Expand Down
Loading
Loading