diff --git a/common/changes/@microsoft/rush/fix-pnpm-minimum-release-age_2026-05-08-18-41.json b/common/changes/@microsoft/rush/fix-pnpm-minimum-release-age_2026-05-08-18-41.json new file mode 100644 index 00000000000..ccb653c3ce0 --- /dev/null +++ b/common/changes/@microsoft/rush/fix-pnpm-minimum-release-age_2026-05-08-18-41.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "Fix minimumReleaseAge and minimumReleaseAgeExclude in pnpm-config.json being silently ignored because they were written to package.json instead of .npmrc", + "type": "none", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush", + "email": "will.porter@workos.com" +} \ No newline at end of file diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts index a7016129ae6..052c182e589 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts @@ -53,7 +53,7 @@ import { SetupPackageRegistry } from '../setup/SetupPackageRegistry'; import { PnpmfileConfiguration } from '../pnpm/PnpmfileConfiguration'; import type { IInstallManagerOptions } from './BaseInstallManagerTypes'; import { isVariableSetInNpmrcFile } from '../../utilities/npmrcUtilities'; -import type { PnpmResolutionMode } from '../pnpm/PnpmOptionsConfiguration'; +import type { PnpmOptionsConfiguration, PnpmResolutionMode } from '../pnpm/PnpmOptionsConfiguration'; import { SubspacePnpmfileConfiguration } from '../pnpm/SubspacePnpmfileConfiguration'; import type { Subspace } from '../../api/Subspace'; import { ProjectImpactGraphGenerator } from '../ProjectImpactGraphGenerator'; @@ -542,6 +542,53 @@ export abstract class BaseInstallManager { ); } + // Build lines to append to the generated .npmrc so they take precedence over user-committed values. + // pnpm does not read minimumReleaseAge/minimumReleaseAgeExclude from package.json, so we inject + // them here as minimum-release-age / minimum-release-age-exclude .npmrc settings instead. + const pnpmNpmrcAppendLines: string[] = []; + if (this.rushConfiguration.isPnpm) { + const pnpmOptions: PnpmOptionsConfiguration = + subspace.getPnpmOptions() ?? this.rushConfiguration.pnpmOptions; + + if (pnpmOptions.minimumReleaseAgeMinutes !== undefined || pnpmOptions.minimumReleaseAgeExclude) { + if ( + this.rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(this.rushConfiguration.rushConfigurationJson.pnpmVersion, '10.16.0') + ) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${this.rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "minimumReleaseAgeMinutes" or "minimumReleaseAgeExclude" fields in ` + + `${this.rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove these fields or upgrade to pnpm 10.16.0 or newer.' + ) + ); + } + + if (pnpmOptions.minimumReleaseAgeMinutes !== undefined) { + if ( + isVariableSetInNpmrcFile( + subspace.getSubspaceConfigFolderPath(), + 'minimum-release-age', + this.rushConfiguration.isPnpm + ) + ) { + terminal.writeWarningLine( + `Warning: PNPM's minimum-release-age is specified in both .npmrc and pnpm-config.json. ` + + `The value in pnpm-config.json will take precedence.` + ); + } + pnpmNpmrcAppendLines.push(`minimum-release-age=${pnpmOptions.minimumReleaseAgeMinutes}`); + } + + if (pnpmOptions.minimumReleaseAgeExclude) { + for (const packageName of pnpmOptions.minimumReleaseAgeExclude) { + pnpmNpmrcAppendLines.push(`minimum-release-age-exclude[]=${packageName}`); + } + } + } + } + // Also copy down the committed .npmrc file, if there is one // "common\config\rush\.npmrc" --> "common\temp\.npmrc" // Also ensure that we remove any old one that may be hanging around @@ -549,6 +596,7 @@ export abstract class BaseInstallManager { sourceNpmrcFolder: subspace.getSubspaceConfigFolderPath(), targetNpmrcFolder: subspace.getSubspaceTempFolderPath(), linesToPrepend: extraNpmrcLines, + linesToAppend: pnpmNpmrcAppendLines.length > 0 ? pnpmNpmrcAppendLines : undefined, createIfMissing: this.rushConfiguration.subspacesFeatureEnabled, supportEnvVarFallbackSyntax: this.rushConfiguration.isPnpm }); diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index fd0ba621639..ddbf7704898 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -35,8 +35,6 @@ interface ICommonPackageJson extends IPackageJson { ignoredOptionalDependencies?: typeof PnpmOptionsConfiguration.prototype.globalIgnoredOptionalDependencies; allowedDeprecatedVersions?: typeof PnpmOptionsConfiguration.prototype.globalAllowedDeprecatedVersions; patchedDependencies?: typeof PnpmOptionsConfiguration.prototype.globalPatchedDependencies; - minimumReleaseAge?: typeof PnpmOptionsConfiguration.prototype.minimumReleaseAgeMinutes; - minimumReleaseAgeExclude?: typeof PnpmOptionsConfiguration.prototype.minimumReleaseAgeExclude; trustPolicy?: typeof PnpmOptionsConfiguration.prototype.trustPolicy; trustPolicyExclude?: typeof PnpmOptionsConfiguration.prototype.trustPolicyExclude; trustPolicyIgnoreAfter?: typeof PnpmOptionsConfiguration.prototype.trustPolicyIgnoreAfterMinutes; @@ -124,31 +122,6 @@ export class InstallHelpers { commonPackageJson.pnpm.patchedDependencies = pnpmOptions.globalPatchedDependencies; } - if (pnpmOptions.minimumReleaseAgeMinutes !== undefined || pnpmOptions.minimumReleaseAgeExclude) { - if ( - rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && - semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '10.16.0') - ) { - terminal.writeWarningLine( - Colorize.yellow( - `Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + - `doesn't support the "minimumReleaseAgeMinutes" or "minimumReleaseAgeExclude" fields in ` + - `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + - 'Remove these fields or upgrade to pnpm 10.16.0 or newer.' - ) - ); - } - - if (pnpmOptions.minimumReleaseAgeMinutes !== undefined) { - // NOTE: the pnpm setting is `minimumReleaseAge`, but the Rush setting is `minimumReleaseAgeMinutes` - commonPackageJson.pnpm.minimumReleaseAge = pnpmOptions.minimumReleaseAgeMinutes; - } - - if (pnpmOptions.minimumReleaseAgeExclude) { - commonPackageJson.pnpm.minimumReleaseAgeExclude = pnpmOptions.minimumReleaseAgeExclude; - } - } - if (pnpmOptions.trustPolicy !== undefined) { if ( rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined &&