mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-29 04:21:39 -04:00
feat(git-resolver): respect proxy settings when resolving git (#9016)
close: #6530
This commit is contained in:
6
.changeset/ninety-countries-dance.md
Normal file
6
.changeset/ninety-countries-dance.md
Normal 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
3
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
})
|
||||
|
||||
@@ -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:",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user