Skip to content

[rush] Make version consistency checks aware of PNPM catalogs - #6140

Draft
Bharat Middha (bmiddha) wants to merge 1 commit into
bmiddha-catalog-aware-preferred-versionsfrom
bmiddha-catalog-aware-version-checks
Draft

Bharat Middha (bmiddha) wants to merge 1 commit into
bmiddha-catalog-aware-preferred-versionsfrom
bmiddha-catalog-aware-version-checks

Conversation

@bmiddha

Copy link
Copy Markdown
Member

Summary

Part of the pnpm-catalog stack. It is stacked on top of #6139 (#6139 → #6138 → #6136 → #6133 → #6131). Review only the top commit.

Rush's version consistency checks compared raw version specifiers, so a project using "typescript": "catalog:" was reported as inconsistent with another project using "typescript": "~5.8.2", or with the same version in preferredVersions. It was even reported as inconsistent with a project using "typescript": "catalog:default". With ensureConsistentVersions: true, this made rush install/rush update/rush check/rush version fail as soon as a repo adopted PNPM catalogs.

Version consistency checks now compare a catalog reference by the version that it resolves to in the catalogs of the project's subspace. This also changes --make-consistent: it no longer rewrites other projects' catalog: references to literal versions.

Details

Resolution (VersionMismatchFinder)

  • getMismatches() computes the subspace's effective catalogs once, with getEffectivePnpmCatalogs(): the subspace's pnpm-config.json, else common/config/rush/pnpm-config.json. These are exactly what install writes. Each VersionMismatchFinderProject receives them.
  • #analyze() resolves each catalog spec with tryResolvePnpmCatalogReference() before grouping:
    • catalog: and catalog:default resolve to the same entry.
    • Named catalogs such as catalog:react17 resolve to their own entry.
    • Two specs that resolve to the same range are consistent with each other, with literals, and with preferredVersions.
  • Unresolvable references (a missing catalog or entry) keep a canonical form of the raw spec (catalog: / catalog:<name>) as their "version". So they surface as their own version instead of crashing or being silently dropped, and catalog: vs catalog:default still collapse together.
  • allowedAlternativeVersions matches the resolved version exactly like a literal. For backward compatibility it also matches the raw catalog spec (e.g. "catalog:react17").
  • Catalogs are per subspace and don't depend on --variant. Every entry point already routes through getMismatches(), so they all pick this up:
    • rush check (including --subspace/--variant/--json)
    • the ensureConsistentVersions gate in install/update
    • rush version
    • rush add/rush upgrade-interactive --make-consistent

Reporting

  • rush check annotates versions that come from a catalog, and any consumer whose raw spec differs from the printed version:
    typescript
      ~5.8.2 (catalog:)
       - A (catalog:)
       - B (catalog:default)
       - D
      ~5.7.0
       - C
    lodash
      catalog: (unresolved PNPM catalog reference)
    
  • rush check --json:
    • version reports the resolved version. That is what PNPM installs, and it groups naturally with literals and preferredVersions.
    • A new optional catalogReferences: [{ project, versionSpecifier }] field is added to a version entry only when some consumer used a catalog.
    • The JSON is backward compatible: there are no removed or renamed fields, and the output is unchanged for repos without catalogs.

--make-consistent guard (PackageJsonUpdater)

  • Before this change, #getUpdates() rewrote every other consumer to the literal version, including projects that reference the package through catalog:, which silently detached them from the catalog.
  • Now, a consumer whose updated dependency (dev, else regular, matching updateProject()) is a catalog reference is skipped. If its resolved version differs, Rush prints a deduped warning:
    Not updating "<pkg>" in "<project>" because it uses the PNPM catalog reference "catalog:" (^7.5.4). Rush does not replace catalog references when making versions consistent; update the catalog in pnpm-config.json instead.
  • The remaining inconsistency is then reported by the normal consistency check.
  • Not in scope here: an explicit rush add/rush upgrade-interactive still overwrites the target project's own catalog: spec. A later layer of this stack covers catalog-aware rush add/upgrade-interactive.

Other

  • Added formatPnpmCatalogSpecifier() to PnpmCatalogReference.ts.
  • ProjectChangeAnalyzer now uses tryParsePnpmCatalogName() to detect catalog references. This trims the catalog name the way PNPM does, so catalog: tools maps to tools.
  • Performance: one catalog lookup per dependency during analysis, which is negligible.

How it was tested

New unit tests:

  • VersionMismatchFinder.test.ts covers:
    • catalog: vs the same literal
    • catalog: vs catalog:default
    • catalog: vs a different literal
    • catalog: vs preferredVersions
    • a named catalog + allowedAlternativeVersions (resolved and raw spec)
    • per-project/per-subspace catalogs
    • unresolvable references
    • exact print() and printAsJson() output
    • getMismatches() on the repoWithCatalogs and repoWithSubspacesCommonCatalogs fixtures (default, fallback, and own-config subspaces)
  • PackageJsonUpdater.test.ts (new):
    • --make-consistent keeps catalog: in other projects and warns once per project.
    • It updates literal consumers without warning when the catalog already matches.
  • PnpmCatalogReference.test.ts covers formatPnpmCatalogSpecifier().
  • ProjectChangeAnalyzer.test.ts covers catalog name normalization.

Commands run on the final commit (664d3dea26):

  • node <dev rush>/start-dev.js build --to @microsoft/rush-lib: passed, with no warnings.
  • cd libraries/rush-lib && node_modules/.bin/heft test --test-path-pattern "VersionMismatchFinder|PnpmCatalogReference|ProjectChangeAnalyzer|PackageJsonUpdater": passed (all 90 tests).
  • cd libraries/rush-lib && node_modules/.bin/heft test: 1976 passed and 1 failed (1979 total). The only failure is the known pre-existing TarExecutable › kills tar when its signal aborts while it runs, which is unrelated.
  • VersionMismatchFinder.getMismatches() on this repo's own rush.json, using the built lib-commonjs: 0 mismatches in both the default and build-tests-subspace subspaces, so there is no regression for a repo without catalogs.

Impacted documentation

Compare PNPM catalog references by the version that they resolve to in the
catalogs of the project's subspace, so "catalog:" no longer mismatches the
same literal version or preferredVersions in "rush check",
ensureConsistentVersions, and "rush version". Unresolvable references are
reported as their own version. "rush check" annotates catalog versions and
"rush check --json" adds an optional "catalogReferences" field.

"--make-consistent" no longer rewrites other projects' catalog references
to literal versions; it warns instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@bmiddha
Bharat Middha (bmiddha) force-pushed the bmiddha-catalog-aware-version-checks branch from eff340a to f2519b0 Compare October 15, 2026 01:47

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.

1 participant