From dfa91df6e8cd475ccb598e0c80d10cc8ca86b335 Mon Sep 17 00:00:00 2001 From: Kalven Schraut <30308012+kalvenschraut@users.noreply.github.com> Date: Fri, 12 Jun 2026 02:51:20 -0500 Subject: [PATCH] fix: resolve musl pacquet binary on musl-based systems (#12347) * fix: resolve musl pacquet binary on musl-based systems The pacquet binary packages are split by libc on linux and only the matching one is installed, but resolvePacquetBin always asked for the glibc name. On Alpine and other musl systems the frozen install failed with: Cannot find module '@pacquet/linux-x64/pacquet'. fix pnpm/pnpm#12049 Co-Authored-By: Claude Fable 5 * style: sort imports in runPacquet.ts Co-Authored-By: Claude Fable 5 * fix: verify the musl pacquet binary package on musl-based systems The signature verification hard-coded the glibc platform package name, so on musl systems it verified a package other than the binary that is actually spawned. Share one platform-package-name helper between resolvePacquetBin and collectPacquetPackagesToVerify. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- installing/commands/package.json | 1 + installing/commands/src/runPacquet.ts | 14 +++++++++++++- installing/commands/src/verifyPacquetIdentity.ts | 4 +++- pnpm-lock.yaml | 3 +++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/installing/commands/package.json b/installing/commands/package.json index d8cb8beaab..4e067093c5 100644 --- a/installing/commands/package.json +++ b/installing/commands/package.json @@ -88,6 +88,7 @@ "@zkochan/table": "catalog:", "chalk": "catalog:", "ci-info": "catalog:", + "detect-libc": "catalog:", "get-npm-tarball-url": "catalog:", "is-subdir": "catalog:", "load-json-file": "catalog:", diff --git a/installing/commands/src/runPacquet.ts b/installing/commands/src/runPacquet.ts index 8ab582fe00..80375f7511 100644 --- a/installing/commands/src/runPacquet.ts +++ b/installing/commands/src/runPacquet.ts @@ -8,6 +8,7 @@ import type { Writable } from 'node:stream' import { PnpmError } from '@pnpm/error' import { logger, streamParser } from '@pnpm/logger' import chalk from 'chalk' +import { familySync as getLibcFamilySync, MUSL } from 'detect-libc' // The runtime `streamParser` is a `Transform` stream (split2 + JSON.parse). // Its public typing only exposes `on`/`removeListener`, so we narrow to the @@ -178,7 +179,18 @@ export function makeRunPacquet (opts: MakeRunPacquetOpts): (callOpts?: RunPacque function resolvePacquetBin (lockfileDir: string, packageName: 'pacquet' | '@pnpm/pacquet'): string { const ext = process.platform === 'win32' ? '.exe' : '' const pacquetPkg = fs.realpathSync(path.join(lockfileDir, 'node_modules/.pnpm-config', packageName, 'package.json')) - return createRequire(pacquetPkg).resolve(`@pacquet/${process.platform}-${process.arch}/pacquet${ext}`) + return createRequire(pacquetPkg).resolve(`${pacquetPlatformPkgName()}/pacquet${ext}`) +} + +/** + * Name of the `@pacquet/-[-musl]` package that holds the + * native pacquet binary for the host. On linux the binary packages are + * split by libc and only the matching one is installed, so spawning and + * signature verification must agree on this exact name. + */ +export function pacquetPlatformPkgName (): string { + const libc = process.platform === 'linux' && getLibcFamilySync() === MUSL ? '-musl' : '' + return `@pacquet/${process.platform}-${process.arch}${libc}` } /** diff --git a/installing/commands/src/verifyPacquetIdentity.ts b/installing/commands/src/verifyPacquetIdentity.ts index accefd808c..30ec3ee266 100644 --- a/installing/commands/src/verifyPacquetIdentity.ts +++ b/installing/commands/src/verifyPacquetIdentity.ts @@ -11,6 +11,8 @@ import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header' import type { CreateFetchFromRegistryOptions, RetryTimeoutOptions } from '@pnpm/network.fetch' import type { Registries, RegistryConfig } from '@pnpm/types' +import { pacquetPlatformPkgName } from './runPacquet.js' + export interface VerifyPacquetIdentityOptions extends CreateFetchFromRegistryOptions { lockfileDir: string rootDir: string @@ -93,7 +95,7 @@ async function collectPacquetPackagesToVerify ( // Only the host's platform binary is ever spawned, so that's the one whose // identity matters. If it isn't in the lockfile, pacquet couldn't run here. - const platformPkgName = `@pacquet/${process.platform}-${process.arch}` + const platformPkgName = pacquetPlatformPkgName() const platformVersion = envLockfile.snapshots[shimKey]?.optionalDependencies?.[platformPkgName] if (platformVersion == null) return undefined const platformKey = `${platformPkgName}@${platformVersion}` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8442b546ee..113a9ceace 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5399,6 +5399,9 @@ importers: ci-info: specifier: 'catalog:' version: 4.4.0 + detect-libc: + specifier: 'catalog:' + version: 2.1.2 get-npm-tarball-url: specifier: 'catalog:' version: 2.1.0