Skip to content

Commit 5b6abe1

Browse files
fix(core): unbreak pnpm 11 installs by acknowledging build-script deps from generators (#36302)
## Current Behavior pnpm 11 fails installs with `ERR_PNPM_IGNORED_BUILDS` when a dependency's build scripts are neither allowed nor denied. Workspaces generated by `create-nx-workspace` only allow `nx`, so any flow that pulls in a build-script dependency hard-fails: jest 30 pulls in `unrs-resolver` (via `jest-resolve`), vite pulls in `esbuild`, swc setups pull in `@swc/core`, etc. This breaks preset installs for real pnpm 11 users and is the dominant cause of the nightly E2E matrix failures (~70 of 139 jobs die in `newProject()`): https://github.com/nrwl/nx/actions/runs/28642151329 Three more nightly root causes ride along: - When a plugin throws a non-Error value, `formatAggregateCreateNodesError` crashes with `Cannot read properties of undefined (reading 'split')`, masking the real error (js-strip-types failures). - `@angular/cli@22.0.5` raised its Node floor to `^22.22.3 || ^24.15.0`, so `ng new` refuses to run on the matrix's pinned Node 22.13.0 / 24.0.0 (e2e-nx-init, e2e-angular). - e2e tests `pnpm add` plugins directly (no generator runs first), so they hit the strict build gate regardless of generator fixes. ## Expected Behavior - Generators that introduce build-script dependencies record the `allowBuilds` decision in `pnpm-workspace.yaml` before their install task runs, via a new comment-preserving `acknowledgePnpmBuildScripts` helper (exposed through `@nx/devkit/internal`): jest acknowledges `unrs-resolver`, vite/esbuild acknowledge `esbuild`, swc setups acknowledge `@swc/core`, nest acknowledges `@nestjs/core` (all `false` = skipped, matching pnpm 10 behavior since they ship prebuilt binaries or only print funding messages); cypress and detox set `true` because their install scripts are required to function. Entries the user already set are never overwritten; the helper no-ops for non-pnpm workspaces and pnpm < 11. Generated workspaces no longer preseed entries for dependencies they may never have. - Non-Error `createNodes` failures are coerced to Errors in the `AggregateCreateNodesError` constructor so the real failure always surfaces. - The e2e matrix pins Node 22.22.3 / 24.15.0, satisfying the Angular CLI floor. - e2e-created pnpm/lerna workspaces are seeded with `strictDepBuilds: false` (pnpm 10's warn-and-skip behavior) since tests install plugins without running generators first. Known gap (documented in the commit): `nx add <plugin>` installs the plugin package before its init generator runs, so a plugin whose own dependency tree carries a build-script package (e.g. `@nx/jest` → `jest-resolve` → `unrs-resolver`) still surfaces pnpm's `approve-builds` error in that flow. ## Related Issue(s) Nightly E2E matrix failure: https://github.com/nrwl/nx/actions/runs/28642151329 <!-- polygraph-session-start --> --- [View session information ↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/PR-for-pnpm-11-ERR_PNPM_IGNORED_BUILDS-fix-f71af2eb) <!-- polygraph-session-end --> --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> (cherry picked from commit e380258)
1 parent c1b9c2d commit 5b6abe1

44 files changed

Lines changed: 747 additions & 76 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ env:
1515
PNPM_HOME: ~/.pnpm
1616
# Pin corepack to the pnpm version from packageManager. Without this, corepack
1717
# falls back to "latest" in directories that have no packageManager field
18-
# (e.g. e2e temp dirs), pulling pnpm 11 and breaking install.
18+
# (e.g. e2e temp dirs) instead of the repo's pinned pnpm.
1919
COREPACK_DEFAULT_TO_LATEST: '0'
2020

2121
jobs:

.github/workflows/e2e-matrix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ env:
1515
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/.cypress
1616
# Pin corepack to the pnpm version from packageManager. Without this, corepack
1717
# falls back to "latest" in directories that have no packageManager field
18-
# (e.g. e2e temp dirs), pulling pnpm 11 and breaking install.
18+
# (e.g. e2e temp dirs) instead of the repo's pinned pnpm.
1919
COREPACK_DEFAULT_TO_LATEST: '0'
2020

2121
permissions: {}

.github/workflows/nightly/process-matrix.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,16 @@ const matrixData: MatrixData = {
7878
package_managers: ['npm', 'pnpm', 'yarn'],
7979
// TODO: re-add '26.0.0' once playwright ships the yauzl fix for node 26 extract hang.
8080
// See https://github.com/microsoft/playwright/issues/40724
81-
node_versions: ['22.13.0', '24.0.0'],
81+
// Floors track @angular/cli engines (^22.22.3 || ^24.15.0): ng new refuses older.
82+
node_versions: ['22.22.3', '24.15.0'],
8283
excluded: ['e2e-detox', 'e2e-react-native', 'e2e-expo']
8384
},
8485
// Docker is not supported on ARM-based macOS runners (no nested virtualization)
8586
// See: https://github.com/docker/setup-docker-action and https://github.com/douglascamata/setup-docker-macos-action
8687
// We may want to look into adding intel only for this docker case, at least until vm-in-vm works on latest macos
8788
// TODO: re-add '26.0.0' once playwright ships the yauzl fix for node 26 extract hang.
8889
// See https://github.com/microsoft/playwright/issues/40724
89-
{ os: 'macos-latest', os_name: 'MacOS', os_timeout: 90, package_managers: ['npm'], node_versions: ['24.0.0'], excluded: ['e2e-docker'] }
90+
{ os: 'macos-latest', os_name: 'MacOS', os_timeout: 90, package_managers: ['npm'], node_versions: ['24.15.0'], excluded: ['e2e-docker'] }
9091
// TODO (Jack): Fix Windows support as gradle fails when running nx build https://staging.nx.app/runs/LgD4vxGn8w?utm_source=pull-request&utm_medium=comment
9192
// { os: 'windows-latest', os_name: 'WinOS', os_timeout: 180, package_managers: ['npm'], node_versions: ['24.0.0'], excluded: ['e2e-detox', 'e2e-react-native', 'e2e-expo'] }
9293
]

.nx/workflows/agents.yaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ common-env-vars: &common-env-vars
77
NX_NATIVE_LOGGING: 'nx::native::db'
88
# Pin corepack to the pnpm version from packageManager. Without this, corepack
99
# falls back to "latest" in directories that have no packageManager field
10-
# (e.g. e2e temp dirs created by create-nx-workspace), pulling pnpm 11 and
11-
# breaking install. Same treatment as .github/workflows/{ci,e2e-matrix}.yml.
10+
# (e.g. e2e temp dirs created by create-nx-workspace) instead of the repo's
11+
# pinned pnpm. Same treatment as .github/workflows/{ci,e2e-matrix}.yml, which
12+
# pair it with `corepack prepare --activate` (see the init step below).
1213
COREPACK_DEFAULT_TO_LATEST: '0'
1314
# These are need for build and link validation for next.js and astro apps
1415
NEXT_PUBLIC_ASTRO_URL: 'https://master--nx-docs.netlify.app'
@@ -31,11 +32,20 @@ common-init-steps: &common-init-steps
3132
- name: Setup toolchains
3233
uses: 'nrwl/nx-cloud-workflows/v6/workflow-steps/install-mise/main.yaml'
3334

35+
# Make the repo's pinned pnpm corepack's default so e2e temp dirs (no
36+
# packageManager field) resolve it too, instead of corepack's bundled
37+
# last-known-good version.
38+
- name: Activate repo pnpm via corepack
39+
script: |
40+
corepack enable
41+
corepack prepare --activate
42+
3443
- name: Verify toolchain versions
3544
script: |
3645
echo "mise: $(mise --version)"
3746
echo "node: $(node --version)"
3847
echo "pnpm: $(pnpm --version)"
48+
echo "pnpm outside repo: $(cd $(mktemp -d) && pnpm --version)"
3949
echo "bun: $(bun --version)"
4050
echo "rust: $(rustc --version) - $(cargo --version)"
4151
echo "dotnet: $(dotnet --version)"

e2e/angular/src/module-federation-setup.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ export function setupModuleFederationTest(): ModuleFederationTestSetup {
2525
}
2626

2727
export function cleanupModuleFederationTest(
28-
setup: ModuleFederationTestSetup
28+
setup: ModuleFederationTestSetup | undefined
2929
): void {
3030
cleanupProject();
31-
process.env.NX_E2E_VERBOSE_LOGGING = setup.oldVerboseLoggingValue;
31+
// setup is undefined when setupModuleFederationTest itself failed; don't
32+
// let cleanup throw and shadow the real error.
33+
process.env.NX_E2E_VERBOSE_LOGGING = setup?.oldVerboseLoggingValue;
3234
}

e2e/gradle/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
],
2121
"dependsOn": [
2222
"@nx/nx-source:populate-local-registry-storage",
23-
"@nx/nx-source:local-registry",
23+
"@nx/nx-source:local-registry-e2e",
2424
":gradle-project-graph:gradle:publishToMavenLocal"
2525
]
2626
},
@@ -34,7 +34,7 @@
3434
],
3535
"dependsOn": [
3636
"@nx/nx-source:populate-local-registry-storage",
37-
"@nx/nx-source:local-registry",
37+
"@nx/nx-source:local-registry-e2e",
3838
":gradle-project-graph:gradle:publishToMavenLocal"
3939
]
4040
}

e2e/jest/src/jest.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
22
import {
33
cleanupProject,
44
expectJestTestsToPass,
5+
getSelectedPackageManager,
56
getStrippedEnvironmentVariables,
67
newProject,
8+
readFile,
79
runCLI,
810
runCLIAsync,
911
uniq,
@@ -25,6 +27,21 @@ describe('Jest', () => {
2527
await expectJestTestsToPass('@nx/js:lib --unitTestRunner=jest');
2628
}, 500000);
2729

30+
it('should record an allowBuilds decision for unrs-resolver on pnpm', () => {
31+
if (getSelectedPackageManager() !== 'pnpm') {
32+
return;
33+
}
34+
35+
const name = uniq('lib');
36+
runCLI(
37+
`generate @nx/js:lib ${name} --unitTestRunner=jest --no-interactive`
38+
);
39+
40+
// pnpm 11 refuses to install deps whose build scripts are neither allowed
41+
// nor denied, so the jest init generator must have recorded a decision
42+
expect(readFile('pnpm-workspace.yaml')).toContain('unrs-resolver: false');
43+
}, 300_000);
44+
2845
it('should be resilient against NODE_ENV values', async () => {
2946
const name = uniq('lib');
3047
runCLI(

e2e/lerna-smoke-tests/src/lerna-smoke-tests.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ describe('Lerna Smoke Tests', () => {
6767
result = result
6868
.replace(/.*\/node_modules\/.*\n/, '') // yarn adds "$ /node_modules/.bin/lerna run print-name" to the output
6969
.replace(/.*package-1@0.*\n/, '') // yarn output doesn't contain "> package-1@0.0.0 print-name"
70-
.replace('$ echo test-package-1', '> echo test-package-1');
70+
// npm and yarn echo the script command; pnpm 11 does not
71+
.replace(/[$>] echo test-package-1\n/, '');
7172
expect(result).toMatchInlineSnapshot(`
7273
7374
> package-1:print-name
74-
> echo test-package-1
7575
test-package-1
7676
Lerna (powered by Nx) Successfully ran target print-name for project package-1
7777
Run duration: {DURATION}

e2e/maven/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
"e2e-local": {
1010
"dependsOn": [
1111
"@nx/nx-source:populate-local-registry-storage",
12-
"@nx/nx-source:local-registry",
12+
"@nx/nx-source:local-registry-e2e",
1313
"nx-maven-plugin:install"
1414
]
1515
},
1616
"e2e-ci--**/**": {
1717
"dependsOn": [
1818
"@nx/nx-source:populate-local-registry-storage",
19-
"@nx/nx-source:local-registry",
19+
"@nx/nx-source:local-registry-e2e",
2020
"nx-maven-plugin:install"
2121
]
2222
}

e2e/release/src/custom-registries.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ describe('nx release - custom npm registries', () => {
233233
runCLI(`generate setup-verdaccio`);
234234

235235
const process = await runCommandUntil(
236-
`local-registry @proj/source --port=${verdaccioPort}`,
236+
// location=none so a killed process can't leak registry config into
237+
// ~/.npmrc; every consumer passes --registry explicitly instead
238+
`local-registry @proj/source --port=${verdaccioPort} --location none`,
237239
(output) => output.includes(`warn --- http address`)
238240
);
239241

0 commit comments

Comments
 (0)