diff --git a/src/utils/GitHelper.ts b/src/utils/GitHelper.ts index 2a8c60f06..5b7a84cc0 100644 --- a/src/utils/GitHelper.ts +++ b/src/utils/GitHelper.ts @@ -10,6 +10,22 @@ import Utils from './Utils' const exec = util.promisify(childPross.exec) export default class GitHelper { + static #SSH_PATH_RE = new RegExp( + [ + /^\s*/, + /(?:(?[a-z]+):\/\/)?/, + /(?:(?[a-z_][a-z0-9_-]+)@)?/, + /(?[^\s\/\?#:]+)/, + /(?::(?[0-9]{1,5}))?/, + /(?:[\/:](?[^\s\/\?#:]+))?/, + /(?:[\/:](?[^\s\/\?#:.]+))/, + /(?:.git)?\/?\s*$/, + ] + .map((r) => r.source) + .join(''), + 'i' + ) + static getLastHash(directory: string) { return git(directory) // .silent(true) // @@ -93,8 +109,7 @@ export default class GitHelper { // input is like this: ssh://git@github.com:22/caprover/caprover-cli.git static getDomainFromSanitizedSshRepoPath(input: string) { - input = input.substring(10) - return input.substring(0, input.indexOf(':')) + return GitHelper.sanitizeRepoPathSsh(input).domain } // It returns a string like this "github.com/username/repository.git" @@ -112,40 +127,22 @@ export default class GitHelper { // It returns a string like this "ssh://git@github.com:22/caprover/caprover-cli.git" static sanitizeRepoPathSsh(input: string) { - input = Utils.removeHttpHttps(input) - if (!input.startsWith('git@')) { - // If we get here, we have something like github.com/username/repository.git - if (input.indexOf(':') < 0) { - input = input.replace('/', ':') - } - input = `git@${input}` - } - - // At this point we have one of the following: - // git@github.com:22/caprover/caprover - // git@github.com:caprover/caprover - - let port = '22' - const split = input.split(':') - if (split.length == 2) { - const secondSplit = split[1].split('/') - if (`${Number(secondSplit[0])}` === secondSplit[0]) { - // input is already in this format: git@github.com:22/caprover/caprover - port = `${Number(secondSplit[0])}` - } else { - input = `${split[0]}:22/${split[1]}` - } - } else { + const found = input.match(GitHelper.#SSH_PATH_RE) + if (!found) { throw new Error(`Malformatted SSH path: ${input}`) } - if (!input.toLowerCase().startsWith('ssh://')) { - input = `ssh://${input}` - } - return { - repoPath: input.replace(/\/$/, ''), - port: port, + user: found.groups?.user ?? 'git', + domain: found.groups?.domain, + port: Number(found.groups?.port ?? 22), + owner: found.groups?.owner ?? '', + repo: found.groups?.repo, + get repoPath() { + return `ssh://${this.user}@${this.domain}:${this.port}/${ + this.owner + }${this.owner && '/'}${this.repo}.git` + }, } } } diff --git a/tests/GitHelper.test.ts b/tests/GitHelper.test.ts index fd38e8a77..6ba3019b4 100644 --- a/tests/GitHelper.test.ts +++ b/tests/GitHelper.test.ts @@ -29,7 +29,7 @@ test('Testing - sanitizeRepoPathSsh - port', () => { GitHelper.sanitizeRepoPathSsh( ' git@github.com:username/repository.git/ ' ).port - ).toBe('22') + ).toBe(22) }) test('Testing - sanitizeRepoPathSsh - custom port', () => { @@ -37,7 +37,7 @@ test('Testing - sanitizeRepoPathSsh - custom port', () => { GitHelper.sanitizeRepoPathSsh( ' git@github.com:1234/username/repository.git/ ' ).port - ).toBe('1234') + ).toBe(1234) }) test('Testing - sanitizeRepoPathSsh from HTTPS', () => { @@ -48,6 +48,50 @@ test('Testing - sanitizeRepoPathSsh from HTTPS', () => { ).toBe('ssh://git@github.com:22/username/repository.git') }) +test('Testing - sanitizeRepoPathSsh - not git suffix', () => { + expect( + GitHelper.sanitizeRepoPathSsh(' github.com/owner/repository ').repoPath + ).toBe('ssh://git@github.com:22/owner/repository.git') +}) + +test('Testing - sanitizeRepoPathSsh - alt domain', () => { + expect( + GitHelper.sanitizeRepoPathSsh( + ' git@git.alt-domain.com/owner/repository.git/ ' + ).repoPath + ).toBe('ssh://git@git.alt-domain.com:22/owner/repository.git') +}) + +test('Testing - sanitizeRepoPathSsh - alt user', () => { + expect( + GitHelper.sanitizeRepoPathSsh( + ' foobar@github.com/owner/repository.git/ ' + ).repoPath + ).toBe('ssh://foobar@github.com:22/owner/repository.git') +}) + +test('Testing - sanitizeRepoPathSsh - default user', () => { + expect( + GitHelper.sanitizeRepoPathSsh(' github.com/owner/repository.git/ ') + .repoPath + ).toBe('ssh://git@github.com:22/owner/repository.git') +}) + +test('Testing - sanitizeRepoPathSsh - no owner', () => { + expect( + GitHelper.sanitizeRepoPathSsh(' git@github.com:repository.git/ ') + .repoPath + ).toBe('ssh://git@github.com:22/repository.git') +}) + +test('Testing - sanitizeRepoPathSsh - invalid url', () => { + expect(() => + GitHelper.sanitizeRepoPathSsh( + ' git:password@github.com/owner/repository.git/ ' + ) + ).toThrow(Error) +}) + test('Testing - getDomainFromSanitizedSshRepoPath - pure', () => { expect( GitHelper.getDomainFromSanitizedSshRepoPath( @@ -75,3 +119,11 @@ test('Testing - getDomainFromSanitizedSshRepoPath from HTTPS', () => { ) ).toBe('github.com') }) + +test('Testing - getDomainFromSanitizedSshRepoPath - alt domain', () => { + expect( + GitHelper.getDomainFromSanitizedSshRepoPath( + ' ssh://user@some.other-domain.com/owner/repository.git/ ' + ) + ).toBe('some.other-domain.com') +})