feat(cli): TRAC-1536 Reconcile Catalyst dependency versions on upgrade - #3192
Merged
chanceaclark merged 1 commit intoAug 26, 2026
Conversation
🦋 Changeset detectedLatest commit: be22892 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
Bundle Size ReportComparing against baseline from No bundle size changes detected. |
chanceaclark
force-pushed
the
chancellorclark/ltrac-1630-explore-dependency-migration-during-catalyst-upgrades
branch
from
August 25, 2026 16:29
5b3dda6 to
05cf895
Compare
Contributor
Unlighthouse Performance Comparison — VercelComparing PR preview deployment Unlighthouse scores vs production Unlighthouse scores. Summary ScoreAggregate score across all categories as reported by Unlighthouse.
Category Scores
Core Web Vitals
|
chanceaclark
marked this pull request as ready for review
August 25, 2026 17:13
chanceaclark
force-pushed
the
chancellorclark/ltrac-1630-explore-dependency-migration-during-catalyst-upgrades
branch
from
August 25, 2026 21:53
05cf895 to
f41f183
Compare
chanceaclark
force-pushed
the
chancellorclark/ltrac-1630-explore-dependency-migration-during-catalyst-upgrades
branch
from
August 26, 2026 15:32
f41f183 to
f7bdff6
Compare
Every Catalyst tag ships core/package.json with `workspace:` specifiers, because @bigcommerce/catalyst-core is private and pnpm's publish-time rewrite never runs on the tagged tree. Both sides of the 3-way merge therefore agreed on those lines, so a project's @bigcommerce/catalyst* dependency versions never moved through an upgrade: flat projects kept whatever `catalyst create` resolved at scaffold time, and monorepo- structure projects kept `workspace:^` indefinitely. Read the versions each tag actually published from that tag's own tarball and pin the downloaded base and target trees to them, so the existing merge carries the bump like any other change, or conflicts when the merchant pinned deliberately. A dependency the project still holds as `workspace:` is skipped on both sides, since normalizing one side alone would manufacture a conflict on something that works fine as-is; those are surfaced instead as an opt-in migration prompt. Also add two advisories the merge cannot cover: a reminder to reinstall when the upgrade changed package.json, and a notice when the pinned @bigcommerce/catalyst is behind the published version, which it always will be eventually since `catalyst create` pins it exactly and the dependency does not exist upstream for a merge to touch. Refs TRAC-1536 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chanceaclark
force-pushed
the
chancellorclark/ltrac-1630-explore-dependency-migration-during-catalyst-upgrades
branch
from
August 26, 2026 15:57
f7bdff6 to
be22892
Compare
chanceaclark
deleted the
chancellorclark/ltrac-1630-explore-dependency-migration-during-catalyst-upgrades
branch
August 26, 2026 17:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Jira: TRAC-1536
What/Why?
catalyst upgradenever moved@bigcommerce/catalyst*dependency versions, for either project layout. Chris hit this upgrading a monorepo-structure project from 1.6.x to 1.7.x.It isn't a bug in the merge.
@bigcommerce/catalyst-coreis private, so it's tagged but never published, which means pnpm's publish-time rewrite never runs on the tagged tree and every tag still saysworkspace:^incore/package.json. Both sides of the 3-way merge agree on those lines, so the merge correctly leaves the project's versions alone — forever. Flat projects keep whatevercatalyst createresolved at scaffold time; monorepo projects keepworkspace:^.Caret ranges pick up minor and patch drift on the next install anyway, so this is mostly pre-emptive. What it guards against is a major bump of a Catalyst package, where a project created on 1.6.x would sit on the wrong major with nothing telling it so.
How it works
Each tag's tarball already contains
packages/*/package.json, so the versions that shipped with a given release are readable straight from it.downloadCorenow grabs them on the way past and caches them alongside the tree. That's the only accurate source: these packages version independently of core, so the npmlatestlookup used elsewhere answers for today rather than for the release being downloaded.Instead of patching up the merge result afterwards, the downloaded base and target manifests are pinned to their own versions before merging. The existing 3-way merge then carries the bump like any other change, and conflicts if the project pinned something deliberately. Nothing downstream needed to change.
Two decisions worth a look:
package.jsonthat still has conflict markers elsewhere.workspace:are left alone on both sides. Changing just one side would invent a conflict on something that works fine, and staying put is a reasonable choice. Those get an opt-in prompt instead:--yesaccepts it, and--dry-runor a non-interactive run just prints the versions. The swap is additionally gated onpackage.jsonhaving merged cleanly, since a text rewrite of a file full of conflict markers could land inside a hunk nobody has reviewed yet.Also here
Two nudges the merge can't cover on its own:
package.json. The command has never run an install or mentioned one, so the lockfile was quietly left behind.@bigcommerce/catalystis out of date.catalyst createpins it exactly, and it doesn't exist upstream, so no merge can move it — the CLI doing the upgrading goes stale permanently. It stays quiet when the range already admits the published version, so a caret range doesn't nag.Follow-up, kept separate
The create path has the same root cause: it resolves workspace deps against npm
latestrather than the release being extracted, socatalyst create --gh-ref <old tag>pulls in today's versions. Different code path, tracked on its own ticket.Testing
pnpm test,pnpm lint, andpnpm typecheckall pass inpackages/catalyst(660 tests).New coverage:
workspace:/ package absent from a release / missing manifest), the migration itself, the out-of-date CLI check against a newregistry.npmjs.orgmock, and the lockfile fallback.test.eachtable walking the prompt gate — accepted, declined,--yes, non-interactive — plus--dry-runasserting the project is untouched.@bigcommerce/catalyst-client1.0.1 to 1.0.2 bump, so that's asserted end to end against live tarballs on both merge engines.Verified by hand
Both layouts, against real tarballs, using the built CLI. Setup is self-contained — no store credentials needed, since it stands in for what
catalyst createwrites:1. Flat project, dependency already on a real range. The bump rides the merge:
The resulting
package.json— the dependency merged cleanly to^1.0.2even though the manifest conflicts elsewhere:That remaining conflict is pre-existing and unrelated:
catalyst createdropsprivate, so theversion/privatehunk always diverges from upstream. Worth noting because it's the common case, which is why the install reminder is chained onto the resolution step (git commit && pnpm install) rather than printed on its own — running an install against a manifest full of conflict markers just fails.2. Old monorepo layout (
core/nested),--dry-run. Reports the workspace references and writes nothing:3. Same project,
catalyst upgrade 1.7.0 --yes. Clean merge, so the migration applies:4. Safety gate. Running the same
--yesagainst a variant whosepackage.jsoncame back conflicted correctly refuses to rewrite it:5. No false nagging. A project on
"@bigcommerce/catalyst": "^1.2.0"with 1.2.0 published gets no CLI advisory at all, since the range already admits it. The exact pin in scenario 3 (1.1.0) does get one.Bugs found while doing the above
Both fixed here:
semver.minVersionthrows rather than returning null on anything that isn't a semver range, so a tarball path or git URL dependency would have crashed the check.package.jsonstill has conflict markers — scenario 1's exact situation — which killed the run before the summary printed. Detection now reads the lockfile first, which is why scenarios 1 and 3 correctly saypnpm installand notnpm install.Migration
None. Nothing moved and nothing breaks; this is additive to an existing command.
Refs TRAC-1536