Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions libraries/rush-lib/src/logic/base/BaseInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -883,8 +883,11 @@ ${gitLfsHookHandling}
// On pnpm@8, disable the "dedupe-peer-dependents" feature when doing a filtered CI install so that filters take effect.
args.push('--config.dedupe-peer-dependents=false');
}
} else if (experiments.usePnpmPreferFrozenLockfileForRushUpdate) {
// In workspaces, we want to avoid unnecessary lockfile churn
} else if (experiments.usePnpmPreferFrozenLockfileForRushUpdate && !onlyShrinkwrap) {
// In workspaces, we want to avoid unnecessary lockfile churn.
// Do NOT use --prefer-frozen-lockfile during the --lockfile-only phase: pnpm v10
// omits the packages:/snapshots: sections when this combination is used, producing
// a broken lockfile that fails the subsequent --frozen-lockfile install phase.
args.push('--prefer-frozen-lockfile');
} else {
// Ensure that Rush's tarball dependencies get synchronized properly with the pnpm-lock.yaml file.
Expand Down
42 changes: 42 additions & 0 deletions libraries/rush-lib/src/logic/test/BaseInstallManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,46 @@ describe('BaseInstallManager Test', () => {
);
}
});

it('usePnpmPreferFrozenLockfileForRushUpdate should not add --prefer-frozen-lockfile when onlyShrinkwrap is true', () => {
const rushJsonFile: string = path.resolve(__dirname, 'ignoreCompatibilityDb/rush3.json');
const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile);
const purgeManager: typeof PurgeManager.prototype = new PurgeManager(rushConfiguration, rushGlobalFolder);

// Enable the usePnpmPreferFrozenLockfileForRushUpdate experiment
Object.defineProperty(rushConfiguration.experimentsConfiguration, 'configuration', {
value: { usePnpmPreferFrozenLockfileForRushUpdate: true },
writable: false,
configurable: true
});

const fakeBaseInstallManager: FakeBaseInstallManager = new FakeBaseInstallManager(
rushConfiguration,
rushGlobalFolder,
purgeManager,
{ subspace: rushConfiguration.defaultSubspace } as IInstallManagerOptions
);

// When onlyShrinkwrap is true (Phase 1 of two-phase install), --prefer-frozen-lockfile must NOT be added.
// pnpm v10 omits packages:/snapshots: sections when --prefer-frozen-lockfile and --lockfile-only are
// combined, producing a broken lockfile that fails the subsequent --frozen-lockfile install phase.
const argsWithOnlyShrinkwrap: string[] = [];
fakeBaseInstallManager.pushConfigurationArgs(
argsWithOnlyShrinkwrap,
{ onlyShrinkwrap: true, pnpmFilterArgumentValues: [] } as unknown as IInstallManagerOptions,
rushConfiguration.defaultSubspace
);
expect(argsWithOnlyShrinkwrap).not.toContain('--prefer-frozen-lockfile');
expect(argsWithOnlyShrinkwrap).toContain('--lockfile-only');

// When onlyShrinkwrap is false (single-phase or Phase 2), --prefer-frozen-lockfile should be added.
const argsWithoutOnlyShrinkwrap: string[] = [];
fakeBaseInstallManager.pushConfigurationArgs(
argsWithoutOnlyShrinkwrap,
{ onlyShrinkwrap: false, pnpmFilterArgumentValues: [] } as unknown as IInstallManagerOptions,
rushConfiguration.defaultSubspace
);
expect(argsWithoutOnlyShrinkwrap).toContain('--prefer-frozen-lockfile');
expect(argsWithoutOnlyShrinkwrap).not.toContain('--lockfile-only');
});
});
Loading