[create-cloudflare] Fix ERR_PNPM_IGNORED_BUILDS on pnpm 11 + close C3 e2e test gap#14193
[create-cloudflare] Fix ERR_PNPM_IGNORED_BUILDS on pnpm 11 + close C3 e2e test gap#14193petebacondarwin wants to merge 9 commits into
Conversation
🦋 Changeset detectedLatest commit: d53022e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Confirmed: This is purely a CI workflow change. Both jobs are bumped consistently. The macOS addition to frameworks-e2e produces a clean matrix expansion with no concurrency-group or artifact-name collisions. Let me do a final sanity check on the matrix - whether macOS gets an experimental run (it doesn't, but that's by design — only ubuntu has the experimental include). The changes are coherent:
No logic bugs, security issues, or backward-compat problems. This is a well-formed CI config change. LGTM |
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
@cloudflare/wrangler-bundler
commit: |
|
✅ All changesets look good |
1946454 to
9b9f0c1
Compare
9b9f0c1 to
458b2ad
Compare
Adds macos-latest to the frameworks-e2e job to verify whether issue #13928 (ERR_PNPM_IGNORED_BUILDS on pnpm 10/11) can be reproduced in CI.
…d, sharp on pnpm 10+ pnpm 11 flipped `strictDepBuilds` to `true` by default, so dependency build scripts that haven't been explicitly approved now fail the install with `ERR_PNPM_IGNORED_BUILDS` instead of warning. `wrangler` depends on `workerd` and `esbuild`, and (via miniflare) on `sharp` — all three need their postinstall scripts to produce platform binaries. Without pre-approval, every C3 scaffold that installs Wrangler aborts on pnpm 11 with a red error box before the project is usable. The scaffold step now writes (or minimally merges into) a `pnpm-workspace.yaml` in the generated project that approves exactly those three packages. C3 deliberately does NOT approve build scripts for packages introduced by framework generators (`@parcel/watcher`, `@swc/core`, `lmdb`, ...) — those are the generator's or the user's responsibility. The merge logic only ever touches the three keys C3 owns; any other entry pnpm or a generator wrote is left untouched. If an install still fails with `ERR_PNPM_IGNORED_BUILDS` (because a framework generator pulled in unapproved build scripts), C3 now prints actionable guidance pointing at `pnpm approve-builds` alongside the raw pnpm output. Interactive build approval is tracked as a fast follow. Fixes #13928.
The C3 e2e cli/workers matrix labelled pnpm jobs as 'pnpm 10.33.0' so the suite never exercised pnpm 11's stricter `strictDepBuilds` default. That gap is the reason the ERR_PNPM_IGNORED_BUILDS regression on pnpm 11 went undetected. Flip the matrix entry to pnpm 11.5.1 and overlay it on PATH via a second `pnpm/action-setup` (the first invocation, in install-dependencies, installs the monorepo's pinned pnpm 10.33.0 — both are needed). The overlay requires three workarounds: 1. Point `pnpm/action-setup` at a stub `package.json` so it doesn't refuse to install with both a `version` input and a root `packageManager` field. 2. Loosen the root `engines.pnpm` so pnpm 11 doesn't bail with ERR_PNPM_UNSUPPORTED_ENGINE on every command at the monorepo root. 3. Set `pnpm_config_pm_on_fail=ignore` and `pnpm_config_verify_deps_before_run=false` on the run-e2e step so pnpm 11 doesn't silently re-exec as the pinned pnpm 10 or trigger an implicit `pnpm install` at the monorepo root before running the script. A verify step fails fast if `pnpm --version` on PATH doesn't match the matrix, so a future regression is obvious. The frameworks-e2e job stays on pnpm 10.33.0 deliberately: several framework generators (Next.js, Nuxt, Analog, Qwik, Hono) pull in additional build-script deps that are outside C3's pre-approval scope, so they'd fail non-interactively on pnpm 11. pnpm 10 only warns, so the scaffold exercise stays meaningful while we work on interactive build approval upstream.
… approve+retry
When pnpm 11 aborts the install because a framework generator pulled in an
unapproved build script, C3 used to dump the entire pnpm transcript twice
(once via the spinner stop, once via the top-level error handler) before
finally printing a hint to run `pnpm approve-builds`. The result was a
giant red wall of noise on every framework that needs a native build step.
Replace that with a focused recovery path:
- Parse the flagged package list out of the pnpm output instead of
showing the raw transcript. `extractIgnoredBuildPackages` strips
`@version` suffixes (handles scoped and unscoped names, dedupes).
- Run `pnpm install` under C3's own spinner with `useSpinner: false`
so failures stop the spinner with a short `install failed` line,
not the full captured stdout.
- In an interactive shell, prompt the user to run
`pnpm approve-builds <pkgs>` and retry the install once.
`pnpm approve-builds` is non-interactive when packages are listed
explicitly (verified against pnpm 11.5.1) and writes to
`pnpm-workspace.yaml#allowBuilds`, matching the format
`writePnpmBuildApprovals` already uses.
- On a declined prompt, a non-TTY shell, or a retry that still fails,
throw a new `IgnoredBuildsError` carrying the parsed package list.
The top-level handler in `cli.ts` now renders `e.message` plus
scoped guidance from `getPnpmIgnoredBuildsGuidance(packages)` —
which inlines the exact `pnpm approve-builds <pkgs>` command — and
nothing else. No more transcript dumps.
Tests:
- 18 new unit tests in `pnpmBuildApprovals.test.ts` covering the
parser, the `IgnoredBuildsError` class + `isIgnoredBuildsError`
discriminator, and the package-aware guidance.
- New `packages.test.ts` (10 tests) covering the install
orchestration: non-pnpm passthrough, idempotency, rethrow of
unrelated failures, non-interactive bail-out, interactive
approve-and-retry happy path, declined prompt, retry-still-fails,
unrelated retry failure, and unparseable error inputs.
…ive, e2e, and non-TTY
Reuse the existing C3 prompt mechanism for the `pnpm approve-builds`
recovery confirmation:
- drop the `isInteractive()` gate in `recoverFromIgnoredBuilds`. The
prompt now fires unconditionally and is answered:
- by the user in a real terminal (TTY);
- by the e2e harness's new background responder in piped-stdin tests
(`packages/create-cloudflare/e2e/helpers/run-c3.ts`);
- by an internal stdin-`end` race in fully non-interactive shells
(e.g. `pnpm create cloudflare < /dev/null`), which would otherwise
drain the event loop and silently exit 0. The race converts the EOF
into an `IgnoredBuildsError` carrying the parsed package list, so
the top-level handler renders the same concise guidance as before.
- catch `CancelError` (Ctrl-C / clack cancel) and the EOF sentinel and
convert both to `IgnoredBuildsError`.
- add a `backgroundResponders` channel to `runC3` that fires
independently of the ordered `promptHandlers` queue. A default
responder accepts the `approve-builds` confirmation prompt so every
framework test inherits the new behaviour without bespoke config.
Verified end-to-end against a real pnpm 11.5.1 + `@parcel/watcher`
project with the actual C3 install path:
- closed stdin (`</dev/null`) -> `RESULT: caught IgnoredBuildsError`
- piped `\r` -> `yes approve-builds` -> install succeeded
- piped `n\r` -> `no approve-builds` -> `IgnoredBuildsError`
`frameworks-e2e` stays on pnpm 10 because several framework generators
delegate install to their own CLI (e.g. `create-hono --install`); those
internal installs aren't routed through C3 and so the recovery path
cannot engage. Workflow comment updated to capture the constraint.
…p Hono on pnpm 11 The frameworks-e2e matrix now exercises both pnpm 10.33.0 and pnpm 11.5.1 (plus npm), so any pnpm 11 regression in C3's install path is caught in CI rather than at user-install time. Mechanism: - The frameworks-e2e job now applies the same pnpm-version override steps that the cli/workers e2e job already uses (stub package.json + pnpm/action-setup + a PATH verify), so scaffolded-project installs actually run the matrix pnpm version instead of silently inheriting the monorepo's pinned pnpm 10.x. - Artifact and job-display names now include `matrix.pm.version` so the pnpm 10 and pnpm 11 runs don't collide. Framework-level skipping: - `FrameworkTestConfig.unsupportedPmRanges` lets a framework opt out of a specific package-manager version range. `shouldRunTest` evaluates it with `semver.satisfies(coerce(version), range)`. - `hono:pages` and `hono:workers` are skipped on pnpm >=11.0.0. Hono's C3 template calls `create-hono --install`, so the dependency install runs inside the framework generator, *before* C3 reaches its own `npmInstall` recovery path. On pnpm 11 the unapproved postinstall scripts pulled in by `wrangler` (workerd, esbuild, sharp) trip `ERR_PNPM_IGNORED_BUILDS`, and that failure happens too early for C3's approve-builds prompt to engage. Until `create-hono` exposes `--no-install` (or pre-approves its own build scripts), the only honest signal is to mark Hono unsupported on pnpm 11.
…esponders; skip docusaurus on pnpm 11
CI run #27072628148 caught two real issues with the pnpm 11 frameworks
matrix:
1. **Nuxt failed on pnpm 11 even though it uses `--no-install`.**
The e2e harness used to call `proc.stdin.end()` as soon as the last
ordered `promptHandler` was consumed. For Nuxt, that handler answers
Nuxi's "Would you like to browse and install modules?" prompt, which
appears *during* `runFrameworkGenerator` — long before C3 reaches
its own `pnpm install`. By the time C3 hit `ERR_PNPM_IGNORED_BUILDS`
and surfaced the `approve-builds` confirmation, stdin was already
closed, so the EOF race in `promptOrEOF` fired immediately and the
background responder never got a chance to write `\r`.
The harness now defers closing stdin via `maybeEndStdin`: stdin only
closes once *both* the ordered prompt queue is drained *and* every
background responder has fired. C3 calls `process.exit` at the end
of its run, so leaving stdin open doesn't hold the child process
alive on the success path.
2. **Docusaurus joins Hono on the pnpm-11 skip list.**
`create-docusaurus` installs its dependencies internally as part of
`runFrameworkGenerator` (no `--no-install` flag is exposed), and on
pnpm 11 those installs trip `ERR_PNPM_IGNORED_BUILDS` on `@swc/core`
/ `core-js` before C3's recovery can engage — same shape as the
Hono case. Mark `docusaurus:pages` and `docusaurus:workers` (both
in the default and experimental lists) with
`unsupportedPmRanges: { pnpm: ">=11.0.0" }` so they're skipped on
pnpm 11 with a clear comment.
The previous attempt to keep e2e harness stdin open until background responders fired regressed pnpm 10 and npm runs. With stdin held open across framework generators spawned with `stdio: "inherit"` (docusaurus, nuxt), the child processes keep their event loops alive on the inherited FD 0 and never exit after their work is done. The result: the test harness times out waiting for C3 to make further progress. Reverting `run-c3.ts` to close stdin immediately after the last ordered `promptHandler` is consumed restores the previous (working) behaviour for those runs. That re-introduces the limitation that the e2e harness can't answer recovery-style prompts (e.g. C3's `pnpm approve-builds <pkgs>`) for framework tests whose ordered handler queue empties *before* the recovery prompt fires. Hono and Docusaurus already opt out of pnpm 11 via `unsupportedPmRanges`. Nuxt joins that list for the same reason: under pnpm 11, Nuxt's deps (notably `@parcel/watcher`) trip `ERR_PNPM_IGNORED_BUILDS` when C3 runs its own `pnpm install` after the `--no-install` generator finishes, and the harness can't answer the resulting prompt. The behaviour for real TTY users (where the interactive recovery prompt is rendered and can be typed at) is unchanged; this is purely a test-infrastructure scoping decision. The comments at the top of `backgroundResponders` now spell out the constraint explicitly so future contributors don't try to widen the matrix without addressing the underlying inherit-stdio interaction.
a734c7d to
d53022e
Compare
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
There was a problem hiding this comment.
🚩 svelte:workers (experimental) lacks unsupportedPmRanges but has promptHandlers — potential e2e fragility on pnpm 11
The experimental svelte:workers test config (test-config.ts:679-740) has a promptHandler for 'Yes looks good, finish update' but does NOT set unsupportedPmRanges: { pnpm: '>=11.0.0' }. Since it uses --no-install, the install runs through C3's npmInstall. If svelte's deps trigger ERR_PNPM_IGNORED_BUILDS, the recovery prompt would fire AFTER stdin is closed (the ordered prompt handler was already consumed). However, this likely works because writePnpmBuildApprovals pre-approves esbuild/workerd/sharp, and svelte's minimal template probably doesn't pull in additional native deps with build scripts. If svelte ever adds such deps, this test would start failing on pnpm 11 without an obvious explanation.
Was this helpful? React with 👍 or 👎 to provide feedback.
Fixes #13928. Supersedes #13967 (thanks @matingathani for filing first and identifying the shape of the fix).
This PR contains three connected changes that together fix the
pnpm create cloudflareregression on pnpm 11, and close the e2e test gap that let it ship undetected.1. Root cause
pnpm 11 flipped
strictDepBuildstotrueby default. That turns the previous warning about unapproved dependency build scripts into a fatalERR_PNPM_IGNORED_BUILDS.wranglerdepends onworkerdandesbuild, and (via miniflare) onsharp— all three need their postinstall scripts to produce platform binaries. So every C3 scaffold that installs Wrangler aborted on pnpm 11 with a red error box before the project was usable.2. C3 fix —
create-cloudflarePre-approve C3's own build scripts. C3 now writes (or minimally merges into) a
pnpm-workspace.yamlin the generated project that approves exactly the three packages C3 itself requires (workerd,esbuild,sharp). The merge logic only ever touches our three keys — any other entry pnpm or a framework generator wrote is left untouched.This deliberately does not pre-approve build scripts for packages introduced by framework generators (
@parcel/watcher,@swc/core,lmdb, etc.) — those approvals are the generator's or the user's responsibility, not C3's.Handle framework-introduced ignored builds gracefully. When pnpm still aborts the install because a framework brought in unapproved build scripts, C3 used to dump the entire pnpm transcript twice (once via the spinner stop, once via the top-level error handler) before printing a hint. The result was a giant red wall of noise on every framework that needs a native build step.
That now becomes a focused recovery path that uses one prompt mechanism for all three runtime shapes:
Parse the flagged package list out of pnpm's output (
extractIgnoredBuildPackages— handles scoped/unscoped names, strips@versionsuffixes, dedupes) instead of showing the raw transcript.Run
pnpm installunder C3's own spinner so failures stop with a shortinstall failedline, not the full captured stdout.Prompt:
backgroundResponderschannel that fires independently of the orderedpromptHandlersqueue. A default responder accepts this prompt, so every framework test inherits the behaviour without bespoke config.pnpm create cloudflare < /dev/null): previously the event loop drained and the process exited0silently. C3 now races the prompt against a stdin-endevent and converts the EOF into anIgnoredBuildsErrorwith the parsed list — same concise output as the explicit-decline path.On confirm, run
pnpm approve-builds <pkgs>(deterministic and non-interactive when packages are listed explicitly — writes topnpm-workspace.yaml#allowBuilds, the same keywritePnpmBuildApprovalsuses) and retry the install once.On a declined prompt, an EOF, a
CancelError(Ctrl-C), or a retry that still fails, throw a typedIgnoredBuildsErrorcarrying the parsed package list. The top-level handler incli.tsrenders a concise summary plus the exactpnpm approve-builds <pkgs>command to run — no more transcript dumps.Verified end-to-end against a real pnpm 11.5.1 environment running C3's actual
npmInstallpath against a project depending on@parcel/watcher:</dev/null(closed)RESULT: caught IgnoredBuildsError; packages = ["@parcel/watcher"]\r(Enter)yes approve-builds→installed via \pnpm install``n\rno approve-builds→IgnoredBuildsErrorThis closes the fast-follow #14201 in the same PR.
3. Test-gap fix —
c3-e2e.ymlThe matrix labelled C3's e2e pnpm jobs as
pnpm 10.33.0butE2E_TEST_PM_VERSIONonly relabels C3'snpm_config_user_agent— the underlying pnpm binary stays at whatever the monorepo's pinnedpackageManagersays (also 10.33.0). So the e2e suite silently exercised pnpm 10 only, and the pnpm-11 regression went undetected.Both
e2e(cli + workers) andframeworks-e2enow genuinely exercise pnpm 11.5.1 via a secondpnpm/action-setupoverlay (kept distinct from the monorepo install via a stubpackage.json+ dedicateddest), withpnpm_config_pm_on_failandpnpm_config_verify_deps_before_runset so pnpm 11 doesn't silently re-exec as the pinned pnpm 10 or trigger an implicit install at the monorepo root. A verify step fails fast ifpnpm --versionon PATH doesn't match the matrix.The
frameworks-e2ematrix now runs three combinations on Linux:pnpm@10.33.0pnpm@11.5.1npmFor frameworks whose generators do their own install (so C3's recovery path can't engage), we now express that as a per-test version constraint instead of pinning the whole job to pnpm 10:
FrameworkTestConfig.unsupportedPmRangesaccepts a per–package-manager semver range.shouldRunTestevaluates it withsemver.satisfies(coerce(version), range).hono:pagesandhono:workersare now markedunsupportedPmRanges: { pnpm: ">=11.0.0" }. Hono's C3 template callscreate-hono --install, so the install runs inside the generator before C3 reaches its ownnpmInstall. On pnpm 11 that internal install tripsERR_PNPM_IGNORED_BUILDS(Wrangler's workerd/esbuild/sharp postinstalls) and there's no upstream--no-installtoggle yet, so until that lands, Hono honestly is unsupported on pnpm 11. Other frameworks remain in scope on pnpm 11.Artifact and job-display names for
frameworks-e2enow includematrix.pm.versionto keep the pnpm 10 and pnpm 11 runs from colliding.Relationship to #13967
@matingathani's #13967 takes the same core approach (pre-approve build scripts via
pnpm-workspace.yaml). This PR diverges on two design choices:workerd/esbuild/sharp(C3's own dependencies) are auto-approved; framework-introduced build scripts go through the new prompt-and-retry flow instead. (fix(create-cloudflare): generate valid pnpm-workspace.yaml for pnpm 9/10 #13967 took a version-branched approach across pnpm 9/10/11;allowBuildswas only added in pnpm v10.26, so the v10.0–v10.25 branch would not have worked anyway.)