Skip to content

[rush] Copy the temp lockfile when pnpm changes it during rush update - #6119

Open
Bruno Paulino (brunojppb) wants to merge 9 commits into
microsoft:mainfrom
brunojppb:brunojppb/6117/pnpm12-lockfile-sync
Open

Bruno Paulino (brunojppb) wants to merge 9 commits into
microsoft:mainfrom
brunojppb:brunojppb/6117/pnpm12-lockfile-sync

Conversation

@brunojppb

@brunojppb Bruno Paulino (brunojppb) commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #6117

With pnpm 12, rush update can leave common/config/rush/pnpm-lock.yaml out of date. pnpm writes a new peer version to common/temp/pnpm-lock.yaml, but Rush does not copy that file back. A new clone then runs rush install and links packages that the committed lockfile does not record.

Details

The problem

rush update gives pnpm --no-prefer-frozen-lockfile, so pnpm resolves the dependencies again. pnpm 11 keeps the peer versions that the lockfile records. pnpm 12 recomputes them, so it can pick a different peer version when no package.json file changed.

Rush copies the temp lockfile back only when its own check finds a package.json change. Here that check finds none, so Rush drops the new lockfile:

temp lockfile:      graphql@16.13.1
committed lockfile: graphql@17.0.0-alpha.7

The fix

After pnpm runs, rush update compares the temp lockfile with the committed lockfile. If they differ, Rush copies the temp lockfile back.

  • The new method BaseInstallManager.shouldCopyTempShrinkwrapAsync holds this decision.
  • It applies to pnpm 12 and later. npm, yarn, and pnpm 11 keep today's behavior.
  • rush install does not change.

Before pnpm runs, Rush copies the committed lockfile into common/temp. So the two files differ only when pnpm changed the temp lockfile, and an unchanged lockfile causes no copy.

How to test this with a repro case

# In the rushstack repo, on this branch, with Node 22:
node common/scripts/install-run-rush.js install
node common/scripts/install-run-rush.js build --to @microsoft/rush

# In https://github.com/brunojppb/rush-pnpm-12-lockfile-issue, with Node 24:
REPRO_RUSH_START=/path/to/rushstack/apps/rush/lib-commonjs/start-dev.js ./repro.sh
  • pnpm versions: pnpm 11.24.0 and 11.28.5 keep the old peer version. pnpm 12.4.0 to 12.10.1 change it.

Impacted documentation

The usePnpmFrozenLockfileForRushInstall docs (https://rushjs.io/pages/configs/experiments_json/) now recommend it for pnpm 12.

@brunojppb

Copy link
Copy Markdown
Contributor Author

Hi Ian Clanton-Thuon (@iclanton) 👋
As you've reviewed some of my PRs in the past, would you be able to have a look at this PR?

Appreciate any guidance! 🙌

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Default pnpm 12 installs can still recompute peer variants and diverge from the committed lockfile.

1 open finding
What changed in this PR

Updates Rush to preserve pnpm 12+ lockfile changes made during rush update.

Changes:

  • Compares temporary and committed lockfiles after pnpm runs.
  • Adds coverage for pnpm versions, variants, missing files, and line endings.
  • Documents frozen lockfiles for pnpm 12 installs.
File Description
BaseInstallManager.ts Adds lockfile comparison and copy decision.
BaseInstallManager.test.ts Tests lockfile synchronization behavior.
rush-pnpm12.json Adds pnpm 12 test configuration.
rush-pnpm12-prerelease.json Adds pnpm 12 prerelease fixture.
rush-pnpm11.json Adds pnpm 11 compatibility fixture.
rush-npm.json Adds npm exclusion fixture.
ExperimentsConfiguration.ts Documents pnpm 12 frozen installs.
experiments.schema.json Updates experiment schema guidance.
assets/​rush-init/​.../​experiments.json Updates generated configuration guidance.
brunojppb-6117-pnpm12-lockfile-sync_2026-10-07-13-00.json Records the Rush change.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

/**
* By default, 'rush install' passes --no-prefer-frozen-lockfile to 'pnpm install'.
* Set this option to true to pass '--frozen-lockfile' instead for faster installs.
* With pnpm 12 and later, set this option to true so that 'rush install' installs the versions that the lockfile records.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reproduced this locally. With pnpm 12.10.1 and a lockfile that still records an old peer version, the default rush install exited 0. But node_modules linked graphql@16.13.1, and the committed lockfile recorded graphql@17.0.0-alpha.7.

In e902154, rush install with pnpm 12 or later now compares the temp lockfile with the committed lockfile after pnpm runs. If they differ, the install fails and tells the user to run rush update. With the same lockfile, rush install now exits 1.

I did not make --frozen-lockfile the default. That changes rush install for every pnpm 12 user. In this case it also keeps the old peer version, which does not satisfy the peer range ^17.0.0. usePnpmFrozenLockfileForRushInstall stays optional, and the docs now describe it that way. The check does not run when that setting is on, because pnpm then installs what the lockfile records.

Filtered installs (--to, --only) do not change a correct lockfile on pnpm 12, so they pass the check. The new tests are in BaseInstallManager.test.ts, under validateTempShrinkwrapAfterInstallAsync.


Reply through Claude Code, reviewed and approved by Bruno Paulino (@brunojppb)

With pnpm 12, "rush install" gives pnpm "--no-prefer-frozen-lockfile".
pnpm can then recompute peer versions and link versions that the committed
lockfile does not record. "rush install" finished without an error.

Now "rush install" compares the temp lockfile with the committed lockfile
after pnpm runs. If they differ, it fails and tells the user to run
"rush update". The check does not run when usePnpmFrozenLockfileForRushInstall
is on, because pnpm then installs what the lockfile records.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The newly copied temporary lockfile bypasses post-update lockfile policy validation.

2 open findings

🧠 Review effort: Balanced

Comment thread libraries/rush-lib/src/logic/base/BaseInstallManager.ts
… rush update

"rush update" ran validateShrinkwrapAfterUpdate on the committed lockfile,
then copied the temp lockfile over it. So the disallowInsecureSha1 policy
checked the old lockfile, and the new lockfile went in without a check.
If no committed lockfile existed, the check did not run at all.

Now "rush update" checks the temp lockfile, which is the file that it copies.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The lockfile synchronization and validation paths are consistent with the intended behavior and have thorough targeted coverage.

1 open finding
1 resolved since last review

🧠 Review effort: Balanced

…ger tests

Two tests expect an error message. The manager wrote it to the console, so
it went to stderr. Rush reports stderr output as a warning, and CI fails
"rush retest" on a warning. The tests now use a StringBufferTerminalProvider
and check the error text there.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation and coverage are coherent; only the stale PR-description statement should be corrected.

1 open finding
Previously missed (1)

In code that hasn't changed since last review

Low severity Update PR description to document pnpm 12+ validation failures

libraries/​rush-lib/​src/​logic/​base/​BaseInstallManager.ts:272

This new validation call changes rush install for pnpm 12+: it can now fail after pnpm rewrites the temp lockfile, while the PR description still states that “rush install does not change.” Since the new failure is intentional and is also documented in the change file, please update that PR-description bullet so reviewers and release consumers see the actual scope.

🧠 Review effort: Balanced

/**
* By default, 'rush install' passes --no-prefer-frozen-lockfile to 'pnpm install'.
* Set this option to true to pass '--frozen-lockfile' instead for faster installs.
* With pnpm 12 and later, 'rush install' fails if pnpm changes the lockfile. Set this option to true to install the versions that the lockfile records instead.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if this should be enabled by default as a separate experiment flag. seems like the correct behavior to enable.
copying the common/temp/pnpm-lock.yaml file over is also what --recheck does. Does this make --recheck redundant?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

[rush] With pnpm 12, rush update does not update pnpm-lock.yaml after pnpm changes a peer variant

3 participants