Skip to content

Commit

Permalink
fix: install packages with incompatible libc (#8569)
Browse files Browse the repository at this point in the history
close #7362
  • Loading branch information
KSXGitHub authored Sep 27, 2024
1 parent 59a745d commit 83681da
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 30 deletions.
8 changes: 8 additions & 0 deletions .changeset/good-points-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@pnpm/store-connection-manager": minor
"@pnpm/plugin-commands-rebuild": minor
"@pnpm/plugin-commands-installation": patch
"pnpm": patch
---

Fix a bug in which pnpm downloads packages whose `libc` differ from `pnpm.supportedArchitectures.libc` [#7362](https://github.com/pnpm/pnpm/issues/7362).
7 changes: 7 additions & 0 deletions .changeset/pink-pans-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@pnpm/constants": major
"@pnpm/npm-resolver": minor
"@pnpm/package-store": minor
---

Keep `libc` field in `clearMeta`.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type StrictRebuildOptions = {
virtualStoreDirMaxLength: number
peersSuffixMaxLength: number
strictStorePkgContentCheck: boolean
fetchFullMetadata?: boolean
} & Pick<Config, 'sslConfigs'>

export type RebuildOptions = Partial<StrictRebuildOptions> &
Expand Down
2 changes: 1 addition & 1 deletion packages/constants/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export const WORKSPACE_MANIFEST_FILENAME = 'pnpm-workspace.yaml'
// of one package/version
export const META_DIR = 'metadata'
export const FULL_META_DIR = 'metadata-full'
export const FULL_FILTERED_META_DIR = 'metadata-v1.1'
export const FULL_FILTERED_META_DIR = 'metadata-v1.2'
4 changes: 4 additions & 0 deletions pkg-manager/plugin-commands-installation/src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ export async function handler (opts: InstallCommandOptions): Promise<void> {
devDependencies: opts.dev !== false,
optionalDependencies: opts.optional !== false,
}
// npm registry's abbreviated metadata currently does not contain libc
// see <https://github.com/pnpm/pnpm/issues/7362#issuecomment-1971964689>
const fetchFullMetadata: true | undefined = opts.rootProjectManifest?.pnpm?.supportedArchitectures?.libc && true
const installDepsOptions: InstallDepsOptions = {
...opts,
frozenLockfileIfExists: isCI && !opts.lockfileOnly &&
Expand All @@ -333,6 +336,7 @@ export async function handler (opts: InstallCommandOptions): Promise<void> {
include,
includeDirect: include,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
fetchFullMetadata,
}
if (opts.resolutionOnly) {
installDepsOptions.lockfileOnly = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export type InstallDepsOptions = Pick<Config,
workspace?: boolean
includeOnlyPackageFiles?: boolean
prepareExecutionEnv: PrepareExecutionEnv
fetchFullMetadata?: boolean
} & Partial<Pick<Config, 'pnpmHomeDir'>>

export async function installDeps (
Expand Down
38 changes: 37 additions & 1 deletion pkg-manager/plugin-commands-installation/test/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import fs from 'fs'
import delay from 'delay'
import path from 'path'
import { add, install } from '@pnpm/plugin-commands-installation'
import { prepareEmpty } from '@pnpm/prepare'
import { prepare, prepareEmpty } from '@pnpm/prepare'
import { sync as rimraf } from '@zkochan/rimraf'
import { DEFAULT_OPTS } from './utils'

const describeOnLinuxOnly = process.platform === 'linux' ? describe : describe.skip

test('install fails if no package.json is found', async () => {
prepareEmpty()

Expand Down Expand Up @@ -52,3 +54,37 @@ test('install with no store integrity validation', async () => {

expect(fs.readFileSync('node_modules/is-positive/readme.md', 'utf8')).toBe('modified')
})

// Covers https://github.com/pnpm/pnpm/issues/7362
describeOnLinuxOnly('filters optional dependencies based on libc', () => {
test.each([
['glibc', '@pnpm.e2e+only-linux-x64-glibc@1.0.0', '@pnpm.e2e+only-linux-x64-musl@1.0.0'],
['musl', '@pnpm.e2e+only-linux-x64-musl@1.0.0', '@pnpm.e2e+only-linux-x64-glibc@1.0.0'],
])('%p → installs %p, does not install %p', async (libc, found, notFound) => {
const rootProjectManifest = {
dependencies: {
'@pnpm.e2e/support-different-architectures': '1.0.0',
},
pnpm: {
supportedArchitectures: {
os: ['linux'],
cpu: ['x64'],
libc: [libc],
},
},
}

prepare(rootProjectManifest)

await install.handler({
...DEFAULT_OPTS,
rootProjectManifest,
dir: process.cwd(),
})

const pkgDirs = fs.readdirSync(path.resolve('node_modules', '.pnpm'))
expect(pkgDirs).toContain('@pnpm.e2e+support-different-architectures@1.0.0')
expect(pkgDirs).toContain(found)
expect(pkgDirs).not.toContain(notFound)
})
})
46 changes: 23 additions & 23 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ catalog:
"@pnpm/npm-package-arg": ^1.0.0
"@pnpm/os.env.path-extender": ^2.0.0
"@pnpm/patch-package": 0.0.0
"@pnpm/registry-mock": 3.40.0
"@pnpm/registry-mock": 3.41.0
"@pnpm/semver-diff": ^1.1.0
"@pnpm/tabtab": ^0.5.4
"@pnpm/util.lex-comparator": 3.0.0
Expand Down
2 changes: 2 additions & 0 deletions resolving/npm-resolver/src/pickPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ function clearMeta (pkg: PackageMeta): PackageMeta {
const versions: PackageMeta['versions'] = {}
for (const [version, info] of Object.entries(pkg.versions)) {
// The list taken from https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
// with the addition of 'libc'
versions[version] = pick([
'name',
'version',
Expand All @@ -250,6 +251,7 @@ function clearMeta (pkg: PackageMeta): PackageMeta {
'peerDependenciesMeta',
'cpu',
'os',
'libc',
'deprecated',
'bundleDependencies',
'bundledDependencies',
Expand Down
5 changes: 3 additions & 2 deletions resolving/npm-resolver/test/publishedBy.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'fs'
import path from 'path'
import { FULL_FILTERED_META_DIR } from '@pnpm/constants'
import { createFetchFromRegistry } from '@pnpm/fetch'
import { createNpmResolver } from '@pnpm/npm-resolver'
import { fixtures } from '@pnpm/test-fixtures'
Expand Down Expand Up @@ -55,8 +56,8 @@ test('request metadata when the one in cache does not have a version satisfying
time: {},
cachedAt: '2016-08-17T19:26:00.508Z',
}
fs.mkdirSync(path.join(cacheDir, 'metadata-v1.1/registry.npmjs.org'), { recursive: true })
fs.writeFileSync(path.join(cacheDir, 'metadata-v1.1/registry.npmjs.org/bad-dates.json'), JSON.stringify(cachedMeta), 'utf8')
fs.mkdirSync(path.join(cacheDir, `${FULL_FILTERED_META_DIR}/registry.npmjs.org`), { recursive: true })
fs.writeFileSync(path.join(cacheDir, `${FULL_FILTERED_META_DIR}/registry.npmjs.org/bad-dates.json`), JSON.stringify(cachedMeta), 'utf8')

nock(registry)
.get('/bad-dates')
Expand Down
1 change: 1 addition & 0 deletions store/package-store/src/storeController/prune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export async function prune ({ cacheDir, storeDir }: PruneOptions, removeAlienFi
rimraf(path.join(cacheDir, 'metadata')),
rimraf(path.join(cacheDir, 'metadata-full')),
rimraf(path.join(cacheDir, 'metadata-v1.1')),
rimraf(path.join(cacheDir, 'metadata-v1.2')),
])
await rimraf(path.join(storeDir, 'tmp'))
globalInfo('Removed all cached metadata files')
Expand Down
2 changes: 1 addition & 1 deletion store/plugin-commands-store/test/storePrune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ test('remove unreferenced packages', async () => {
message: 'Removed 1 package',
})
)
expect(fs.readdirSync(cacheDir).length).toEqual(0)
expect(fs.readdirSync(cacheDir)).toStrictEqual([])
})

test.skip('remove packages that are used by project that no longer exist', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick<Confi
> & {
cafsLocker?: CafsLocker
ignoreFile?: (filename: string) => boolean
fetchFullMetadata?: boolean
} & Partial<Pick<Config, 'userConfig' | 'deployAllFiles' | 'sslConfigs' | 'strictStorePkgContentCheck'>> & Pick<ClientOptions, 'resolveSymlinksInInjectedDirs'>

export async function createNewStoreController (
opts: CreateNewStoreControllerOptions
): Promise<{ ctrl: StoreController, dir: string }> {
const fullMetadata = opts.resolutionMode === 'time-based' && !opts.registrySupportsTimeField
const fullMetadata = opts.fetchFullMetadata ?? (opts.resolutionMode === 'time-based' && !opts.registrySupportsTimeField)
const { resolve, fetchers, clearResolutionCache } = createClient({
customFetchers: opts.hooks?.fetchers,
userConfig: opts.userConfig,
Expand Down

0 comments on commit 83681da

Please sign in to comment.