Skip to content

pnpm 12 is incompatible with the image's --ignore-scripts install #53

Description

@dtump

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

  1. 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).
  2. 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.
  3. 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.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdependenciesPull requests that update a dependency filedockerPull requests that update docker code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions