From fbeee82c02aa2693c00591f8588f4fef6464aa24 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Wed, 6 May 2026 10:46:14 +0200 Subject: [PATCH] fix(exe): restore execute bit on node-gyp shims in @pnpm/exe (#11485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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`. --- .changeset/exe-node-gyp-executable.md | 6 ++++++ .meta-updater/src/index.ts | 13 +++++++++++++ pnpm/artifacts/exe/package.json | 7 ++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .changeset/exe-node-gyp-executable.md diff --git a/.changeset/exe-node-gyp-executable.md b/.changeset/exe-node-gyp-executable.md new file mode 100644 index 0000000000..1f574f81ce --- /dev/null +++ b/.changeset/exe-node-gyp-executable.md @@ -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). diff --git a/.meta-updater/src/index.ts b/.meta-updater/src/index.ts index 8d5b7f1517..5e2e648ffe 100644 --- a/.meta-updater/src/index.ts +++ b/.meta-updater/src/index.ts @@ -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) { diff --git a/pnpm/artifacts/exe/package.json b/pnpm/artifacts/exe/package.json index 1d238b9e39..a71e05f1c5 100644 --- a/pnpm/artifacts/exe/package.json +++ b/pnpm/artifacts/exe/package.json @@ -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" + ] } }