From 2e7e5f7b5ccd114e2a8df061b5752bbffdc79346 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Mon, 27 Apr 2026 01:26:06 +0200 Subject: [PATCH] fix(npm): preserve executable bit on platform binaries Per-platform packages were published with the binary at mode 0644, so pacquet's JS shim's spawnSync failed with EACCES on install. The chmodSync(0o755) in generate-packages.mjs ran on the publish runner, but npm/pnpm pack normalizes file modes for deterministic tarballs: files referenced in the manifest's bin field become 0755 and everything else 0644. publishConfig.executableFiles is the npm-native way to keep additional files at 0755 in the published tarball without overloading bin's "this is a CLI entry point" semantics. Both npm and pnpm honor it. --- pacquet/npm/pacquet/scripts/generate-packages.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pacquet/npm/pacquet/scripts/generate-packages.mjs b/pacquet/npm/pacquet/scripts/generate-packages.mjs index 8a45ae7730..2155db075d 100644 --- a/pacquet/npm/pacquet/scripts/generate-packages.mjs +++ b/pacquet/npm/pacquet/scripts/generate-packages.mjs @@ -29,6 +29,11 @@ function generateNativePackage(platform, arch) { // Generate the package.json manifest const { version } = rootManifest; + // publishConfig.executableFiles tells pnpm pack to keep mode 0755 + // on the binary in the published tarball. Without it, pack normalizes + // every non-bin file to 0644 and the JS shim's spawnSync fails with + // EACCES on install. + const ext = platform === "win32" ? ".exe" : ""; const manifest = JSON.stringify({ name: packageName, version, @@ -38,6 +43,9 @@ function generateNativePackage(platform, arch) { type: "git", url: "https://github.com/pnpm/pacquet", }, + publishConfig: { + executableFiles: [`./${BIN_NAME}${ext}`], + }, }, null, 2); const manifestPath = resolve(packageRoot, "package.json"); @@ -45,7 +53,6 @@ function generateNativePackage(platform, arch) { fs.writeFileSync(manifestPath, manifest); // Copy the binary - const ext = platform === "win32" ? ".exe" : ""; const binarySource = resolve(REPO_ROOT, `${BIN_NAME}-${platform}-${arch}${ext}`); const binaryTarget = resolve(packageRoot, `${BIN_NAME}${ext}`);