mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-22 21:53:02 -04:00
fix(exe): restore legacy @pnpm/{macos,win,linux,linuxstatic}-{x64,arm64} names (#11327)
* fix(exe): restore legacy @pnpm/{macos,win,linux,linuxstatic}-{x64,arm64} package names
Reverts the published package names renamed in #11316 back to the legacy
scheme so `pnpm self-update` from v10 continues to resolve. v10's
self-updater looks up the platform child by its legacy name; the
scope-nested `@pnpm/exe.<platform>-<arch>[-musl]` rename broke that lookup.
Workspace directory layout (`pnpm/artifacts/<platform>-<arch>[-musl]/`)
and the GitHub release asset filenames (`pnpm-linux-x64-musl.tar.gz`,
`pnpm-darwin-*.tar.gz`, `pnpm-win32-*.zip`) stay on the new scheme.
`linkExePlatformBinary` now checks both legacy and future naming
schemes, so a later rename can ship without a v10-compatibility hazard.
* style: fix indentation in setup.test.ts
* refactor: extract legacyOsSegment into a switch helper
* refactor: defer platform detection until after exe dir check
* test: use familySync() in fixtures so musl hosts match implementation
Test fixtures were passing null for libcFamily while linkExePlatformBinary
and setup.js both use detect-libc at runtime. On musl Linux the fixtures
built linux-*/exe.linux-* while the implementation looked up
linuxstatic-*/exe.linux-*-musl. Also bump @pnpm/exe in the changeset.
This commit is contained in:
7
.changeset/restore-exe-platform-package-names.md
Normal file
7
.changeset/restore-exe-platform-package-names.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/engine.pm.commands": patch
|
||||
"@pnpm/exe": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Restored the legacy `@pnpm/{macos,win,linux,linuxstatic}-{x64,arm64}` npm names for the platform-specific optional dependencies of `@pnpm/exe`, reverting the scope-nested `@pnpm/exe.<platform>-<arch>[-musl]` rename from [#11316](https://github.com/pnpm/pnpm/pull/11316) on the published package names only — the workspace directory layout (`pnpm/artifacts/<platform>-<arch>[-musl]/`) and the GitHub release asset filenames stay on the new scheme. The rename broke `pnpm self-update` from v10, which looks up the platform child by its legacy name. `linkExePlatformBinary` now checks for both schemes so a later rename can ship without a v10-compatibility hazard.
|
||||
@@ -127,14 +127,14 @@ export default async (workspaceDir: string) => { // eslint-disable-line
|
||||
manifest.version = pnpmVersion
|
||||
if (manifest.name === '@pnpm/exe') {
|
||||
for (const depName of [
|
||||
'@pnpm/exe.darwin-arm64',
|
||||
'@pnpm/exe.darwin-x64',
|
||||
'@pnpm/exe.linux-arm64',
|
||||
'@pnpm/exe.linux-arm64-musl',
|
||||
'@pnpm/exe.linux-x64',
|
||||
'@pnpm/exe.linux-x64-musl',
|
||||
'@pnpm/exe.win32-arm64',
|
||||
'@pnpm/exe.win32-x64',
|
||||
'@pnpm/linux-arm64',
|
||||
'@pnpm/linux-x64',
|
||||
'@pnpm/linuxstatic-arm64',
|
||||
'@pnpm/linuxstatic-x64',
|
||||
'@pnpm/macos-arm64',
|
||||
'@pnpm/macos-x64',
|
||||
'@pnpm/win-arm64',
|
||||
'@pnpm/win-x64',
|
||||
]) {
|
||||
manifest.optionalDependencies![depName] = 'workspace:*'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { selfUpdate } from './self-updater/index.js'
|
||||
export { exePlatformPkgDirName, installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js'
|
||||
export { exePlatformPkgDirName, exePlatformPkgDirNameNext, installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js'
|
||||
export { setup } from './setup/index.js'
|
||||
export { withCmd } from './with/index.js'
|
||||
|
||||
@@ -322,14 +322,40 @@ async function installFromResolution (
|
||||
|
||||
/**
|
||||
* Computes the scope-local directory name of the `@pnpm/exe` platform
|
||||
* package for a given host: `exe.<platform>-<arch>[-musl]`. Pure so that the
|
||||
* musl branch is unit-testable without mocking detect-libc or patching
|
||||
* process.platform.
|
||||
* package for a given host. Returns the legacy name currently published on npm
|
||||
* (`macos-<arch>`, `win-<arch>`, `linux-<arch>`, `linuxstatic-<arch>`); callers
|
||||
* should also consider the future `exe.<platform>-<arch>[-musl]` scheme, since
|
||||
* a later release will switch to it. Pure so that the musl branch is
|
||||
* unit-testable without mocking detect-libc or patching process.platform.
|
||||
*/
|
||||
export function exePlatformPkgDirName (
|
||||
platform: NodeJS.Platform,
|
||||
arch: string,
|
||||
libcFamily: string | null
|
||||
): string {
|
||||
const normalizedArch = platform === 'win32' && arch === 'ia32' ? 'x86' : arch
|
||||
return `${legacyOsSegment(platform, libcFamily)}-${normalizedArch}`
|
||||
}
|
||||
|
||||
function legacyOsSegment (platform: NodeJS.Platform, libcFamily: string | null): string {
|
||||
switch (platform) {
|
||||
case 'darwin': return 'macos'
|
||||
case 'win32': return 'win'
|
||||
case 'linux': return libcFamily === 'musl' ? 'linuxstatic' : 'linux'
|
||||
default: return platform
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Future scope-local directory name of the `@pnpm/exe` platform package, under
|
||||
* the `exe.<platform>-<arch>[-musl]` scheme that matches the workspace
|
||||
* directory layout. `linkExePlatformBinary` checks this as a fallback so a
|
||||
* future rename of the published packages works without touching this logic.
|
||||
*/
|
||||
export function exePlatformPkgDirNameNext (
|
||||
platform: NodeJS.Platform,
|
||||
arch: string,
|
||||
libcFamily: string | null
|
||||
): string {
|
||||
const normalizedArch = platform === 'win32' && arch === 'ia32' ? 'x86' : arch
|
||||
const libcSuffix = platform === 'linux' && libcFamily === 'musl' ? '-musl' : ''
|
||||
@@ -337,23 +363,36 @@ export function exePlatformPkgDirName (
|
||||
}
|
||||
|
||||
// @pnpm/exe bundles Node.js via optional platform-specific packages
|
||||
// (e.g. @pnpm/exe.darwin-arm64, @pnpm/exe.linux-x64-musl).
|
||||
// Its postinstall script links the correct binary into the @pnpm/exe package dir.
|
||||
// Since scripts are disabled during install (to support systems without Node.js),
|
||||
// we replicate that linking here.
|
||||
// (e.g. @pnpm/macos-arm64, @pnpm/linuxstatic-x64; or, after a future rename,
|
||||
// @pnpm/exe.darwin-arm64, @pnpm/exe.linux-x64-musl). Its postinstall script
|
||||
// links the correct binary into the @pnpm/exe package dir. Since scripts are
|
||||
// disabled during install (to support systems without Node.js), we replicate
|
||||
// that linking here, checking both naming schemes so self-update works across
|
||||
// the rename.
|
||||
export function linkExePlatformBinary (installDir: string): void {
|
||||
const platform = process.platform
|
||||
const pkgDirName = exePlatformPkgDirName(platform, process.arch, familySync())
|
||||
const exePkgDir = path.join(installDir, 'node_modules', '@pnpm', 'exe')
|
||||
if (!fs.existsSync(exePkgDir)) return
|
||||
// In pnpm's symlinked node_modules layout, the platform package is not hoisted
|
||||
// to the top-level node_modules. It's a dependency of @pnpm/exe and lives as a
|
||||
// sibling in the virtual store. Resolve through the @pnpm/exe symlink to find it.
|
||||
const exeRealDir = fs.realpathSync(exePkgDir)
|
||||
const platformPkgDir = path.join(path.dirname(exeRealDir), pkgDirName)
|
||||
const platform = process.platform
|
||||
const arch = process.arch
|
||||
const libcFamily = familySync()
|
||||
const executable = platform === 'win32' ? 'pnpm.exe' : 'pnpm'
|
||||
const src = path.join(platformPkgDir, executable)
|
||||
if (!fs.existsSync(src)) return
|
||||
const candidateDirNames = [
|
||||
exePlatformPkgDirName(platform, arch, libcFamily),
|
||||
exePlatformPkgDirNameNext(platform, arch, libcFamily),
|
||||
]
|
||||
let src: string | undefined
|
||||
for (const dirName of candidateDirNames) {
|
||||
const candidate = path.join(path.dirname(exeRealDir), dirName, executable)
|
||||
if (fs.existsSync(candidate)) {
|
||||
src = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if (src == null) return
|
||||
const dest = path.join(exePkgDir, executable)
|
||||
forceLink(src, dest)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { prependDirsToPath } from '@pnpm/shell.path'
|
||||
import { getRegisteredProjects } from '@pnpm/store.controller'
|
||||
import { getMockAgent, setupMockAgent, teardownMockAgent } from '@pnpm/testing.mock-agent'
|
||||
import spawn from 'cross-spawn'
|
||||
import { familySync } from 'detect-libc'
|
||||
|
||||
const require = createRequire(import.meta.dirname)
|
||||
const pnpmTarballPath = require.resolve('@pnpm/tgz-fixtures/tgz/pnpm-9.1.0.tgz')
|
||||
@@ -23,7 +24,7 @@ jest.unstable_mockModule('@pnpm/cli.meta', () => {
|
||||
},
|
||||
}
|
||||
})
|
||||
const { selfUpdate, installPnpm, linkExePlatformBinary, exePlatformPkgDirName } = await import('@pnpm/engine.pm.commands')
|
||||
const { selfUpdate, installPnpm, linkExePlatformBinary, exePlatformPkgDirName, exePlatformPkgDirNameNext } = await import('@pnpm/engine.pm.commands')
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupMockAgent()
|
||||
@@ -511,10 +512,11 @@ describe('linkExePlatformBinary', () => {
|
||||
const platform = process.platform
|
||||
const arch = platform === 'win32' && process.arch === 'ia32' ? 'x86' : process.arch
|
||||
const executable = platform === 'win32' ? 'pnpm.exe' : 'pnpm'
|
||||
// NOTE: the test layout doesn't set up a musl libc marker on Linux, so the
|
||||
// non-musl platform package is what gets linked here. Matching what
|
||||
// linkExePlatformBinary detects via detect-libc.
|
||||
const platformPkgName = `exe.${platform}-${arch}`
|
||||
// Match the libc family linkExePlatformBinary detects at runtime so the
|
||||
// fixture directory matches what the implementation looks up, including on
|
||||
// musl hosts (Alpine CI).
|
||||
const libcFamily = familySync()
|
||||
const platformPkgName = exePlatformPkgDirName(platform, arch, libcFamily)
|
||||
|
||||
test('links platform binary in pnpm symlinked node_modules layout', () => {
|
||||
const dir = tempDir(false)
|
||||
@@ -599,27 +601,74 @@ describe('linkExePlatformBinary', () => {
|
||||
const result = fs.readFileSync(path.join(exeDir, executable), 'utf8')
|
||||
expect(result).toBe(placeholder)
|
||||
})
|
||||
|
||||
test('falls back to future exe.<platform>-<arch> naming scheme', () => {
|
||||
const dir = tempDir(false)
|
||||
|
||||
// Simulate a future release where only the new-scheme platform package
|
||||
// directory exists under the virtual store — the legacy name is absent.
|
||||
const nextPkgName = exePlatformPkgDirNameNext(platform, arch, libcFamily)
|
||||
const exeDir = path.join(dir, 'node_modules', '@pnpm', 'exe')
|
||||
const platformDir = path.join(dir, 'node_modules', '@pnpm', nextPkgName)
|
||||
|
||||
fs.mkdirSync(exeDir, { recursive: true })
|
||||
fs.mkdirSync(platformDir, { recursive: true })
|
||||
|
||||
fs.writeFileSync(path.join(exeDir, executable), 'This file intentionally left blank')
|
||||
fs.writeFileSync(path.join(exeDir, 'package.json'), JSON.stringify({ bin: { pnpm: 'pnpm' } }))
|
||||
|
||||
const fakeBinaryContent = '#!/bin/sh\necho "fake pnpm binary"'
|
||||
fs.writeFileSync(path.join(platformDir, executable), fakeBinaryContent)
|
||||
|
||||
linkExePlatformBinary(dir)
|
||||
|
||||
const result = fs.readFileSync(path.join(exeDir, executable), 'utf8')
|
||||
expect(result).toBe(fakeBinaryContent)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exePlatformPkgDirName', () => {
|
||||
test('appends -musl for linux + musl libc family', () => {
|
||||
expect(exePlatformPkgDirName('linux', 'x64', 'musl')).toBe('exe.linux-x64-musl')
|
||||
expect(exePlatformPkgDirName('linux', 'arm64', 'musl')).toBe('exe.linux-arm64-musl')
|
||||
test('uses linuxstatic- prefix for linux + musl libc family', () => {
|
||||
expect(exePlatformPkgDirName('linux', 'x64', 'musl')).toBe('linuxstatic-x64')
|
||||
expect(exePlatformPkgDirName('linux', 'arm64', 'musl')).toBe('linuxstatic-arm64')
|
||||
})
|
||||
|
||||
test('does not append -musl when libc is glibc or unknown', () => {
|
||||
expect(exePlatformPkgDirName('linux', 'x64', 'glibc')).toBe('exe.linux-x64')
|
||||
expect(exePlatformPkgDirName('linux', 'arm64', null)).toBe('exe.linux-arm64')
|
||||
test('uses linux- prefix when libc is glibc or unknown', () => {
|
||||
expect(exePlatformPkgDirName('linux', 'x64', 'glibc')).toBe('linux-x64')
|
||||
expect(exePlatformPkgDirName('linux', 'arm64', null)).toBe('linux-arm64')
|
||||
})
|
||||
|
||||
test('libc is irrelevant on non-linux platforms', () => {
|
||||
expect(exePlatformPkgDirName('darwin', 'arm64', 'musl')).toBe('exe.darwin-arm64')
|
||||
expect(exePlatformPkgDirName('darwin', 'x64', null)).toBe('exe.darwin-x64')
|
||||
expect(exePlatformPkgDirName('win32', 'x64', 'musl')).toBe('exe.win32-x64')
|
||||
expect(exePlatformPkgDirName('darwin', 'arm64', 'musl')).toBe('macos-arm64')
|
||||
expect(exePlatformPkgDirName('darwin', 'x64', null)).toBe('macos-x64')
|
||||
expect(exePlatformPkgDirName('win32', 'x64', 'musl')).toBe('win-x64')
|
||||
})
|
||||
|
||||
test('normalizes ia32 to x86 on win32 only', () => {
|
||||
expect(exePlatformPkgDirName('win32', 'ia32', null)).toBe('exe.win32-x86')
|
||||
expect(exePlatformPkgDirName('linux', 'ia32', null)).toBe('exe.linux-ia32')
|
||||
expect(exePlatformPkgDirName('win32', 'ia32', null)).toBe('win-x86')
|
||||
expect(exePlatformPkgDirName('linux', 'ia32', null)).toBe('linux-ia32')
|
||||
})
|
||||
})
|
||||
|
||||
describe('exePlatformPkgDirNameNext', () => {
|
||||
test('appends -musl for linux + musl libc family', () => {
|
||||
expect(exePlatformPkgDirNameNext('linux', 'x64', 'musl')).toBe('exe.linux-x64-musl')
|
||||
expect(exePlatformPkgDirNameNext('linux', 'arm64', 'musl')).toBe('exe.linux-arm64-musl')
|
||||
})
|
||||
|
||||
test('does not append -musl when libc is glibc or unknown', () => {
|
||||
expect(exePlatformPkgDirNameNext('linux', 'x64', 'glibc')).toBe('exe.linux-x64')
|
||||
expect(exePlatformPkgDirNameNext('linux', 'arm64', null)).toBe('exe.linux-arm64')
|
||||
})
|
||||
|
||||
test('libc is irrelevant on non-linux platforms', () => {
|
||||
expect(exePlatformPkgDirNameNext('darwin', 'arm64', 'musl')).toBe('exe.darwin-arm64')
|
||||
expect(exePlatformPkgDirNameNext('darwin', 'x64', null)).toBe('exe.darwin-x64')
|
||||
expect(exePlatformPkgDirNameNext('win32', 'x64', 'musl')).toBe('exe.win32-x64')
|
||||
})
|
||||
|
||||
test('normalizes ia32 to x86 on win32 only', () => {
|
||||
expect(exePlatformPkgDirNameNext('win32', 'ia32', null)).toBe('exe.win32-x86')
|
||||
expect(exePlatformPkgDirNameNext('linux', 'ia32', null)).toBe('exe.linux-ia32')
|
||||
})
|
||||
})
|
||||
|
||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@@ -7588,13 +7588,13 @@ importers:
|
||||
|
||||
pnpm/artifacts/darwin-arm64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.darwin-arm64':
|
||||
'@pnpm/macos-arm64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/darwin-x64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.darwin-x64':
|
||||
'@pnpm/macos-x64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
@@ -7620,64 +7620,64 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: safe-execa@0.3.0
|
||||
optionalDependencies:
|
||||
'@pnpm/exe.darwin-arm64':
|
||||
specifier: workspace:*
|
||||
version: link:../darwin-arm64
|
||||
'@pnpm/exe.darwin-x64':
|
||||
specifier: workspace:*
|
||||
version: link:../darwin-x64
|
||||
'@pnpm/exe.linux-arm64':
|
||||
'@pnpm/linux-arm64':
|
||||
specifier: workspace:*
|
||||
version: link:../linux-arm64
|
||||
'@pnpm/exe.linux-arm64-musl':
|
||||
specifier: workspace:*
|
||||
version: link:../linux-arm64-musl
|
||||
'@pnpm/exe.linux-x64':
|
||||
'@pnpm/linux-x64':
|
||||
specifier: workspace:*
|
||||
version: link:../linux-x64
|
||||
'@pnpm/exe.linux-x64-musl':
|
||||
'@pnpm/linuxstatic-arm64':
|
||||
specifier: workspace:*
|
||||
version: link:../linux-arm64-musl
|
||||
'@pnpm/linuxstatic-x64':
|
||||
specifier: workspace:*
|
||||
version: link:../linux-x64-musl
|
||||
'@pnpm/exe.win32-arm64':
|
||||
'@pnpm/macos-arm64':
|
||||
specifier: workspace:*
|
||||
version: link:../darwin-arm64
|
||||
'@pnpm/macos-x64':
|
||||
specifier: workspace:*
|
||||
version: link:../darwin-x64
|
||||
'@pnpm/win-arm64':
|
||||
specifier: workspace:*
|
||||
version: link:../win32-arm64
|
||||
'@pnpm/exe.win32-x64':
|
||||
'@pnpm/win-x64':
|
||||
specifier: workspace:*
|
||||
version: link:../win32-x64
|
||||
|
||||
pnpm/artifacts/linux-arm64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.linux-arm64':
|
||||
'@pnpm/linux-arm64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/linux-arm64-musl:
|
||||
devDependencies:
|
||||
'@pnpm/exe.linux-arm64-musl':
|
||||
'@pnpm/linuxstatic-arm64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/linux-x64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.linux-x64':
|
||||
'@pnpm/linux-x64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/linux-x64-musl:
|
||||
devDependencies:
|
||||
'@pnpm/exe.linux-x64-musl':
|
||||
'@pnpm/linuxstatic-x64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/win32-arm64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.win32-arm64':
|
||||
'@pnpm/win-arm64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pnpm/artifacts/win32-x64:
|
||||
devDependencies:
|
||||
'@pnpm/exe.win32-x64':
|
||||
'@pnpm/win-x64':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.darwin-arm64",
|
||||
"name": "@pnpm/macos-arm64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.darwin-arm64": "workspace:*"
|
||||
"@pnpm/macos-arm64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.darwin-x64",
|
||||
"name": "@pnpm/macos-x64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.darwin-x64": "workspace:*"
|
||||
"@pnpm/macos-x64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -37,14 +37,14 @@
|
||||
"detect-libc": "catalog:"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pnpm/exe.darwin-arm64": "workspace:*",
|
||||
"@pnpm/exe.darwin-x64": "workspace:*",
|
||||
"@pnpm/exe.linux-arm64": "workspace:*",
|
||||
"@pnpm/exe.linux-arm64-musl": "workspace:*",
|
||||
"@pnpm/exe.linux-x64": "workspace:*",
|
||||
"@pnpm/exe.linux-x64-musl": "workspace:*",
|
||||
"@pnpm/exe.win32-arm64": "workspace:*",
|
||||
"@pnpm/exe.win32-x64": "workspace:*"
|
||||
"@pnpm/linux-arm64": "workspace:*",
|
||||
"@pnpm/linux-x64": "workspace:*",
|
||||
"@pnpm/linuxstatic-arm64": "workspace:*",
|
||||
"@pnpm/linuxstatic-x64": "workspace:*",
|
||||
"@pnpm/macos-arm64": "workspace:*",
|
||||
"@pnpm/macos-x64": "workspace:*",
|
||||
"@pnpm/win-arm64": "workspace:*",
|
||||
"@pnpm/win-x64": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "catalog:",
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
// Shared between setup.js (preinstall hook) and the test suite.
|
||||
// Computes the npm package name of the matching @pnpm/exe platform child for a
|
||||
// given host: `@pnpm/exe.<platform>-<arch>[-musl]`. Pure — no I/O, no detect-libc
|
||||
// call — so the musl branch is unit-testable without mocking.
|
||||
// given host. Returns `@pnpm/<os>-<arch>`, where <os> is `macos` (darwin),
|
||||
// `win` (win32), `linux` (glibc), or `linuxstatic` (musl). Pure — no I/O, no
|
||||
// detect-libc call — so the musl branch is unit-testable without mocking.
|
||||
export function exePlatformPkgName(platform, arch, libcFamily) {
|
||||
const normalizedArch = platform === 'win32' && arch === 'ia32' ? 'x86' : arch
|
||||
const libcSuffix = platform === 'linux' && libcFamily === 'musl' ? '-musl' : ''
|
||||
return `@pnpm/exe.${platform}-${normalizedArch}${libcSuffix}`
|
||||
return `@pnpm/${legacyOsSegment(platform, libcFamily)}-${normalizedArch}`
|
||||
}
|
||||
|
||||
function legacyOsSegment(platform, libcFamily) {
|
||||
switch (platform) {
|
||||
case 'darwin': return 'macos'
|
||||
case 'win32': return 'win'
|
||||
case 'linux': return libcFamily === 'musl' ? 'linuxstatic' : 'linux'
|
||||
default: return platform
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,14 @@ import fs from 'fs'
|
||||
import { familySync } from 'detect-libc'
|
||||
import { exePlatformPkgName } from './platform-pkg-name.js'
|
||||
|
||||
// Platform names match process.platform (linux | darwin | win32). On linux,
|
||||
// add a `-musl` libc suffix when detect-libc reports musl, matching the
|
||||
// @pnpm/exe.linux-<arch>-musl optional-dep naming. The name computation lives
|
||||
// in platform-pkg-name.js so it can be unit-tested without triggering the
|
||||
// side effects of this preinstall script.
|
||||
// Platform package names use the legacy scheme: `@pnpm/macos-<arch>` (darwin),
|
||||
// `@pnpm/win-<arch>` (win32), `@pnpm/linux-<arch>` (glibc), and
|
||||
// `@pnpm/linuxstatic-<arch>` (musl Linux, detected via detect-libc). This is
|
||||
// the naming published on npm, even though the workspace directories use the
|
||||
// newer `<os>-<arch>[-musl]` scheme. Keeping these names lets `pnpm
|
||||
// self-update` from older majors continue to resolve the right platform child.
|
||||
// The name computation lives in platform-pkg-name.js so it can be unit-tested
|
||||
// without triggering the side effects of this preinstall script.
|
||||
const platform = process.platform
|
||||
const pkgName = exePlatformPkgName(platform, process.arch, familySync())
|
||||
const pkgJson = fileURLToPath(import.meta.resolve(`${pkgName}/package.json`))
|
||||
|
||||
@@ -2,43 +2,42 @@ import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
import { familySync } from 'detect-libc'
|
||||
|
||||
// @ts-expect-error — JS helper without type declarations
|
||||
import { exePlatformPkgName } from '../platform-pkg-name.js'
|
||||
|
||||
const exeDir = path.resolve(import.meta.dirname, '..')
|
||||
const platform = process.platform
|
||||
const arch = platform === 'win32' && process.arch === 'ia32' ? 'x86' : process.arch
|
||||
const isWindows = platform === 'win32'
|
||||
// The test doesn't create a musl libc marker, so setup.js's detect-libc call
|
||||
// reports the host's native libc; on a glibc Linux CI box that resolves to the
|
||||
// non-musl package name. For non-Linux hosts there is no libc suffix.
|
||||
// Match setup.js's detect-libc call so the fixture path lines up with the
|
||||
// package `setup.js` actually resolves on this host (including musl).
|
||||
const platformBin = path.join(
|
||||
exeDir, 'node_modules', '@pnpm', `exe.${platform}-${arch}`,
|
||||
exeDir, 'node_modules', exePlatformPkgName(platform, process.arch, familySync()),
|
||||
isWindows ? 'pnpm.exe' : 'pnpm'
|
||||
)
|
||||
const hasPlatformBinary = fs.existsSync(platformBin)
|
||||
|
||||
describe('exePlatformPkgName', () => {
|
||||
test('appends -musl for linux + musl libc family', () => {
|
||||
expect(exePlatformPkgName('linux', 'x64', 'musl')).toBe('@pnpm/exe.linux-x64-musl')
|
||||
expect(exePlatformPkgName('linux', 'arm64', 'musl')).toBe('@pnpm/exe.linux-arm64-musl')
|
||||
test('uses linuxstatic- prefix for linux + musl libc family', () => {
|
||||
expect(exePlatformPkgName('linux', 'x64', 'musl')).toBe('@pnpm/linuxstatic-x64')
|
||||
expect(exePlatformPkgName('linux', 'arm64', 'musl')).toBe('@pnpm/linuxstatic-arm64')
|
||||
})
|
||||
|
||||
test('does not append -musl when libc is glibc or unknown', () => {
|
||||
expect(exePlatformPkgName('linux', 'x64', 'glibc')).toBe('@pnpm/exe.linux-x64')
|
||||
expect(exePlatformPkgName('linux', 'arm64', null)).toBe('@pnpm/exe.linux-arm64')
|
||||
test('uses linux- prefix when libc is glibc or unknown', () => {
|
||||
expect(exePlatformPkgName('linux', 'x64', 'glibc')).toBe('@pnpm/linux-x64')
|
||||
expect(exePlatformPkgName('linux', 'arm64', null)).toBe('@pnpm/linux-arm64')
|
||||
})
|
||||
|
||||
test('libc is irrelevant on non-linux platforms', () => {
|
||||
expect(exePlatformPkgName('darwin', 'arm64', 'musl')).toBe('@pnpm/exe.darwin-arm64')
|
||||
expect(exePlatformPkgName('darwin', 'x64', null)).toBe('@pnpm/exe.darwin-x64')
|
||||
expect(exePlatformPkgName('win32', 'x64', 'musl')).toBe('@pnpm/exe.win32-x64')
|
||||
expect(exePlatformPkgName('darwin', 'arm64', 'musl')).toBe('@pnpm/macos-arm64')
|
||||
expect(exePlatformPkgName('darwin', 'x64', null)).toBe('@pnpm/macos-x64')
|
||||
expect(exePlatformPkgName('win32', 'x64', 'musl')).toBe('@pnpm/win-x64')
|
||||
})
|
||||
|
||||
test('normalizes ia32 to x86 on win32 only', () => {
|
||||
expect(exePlatformPkgName('win32', 'ia32', null)).toBe('@pnpm/exe.win32-x86')
|
||||
expect(exePlatformPkgName('linux', 'ia32', null)).toBe('@pnpm/exe.linux-ia32')
|
||||
expect(exePlatformPkgName('win32', 'ia32', null)).toBe('@pnpm/win-x86')
|
||||
expect(exePlatformPkgName('linux', 'ia32', null)).toBe('@pnpm/linux-ia32')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.linux-arm64-musl",
|
||||
"name": "@pnpm/linuxstatic-arm64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.linux-arm64-musl": "workspace:*"
|
||||
"@pnpm/linuxstatic-arm64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.linux-arm64",
|
||||
"name": "@pnpm/linux-arm64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.linux-arm64": "workspace:*"
|
||||
"@pnpm/linux-arm64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.linux-x64-musl",
|
||||
"name": "@pnpm/linuxstatic-x64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.linux-x64-musl": "workspace:*"
|
||||
"@pnpm/linuxstatic-x64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.linux-x64",
|
||||
"name": "@pnpm/linux-x64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.linux-x64": "workspace:*"
|
||||
"@pnpm/linux-x64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.win32-arm64",
|
||||
"name": "@pnpm/win-arm64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm.exe || (echo 'Error: pnpm.exe is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.win32-arm64": "workspace:*"
|
||||
"@pnpm/win-arm64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@pnpm/exe.win32-x64",
|
||||
"name": "@pnpm/win-x64",
|
||||
"version": "11.0.0-rc.3",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
@@ -20,7 +20,7 @@
|
||||
"prepublishOnly": "test -f pnpm.exe || (echo 'Error: pnpm.exe is missing' && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/exe.win32-x64": "workspace:*"
|
||||
"@pnpm/win-x64": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"os": [
|
||||
|
||||
Reference in New Issue
Block a user