fix(exe): restore execute bit on node-gyp shims in @pnpm/exe (#11485)

- Closes #11483.
- `@pnpm/exe@11.x` was packing `dist/node-gyp-bin/node-gyp`, `dist/node-gyp-bin/node-gyp.cmd`, and `dist/node_modules/node-gyp/bin/node-gyp.js` with mode `0644` instead of `0755`. Any install whose lifecycle script invokes `node-gyp rebuild` while pnpm has prepended `dist/node-gyp-bin/` to `PATH` failed with `sh: 1: node-gyp: Permission denied` — most visibly via `pnpm/action-setup@v6`'s standalone path on GitHub Actions runners with system Node < 22.13.
- The regular `pnpm` package's `package.json` already declares `publishConfig.executableFiles` for these three shims, which `pnpm publish` honors by packing matching tar entries with mode `0755` (see `releasing/commands/src/publish/pack.ts:273` and the `bins`-driven mode selection at `pack.ts:360-361`). `@pnpm/exe`'s `package.json` was missing the same field, so the shims it copies from `pnpm/dist/` shipped with `0644`. This PR mirrors the field into `@pnpm/exe`.
- To prevent the two manifests from drifting again, meta-updater is now the single source of truth for the executable-files list and writes it into both `pnpm/package.json` and `pnpm/artifacts/exe/package.json`.
This commit is contained in:
Zoltan Kochan
2026-05-06 10:46:14 +02:00
parent 2ea6c2097d
commit fbeee82c02
3 changed files with 25 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/exe": patch
"pnpm": patch
---
Restore the execute bit on the `node-gyp` shims packed inside `@pnpm/exe` (`dist/node-gyp-bin/node-gyp`, `dist/node-gyp-bin/node-gyp.cmd`, and `dist/node_modules/node-gyp/bin/node-gyp.js`). Without this, `pnpm/action-setup`'s standalone path (used on runners with Node.js < 22.13) failed any install whose lifecycle script invoked `node-gyp rebuild` with `sh: 1: node-gyp: Permission denied` [#11483](https://github.com/pnpm/pnpm/issues/11483).

View File

@@ -22,6 +22,16 @@ const EXPERIMENTAL_PKGS = new Set([
'pnpm-agent',
])
// Files that must be packed with mode 0755 in both `pnpm` and `@pnpm/exe`.
// `@pnpm/exe` ships the same `dist/` tree as `pnpm`, so the two manifests'
// `publishConfig.executableFiles` lists must stay identical — otherwise the
// shims end up packed at 0644 in one of the tarballs (see #11483).
const PUBLISH_EXECUTABLE_FILES = [
'./dist/node-gyp-bin/node-gyp',
'./dist/node-gyp-bin/node-gyp.cmd',
'./dist/node_modules/node-gyp/bin/node-gyp.js',
]
// Packages whose tests spawn the local pnpm CLI binary (pnpm/bin/pnpm.mjs)
// and therefore need the CLI bundle (pnpm/dist/pnpm.mjs) to be built first.
const PKGS_NEEDING_CLI_COMPILE = new Set([
@@ -137,6 +147,8 @@ export default async (workspaceDir: string) => { // eslint-disable-line
]) {
manifest.optionalDependencies![depName] = 'workspace:*'
}
manifest.publishConfig ??= {}
manifest.publishConfig.executableFiles = [...PUBLISH_EXECUTABLE_FILES]
}
return sortKeysInManifest(manifest)
}
@@ -337,6 +349,7 @@ async function updateManifest (workspaceDir: string, manifest: ProjectManifest,
delete scripts._compile
if (manifest.name === CLI_PKG_NAME) {
manifest.publishConfig!.tag = nextTag
manifest.publishConfig!.executableFiles = [...PUBLISH_EXECUTABLE_FILES]
}
if (scripts['.test']) {
if (scripts.pretest) {

View File

@@ -78,6 +78,11 @@
"pn": "pn",
"pnpx": "pnpx",
"pnx": "pnx"
}
},
"executableFiles": [
"./dist/node-gyp-bin/node-gyp",
"./dist/node-gyp-bin/node-gyp.cmd",
"./dist/node_modules/node-gyp/bin/node-gyp.js"
]
}
}