From d6a4ff189208b18c3ccd6bea67079560b551a555 Mon Sep 17 00:00:00 2001 From: mato533 <35281743+mato533@users.noreply.github.com> Date: Wed, 29 Jan 2025 02:48:37 +0900 Subject: [PATCH] feat(git-resolver): respect proxy settings when resolving git (#9016) close: #6530 --- .changeset/ninety-countries-dance.md | 6 ++++++ pnpm-lock.yaml | 3 +++ .../git-resolver/__mocks__/@pnpm/fetch.js | 2 +- resolving/git-resolver/package.json | 1 + resolving/git-resolver/src/index.ts | 5 +++-- resolving/git-resolver/src/parsePref.ts | 19 ++++++++++--------- resolving/git-resolver/test/index.ts | 8 ++++---- resolving/git-resolver/test/parsePref.test.ts | 6 +++--- 8 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 .changeset/ninety-countries-dance.md diff --git a/.changeset/ninety-countries-dance.md b/.changeset/ninety-countries-dance.md new file mode 100644 index 0000000000..2d9fdfa45e --- /dev/null +++ b/.changeset/ninety-countries-dance.md @@ -0,0 +1,6 @@ +--- +"@pnpm/git-resolver": patch +"pnpm": patch +--- + +Proxy settings should be respected, when resolving Git-hosted dependencies [#6530](https://github.com/pnpm/pnpm/issues/6530). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d2698a4f5..4fd51912c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6473,6 +6473,9 @@ importers: '@pnpm/git-resolver': specifier: workspace:* version: 'link:' + '@pnpm/network.agent': + specifier: 'catalog:' + version: 2.0.0 '@types/hosted-git-info': specifier: 'catalog:' version: 3.0.5 diff --git a/resolving/git-resolver/__mocks__/@pnpm/fetch.js b/resolving/git-resolver/__mocks__/@pnpm/fetch.js index ac4188c807..3cd956ef3a 100644 --- a/resolving/git-resolver/__mocks__/@pnpm/fetch.js +++ b/resolving/git-resolver/__mocks__/@pnpm/fetch.js @@ -1,6 +1,6 @@ module.exports = jest.createMockFromModule('@pnpm/fetch') // default implementation -module.exports.fetch.mockImplementation(async (_url, _opts) => { +module.exports.fetchWithAgent.mockImplementation(async (_url, _opts) => { return { ok: true } }) diff --git a/resolving/git-resolver/package.json b/resolving/git-resolver/package.json index 74e5737f9a..3b24dcfae4 100644 --- a/resolving/git-resolver/package.json +++ b/resolving/git-resolver/package.json @@ -40,6 +40,7 @@ }, "devDependencies": { "@pnpm/git-resolver": "workspace:*", + "@pnpm/network.agent": "catalog:", "@types/hosted-git-info": "catalog:", "@types/is-windows": "catalog:", "@types/semver": "catalog:", diff --git a/resolving/git-resolver/src/index.ts b/resolving/git-resolver/src/index.ts index 6cd96a6be2..f0b0f6eab9 100644 --- a/resolving/git-resolver/src/index.ts +++ b/resolving/git-resolver/src/index.ts @@ -3,6 +3,7 @@ import git from 'graceful-git' import semver from 'semver' import { parsePref, type HostedPackageSpec } from './parsePref' import { createGitHostedPkgId } from './createGitHostedPkgId' +import { type AgentOptions } from '@pnpm/network.agent' export { createGitHostedPkgId } @@ -13,10 +14,10 @@ export type GitResolver = (wantedDependency: { }) => Promise export function createGitResolver ( - opts: unknown + opts: AgentOptions ): GitResolver { return async function resolveGit (wantedDependency): Promise { - const parsedSpec = await parsePref(wantedDependency.pref) + const parsedSpec = await parsePref(wantedDependency.pref, opts) if (parsedSpec == null) return null diff --git a/resolving/git-resolver/src/parsePref.ts b/resolving/git-resolver/src/parsePref.ts index 4c4cf3cd36..6c15421b32 100644 --- a/resolving/git-resolver/src/parsePref.ts +++ b/resolving/git-resolver/src/parsePref.ts @@ -1,6 +1,7 @@ // cspell:ignore sshurl import urlLib, { URL } from 'url' -import { fetch } from '@pnpm/fetch' +import { fetchWithAgent } from '@pnpm/fetch' +import { type AgentOptions } from '@pnpm/network.agent' import git from 'graceful-git' import HostedGit from 'hosted-git-info' @@ -31,10 +32,10 @@ const gitProtocols = new Set([ 'ssh', ]) -export async function parsePref (pref: string): Promise { +export async function parsePref (pref: string, opts: AgentOptions): Promise { const hosted = HostedGit.fromUrl(pref) if (hosted != null) { - return fromHostedGit(hosted) + return fromHostedGit(hosted, opts) } const colonsPos = pref.indexOf(':') if (colonsPos === -1) return null @@ -63,11 +64,11 @@ function urlToFetchSpec (url: URL): string { return fetchSpec } -async function fromHostedGit (hosted: any): Promise { // eslint-disable-line +async function fromHostedGit (hosted: any, agentOptions: AgentOptions): Promise { // eslint-disable-line let fetchSpec: string | null = null // try git/https url before fallback to ssh url const gitHttpsUrl = hosted.https({ noCommittish: true, noGitPlus: true }) - if (gitHttpsUrl && await isRepoPublic(gitHttpsUrl) && await accessRepository(gitHttpsUrl)) { + if (gitHttpsUrl && await isRepoPublic(gitHttpsUrl, agentOptions) && await accessRepository(gitHttpsUrl)) { fetchSpec = gitHttpsUrl } else { const gitSshUrl = hosted.ssh({ noCommittish: true }) @@ -79,7 +80,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 isRepoPublic(httpsUrl)) && await accessRepository(httpsUrl)) { + if ((hosted.auth || !await isRepoPublic(httpsUrl, agentOptions)) && await accessRepository(httpsUrl)) { return { fetchSpec: httpsUrl, hosted: { @@ -98,7 +99,7 @@ async function fromHostedGit (hosted: any): Promise { // esli // npm instead tries git ls-remote directly which prompts user for login credentials. // HTTP HEAD on https://domain/user/repo, strip out ".git" - const response = await fetch(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 } }) + const response = await fetchWithAgent(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 }, agentOptions }) if (response.ok) { fetchSpec = httpsUrl } @@ -126,9 +127,9 @@ async function fromHostedGit (hosted: any): Promise { // esli } } -async function isRepoPublic (httpsUrl: string): Promise { +async function isRepoPublic (httpsUrl: string, agentOptions: AgentOptions): Promise { try { - const response = await fetch(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 } }) + const response = await fetchWithAgent(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 }, agentOptions }) return response.ok } catch { return false diff --git a/resolving/git-resolver/test/index.ts b/resolving/git-resolver/test/index.ts index c08b98db98..ca896a27cb 100644 --- a/resolving/git-resolver/test/index.ts +++ b/resolving/git-resolver/test/index.ts @@ -3,14 +3,14 @@ import path from 'path' import { createGitResolver } from '@pnpm/git-resolver' import git from 'graceful-git' import isWindows from 'is-windows' -import { fetch } from '@pnpm/fetch' +import { fetchWithAgent } from '@pnpm/fetch' const resolveFromGit = createGitResolver({}) function mockFetchAsPrivate (): void { - type Fetch = typeof fetch - type MockedFetch = jest.MockedFunction - (fetch as MockedFetch).mockImplementation(async (_url, _opts) => { + type FetchWithAgent = typeof fetchWithAgent + type MockedFetchWithAgent = jest.MockedFunction + (fetchWithAgent as MockedFetchWithAgent).mockImplementation(async (_url, _opts) => { return { ok: false } as any // eslint-disable-line @typescript-eslint/no-explicit-any }) } diff --git a/resolving/git-resolver/test/parsePref.test.ts b/resolving/git-resolver/test/parsePref.test.ts index 166c8476b5..087c7cd0f9 100644 --- a/resolving/git-resolver/test/parsePref.test.ts +++ b/resolving/git-resolver/test/parsePref.test.ts @@ -13,7 +13,7 @@ test.each([ ['git+ssh://username:password@example.com:22/repo/@foo.git#path:/a/@b', 'ssh://username:password@example.com:22/repo/@foo.git'], ['git+ssh://username:password@example.com:22/repo/@foo.git#path:/a/@b&dev', 'ssh://username:password@example.com:22/repo/@foo.git'], ])('the right colon is escaped in %s', async (input, output) => { - const parsed = await parsePref(input) + const parsed = await parsePref(input, {}) expect(parsed?.fetchSpec).toBe(output) }) @@ -41,13 +41,13 @@ test.each([ ['git+ssh://username:password@example.com:22/repo/@foo.git', undefined], ['git+ssh://username:password@example.com:22/repo/@foo.git#dev', undefined], ])('the path of %s should be %s', async (input, output) => { - const parsed = await parsePref(input) + 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) + const parsed = await parsePref(input, {}) expect(parsed?.fetchSpec).toBe(output) })