pnpm 12 cannot be installed the way the image installs npm tools, so the pin is
held at 11.23.0 (PR #52). This issue tracks getting back onto a supported pnpm.
What changed upstream
pnpm 12 moved its npm distribution from a JS bundle to a native binary:
|
v11.23.0 |
v12.1.0 |
bin.pnpm |
bin/pnpm.mjs (real JS entrypoint) |
pnpm — a shebang-less placeholder text file |
scripts.preinstall |
— |
node install.js |
| binary source |
bundled in the tarball |
@pnpm/exe.linux-{x64,arm64} optional dep |
install.js hard-links (or copies) the native binary from the optional dep over
that placeholder. Dockerfile:243 installs with --ignore-scripts, so it never
runs, /usr/bin/pnpm stays the 282-byte placeholder, and sh tries to execute
its prose:
/usr/bin/pnpm: line 4: syntax error near unexpected token `)'
/usr/bin/pnpm: line 4: `pnpm's build under pnpm or Bun, or drop --ignore-scripts).'
FAIL pnpm pinned=12.1.0 reported=<probe failed>
pnpm ships no fallback here, by design — from install.js:
A placeholder (not a Node launcher) is required because the Windows shim is
generated from the bin file and npm won't re-read package.json after
preinstall; the tradeoff is no fallback when build scripts are blocked
(--ignore-scripts, pnpm/Bun default).
So this is not a bug that will be patched away. Any pnpm 12+ bump breaks the
image until we choose a path.
Options
- Run
install.js ourselves after the install — the same carve-out
claude-code's install.cjs already gets on the line below. It is
platform-detect plus a hard-link/copy from an already-installed optional dep:
no network, no exec. Cheapest fix, and it keeps --ignore-scripts on for
every package including pnpm's transitive deps. Costs a second entry in that
comment block and the same re-read-on-each-bump discipline (the script is
upstream code that runs at build time, so a bump could change what it does).
- Install pnpm from
@pnpm/exe.linux-* directly and place the binary
ourselves, skipping the wrapper package entirely. No upstream script runs at
all. Needs per-arch handling in the Dockerfile (amd64 + arm64) and a pins
change, since the version would come from a different package.
- Enable Corepack instead.
install.js notes Corepack runs no lifecycle
scripts and enters through bin/pnpm.mjs, so it sidesteps the placeholder.
But Corepack resolves and downloads pnpm at first use, which moves a pinned
build-time dependency to an unpinned runtime fetch — probably not what this
image wants.
- Stay on 11.x indefinitely. Viable short-term (11.x still gets patches),
but it accrues drift and 11 will eventually go EOL.
Option 1 looks right; option 2 is the stricter alternative if we would rather
not run upstream install scripts at all.
Also needed: stop the weekly refresh from re-proposing pnpm 12
update_pins.py picks the newest soaked stable version per tool. Nothing stops
it re-proposing 12.x next Monday, so this PR's failure repeats weekly until this
issue is closed. --block-major-bumps exists but is a global flag, applied to
every tool on the run — too blunt.
Preferred: a per-tool major ceiling on Tool (pnpm stays within its current
major) rather than a blanket ignore, so pnpm still picks up 11.x patch and
security releases automatically. Removing the ceiling becomes part of closing
this issue. Wants an OpenSpec change of its own.
pnpm 12 cannot be installed the way the image installs npm tools, so the pin is
held at
11.23.0(PR #52). This issue tracks getting back onto a supported pnpm.What changed upstream
pnpm 12 moved its npm distribution from a JS bundle to a native binary:
bin.pnpmbin/pnpm.mjs(real JS entrypoint)pnpm— a shebang-less placeholder text filescripts.preinstallnode install.js@pnpm/exe.linux-{x64,arm64}optional depinstall.jshard-links (or copies) the native binary from the optional dep overthat placeholder.
Dockerfile:243installs with--ignore-scripts, so it neverruns,
/usr/bin/pnpmstays the 282-byte placeholder, andshtries to executeits prose:
pnpm ships no fallback here, by design — from
install.js:So this is not a bug that will be patched away. Any pnpm 12+ bump breaks the
image until we choose a path.
Options
install.jsourselves after the install — the same carve-outclaude-code'sinstall.cjsalready gets on the line below. It isplatform-detect plus a hard-link/copy from an already-installed optional dep:
no network, no exec. Cheapest fix, and it keeps
--ignore-scriptson forevery package including pnpm's transitive deps. Costs a second entry in that
comment block and the same re-read-on-each-bump discipline (the script is
upstream code that runs at build time, so a bump could change what it does).
@pnpm/exe.linux-*directly and place the binaryourselves, skipping the wrapper package entirely. No upstream script runs at
all. Needs per-arch handling in the Dockerfile (amd64 + arm64) and a pins
change, since the version would come from a different package.
install.jsnotes Corepack runs no lifecyclescripts and enters through
bin/pnpm.mjs, so it sidesteps the placeholder.But Corepack resolves and downloads pnpm at first use, which moves a pinned
build-time dependency to an unpinned runtime fetch — probably not what this
image wants.
but it accrues drift and 11 will eventually go EOL.
Option 1 looks right; option 2 is the stricter alternative if we would rather
not run upstream install scripts at all.
Also needed: stop the weekly refresh from re-proposing pnpm 12
update_pins.pypicks the newest soaked stable version per tool. Nothing stopsit re-proposing 12.x next Monday, so this PR's failure repeats weekly until this
issue is closed.
--block-major-bumpsexists but is a global flag, applied toevery tool on the run — too blunt.
Preferred: a per-tool major ceiling on
Tool(pnpm stays within its currentmajor) rather than a blanket ignore, so pnpm still picks up 11.x patch and
security releases automatically. Removing the ceiling becomes part of closing
this issue. Wants an OpenSpec change of its own.