fix: get the final address by installing package through links (#8842)

close #8833

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
btea
2024-12-11 20:22:15 +08:00
committed by GitHub
parent c05a993820
commit b0f3c71b7b
11 changed files with 65 additions and 17 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/default-resolver": major
"@pnpm/tarball-resolver": major
"@pnpm/core": major
"pnpm": major
---
Dependencies specified via a URL are now recorded in the lockfile using their final resolved URL. Thus, if the original URL redirects, the final redirect target will be saved in the lockfile [#8833](https://github.com/pnpm/pnpm/issues/8833).

View File

@@ -0,0 +1,6 @@
---
"@pnpm/fetch": minor
"@pnpm/fetching-types": minor
---
The `fetch` function accepts a `method` option now.

View File

@@ -67,6 +67,7 @@ export function createFetchFromRegistry (
},
// if verifying integrity, node-fetch must not decompress
compress: opts?.compress ?? false,
method: opts?.method,
headers,
redirect: 'manual',
retry: opts?.retry,

View File

@@ -1,11 +1,16 @@
import { type RetryTimeoutOptions } from '@zkochan/retry'
import { type Response } from 'node-fetch'
import { type Response, type RequestInit as NodeRequestInit } from 'node-fetch'
export type { RetryTimeoutOptions }
export interface RequestInit extends NodeRequestInit {
retry?: RetryTimeoutOptions
timeout?: number
}
export type FetchFromRegistry = (
url: string,
opts?: {
opts?: RequestInit & {
authHeaderValue?: string
compress?: boolean
retry?: RetryTimeoutOptions

View File

@@ -1118,6 +1118,9 @@ test('tarball domain differs from registry domain', async () => {
})
test('tarball installed through non-standard URL endpoint from the registry domain', async () => {
nock('https://registry.npmjs.org', { allowUnmocked: true })
.head('/is-positive/download/is-positive-3.1.0.tgz')
.reply(200, '')
nock('https://registry.npmjs.org', { allowUnmocked: true })
.get('/is-positive/download/is-positive-3.1.0.tgz')
.replyWithFile(200, tarballPath)

6
pnpm-lock.yaml generated
View File

@@ -6517,10 +6517,16 @@ importers:
resolving/tarball-resolver:
dependencies:
'@pnpm/fetching-types':
specifier: workspace:*
version: link:../../network/fetching-types
'@pnpm/resolver-base':
specifier: workspace:*
version: link:../resolver-base
devDependencies:
'@pnpm/fetch':
specifier: workspace:*
version: link:../../network/fetch
'@pnpm/tarball-resolver':
specifier: workspace:*
version: 'link:'

View File

@@ -30,7 +30,7 @@ export function createResolver (
resolve: async (wantedDependency, opts) => {
const resolution = await resolveFromNpm(wantedDependency, opts as ResolveFromNpmOptions) ??
(wantedDependency.pref && (
await resolveFromTarball(wantedDependency as { pref: string }) ??
await resolveFromTarball(fetchFromRegistry, wantedDependency as { pref: string }) ??
await resolveFromGit(wantedDependency as { pref: string }) ??
await resolveFromLocal(wantedDependency as { pref: string }, opts)
))

View File

@@ -26,6 +26,7 @@
},
"homepage": "https://github.com/pnpm/pnpm/blob/main/resolving/tarball-resolver#readme",
"dependencies": {
"@pnpm/fetching-types": "workspace:*",
"@pnpm/resolver-base": "workspace:*"
},
"funding": "https://opencollective.com/pnpm",
@@ -33,6 +34,7 @@
"pnpm10"
],
"devDependencies": {
"@pnpm/fetch": "workspace:*",
"@pnpm/tarball-resolver": "workspace:*"
},
"exports": {

View File

@@ -1,6 +1,8 @@
import { type PkgResolutionId, type ResolveResult } from '@pnpm/resolver-base'
import { type FetchFromRegistry } from '@pnpm/fetching-types'
export async function resolveFromTarball (
fetchFromRegistry: FetchFromRegistry,
wantedDependency: { pref: string }
): Promise<ResolveResult | null> {
if (!wantedDependency.pref.startsWith('http:') && !wantedDependency.pref.startsWith('https:')) {
@@ -9,11 +11,14 @@ export async function resolveFromTarball (
if (isRepository(wantedDependency.pref)) return null
// If there are redirects, we want to get the final URL address
const { url: resolvedUrl } = await fetchFromRegistry(wantedDependency.pref, { method: 'HEAD' })
return {
id: wantedDependency.pref as PkgResolutionId,
normalizedPref: wantedDependency.pref,
id: resolvedUrl as PkgResolutionId,
normalizedPref: resolvedUrl,
resolution: {
tarball: wantedDependency.pref,
tarball: resolvedUrl,
},
resolvedVia: 'url',
}

View File

@@ -1,22 +1,28 @@
/// <reference path="../../../__typings__/index.d.ts"/>
// cspell:ignore buildserver
import { resolveFromTarball } from '@pnpm/tarball-resolver'
import { resolveFromTarball as _resolveFromTarball } from '@pnpm/tarball-resolver'
import { createFetchFromRegistry } from '@pnpm/fetch'
const fetch = createFetchFromRegistry({})
const resolveFromTarball = _resolveFromTarball.bind(null, fetch)
test('tarball from npm registry', async () => {
const resolutionResult = await resolveFromTarball({ pref: 'http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz' })
expect(resolutionResult).toStrictEqual({
id: 'http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
normalizedPref: 'http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
id: 'https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
normalizedPref: 'https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
resolution: {
tarball: 'http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
tarball: 'https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz',
},
resolvedVia: 'url',
})
})
test('tarball from URL that contain port number', async () => {
const resolutionResult = await resolveFromTarball({ pref: 'http://buildserver.mycompany.com:81/my-private-package-0.1.6.tgz' })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const fetch: any = async (url: string) => ({ url })
const resolutionResult = await _resolveFromTarball(fetch, { pref: 'http://buildserver.mycompany.com:81/my-private-package-0.1.6.tgz' })
expect(resolutionResult).toStrictEqual({
id: 'http://buildserver.mycompany.com:81/my-private-package-0.1.6.tgz',
@@ -32,10 +38,10 @@ test('tarball not from npm registry', async () => {
const resolutionResult = await resolveFromTarball({ pref: 'https://github.com/hegemonic/taffydb/tarball/master' })
expect(resolutionResult).toStrictEqual({
id: 'https://github.com/hegemonic/taffydb/tarball/master',
normalizedPref: 'https://github.com/hegemonic/taffydb/tarball/master',
id: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master',
normalizedPref: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master',
resolution: {
tarball: 'https://github.com/hegemonic/taffydb/tarball/master',
tarball: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master',
},
resolvedVia: 'url',
})
@@ -45,10 +51,10 @@ test('tarballs from GitHub (is-negative)', async () => {
const resolutionResult = await resolveFromTarball({ pref: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz' })
expect(resolutionResult).toStrictEqual({
id: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz',
normalizedPref: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz',
id: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c',
normalizedPref: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c',
resolution: {
tarball: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz',
tarball: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c',
},
resolvedVia: 'url',
})

View File

@@ -9,6 +9,12 @@
"../../__typings__/**/*.d.ts"
],
"references": [
{
"path": "../../network/fetch"
},
{
"path": "../../network/fetching-types"
},
{
"path": "../resolver-base"
}