feat(git-resolver): respect proxy settings when resolving git (#9016)

close: #6530
This commit is contained in:
mato533
2025-01-29 02:48:37 +09:00
committed by GitHub
parent e8c2b173ca
commit d6a4ff1892
8 changed files with 31 additions and 19 deletions

View File

@@ -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).

3
pnpm-lock.yaml generated
View File

@@ -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

View File

@@ -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 }
})

View File

@@ -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:",

View File

@@ -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<ResolveResult | null>
export function createGitResolver (
opts: unknown
opts: AgentOptions
): GitResolver {
return async function resolveGit (wantedDependency): Promise<ResolveResult | null> {
const parsedSpec = await parsePref(wantedDependency.pref)
const parsedSpec = await parsePref(wantedDependency.pref, opts)
if (parsedSpec == null) return null

View File

@@ -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<HostedPackageSpec | null> {
export async function parsePref (pref: string, opts: AgentOptions): Promise<HostedPackageSpec | null> {
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<HostedPackageSpec> { // eslint-disable-line
async function fromHostedGit (hosted: any, agentOptions: AgentOptions): Promise<HostedPackageSpec> { // 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<HostedPackageSpec> { // 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<HostedPackageSpec> { // 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<HostedPackageSpec> { // esli
}
}
async function isRepoPublic (httpsUrl: string): Promise<boolean> {
async function isRepoPublic (httpsUrl: string, agentOptions: AgentOptions): Promise<boolean> {
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

View File

@@ -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>
(fetch as MockedFetch).mockImplementation(async (_url, _opts) => {
type FetchWithAgent = typeof fetchWithAgent
type MockedFetchWithAgent = jest.MockedFunction<FetchWithAgent>
(fetchWithAgent as MockedFetchWithAgent).mockImplementation(async (_url, _opts) => {
return { ok: false } as any // eslint-disable-line @typescript-eslint/no-explicit-any
})
}

View File

@@ -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)
})