From 460ea0f091dcd91b05b90ac2489d4ab74b3b88be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 03:06:34 +0000 Subject: [PATCH 1/4] feat: add globalAllowBuilds support for pnpm 11+, deprecate globalNeverBuiltDependencies --- .../common/config/rush/pnpm-config.json | 32 ++++++++++ .../logic/installManager/InstallHelpers.ts | 27 +++++++- .../installManager/WorkspaceInstallManager.ts | 19 ++++++ .../logic/pnpm/PnpmOptionsConfiguration.ts | 32 ++++++++++ .../src/logic/pnpm/PnpmWorkspaceFile.ts | 21 +++++++ .../test/PnpmOptionsConfiguration.test.ts | 23 +++++++ .../logic/pnpm/test/PnpmWorkspaceFile.test.ts | 63 +++++++++++++++++++ .../PnpmWorkspaceFile.test.ts.snap | 36 +++++++++++ .../pnpm-config-allowBuilds-conflict.json | 6 ++ .../jsonFiles/pnpm-config-allowBuilds.json | 8 +++ .../__snapshots__/InstallHelpers.test.ts.snap | 3 - .../src/schemas/pnpm-config.schema.json | 9 ++- 12 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds-conflict.json create mode 100644 libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json delete mode 100644 libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json index 9d5f764bc67..ac7863a390b 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json @@ -344,6 +344,8 @@ }, /** + * @deprecated Use `globalAllowBuilds` instead. + * * The `globalNeverBuiltDependencies` setting suppresses the `preinstall`, `install`, and `postinstall` * lifecycle events for the specified NPM dependencies. This is useful for scripts with poor practices * such as downloading large binaries without retries or attempting to invoke OS tools such as @@ -352,12 +354,42 @@ * The settings are copied into the `pnpm.neverBuiltDependencies` field of the `common/temp/package.json` * file that is generated by Rush during installation. * + * NOTE: This setting is not supported in pnpm 11.0.0+. Use `globalAllowBuilds` instead. + * * PNPM documentation: https://pnpm.io/package_json#pnpmneverbuiltdependencies */ "globalNeverBuiltDependencies": [ /*[LINE "HYPOTHETICAL"]*/ "fsevents" ], + /** + * The `globalAllowBuilds` setting is a map of package names to booleans that controls which + * dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` + * lifecycle events). A value of `true` explicitly permits a package to run build scripts; + * a value of `false` explicitly blocks it. Packages not listed inherit the default behavior. + * + * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, + * and is the only way to control build permissions in pnpm 11+. The settings are written to the + * `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during + * installation. + * + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#allowbuilds + * + * Example: + * "globalAllowBuilds": { + * "esbuild": true, + * "playwright": true, + * "core-js": false + * } + */ + /*[BEGIN "HYPOTHETICAL"]*/ + "globalAllowBuilds": { + "esbuild": true + }, + /*[END "HYPOTHETICAL"]*/ + /** * The `globalOnlyBuiltDependencies` setting specifies which dependencies are permitted to run * build scripts (`preinstall`, `install`, and `postinstall` lifecycle events). This is the inverse diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index fd0ba621639..ca10c89f3aa 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -77,7 +77,32 @@ export class InstallHelpers { } if (pnpmOptions.globalNeverBuiltDependencies) { - commonPackageJson.pnpm.neverBuiltDependencies = pnpmOptions.globalNeverBuiltDependencies; + if ( + rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.gte(rushConfiguration.rushConfigurationJson.pnpmVersion, '11.0.0') + ) { + terminal.writeWarningLine( + Colorize.yellow( + `The "globalNeverBuiltDependencies" field in ` + + `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename} ` + + `is deprecated and is not supported by pnpm ${rushConfiguration.rushConfigurationJson.pnpmVersion}. ` + + 'Migrate to "globalAllowBuilds" instead. ' + + `For example, replace "globalNeverBuiltDependencies": ["pkg"] ` + + `with "globalAllowBuilds": { "pkg": false }.` + ) + ); + } else { + terminal.writeWarningLine( + Colorize.yellow( + `The "globalNeverBuiltDependencies" field in ` + + `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename} ` + + 'is deprecated. Migrate to "globalAllowBuilds" instead. ' + + `For example, replace "globalNeverBuiltDependencies": ["pkg"] ` + + `with "globalAllowBuilds": { "pkg": false }.` + ) + ); + commonPackageJson.pnpm!.neverBuiltDependencies = pnpmOptions.globalNeverBuiltDependencies; + } } if (pnpmOptions.globalOnlyBuiltDependencies) { diff --git a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts index 9526f0e8d59..180599a4a87 100644 --- a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts @@ -469,6 +469,25 @@ export class WorkspaceInstallManager extends BaseInstallManager { workspaceFile.setCatalogs(catalogs); } + // Set allowBuilds in the workspace file if specified (requires pnpm 11.0.0+) + if (pnpmOptions.globalAllowBuilds) { + if ( + this.rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '11.0.0') + ) { + this._terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${this.rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "globalAllowBuilds" field in ` + + `${this.rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove this field or upgrade to pnpm 11.0.0 or newer.' + ) + ); + } + + workspaceFile.setAllowBuilds(pnpmOptions.globalAllowBuilds); + } + // Save the generated workspace file. Don't update the file timestamp unless the content has changed, // since "rush install" will consider this timestamp workspaceFile.save(workspaceFile.workspaceFilename, { onlyIfChanged: true }); diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index 22d4bfaa45f..2305f496f69 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -121,6 +121,11 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { */ globalPackageExtensions?: Record; /** + * {@inheritDoc PnpmOptionsConfiguration.globalAllowBuilds} + */ + globalAllowBuilds?: Record; + /** + * @deprecated Use {@link IPnpmOptionsJson.globalAllowBuilds} instead. * {@inheritDoc PnpmOptionsConfiguration.globalNeverBuiltDependencies} */ globalNeverBuiltDependencies?: string[]; @@ -425,6 +430,23 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly globalPackageExtensions: Record | undefined; + /** + * The `globalAllowBuilds` setting is a map of package names to booleans that controls which + * dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` + * lifecycle events). A value of `true` explicitly permits a package to run build scripts; + * a value of `false` explicitly blocks it. Packages not listed inherit the default behavior. + * + * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, + * and is the only way to control build permissions in pnpm 11+. The settings are written to the + * `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during + * installation. + * + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#allowbuilds + */ + public readonly globalAllowBuilds: Record | undefined; + /** * The `globalNeverBuiltDependencies` setting suppresses the `preinstall`, `install`, and `postinstall` * lifecycle events for the specified NPM dependencies. This is useful for scripts with poor practices @@ -434,6 +456,8 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration * The settings are copied into the `pnpm.neverBuiltDependencies` field of the `common/temp/package.json` * file that is generated by Rush during installation. * + * @deprecated Use {@link PnpmOptionsConfiguration.globalAllowBuilds} instead. + * * PNPM documentation: https://pnpm.io/package_json#pnpmneverbuiltdependencies */ public readonly globalNeverBuiltDependencies: string[] | undefined; @@ -554,6 +578,14 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration this.globalOverrides = json.globalOverrides; this.globalPeerDependencyRules = json.globalPeerDependencyRules; this.globalPackageExtensions = json.globalPackageExtensions; + + if (json.globalNeverBuiltDependencies !== undefined && json.globalAllowBuilds !== undefined) { + throw new Error( + 'The "globalNeverBuiltDependencies" setting is deprecated. Use "globalAllowBuilds" instead.' + + ' Both settings cannot be specified together in pnpm-config.json.' + ); + } + this.globalAllowBuilds = json.globalAllowBuilds; this.globalNeverBuiltDependencies = json.globalNeverBuiltDependencies; this.globalOnlyBuiltDependencies = json.globalOnlyBuiltDependencies; this.globalIgnoredOptionalDependencies = json.globalIgnoredOptionalDependencies; diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts index 99f007c1003..14612901a57 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts @@ -31,6 +31,8 @@ interface IPnpmWorkspaceYaml { packages: string[]; /** Catalog definitions for centralized version management */ catalogs?: Record>; + /** Per-package build permission map. True permits build scripts, false blocks them. (pnpm 11+) */ + allowBuilds?: Record; } export class PnpmWorkspaceFile extends BaseWorkspaceFile { @@ -41,6 +43,7 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { private _workspacePackages: Set; private _catalogs: Record> | undefined; + private _allowBuilds: Record | undefined; /** * The PNPM workspace file is used to specify the location of workspaces relative to the root @@ -54,6 +57,7 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { // If we need to support manual customization, that should be an additional parameter for "base file" this._workspacePackages = new Set(); this._catalogs = undefined; + this._allowBuilds = undefined; } /** @@ -64,6 +68,19 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { this._catalogs = catalogs; } + /** + * Sets the `allowBuilds` map for the workspace. Each key is a package name and each value + * is `true` (permit build scripts) or `false` (block build scripts). + * + * @remarks + * This writes to the `allowBuilds` field in `pnpm-workspace.yaml`, which requires pnpm 11.0.0+. + * + * @param allowBuilds - A map of package name to boolean permission flag + */ + public setAllowBuilds(allowBuilds: Record | undefined): void { + this._allowBuilds = allowBuilds; + } + /** @override */ public addPackage(packagePath: string): void { // Ensure the path is relative to the pnpm-workspace.yaml file @@ -89,6 +106,10 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { workspaceYaml.catalogs = this._catalogs; } + if (this._allowBuilds && Object.keys(this._allowBuilds).length > 0) { + workspaceYaml.allowBuilds = this._allowBuilds; + } + return yamlModule.dump(workspaceYaml, PNPM_SHRINKWRAP_YAML_FORMAT); } } diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts index 531c4e78c77..8e9293bd470 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts @@ -87,6 +87,29 @@ describe(PnpmOptionsConfiguration.name, () => { ]); }); + it('loads allowBuilds', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-allowBuilds.json`, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalAllowBuilds)).toEqual({ + esbuild: true, + playwright: true, + 'core-js': false, + '@swc/core': true + }); + }); + + it('throws if both globalNeverBuiltDependencies and globalAllowBuilds are specified', () => { + expect(() => + PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-allowBuilds-conflict.json`, + fakeCommonTempFolder + ) + ).toThrow(/Both settings cannot be specified together/); + }); + it('loads minimumReleaseAgeMinutes', () => { const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge.json`, diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts index d8cdb149a88..64558dd9a1c 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts @@ -180,4 +180,67 @@ describe(PnpmWorkspaceFile.name, () => { expect(content).toMatchSnapshot(); }); }); + + describe('allowBuilds functionality', () => { + it('generates workspace file with allowBuilds', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setAllowBuilds({ + esbuild: true, + playwright: true, + 'core-js': false + }); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('handles empty allowBuilds object', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setAllowBuilds({}); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('handles undefined allowBuilds', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setAllowBuilds(undefined); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('generates workspace file with both catalogs and allowBuilds', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setCatalogs({ + default: { + react: '^18.0.0' + } + }); + + workspaceFile.setAllowBuilds({ + esbuild: true, + 'core-js': false + }); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + }); }); diff --git a/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap index 97c62b0db79..87c09674778 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap +++ b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap @@ -65,3 +65,39 @@ exports[`PnpmWorkspaceFile catalog functionality handles undefined catalog 1`] = - projects/app1 " `; + +exports[`PnpmWorkspaceFile allowBuilds functionality generates workspace file with allowBuilds 1`] = ` +"allowBuilds: + core-js: false + esbuild: true + playwright: true +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile allowBuilds functionality generates workspace file with both catalogs and allowBuilds 1`] = ` +"allowBuilds: + core-js: false + esbuild: true +catalogs: + default: + react: ^18.0.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile allowBuilds functionality handles empty allowBuilds object 1`] = ` +"packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile allowBuilds functionality handles undefined allowBuilds 1`] = ` +"packages: + - projects/app1 +" +`; + + diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds-conflict.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds-conflict.json new file mode 100644 index 00000000000..76c8b404d47 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds-conflict.json @@ -0,0 +1,6 @@ +{ + "globalNeverBuiltDependencies": ["fsevents"], + "globalAllowBuilds": { + "esbuild": true + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json new file mode 100644 index 00000000000..2d2074f67ac --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json @@ -0,0 +1,8 @@ +{ + "globalAllowBuilds": { + "esbuild": true, + "playwright": true, + "core-js": false, + "@swc/core": true + } +} diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap deleted file mode 100644 index 080d68c7baf..00000000000 --- a/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`InstallHelpers generateCommonPackageJson generates correct package json with pnpm configurations: Terminal Output 1`] = `Array []`; diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index 6bf53443cec..de8822a516d 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -141,8 +141,15 @@ } }, + "globalAllowBuilds": { + "description": "The `globalAllowBuilds` setting is a map of package names to booleans that controls which dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` lifecycle events). A value of `true` explicitly permits a package to run build scripts; a value of `false` explicitly blocks it. Packages not listed inherit the default behavior.\n\nThis is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, and is the only way to control build permissions in pnpm 11+. The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during installation.\n\n(SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#allowbuilds", + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, "globalNeverBuiltDependencies": { - "description": "This field allows to ignore the builds of specific dependencies. The \"preinstall\", \"install\", and \"postinstall\" scripts of the listed packages will not be executed during installation.", + "description": "@deprecated - Use `globalAllowBuilds` instead.\n\nThis field allows to ignore the builds of specific dependencies. The \"preinstall\", \"install\", and \"postinstall\" scripts of the listed packages will not be executed during installation.", "type": "array", "items": { "description": "Specify package name of the dependency", From c079fff3df7868417fcfa22a5787c3312195c744 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 03:08:50 +0000 Subject: [PATCH 2/4] fixup: improve deprecation warning clarity for pnpm 11+ globalNeverBuiltDependencies --- libraries/rush-lib/src/logic/installManager/InstallHelpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index ca10c89f3aa..81f65019a08 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -85,7 +85,8 @@ export class InstallHelpers { Colorize.yellow( `The "globalNeverBuiltDependencies" field in ` + `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename} ` + - `is deprecated and is not supported by pnpm ${rushConfiguration.rushConfigurationJson.pnpmVersion}. ` + + `is deprecated and will be ignored by pnpm ${rushConfiguration.rushConfigurationJson.pnpmVersion} ` + + `(pnpm 11+ no longer reads build settings from package.json). ` + 'Migrate to "globalAllowBuilds" instead. ' + `For example, replace "globalNeverBuiltDependencies": ["pkg"] ` + `with "globalAllowBuilds": { "pkg": false }.` From b9c7df8d443e807716be1e56c620fcd5f9fecd6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 01:40:18 +0000 Subject: [PATCH 3/4] feat: support pnpm strictDepBuilds, allowBuilds (10.26.0+), and dangerouslyAllowAllBuilds settings - Add globalStrictDepBuilds (pnpm 10.3.0+) to PnpmOptionsConfiguration and PnpmWorkspaceFile - Add globalDangerouslyAllowAllBuilds (pnpm 10.9.0+) to PnpmOptionsConfiguration and PnpmWorkspaceFile - Update globalAllowBuilds version requirement from pnpm 11.0.0 to 10.26.0 - Add strictDepBuilds/dangerouslyAllowAllBuilds handling in WorkspaceInstallManager - Update pnpm-config.schema.json with new fields - Add test fixtures and tests --- .../installManager/WorkspaceInstallManager.ts | 44 ++++++++++++- .../logic/pnpm/PnpmOptionsConfiguration.ts | 51 +++++++++++++-- .../src/logic/pnpm/PnpmWorkspaceFile.ts | 53 ++++++++++++++- .../test/PnpmOptionsConfiguration.test.ts | 18 +++++ .../logic/pnpm/test/PnpmWorkspaceFile.test.ts | 65 +++++++++++++++++++ ...pnpm-config-dangerouslyAllowAllBuilds.json | 3 + .../pnpm-config-strictDepBuilds.json | 3 + .../src/schemas/pnpm-config.schema.json | 12 +++- 8 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-dangerouslyAllowAllBuilds.json create mode 100644 libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-strictDepBuilds.json diff --git a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts index 180599a4a87..bd59666a122 100644 --- a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts @@ -469,18 +469,37 @@ export class WorkspaceInstallManager extends BaseInstallManager { workspaceFile.setCatalogs(catalogs); } - // Set allowBuilds in the workspace file if specified (requires pnpm 11.0.0+) + // Set strictDepBuilds in the workspace file if specified (requires pnpm 10.3.0+) + if (pnpmOptions.globalStrictDepBuilds !== undefined) { + if ( + this.rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '10.3.0') + ) { + this._terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${this.rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "globalStrictDepBuilds" field in ` + + `${this.rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove this field or upgrade to pnpm 10.3.0 or newer.' + ) + ); + } + + workspaceFile.setStrictDepBuilds(pnpmOptions.globalStrictDepBuilds); + } + + // Set allowBuilds in the workspace file if specified (requires pnpm 10.26.0+) if (pnpmOptions.globalAllowBuilds) { if ( this.rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && - semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '11.0.0') + semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '10.26.0') ) { this._terminal.writeWarningLine( Colorize.yellow( `Your version of pnpm (${this.rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + `doesn't support the "globalAllowBuilds" field in ` + `${this.rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + - 'Remove this field or upgrade to pnpm 11.0.0 or newer.' + 'Remove this field or upgrade to pnpm 10.26.0 or newer.' ) ); } @@ -488,6 +507,25 @@ export class WorkspaceInstallManager extends BaseInstallManager { workspaceFile.setAllowBuilds(pnpmOptions.globalAllowBuilds); } + // Set dangerouslyAllowAllBuilds in the workspace file if specified (requires pnpm 10.9.0+) + if (pnpmOptions.globalDangerouslyAllowAllBuilds !== undefined) { + if ( + this.rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '10.9.0') + ) { + this._terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${this.rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "globalDangerouslyAllowAllBuilds" field in ` + + `${this.rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove this field or upgrade to pnpm 10.9.0 or newer.' + ) + ); + } + + workspaceFile.setDangerouslyAllowAllBuilds(pnpmOptions.globalDangerouslyAllowAllBuilds); + } + // Save the generated workspace file. Don't update the file timestamp unless the content has changed, // since "rush install" will consider this timestamp workspaceFile.save(workspaceFile.workspaceFilename, { onlyIfChanged: true }); diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index 2305f496f69..266ca35cfc3 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -120,10 +120,18 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * {@inheritDoc PnpmOptionsConfiguration.globalPackageExtensions} */ globalPackageExtensions?: Record; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalStrictDepBuilds} + */ + globalStrictDepBuilds?: boolean; /** * {@inheritDoc PnpmOptionsConfiguration.globalAllowBuilds} */ globalAllowBuilds?: Record; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalDangerouslyAllowAllBuilds} + */ + globalDangerouslyAllowAllBuilds?: boolean; /** * @deprecated Use {@link IPnpmOptionsJson.globalAllowBuilds} instead. * {@inheritDoc PnpmOptionsConfiguration.globalNeverBuiltDependencies} @@ -430,23 +438,54 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly globalPackageExtensions: Record | undefined; + /** + * The `globalStrictDepBuilds` setting causes the installation to fail with a non-zero exit code if + * any dependencies have unreviewed build scripts (i.e., scripts not explicitly listed in + * `globalAllowBuilds`). This helps enforce that all package build permissions are intentionally + * reviewed and approved. The setting maps to the `strictDepBuilds` field of the + * `pnpm-workspace.yaml` file generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.3.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#strictdepbuilds + */ + public readonly globalStrictDepBuilds: boolean | undefined; + /** * The `globalAllowBuilds` setting is a map of package names to booleans that controls which * dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` * lifecycle events). A value of `true` explicitly permits a package to run build scripts; * a value of `false` explicitly blocks it. Packages not listed inherit the default behavior. * - * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, - * and is the only way to control build permissions in pnpm 11+. The settings are written to the - * `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during - * installation. + * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`. + * The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that + * is generated by Rush during installation. * - * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + * (SUPPORTED ONLY IN PNPM 10.26.0 AND NEWER) * * PNPM documentation: https://pnpm.io/settings#allowbuilds */ public readonly globalAllowBuilds: Record | undefined; + /** + * The `globalDangerouslyAllowAllBuilds` setting, when `true`, allows all build scripts + * (`preinstall`, `install`, `postinstall`) from all dependencies to run automatically without + * requiring explicit approval in `globalAllowBuilds`. The setting maps to the + * `dangerouslyAllowAllBuilds` field of the `pnpm-workspace.yaml` file generated by Rush during + * installation. + * + * @remarks + * **Use with caution.** This permits every dependency—including transitive ones—to execute + * install scripts, both now and in the future. Future dependency updates or package compromises + * could introduce malicious code. For maximum safety, explicitly review and allow builds using + * `globalAllowBuilds` instead. + * + * (SUPPORTED ONLY IN PNPM 10.9.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#dangerouslyallowallbuilds + */ + public readonly globalDangerouslyAllowAllBuilds: boolean | undefined; + /** * The `globalNeverBuiltDependencies` setting suppresses the `preinstall`, `install`, and `postinstall` * lifecycle events for the specified NPM dependencies. This is useful for scripts with poor practices @@ -585,7 +624,9 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration ' Both settings cannot be specified together in pnpm-config.json.' ); } + this.globalStrictDepBuilds = json.globalStrictDepBuilds; this.globalAllowBuilds = json.globalAllowBuilds; + this.globalDangerouslyAllowAllBuilds = json.globalDangerouslyAllowAllBuilds; this.globalNeverBuiltDependencies = json.globalNeverBuiltDependencies; this.globalOnlyBuiltDependencies = json.globalOnlyBuiltDependencies; this.globalIgnoredOptionalDependencies = json.globalIgnoredOptionalDependencies; diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts index 14612901a57..ed0ed6f2920 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts @@ -31,8 +31,18 @@ interface IPnpmWorkspaceYaml { packages: string[]; /** Catalog definitions for centralized version management */ catalogs?: Record>; - /** Per-package build permission map. True permits build scripts, false blocks them. (pnpm 11+) */ + /** Per-package build permission map. True permits build scripts, false blocks them. (pnpm 10.26.0+) */ allowBuilds?: Record; + /** + * When true, installation exits with non-zero if any dependencies have unreviewed build scripts. + * (pnpm 10.3.0+) + */ + strictDepBuilds?: boolean; + /** + * When true, all build scripts from dependencies run automatically without requiring approval. + * (pnpm 10.9.0+) + */ + dangerouslyAllowAllBuilds?: boolean; } export class PnpmWorkspaceFile extends BaseWorkspaceFile { @@ -44,6 +54,8 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { private _workspacePackages: Set; private _catalogs: Record> | undefined; private _allowBuilds: Record | undefined; + private _strictDepBuilds: boolean | undefined; + private _dangerouslyAllowAllBuilds: boolean | undefined; /** * The PNPM workspace file is used to specify the location of workspaces relative to the root @@ -58,6 +70,8 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { this._workspacePackages = new Set(); this._catalogs = undefined; this._allowBuilds = undefined; + this._strictDepBuilds = undefined; + this._dangerouslyAllowAllBuilds = undefined; } /** @@ -73,7 +87,7 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { * is `true` (permit build scripts) or `false` (block build scripts). * * @remarks - * This writes to the `allowBuilds` field in `pnpm-workspace.yaml`, which requires pnpm 11.0.0+. + * This writes to the `allowBuilds` field in `pnpm-workspace.yaml`, which requires pnpm 10.26.0+. * * @param allowBuilds - A map of package name to boolean permission flag */ @@ -81,6 +95,33 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { this._allowBuilds = allowBuilds; } + /** + * Sets the `strictDepBuilds` flag for the workspace. When `true`, installation exits with a + * non-zero exit code if any dependencies have unreviewed build scripts. + * + * @remarks + * This writes to the `strictDepBuilds` field in `pnpm-workspace.yaml`, which requires pnpm 10.3.0+. + * + * @param strictDepBuilds - Whether to enforce strict build script review + */ + public setStrictDepBuilds(strictDepBuilds: boolean | undefined): void { + this._strictDepBuilds = strictDepBuilds; + } + + /** + * Sets the `dangerouslyAllowAllBuilds` flag for the workspace. When `true`, all build scripts + * from all dependencies run automatically without requiring approval. + * + * @remarks + * This writes to the `dangerouslyAllowAllBuilds` field in `pnpm-workspace.yaml`, which requires + * pnpm 10.9.0+. + * + * @param dangerouslyAllowAllBuilds - Whether to allow all build scripts unconditionally + */ + public setDangerouslyAllowAllBuilds(dangerouslyAllowAllBuilds: boolean | undefined): void { + this._dangerouslyAllowAllBuilds = dangerouslyAllowAllBuilds; + } + /** @override */ public addPackage(packagePath: string): void { // Ensure the path is relative to the pnpm-workspace.yaml file @@ -110,6 +151,14 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { workspaceYaml.allowBuilds = this._allowBuilds; } + if (this._strictDepBuilds !== undefined) { + workspaceYaml.strictDepBuilds = this._strictDepBuilds; + } + + if (this._dangerouslyAllowAllBuilds !== undefined) { + workspaceYaml.dangerouslyAllowAllBuilds = this._dangerouslyAllowAllBuilds; + } + return yamlModule.dump(workspaceYaml, PNPM_SHRINKWRAP_YAML_FORMAT); } } diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts index 8e9293bd470..c34683bde66 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts @@ -87,6 +87,15 @@ describe(PnpmOptionsConfiguration.name, () => { ]); }); + it('loads strictDepBuilds', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-strictDepBuilds.json`, + fakeCommonTempFolder + ); + + expect(pnpmConfiguration.globalStrictDepBuilds).toBe(true); + }); + it('loads allowBuilds', () => { const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( `${__dirname}/jsonFiles/pnpm-config-allowBuilds.json`, @@ -110,6 +119,15 @@ describe(PnpmOptionsConfiguration.name, () => { ).toThrow(/Both settings cannot be specified together/); }); + it('loads dangerouslyAllowAllBuilds', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-dangerouslyAllowAllBuilds.json`, + fakeCommonTempFolder + ); + + expect(pnpmConfiguration.globalDangerouslyAllowAllBuilds).toBe(true); + }); + it('loads minimumReleaseAgeMinutes', () => { const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge.json`, diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts index 64558dd9a1c..dd226520513 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts @@ -243,4 +243,69 @@ describe(PnpmWorkspaceFile.name, () => { expect(content).toMatchSnapshot(); }); }); + + describe('strictDepBuilds functionality', () => { + it('generates workspace file with strictDepBuilds enabled', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setStrictDepBuilds(true); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('generates workspace file with strictDepBuilds disabled', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setStrictDepBuilds(false); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('does not include strictDepBuilds when undefined', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setStrictDepBuilds(undefined); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + }); + + describe('dangerouslyAllowAllBuilds functionality', () => { + it('generates workspace file with dangerouslyAllowAllBuilds enabled', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setDangerouslyAllowAllBuilds(true); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + + it('generates workspace file with all build-related settings combined', () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(path.join(projectsDir, 'app1')); + + workspaceFile.setStrictDepBuilds(true); + workspaceFile.setAllowBuilds({ esbuild: true, 'core-js': false }); + + workspaceFile.save(workspaceFilePath, { onlyIfChanged: true }); + + const content: string = FileSystem.readFile(workspaceFilePath); + expect(content).toMatchSnapshot(); + }); + }); }); diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-dangerouslyAllowAllBuilds.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-dangerouslyAllowAllBuilds.json new file mode 100644 index 00000000000..e025552fd21 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-dangerouslyAllowAllBuilds.json @@ -0,0 +1,3 @@ +{ + "globalDangerouslyAllowAllBuilds": true +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-strictDepBuilds.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-strictDepBuilds.json new file mode 100644 index 00000000000..93c814f79c2 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-strictDepBuilds.json @@ -0,0 +1,3 @@ +{ + "globalStrictDepBuilds": true +} diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index de8822a516d..88b313515ea 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -141,13 +141,23 @@ } }, + "globalStrictDepBuilds": { + "description": "When `globalStrictDepBuilds` is enabled, the installation will exit with a non-zero exit code if any dependencies have unreviewed build scripts (i.e., scripts not explicitly listed in `globalAllowBuilds`). This helps enforce that all package build permissions are intentionally reviewed and approved. The setting maps to the `strictDepBuilds` field of the `pnpm-workspace.yaml` file generated by Rush during installation.\n\n(SUPPORTED ONLY IN PNPM 10.3.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#strictdepbuilds", + "type": "boolean" + }, + "globalAllowBuilds": { - "description": "The `globalAllowBuilds` setting is a map of package names to booleans that controls which dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` lifecycle events). A value of `true` explicitly permits a package to run build scripts; a value of `false` explicitly blocks it. Packages not listed inherit the default behavior.\n\nThis is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, and is the only way to control build permissions in pnpm 11+. The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during installation.\n\n(SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#allowbuilds", + "description": "The `globalAllowBuilds` setting is a map of package names to booleans that controls which dependencies are permitted to run build scripts (`preinstall`, `install`, `postinstall` lifecycle events). A value of `true` explicitly permits a package to run build scripts; a value of `false` explicitly blocks it. Packages not listed inherit the default behavior.\n\nThis is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`. The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during installation.\n\n(SUPPORTED ONLY IN PNPM 10.26.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#allowbuilds", "type": "object", "additionalProperties": { "type": "boolean" } }, + + "globalDangerouslyAllowAllBuilds": { + "description": "If set to `true`, all build scripts (`preinstall`, `install`, `postinstall`) from all dependencies will run automatically without requiring explicit approval via `globalAllowBuilds`. The setting maps to the `dangerouslyAllowAllBuilds` field of the `pnpm-workspace.yaml` file generated by Rush during installation.\n\n**WARNING:** This allows all dependencies—including transitive ones—to run install scripts, both now and in the future. Future updates may introduce new, untrusted dependencies, or existing packages may add malicious scripts. For maximum safety, use `globalAllowBuilds` to explicitly review and allow builds.\n\n(SUPPORTED ONLY IN PNPM 10.9.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#dangerouslyallowallbuilds", + "type": "boolean" + }, "globalNeverBuiltDependencies": { "description": "@deprecated - Use `globalAllowBuilds` instead.\n\nThis field allows to ignore the builds of specific dependencies. The \"preinstall\", \"install\", and \"postinstall\" scripts of the listed packages will not be executed during installation.", "type": "array", From d678f071a2ad9f99770232dbfa7c28040cdad815 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 01:42:45 +0000 Subject: [PATCH 4/4] fix: update pnpm-config template with correct version and new settings --- .../common/config/rush/pnpm-config.json | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json index ac7863a390b..ec3ac1a55a5 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json @@ -368,12 +368,11 @@ * lifecycle events). A value of `true` explicitly permits a package to run build scripts; * a value of `false` explicitly blocks it. Packages not listed inherit the default behavior. * - * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`, - * and is the only way to control build permissions in pnpm 11+. The settings are written to the - * `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during - * installation. + * This is the replacement for `globalNeverBuiltDependencies` and `globalOnlyBuiltDependencies`. + * The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that + * is generated by Rush during installation. * - * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + * (SUPPORTED ONLY IN PNPM 10.26.0 AND NEWER) * * PNPM documentation: https://pnpm.io/settings#allowbuilds * @@ -390,6 +389,36 @@ }, /*[END "HYPOTHETICAL"]*/ + /** + * When `globalStrictDepBuilds` is enabled, the installation will exit with a non-zero exit code + * if any dependencies have unreviewed build scripts (i.e., scripts not explicitly listed in + * `globalAllowBuilds`). This helps enforce that all package build permissions are intentionally + * reviewed and approved. The setting maps to the `strictDepBuilds` field of the + * `pnpm-workspace.yaml` file generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.3.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#strictdepbuilds + */ + /*[LINE "HYPOTHETICAL"]*/ "globalStrictDepBuilds": false, + + /** + * If set to `true`, all build scripts (`preinstall`, `install`, `postinstall`) from all + * dependencies will run automatically without requiring explicit approval via `globalAllowBuilds`. + * The setting maps to the `dangerouslyAllowAllBuilds` field of the `pnpm-workspace.yaml` file + * generated by Rush during installation. + * + * WARNING: This allows all dependencies—including transitive ones—to run install scripts, both + * now and in the future. Future updates may introduce new, untrusted dependencies, or existing + * packages may add malicious scripts. For maximum safety, use `globalAllowBuilds` to explicitly + * review and allow builds. + * + * (SUPPORTED ONLY IN PNPM 10.9.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#dangerouslyallowallbuilds + */ + /*[LINE "HYPOTHETICAL"]*/ "globalDangerouslyAllowAllBuilds": false, + /** * The `globalOnlyBuiltDependencies` setting specifies which dependencies are permitted to run * build scripts (`preinstall`, `install`, and `postinstall` lifecycle events). This is the inverse