Conversation
Remove the 9MB bundled pnpm.cjs/worker.js and instead use npm ci with committed package-lock.json files (~5KB) to install a bootstrap pnpm, which then installs the target version with integrity verification via the project's pnpm-lock.yaml. Also switch from ncc to esbuild and modernize to ESM. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @actions/* packages use CJS require() for Node.js builtins, which fails with "Dynamic require of 'os' is not supported" when bundled as ESM. Switch esbuild output to CJS format. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node.js treats dist/index.js as ESM due to "type": "module", but the bundle uses CJS require() calls. Remove the field so Node.js defaults to CJS for .js files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove packageManager from package.json to avoid version conflict when the action tests against itself (uses: ./) - Use shell: true on Windows so spawn can find npm.cmd Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bootstrap only needs regular pnpm to install the target package. @pnpm/exe requires install scripts which we skip with --ignore-scripts. Also regenerate pnpm-lock.yaml to match current package.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
--lockfile-dir pointing to GITHUB_WORKSPACE causes the bootstrap pnpm to use the project's pnpm-lock.yaml (which tracks project deps, not pnpm itself), corrupting the install. Revert to --no-lockfile for now. Lockfile-based integrity verification can be added when pnpm v11 has proper support for verifying the pnpm package itself. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use `node .../pnpm/bin/pnpm.cjs` to run the bootstrap pnpm, matching the approach used by the old bundled pnpm.cjs. This avoids issues with the .bin symlink on different platforms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bootstrap pnpm via npm ci (verified by lockfile) - Use `pnpm self-update <version>` for explicit version - Let pnpm handle packageManager field automatically - Remove standalone/exe-specific install logic (pnpm handles this) - Update tests to not run pnpm install against the action repo itself Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- When standalone=true, bootstrap with @pnpm/exe via npm ci - When standalone=false, bootstrap with pnpm via npm ci - Both use pnpm self-update to reach the target version - Remove --ignore-scripts from npm ci so @pnpm/exe install scripts run - Add standalone test back to CI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log .bin directory contents after npm ci to understand why pnpm binary is not found in subsequent CI steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm ci sometimes doesn't create the .bin/pnpm symlink for @pnpm/exe (observed on Linux CI). Manually create the symlink if it's missing after npm ci completes. This fixes the case where standalone=true with no explicit version (relying on packageManager field) — pnpm self-update wouldn't run, leaving .bin empty and pnpm not found on PATH. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pnpm v11 moved global binaries from PNPM_HOME to PNPM_HOME/bin. Add the new bin subdirectory to PATH so that pnpm's global bin directory check passes. This is backwards compatible — the extra PATH entry is harmless for older pnpm versions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm v9 requires the packages field in pnpm-workspace.yaml. Without it, `pnpm --version` fails with "packages field missing or empty". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR pnpm/action-setup#212 (merged 2026-03-21) replaced the bundled pnpm.cjs with an "npm bootstrap then self-update" architecture, but pnpm self-update doesn't actually replace the npm-installed bootstrap binary in the action's temp dir, so the version input is silently ignored. Tracked upstream as pnpm/action-setup#225. Pin to 2e223e0, the commit immediately before #212, which still uses the bundled-binary approach and honours the version input. This also restores the pnpm store cache via actions/setup-node, which had been dropped along with the action when we briefly switched to corepack.
PR pnpm/action-setup#212 (merged 2026-03-21) replaced the bundled pnpm.cjs with an "npm bootstrap then self-update" architecture, but pnpm self-update doesn't actually replace the npm-installed bootstrap binary in the action's temp dir, so the version input is silently ignored. Tracked upstream as pnpm/action-setup#225. Pin CI to 2e223e0, the commit immediately before #212, matching the pin already in place for the pages workflow.
| await writeFile(path.join(dest, 'package.json'), packageJson) | ||
| await writeFile(path.join(dest, 'package-lock.json'), JSON.stringify(lockfile)) | ||
|
|
||
| const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest }) |
There was a problem hiding this comment.
I know this is already marked as a breaking change, but I just want to point out that pnpm/action-setup@5 solved a challenging chicken-egg scenario. With v6 and this change, that solution is gone:
- Use
npm ciwith committedpackage-lock.jsonfiles (~5KB) to install a bootstrap pnpm with integrity verification
Consumers inside a corporate GHE instance are likely to be using clean-slate GitHub actions runners without Node or NPM preinstalled. So npm ci is guaranteed to fail in that situation. We can't assume NPM exists on the runner before installing PNPM. PNPM needs to exist first so the package cache can be immediately configured
- name: Install PNPM
uses: actions/pnpm-action-setup@v5.0.0
- name: Install Node.js
uses: actions/setup-node@v6.3.0
with:
cache: pnpm # not possible unless PNPM is already installed
node-version-file: .nvmrcThe only known workaround is to run actions/setup-node twice. Once before installing PNPM, then again afterwards to configure the cache. Highly inefficient.
FYI @Eynorey
There was a problem hiding this comment.
Unfortunately the "use actions/setup-node twice" workaround also does not work. Steps like the following result in a cryptic error. We wont be able to upgrade to v6 in its current state.
- name: Install Node.js
uses: actions/setup-node@v6.3.0
with:
node-version-file: .nvmrc
- name: Install PNPM
uses: actions/pnpm-action-setup@v6.0.0
- name: Configure Dependency Cache
uses: actions/setup-node@v6.3.0
with:
cache: pnpmError: /home/runner/setup-pnpm/node_modules/.bin/pnpm: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory
There was a problem hiding this comment.
This deserves it's own issue FYI, you should open one. I was wondering why our GHE Actions had different errors compared to the GitHub ones.
There was a problem hiding this comment.
Good idea, I'll try to find some time to explain this properly and open an issue.
From some quick research, it should be possible to use the NPM binary that lives beside the Node runtime the action is already executing on (via using: node24). I'll do some testing and make a suggestion if it pans out:
| const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest }) | |
| const npmBinary = process.platform === 'win32' ? 'npm.cmd' : 'npm'; | |
| const npmPath = path.join(path.dirname(process.execPath), npmBinary); | |
| const npmExitCode = await runCommand(npmPath, ['ci'], { cwd: dest }) |
Summary
pnpm.cjs/worker.jsbinaries from gitnpm ciwith committedpackage-lock.jsonfiles (~5KB) to install a bootstrap pnpm with integrity verificationpnpm-lock.yaml(using--lockfile-dir)@vercel/ncctoesbuild(faster, handles ESM + JSON natively)"type": "module",module: "ESNext",moduleResolution: "bundler")Trust chain
Test plan
versioninput specifying a pnpm versionpackageManagerfield in package.jsonstandalone: true(@pnpm/exe).npmrcfor private registry🤖 Generated with Claude Code