diff --git a/.changeset/bright-oranges-like.md b/.changeset/bright-oranges-like.md new file mode 100644 index 0000000000..173eb34cc0 --- /dev/null +++ b/.changeset/bright-oranges-like.md @@ -0,0 +1,6 @@ +--- +"@pnpm/tarball-resolver": patch +pnpm: patch +--- + +Dependencies specified via a URL that redirects will only be locked to the target if it is immutable, fixing a regression when installing from GitHub releases. ([#9531](https://github.com/pnpm/pnpm/issues/9531)) diff --git a/resolving/tarball-resolver/src/index.ts b/resolving/tarball-resolver/src/index.ts index 39cf49c1f7..069324be67 100644 --- a/resolving/tarball-resolver/src/index.ts +++ b/resolving/tarball-resolver/src/index.ts @@ -11,8 +11,15 @@ export async function resolveFromTarball ( if (isRepository(wantedDependency.bareSpecifier)) return null - // If there are redirects, we want to get the final URL address - const { url: resolvedUrl } = await fetchFromRegistry(wantedDependency.bareSpecifier, { method: 'HEAD' }) + let resolvedUrl + + // If there are redirects and the response is immutable, we want to get the final URL address + const response = await fetchFromRegistry(wantedDependency.bareSpecifier, { method: 'HEAD' }) + if (response?.headers?.get('cache-control')?.includes('immutable')) { + resolvedUrl = response.url + } else { + resolvedUrl = wantedDependency.bareSpecifier + } return { id: resolvedUrl as PkgResolutionId, diff --git a/resolving/tarball-resolver/test/index.ts b/resolving/tarball-resolver/test/index.ts index 2079f644a6..6280dab59d 100644 --- a/resolving/tarball-resolver/test/index.ts +++ b/resolving/tarball-resolver/test/index.ts @@ -6,7 +6,7 @@ import { createFetchFromRegistry } from '@pnpm/fetch' const fetch = createFetchFromRegistry({}) const resolveFromTarball = _resolveFromTarball.bind(null, fetch) -test('tarball from npm registry', async () => { +test('tarball from npm registry (immutable)', async () => { const resolutionResult = await resolveFromTarball({ bareSpecifier: 'http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz' }) expect(resolutionResult).toStrictEqual({ @@ -18,6 +18,18 @@ test('tarball from npm registry', async () => { resolvedVia: 'url', }) }) +test('tarball from npm.jsr.io registry (immutable)', async () => { + const resolutionResult = await resolveFromTarball({ bareSpecifier: 'http://npm.jsr.io/~/11/@jsr/luca__flag/1.0.1.tgz' }) + + expect(resolutionResult).toStrictEqual({ + id: 'https://npm.jsr.io/~/11/@jsr/luca__flag/1.0.1.tgz', + normalizedBareSpecifier: 'https://npm.jsr.io/~/11/@jsr/luca__flag/1.0.1.tgz', + resolution: { + tarball: 'https://npm.jsr.io/~/11/@jsr/luca__flag/1.0.1.tgz', + }, + resolvedVia: 'url', + }) +}) test('tarball from URL that contain port number', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -34,14 +46,14 @@ test('tarball from URL that contain port number', async () => { }) }) -test('tarball not from npm registry', async () => { +test('tarball not from npm registry (mutable)', async () => { const resolutionResult = await resolveFromTarball({ bareSpecifier: 'https://github.com/hegemonic/taffydb/tarball/master' }) expect(resolutionResult).toStrictEqual({ - id: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master', - normalizedBareSpecifier: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master', + id: 'https://github.com/hegemonic/taffydb/tarball/master', + normalizedBareSpecifier: 'https://github.com/hegemonic/taffydb/tarball/master', resolution: { - tarball: 'https://codeload.github.com/hegemonic/taffydb/legacy.tar.gz/refs/heads/master', + tarball: 'https://github.com/hegemonic/taffydb/tarball/master', }, resolvedVia: 'url', }) @@ -51,10 +63,10 @@ test('tarballs from GitHub (is-negative)', async () => { const resolutionResult = await resolveFromTarball({ bareSpecifier: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz' }) expect(resolutionResult).toStrictEqual({ - id: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c', - normalizedBareSpecifier: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c', + id: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz', + normalizedBareSpecifier: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz', resolution: { - tarball: 'https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c', + tarball: 'https://github.com/kevva/is-negative/archive/1d7e288222b53a0cab90a331f1865220ec29560c.tar.gz', }, resolvedVia: 'url', })