diff --git a/services/apps/packages_worker/src/utils/__tests__/canonicalizeRepoUrl.test.ts b/services/apps/packages_worker/src/utils/__tests__/canonicalizeRepoUrl.test.ts index e986512efd..544c368b30 100644 --- a/services/apps/packages_worker/src/utils/__tests__/canonicalizeRepoUrl.test.ts +++ b/services/apps/packages_worker/src/utils/__tests__/canonicalizeRepoUrl.test.ts @@ -22,6 +22,13 @@ describe('canonicalizeRepoUrl', () => { ['gitlab:group/project', 'https://gitlab.com/group/project', 'gitlab'], ['bitbucket:team/repo', 'https://bitbucket.org/team/repo', 'bitbucket'], ['https://example.com/owner/repo', 'https://example.com/owner/repo', 'other'], + ['git+https://github.com/1aGh/md-claude.git', 'https://github.com/1agh/md-claude', 'github'], + [ + 'ssh://git@github.com:1inch/limit-order-protocol-utils.git', + 'https://github.com/1inch/limit-order-protocol-utils', + 'github', + ], + ['ssh://git@github.com:2222/foo/bar.git', 'https://github.com/foo/bar', 'github'], ])('canonicalizes %s', (input, expectedUrl, expectedHost) => { expect(canonicalizeRepoUrl(input)).toEqual({ url: expectedUrl, host: expectedHost }) }) @@ -44,10 +51,14 @@ describe('canonicalizeRepoUrl', () => { }) }) - it.each([['not a url'], ['https://github.com/onlyowner'], [''], [' ']])( - 'returns null for unparseable input %s', - (input) => { - expect(canonicalizeRepoUrl(input)).toBeNull() - }, - ) + it.each([ + ['not a url'], + ['https://github.com/onlyowner'], + [''], + [' '], + ['123'], + ['https://github.com/Wscats'], + ])('returns null for unparseable input %s', (input) => { + expect(canonicalizeRepoUrl(input)).toBeNull() + }) }) diff --git a/services/apps/packages_worker/src/utils/canonicalizeRepoUrl.ts b/services/apps/packages_worker/src/utils/canonicalizeRepoUrl.ts index 6e24e3218c..a308697a45 100644 --- a/services/apps/packages_worker/src/utils/canonicalizeRepoUrl.ts +++ b/services/apps/packages_worker/src/utils/canonicalizeRepoUrl.ts @@ -54,7 +54,18 @@ export function canonicalizeRepoUrl(raw: string): CanonicalRepo | null { s = `https://${scp[1]}/${scp[2]}` } - s = s.replace(/^ssh:\/\/git@([^/]+)\//, 'https://$1/') + // ssh:// with an scp-style `host:path` (colon instead of slash) is not valid URL + // syntax — the part after `:` looks like a port to the URL parser and throws. + // Rewrite it before the generic ssh://git@host/path case below. A numeric-only + // segment before the next `/` is a real port (e.g. `ssh://git@host:2222/owner/repo`), + // not an scp-style owner — leave those for the generic case, which URL parses fine. + const sshScp = s.match(/^ssh:\/\/git@([^/:]+):(.+)$/) + const sshScpIsPort = sshScp ? /^\d+$/.test(sshScp[2].split('/')[0]) : false + if (sshScp && !sshScpIsPort) { + s = `https://${sshScp[1]}/${sshScp[2]}` + } else { + s = s.replace(/^ssh:\/\/git@([^/]+)\//, 'https://$1/') + } s = s.replace(/^git:\/\//, 'https://') let u: URL