fix(bitbucket): recognize ssh.bitbucket.org remotes (#31002)

* fix(bitbucket): recognize the SSH host alias

* fix(bitbucket): recognize the SSH host alias on current main

Apply Chen's original SSH alias fix to current main, preserving canonical
Bitbucket browser links and adding case/port and lookalike-host coverage.

Co-authored-by: Chen <zwq19980411@gmail.com>

---------

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Pr1p
2026-10-23 20:43:39 -07:00
committed by GitHub
co-authored by Chen Neil
parent df97aa3a6f
commit 8604a4d570
4 changed files with 43 additions and 3 deletions
+20
View File
@@ -37,9 +37,29 @@ describe('Bitbucket repository refs', () => {
workspace: 'team',
repoSlug: 'project'
})
expect(parseBitbucketRepoRef('git@ssh.bitbucket.org:team/project.git')).toEqual({
workspace: 'team',
repoSlug: 'project'
})
expect(parseBitbucketRepoRef('ssh://git@ssh.bitbucket.org/team/project.git')).toEqual({
workspace: 'team',
repoSlug: 'project'
})
expect(parseBitbucketRepoRef('https://github.com/team/project.git')).toBeNull()
})
it('recognizes the SSH host case-insensitively without accepting lookalike hosts', () => {
expect(parseBitbucketRepoRef('git@SSH.BITBUCKET.ORG:team/project.git')).toEqual({
workspace: 'team',
repoSlug: 'project'
})
expect(parseBitbucketRepoRef('git@ssh.bitbucket.org.example.com:team/project.git')).toBeNull()
expect(
parseBitbucketRepoRef('ssh://git@ssh.bitbucket.org.example.com/team/project.git')
).toBeNull()
expect(parseBitbucketRepoRef('git@notssh.bitbucket.org:team/project.git')).toBeNull()
})
it('strips trailing slashes after .git suffixes', () => {
expect(parseBitbucketRepoRef('https://bitbucket.org/team/project.git/')).toEqual({
workspace: 'team',
+3 -2
View File
@@ -51,14 +51,15 @@ function parseBitbucketPath(pathname: string): BitbucketRepoRef | null {
export function parseBitbucketRepoRef(remoteUrl: string): BitbucketRepoRef | null {
const trimmed = remoteUrl.trim()
const scpLike = trimmed.match(/^(?:[^@]+@)?bitbucket\.org:([^\s]+?)(?:\.git)?$/i)
const scpLike = trimmed.match(/^(?:[^@]+@)?(?:ssh\.)?bitbucket\.org:([^\s]+?)(?:\.git)?$/i)
if (scpLike) {
return parseBitbucketPath(scpLike[1])
}
try {
const url = new URL(trimmed)
if (url.hostname.toLowerCase() !== 'bitbucket.org') {
const host = url.hostname.toLowerCase()
if (host !== 'bitbucket.org' && host !== 'ssh.bitbucket.org') {
return null
}
return parseBitbucketPath(url.pathname)
+19
View File
@@ -55,6 +55,16 @@ describe('hosted remote URLs', () => {
path: 'team/repo',
provider: 'bitbucket'
})
expect(parseHostedRemote('git@ssh.bitbucket.org:team/repo.git')).toEqual({
host: 'bitbucket.org',
path: 'team/repo',
provider: 'bitbucket'
})
expect(parseHostedRemote('ssh://git@ssh.bitbucket.org/team/repo.git')).toEqual({
host: 'bitbucket.org',
path: 'team/repo',
provider: 'bitbucket'
})
})
it('builds file URLs with encoded branches and paths', () => {
@@ -86,6 +96,9 @@ describe('hosted remote URLs', () => {
5
)
).toBe('https://github.com/Org/Repo/blob/feature%2Fx/src/a.ts#L5')
expect(
buildHostedRemoteFileUrl('git@ssh.bitbucket.org:team/repo.git', 'src/a.ts', 'feature/x', 5)
).toBe('https://bitbucket.org/team/repo/src/feature%2Fx/src/a.ts#a.ts-5')
})
it('builds Bitbucket line fragments from the target file name', () => {
@@ -105,6 +118,9 @@ describe('hosted remote URLs', () => {
expect(buildHostedRemoteCommitUrl('git@bitbucket.org:team/repo.git', sha)).toBe(
`https://bitbucket.org/team/repo/commits/${sha}`
)
expect(buildHostedRemoteCommitUrl('ssh://git@SSH.BITBUCKET.ORG:443/team/repo.git', sha)).toBe(
`https://bitbucket.org/team/repo/commits/${sha}`
)
expect(buildHostedRemoteCommitUrl('git@ALTSSH.GITLAB.COM:team/subgroup/project.git', sha)).toBe(
`https://gitlab.com/team/subgroup/project/-/commit/${sha}`
)
@@ -124,6 +140,9 @@ describe('hosted remote URLs', () => {
expect(parseHostedRemote('git@example.com:team/repo.git')).toBeNull()
expect(parseHostedRemote('git@github.com:repo.git')).toBeNull()
expect(parseHostedRemote('ftp://github.com/Org/Repo.git')).toBeNull()
expect(parseHostedRemote('git@ssh.bitbucket.org.example.com:team/repo.git')).toBeNull()
expect(parseHostedRemote('ssh://git@ssh.bitbucket.org.example.com/team/repo.git')).toBeNull()
expect(parseHostedRemote('git@notssh.bitbucket.org:team/repo.git')).toBeNull()
expect(parseHostedRemote('git@altssh.gitlab.com.example.com:team/repo.git')).toBeNull()
expect(parseHostedRemote('ssh://git@altssh.gitlab.com.example.com/team/repo.git')).toBeNull()
expect(parseHostedRemote('git@notaltssh.gitlab.com:team/repo.git')).toBeNull()
+1 -1
View File
@@ -30,7 +30,7 @@ function providerForHost(host: string): HostedRemoteHost | null {
if (normalized === 'gitlab.com') {
return { host: 'gitlab.com', provider: 'gitlab' }
}
if (normalized === 'bitbucket.org') {
if (normalized === 'bitbucket.org' || normalized === 'ssh.bitbucket.org') {
return { host: 'bitbucket.org', provider: 'bitbucket' }
}
return null