Repository navigation
fix(version)!: sync every committed Cargo.lock that records a bumped crate - #674
Merged
Merged
Conversation
The #496 lock sync only ran from updateCargoFiles, so crates bumped through version.groups (grouped and ungrouped) or the per-package processor left their own entry in the committed Cargo.lock at the old version. The sync now runs inside updateCargoVersion, the one function every version-step Cargo.toml write goes through. The sync also stops shelling out to `cargo update --workspace --offline`, which exits 101 on a runner with an empty CARGO_HOME (no cached crates.io index) and needs a Rust toolchain the release job may not have. Core's new syncCargoLockVersion edits the lock text the way cargo does for a path package's version change: the entry's version line plus any "name old-version" dependency references. Registry and git entries are never touched, and a lock where the edit would change how cargo tells same-named packages apart is left alone with a warning. Publish's prepare-stage sync uses the same helper. Closes #650 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
Contributor
|
Standing release PR: #614 · v0.42.0 queued (4 packages) · open 82d 18h 14m · ✅ ready to merge Release Preview — v0.41.2 → v0.41.3
These changes will be added to the release PR (#614) when merged: Changelogmonorepo 0.41.2 → 0.41.3
After merge — predicted release
Updated automatically by ReleaseKit |
5 of 19 tasks
…rate #650 now asks that no committed Cargo.lock needs a follow-up sync PR after a release. - Locks that use a crate through a path dependency are synced too: for each bumped crate, every Cargo.lock tracked by git with a sourceless entry of that name is updated, leaving registry and git entries of the name alone. The crate's own lock is still synced even when untracked. The tracked locks are listed once per run through the git seam (new Git.lsFiles). - updateCargoVersion records each bump and VersionEngine.run syncs the locks once the strategy is done, so every strategy is covered, a lock shared by several crates is rewritten once, and every edit is worked out before the first write. - A lock that can't be synced fails the run with CargoLockSyncError (CARGO_LOCK_SYNC_ERROR, VERSION_ERROR exit family) instead of warning. A dry run works out the same edits, so it fails too, and records them as pending writes that release's flush writes with the manifests. - Publish's prepare stage syncs and stages every such lock, so the direct-commit flow commits fixture locks as well. It stays non-fatal: in the standing-PR flow it runs after the release PR has merged. - The real-cargo integration test runs `releasekit version` with no cargo on PATH and an empty CARGO_HOME for version.groups, the per-package processor and updateCargoFiles, and checks that `cargo metadata --locked` accepts every such lock unchanged. The syncCargoLockfile export of @releasekit/version (added for the integration test in #498) is removed: the sync is now per run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
Cargo orders same-name [[package]] entries by semver and a package's dependency references by the version as a plain string, so "dupe 10.0.0" sorts before "dupe 2.0.0" (checked with cargo 1.97). Rewriting a path crate's version in place left both in their old order whenever the new version passed a same-named registry or git entry, and the next cargo run would reorder the lock. setCargoLockVersion now moves the edited entry, and the run of references around each edited reference, back to cargo's order. The real-cargo byte-for-byte test bumps crate-b from 0.1.0 to 0.2.0 past git copies at 0.1.5 (entry order) and 0.10.0 (reference order). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
This was referenced Oct 22, 2026
Same sweep as #679, run on this branch so merging main resolves cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
# Conflicts: # packages/publish/src/stages/prepare.ts # packages/publish/src/utils/cargo.ts # packages/version/src/core/versionStrategies.ts
7 of 20 tasks
…an ignored lock Crates in different workspaces can share a name (every `tauri init` app is `app`), and every bump was applied to every lock with a path entry of that name, so one app's lock took another app's version. A crate's own lock still takes the crate's bump whatever version it records. In any other lock, the entry follows a bump only when something in that lock depends on it (an entry nothing depends on is that lock's own root or member, a different crate) and it is at the crate's old or new version; bumps record the manifest's pre-bump version for this. Same-named bumps to different versions that fit one entry fail the run. A crate's own lock that git ignores is no longer synced or staged: the direct-commit release's `git add` refused it and failed the release. `Git` gains `isIgnored` (`check-ignore -q`); the planner now does its own discovery, so `findCommittedCargoLockfiles` is gone. Also: version's syncCargoLockfiles returns void, the unused `git` parameters and FakeGit's trackedFiles seed are dropped, the duplicate comment in VersionEngine.run is removed, and tests pin numeric pre-release ordering and the several-path-entries refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
docs/cli.md lists the code (category `cargo-lock-sync`, exit code 8). docs/rust.md states which lock entries follow a bump and covers every reason the sync refuses a lock, not only the other-source one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
8 of 20 tasks
findCargoLockfile doesn't read an ancestor workspace's `exclude`, so a crate excluded from it (a Tauri app's src-tauri, say) got the ancestor's lock as its own, and its real lock was matched as another workspace's: its root entry, which nothing depends on, was skipped and left stale. A lock beside the manifest can only be the crate's own (or an unused stray), so it now counts as one. When the manifest records no version (`version.workspace = true`, or none), the old version now comes from the crate's own lock, so other locks are still matched on it; only without an own lock does any version match. Tests pin both, the any-version fallback, publish reporting the replaced version, and the versioned-reference match in core. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
…it codes Only other apps' own crates of the same name are told apart; a different same-named crate that a lock depends on at the bumped crate's old or new version is not, so the unsupported list says so and how to avoid it. The lock-sync error exits 8 from `release` and `standing-pr` but 1 from `releasekit version`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
findCargoLockfile took the first ancestor `[workspace]` as the crate's root without reading its `exclude`, so a crate excluded from the repo's workspace treated that workspace's lock as its own, where every bump is applied: two excluded Tauri apps named `app` failed the run as ambiguous in a root lock that has no `app`, and bumping one excluded `app` set the root workspace's own member `app` to its version. It now follows cargo: a crate whose manifest has a `[workspace]` is its own root; otherwise the nearest ancestor workspace that doesn't exclude it is (an `exclude` path prefix not overridden by a `members` one, both literal); with none, the crate is its own root. Only the root's lock is returned, which also drops the nearest-lock fallback for standalone crates (cargo never reads a non-root ancestor's lock). Checked against cargo 1.97. `exclude` is read with smol-toml, now a core dependency. The round-2 "lock beside the manifest" own-lock candidate is dropped: the new rule returns it whenever it is the crate's lock, and a member's stray lock is unused by cargo. The planner's doc now notes that any version matches when neither the manifest nor the crate's own lock records the old one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
# Conflicts: # pnpm-lock.yaml
… workspace pointer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
This was referenced Oct 22, 2026
Drop doc comments that restate a name or signature, issue numbers and history; shorten the rest to the cargo behaviour the code relies on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… crate by path Name and version can't identify a path crate: two workspaces can each have a `core` 0.1.0, and bumping one rewrote the other's lock, breaking `--locked` builds. A lock other than the crate's own now takes a bump only if its workspace reaches the crate's directory through path dependencies (target-specific, `workspace = true`, `[patch]`; dev-dependencies of its own members only), as cargo resolves them. Drops the version heuristic and `fromVersion`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tch] Cargo honours [patch] only in the workspace root's manifest, so following it from every visited manifest could rewrite an unrelated same-named entry. And a member outside its workspace's directory points back with package.workspace; without following it, its real lock went stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also document that `[patch]` in `.cargo/config.toml` isn't followed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cargo drops a patch the bumped version no longer satisfies, so following it could write a lock cargo never would. Support is tracked in #706. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
goosewobbler
pushed a commit
that referenced
this pull request
Oct 23, 2026
…e-commands Keeps #674's CARGO_LOCK_SYNC_ERROR in the version group of the expanded exit-code table. With version exiting by its code's family, docs/rust.md no longer says the standalone command exits 1 for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HQN3v9Z4f1mu3xK4Z7K44e
13 of 20 tasks
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.
Description
Releases now update every committed
Cargo.lockthat records a bumped crate, so no follow-up sync PR is needed. It needs no Rust toolchain or network, and a lock that can't be updated fails the run instead of warning.Checked against webdriverio/desktop-mobile#748, the last manual sync PR: replaying its bumps changes the same four locks, byte for byte.
Every bump path syncs. Every version-step
Cargo.tomlwrite goes throughupdateCargoVersion, which now records the bump;VersionEngine.runsyncs the locks once at the end. Groups, the per-package processor andupdateCargoFilesare all covered, and a new bump path can't skip it. Every edit is worked out before any lock is written.No cargo. The lock text is edited the way cargo would edit it: the entry's
versionline, any"<name> <old version>"references, and cargo's ordering when the lock also holds another version of the crate. Byte-identical to cargo for v3 and v4 locks.Which locks. Every git-tracked
Cargo.lock, plus each crate's own lock (at its workspace root, by cargo's rule) unless git ignores it. Crate names repeat across workspaces (everytauri initapp isapp, and0.1.0is cargo's default version), so another workspace's lock follows a bump only if that workspace reaches the crate's directory throughpathdependencies, as cargo resolves them: target-specific,workspace = true, and dev-dependencies of its own members.[patch]isn't followed yet (#706): cargo drops a patch the bumped version no longer satisfies.Failures. An ambiguous lock (several path entries with the name, or another source holding the crate at the old or new version), or one in a format newer than v4, throws
CARGO_LOCK_SYNC_ERROR, exit familyVERSION_ERROR, in dry runs too.docs/rust.mdcovers each case.Where the edits land.
git add -Astages them.releasekit version: writes them.Breaking changes
@releasekit/version'ssyncCargoLockfileexport; only the integration test used it.findCargoLockfilefollows cargo's workspace-root rule, includingexcludeandpackage.workspacepointers, and no longer returns an ancestor lock that isn't the root.planCargoLockUpdatesand its types.@releasekit/gitgainslsFilesandisIgnored.createSync/Single/AsyncStrategycalled directly, withoutVersionEngine, no longer sync locks. No in-repo caller does that.CARGO_LOCK_SYNC_ERRORsits next to where #616 expandsCODE_TO_EXIT; whichever lands second keeps both.Related Issue(s)
Closes #650. Follow-up to #496.
Type of Change
feat— New featurefix— Bug fixdocs— Documentation changerefactor— Code refactoringtest— Adding or updating testschore— Maintenance, dependencies, toolingScope
@releasekit/version@releasekit/notes@releasekit/publish@releasekit/config@releasekit/coreChecklist
pnpm lint)pnpm test)pnpm typecheck)Testing Notes
test/integration/cargo-lock-sync.spec.tsruns the built CLI with no cargo and an emptyCARGO_HOME, then checks the result with real cargo. Every lock passescargo metadata --lockedunchanged, for groups, the per-package processor andupdateCargoFiles; the edit matchescargo update --workspacebyte for byte, reordering included; a same-named app's lock is left alone. CI's Integration Tests job installs a Rust toolchain for it.VersionEngine.runin a real git work tree, and cover the lock edit, the workspace-root rule and the planner in core.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge with the documented limitation for patch-only dependencies.Summary
The PR updates related
Cargo.lockfiles without running Cargo and fails the version step when a selected lock cannot be edited safely.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Write Cargo.toml versions] --> B[Record bumped crates] B --> C[Find related Cargo.lock files] C --> D[Plan all lock edits] D --> E{Can selected locks be edited?} E -->|Yes| F[Write locks or record dry-run edits] E -->|No| G[Fail the version step]Reviews (5) · Last reviewed commit: "fix(core): stop following [patch] when s..." · Reviewed by Greptile