diff --git a/apps/desktop/src/lib/forge/forgeFactory.svelte.ts b/apps/desktop/src/lib/forge/forgeFactory.svelte.ts index 7a9717015ef..22bd95c982a 100644 --- a/apps/desktop/src/lib/forge/forgeFactory.svelte.ts +++ b/apps/desktop/src/lib/forge/forgeFactory.svelte.ts @@ -1,9 +1,9 @@ -import { AzureDevOps } from "$lib/forge/azure/azure"; -import { BitBucket } from "$lib/forge/bitbucket/bitbucket"; +import { AZURE_DOMAIN, AzureDevOps } from "$lib/forge/azure/azure"; +import { BitBucket, BITBUCKET_DOMAIN } from "$lib/forge/bitbucket/bitbucket"; import { DefaultForge } from "$lib/forge/default/default"; -import { GitHub } from "$lib/forge/github/github"; +import { GitHub, GITHUB_DOMAIN } from "$lib/forge/github/github"; import { GitHubClient } from "$lib/forge/github/githubClient"; -import { GitLab } from "$lib/forge/gitlab/gitlab"; +import { GitLab, GITLAB_DOMAIN, GITLAB_SUB_DOMAIN } from "$lib/forge/gitlab/gitlab"; import { InjectionToken } from "@gitbutler/core/context"; import { deepCompare } from "@gitbutler/shared/compare"; import type { ForgeProvider } from "$lib/baseBranch/baseBranch"; @@ -113,16 +113,16 @@ export class DefaultForgeFactory implements Reactive { } = config; this._githubError = githubError; if (repo && baseBranch) { - const forgeType = forgeOverride ?? detectedForgeProvider ?? "default"; - this._determinedForgeType = forgeType; + this._determinedForgeType = this.determineForgeType(repo, detectedForgeProvider); this._forge = this.build({ repo, pushRepo, baseBranch, - forgeType, githubAuthenticated, forgeIsLoading, gitlabAuthenticated, + detectedForgeProvider, + forgeOverride, }); } else { this._determinedForgeType = "default"; @@ -134,19 +134,25 @@ export class DefaultForgeFactory implements Reactive { repo, pushRepo, baseBranch, - forgeType, githubAuthenticated, forgeIsLoading, gitlabAuthenticated, + detectedForgeProvider, + forgeOverride, }: { repo: RepoInfo; pushRepo?: RepoInfo; baseBranch: string; - forgeType: ForgeName; githubAuthenticated?: boolean; forgeIsLoading?: boolean; gitlabAuthenticated?: boolean; + detectedForgeProvider: ForgeProvider | undefined; + forgeOverride: ForgeName | undefined; }): Forge { + let forgeType = this.determineForgeType(repo, detectedForgeProvider); + if (forgeType === "default" && forgeOverride) { + forgeType = forgeOverride; + } const forkStr = pushRepo && pushRepo.hash !== repo.hash ? `${pushRepo.owner}:${pushRepo.name}` : undefined; @@ -192,6 +198,35 @@ export class DefaultForgeFactory implements Reactive { return this.default; } + private determineForgeType( + repo: RepoInfo, + detectedForgeProvider: ForgeProvider | undefined, + ): ForgeName { + if (detectedForgeProvider) { + return detectedForgeProvider; + } + const domain = repo.domain; + + if (domain.includes(GITHUB_DOMAIN)) { + return "github"; + } + if ( + domain === GITLAB_DOMAIN || + domain.startsWith(GITLAB_SUB_DOMAIN + ".") || + domain.startsWith("xy" + GITLAB_SUB_DOMAIN + ".") // Temporary workaround until we have foerge overrides implemented + ) { + return "gitlab"; + } + if (domain.includes(BITBUCKET_DOMAIN)) { + return "bitbucket"; + } + if (domain.includes(AZURE_DOMAIN)) { + return "azure"; + } + + return "default"; + } + invalidate(tags: TagDescription[]) { const action = this.current.invalidate(tags); const { dispatch } = this.params; diff --git a/apps/desktop/src/lib/forge/forgeFactory.test.ts b/apps/desktop/src/lib/forge/forgeFactory.test.ts index 29dfdb77efb..55473fb05bc 100644 --- a/apps/desktop/src/lib/forge/forgeFactory.test.ts +++ b/apps/desktop/src/lib/forge/forgeFactory.test.ts @@ -52,12 +52,13 @@ describe.concurrent("DefaultforgeFactory", () => { owner: "test-owner", }, baseBranch: "some-base", - forgeType: "github", + detectedForgeProvider: undefined, + forgeOverride: undefined, }), ).instanceOf(GitHub); }); - test("Create GitLab service", async () => { + test("Create self hosted Gitlab service", async () => { const factory = new DefaultForgeFactory({ gitHubClient, gitHubApi, @@ -70,17 +71,18 @@ describe.concurrent("DefaultforgeFactory", () => { expect( factory.build({ repo: { - domain: "gitlab.com", + domain: "gitlab.domain.com", name: "test-repo", owner: "test-owner", }, baseBranch: "some-base", - forgeType: "gitlab", + detectedForgeProvider: undefined, + forgeOverride: undefined, }), ).instanceOf(GitLab); }); - test("setConfig uses detectedForgeProvider when present", async () => { + test("Create Gitlab service", async () => { const factory = new DefaultForgeFactory({ gitHubClient, gitHubApi, @@ -90,17 +92,21 @@ describe.concurrent("DefaultforgeFactory", () => { posthog, dispatch, }); - factory.setConfig({ - repo: { domain: "github.example.net", name: "test-repo", owner: "test-owner" }, - baseBranch: "main", - detectedForgeProvider: "github", - forgeOverride: undefined, - }); - expect(factory.current).instanceOf(GitHub); - expect(factory.determinedForgeType).toBe("github"); + expect( + factory.build({ + repo: { + domain: "gitlab.com", + name: "test-repo", + owner: "test-owner", + }, + baseBranch: "some-base", + detectedForgeProvider: undefined, + forgeOverride: undefined, + }), + ).instanceOf(GitLab); }); - test("setConfig falls back to detectedForgeProvider when forgeOverride is absent", async () => { + test("Respects detectedForgeProvider: GitHub", async () => { const factory = new DefaultForgeFactory({ gitHubClient, gitHubApi, @@ -110,52 +116,39 @@ describe.concurrent("DefaultforgeFactory", () => { posthog, dispatch, }); - factory.setConfig({ - repo: { domain: "github.example.net", name: "test-repo", owner: "test-owner" }, + const result = factory.build({ + repo: { + domain: "gitlab.com", + name: "test-repo", + owner: "test-owner", + }, baseBranch: "main", detectedForgeProvider: "github", forgeOverride: undefined, }); - expect(factory.current).instanceOf(GitHub); - expect(factory.determinedForgeType).toBe("github"); + expect(result).instanceOf(GitHub); }); - test("setConfig resolves to default when both detectedForgeProvider and forgeOverride are absent", async () => { + test("Respects detectedForgeProvider: GitLab", async () => { const factory = new DefaultForgeFactory({ gitHubClient, gitHubApi, - backendApi, gitLabClient, - gitLabApi, - posthog, - dispatch, - }); - factory.setConfig({ - repo: { domain: "custom.example.com", name: "test-repo", owner: "test-owner" }, - baseBranch: "main", - detectedForgeProvider: undefined, - forgeOverride: undefined, - }); - expect(factory.determinedForgeType).toBe("default"); - }); - - test("forgeOverride takes precedence over detectedForgeProvider", async () => { - const factory = new DefaultForgeFactory({ - gitHubClient, - gitHubApi, backendApi, - gitLabClient, gitLabApi, posthog, dispatch, }); - factory.setConfig({ - repo: { domain: "github.com", name: "test-repo", owner: "test-owner" }, + const result = factory.build({ + repo: { + domain: "github.com", + name: "test-repo", + owner: "test-owner", + }, baseBranch: "main", - detectedForgeProvider: "github", - forgeOverride: "gitlab", + detectedForgeProvider: "gitlab", + forgeOverride: undefined, }); - expect(factory.current).instanceOf(GitLab); - expect(factory.determinedForgeType).toBe("gitlab"); + expect(result).instanceOf(GitLab); }); }); diff --git a/crates/but-forge/src/lib.rs b/crates/but-forge/src/lib.rs index 9911958c1d2..77fc8e6f0de 100644 --- a/crates/but-forge/src/lib.rs +++ b/crates/but-forge/src/lib.rs @@ -148,8 +148,7 @@ pub fn get_all_forge_accounts() -> anyhow::Result> { #[cfg(test)] mod tests { use super::{ - ForgeName, ForgeUser, derive_forge_repo_info, match_host_to_accounts_custom_host, - normalize_host_for_comparison, + ForgeName, ForgeUser, match_host_to_accounts_custom_host, normalize_host_for_comparison, }; #[test] @@ -281,23 +280,4 @@ mod tests { "repository.com" ); } - - #[test] - fn derive_forge_detects_github_enterprise_subdomain_ssh() { - let info = derive_forge_repo_info("git@github.ourinternaldomain.net:our-org/my-repo.git") - .expect("should detect GHES from github.* subdomain"); - assert_eq!(info.forge, ForgeName::GitHub); - assert_eq!(info.owner, "our-org"); - assert_eq!(info.repo, "my-repo"); - } - - #[test] - fn derive_forge_detects_github_enterprise_subdomain_https() { - let info = - derive_forge_repo_info("https://github.ourinternaldomain.net/our-org/my-repo.git") - .expect("should detect GHES from github.* subdomain"); - assert_eq!(info.forge, ForgeName::GitHub); - assert_eq!(info.owner, "our-org"); - assert_eq!(info.repo, "my-repo"); - } }