diff --git a/packages/shared/src/sourceControl.test.ts b/packages/shared/src/sourceControl.test.ts index 368e8387ee60..bfee883dd9f5 100644 --- a/packages/shared/src/sourceControl.test.ts +++ b/packages/shared/src/sourceControl.test.ts @@ -57,6 +57,22 @@ describe("detectSourceControlProviderFromRemoteUrl", () => { ).toBe("bitbucket"); }); + it("detects Azure DevOps SSH remotes", () => { + // The default Azure DevOps SSH clone URL uses the ssh.dev.azure.com host. + expect( + detectSourceControlProviderFromRemoteUrl("git@ssh.dev.azure.com:v3/org/project/repo")?.kind, + ).toBe("azure-devops"); + expect( + detectSourceControlProviderFromRemoteUrl("ssh://git@ssh.dev.azure.com:22/v3/org/project/repo") + ?.kind, + ).toBe("azure-devops"); + // Legacy visualstudio.com SSH host stays classified too. + expect( + detectSourceControlProviderFromRemoteUrl("git@vs-ssh.visualstudio.com:v3/org/project/repo") + ?.kind, + ).toBe("azure-devops"); + }); + it("preserves ports while classifying by hostname", () => { expect( detectSourceControlProviderFromRemoteUrl("https://gitlab.com:8443/group/repo.git"), diff --git a/packages/shared/src/sourceControl.ts b/packages/shared/src/sourceControl.ts index 15a98dc7355e..a29fe968e44d 100644 --- a/packages/shared/src/sourceControl.ts +++ b/packages/shared/src/sourceControl.ts @@ -176,7 +176,15 @@ function isGitLabHost(host: string): boolean { } function isAzureDevOpsHost(host: string): boolean { - return host === "dev.azure.com" || host.endsWith(".visualstudio.com"); + // `ssh.dev.azure.com` is the default Azure DevOps SSH clone host + // (git@ssh.dev.azure.com:v3/org/project/repo), so match any `*.dev.azure.com` + // subdomain, not just the bare `dev.azure.com`. Legacy hosts stay under + // `.visualstudio.com` (including `vs-ssh.visualstudio.com`). + return ( + host === "dev.azure.com" || + host.endsWith(".dev.azure.com") || + host.endsWith(".visualstudio.com") + ); } function isBitbucketHost(host: string): boolean {