diff --git a/.changeset/loud-pans-matter.md b/.changeset/loud-pans-matter.md new file mode 100644 index 0000000000..bdf952327e --- /dev/null +++ b/.changeset/loud-pans-matter.md @@ -0,0 +1,6 @@ +--- +"@pnpm/git-resolver": patch +"pnpm": patch +--- + +Do not fall back to SSH, when resolving a git-hosted package if `git ls-remote` works via HTTPS [#8906](https://github.com/pnpm/pnpm/pull/8906). diff --git a/resolving/git-resolver/src/parsePref.ts b/resolving/git-resolver/src/parsePref.ts index e568c1cc4f..4c4cf3cd36 100644 --- a/resolving/git-resolver/src/parsePref.ts +++ b/resolving/git-resolver/src/parsePref.ts @@ -79,7 +79,7 @@ async function fromHostedGit (hosted: any): Promise { // esli if (!fetchSpec) { const httpsUrl: string | null = hosted.https({ noGitPlus: true, noCommittish: true }) if (httpsUrl) { - if (hosted.auth && await accessRepository(httpsUrl)) { + if ((hosted.auth || !await isRepoPublic(httpsUrl)) && await accessRepository(httpsUrl)) { return { fetchSpec: httpsUrl, hosted: { @@ -94,7 +94,7 @@ async function fromHostedGit (hosted: any): Promise { // esli try { // when git ls-remote private repo, it asks for login credentials. // use HTTP HEAD request to test whether this is a private repo, to avoid login prompt. - // this is very similar to yarn's behavior. + // this is very similar to yarn classic's behavior. // npm instead tries git ls-remote directly which prompts user for login credentials. // HTTP HEAD on https://domain/user/repo, strip out ".git" diff --git a/resolving/git-resolver/test/index.ts b/resolving/git-resolver/test/index.ts index ad8a597490..c08b98db98 100644 --- a/resolving/git-resolver/test/index.ts +++ b/resolving/git-resolver/test/index.ts @@ -461,6 +461,29 @@ test('resolve a private repository using the HTTPS protocol without auth token', }) }) +test('resolve a private repository using the HTTPS protocol with a commit hash', async () => { + git.mockImplementation(async (args: string[]) => { + expect(args).toContain('ls-remote') + expect(args).toContain('https://github.com/foo/bar.git') + return { + // cspell:ignore aabbccddeeff + stdout: 'aabbccddeeff\tHEAD', + } + }) + const resolveResult = await resolveFromGit({ pref: 'git+https://github.com/foo/bar.git#aabbccddeeff' }) + expect(resolveResult).toStrictEqual({ + id: 'git+https://github.com/foo/bar.git#aabbccddeeff', + normalizedPref: 'git+https://github.com/foo/bar.git', + resolution: { + // cspell:ignore aabbccddeeff + commit: 'aabbccddeeff', + repo: 'https://github.com/foo/bar.git', + type: 'git', + }, + resolvedVia: 'git-repository', + }) +}) + test('resolve a private repository using the HTTPS protocol and an auth token', async () => { git.mockImplementation(async (args: string[]) => { if (!args.includes('https://0000000000000000000000000000000000000000:x-oauth-basic@github.com/foo/bar.git')) throw new Error('') diff --git a/resolving/git-resolver/test/parsePref.test.ts b/resolving/git-resolver/test/parsePref.test.ts index a7f280ba9d..166c8476b5 100644 --- a/resolving/git-resolver/test/parsePref.test.ts +++ b/resolving/git-resolver/test/parsePref.test.ts @@ -44,3 +44,10 @@ test.each([ const parsed = await parsePref(input) expect(parsed?.path).toBe(output) }) + +test.each([ + ['git+https://github.com/pnpm/pnpm.git', 'https://github.com/pnpm/pnpm.git'], +])('the fetchSpec of %s should be %s', async (input, output) => { + const parsed = await parsePref(input) + expect(parsed?.fetchSpec).toBe(output) +})