fix(lockfile): preserve URL and prefer sha256 when merging platform info#7923
fix(lockfile): preserve URL and prefer sha256 when merging platform info#7923
Conversation
When merging platform info during lockfile updates (e.g., from `mise upgrade`), the merge logic now properly: - Preserves existing URLs when new platform info doesn't have one - Prefers sha256 checksums over blake3 (sha256 comes from official releases and is more portable/verifiable) Previously, using `or_insert` would not merge platform info when both new and existing entries had the same platform key. This caused URLs to be lost and locally-generated blake3 checksums to replace sha256 checksums from the lockfile. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary of ChangesHello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request resolves an issue in Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug in the lockfile merging logic where platform URLs and sha256 checksums were being lost during mise upgrade operations. The fix introduces proper merging behavior that preserves URLs and prefers sha256 checksums over blake3.
Changes:
- Added
merge_withmethod toPlatformInfothat properly merges platform information by preferring sha256 checksums and preserving URLs - Updated
merge_tool_entries_with_envto useand_modifywith the new merge method instead of simpleor_insert - Added e2e test to verify URLs and sha256 checksums are preserved during lockfile updates
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/lockfile.rs | Implements merge_with method for PlatformInfo and updates merge logic to use it |
| e2e/cli/test_lock_platform_merge | Adds e2e test validating URL and checksum preservation during lockfile merging |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Merge this PlatformInfo with another, preserving important data. | ||
| /// - Prefers sha256 checksums over blake3 (more portable/verifiable) | ||
| /// - Preserves URL if missing in self | ||
| /// - Preserves url_api if missing in self |
There was a problem hiding this comment.
The method documentation should clarify which parameter takes precedence. Based on the implementation, self is the new/incoming data and other is the existing data, but this isn't obvious from the doc comment. Consider updating the doc comment to specify: 'Merge self (new) with other (existing), preserving important data from both.'
| /// Merge this PlatformInfo with another, preserving important data. | |
| /// - Prefers sha256 checksums over blake3 (more portable/verifiable) | |
| /// - Preserves URL if missing in self | |
| /// - Preserves url_api if missing in self | |
| /// Merge `self` (new/incoming) with `other` (existing), preserving important data from both. | |
| /// - Prefers sha256 checksums over blake3 (more portable/verifiable) | |
| /// - Prefers values from `self` when present, otherwise falls back to `other` | |
| /// - Preserves URL and url_api from `other` if they are missing in `self` |
| .0 | ||
| .platforms | ||
| .entry(platform) | ||
| .and_modify(|existing| *existing = info.merge_with(existing)) |
There was a problem hiding this comment.
The parameter order in info.merge_with(existing) is potentially confusing. Here info is the new data and existing is the old, but in the merge_with method, self receives the new data while other receives the existing. Consider renaming the parameters in merge_with to new and existing for clarity, or swapping the call to existing.merge_with(&info) to make the precedence more intuitive.
| .and_modify(|existing| *existing = info.merge_with(existing)) | |
| .and_modify(|existing| *existing = existing.merge_with(&info)) |
| .0 | ||
| .platforms | ||
| .entry(platform.clone()) | ||
| .and_modify(|existing| *existing = existing.merge_with(info)) |
There was a problem hiding this comment.
The merge order here is reversed compared to line 602. At line 602, new data (info) is merged with existing (info.merge_with(existing)), but here existing is merged with new (existing.merge_with(info)). This inconsistency means the two merge sites will have different precedence rules. Both calls should use the same order to ensure consistent behavior - likely info.merge_with(existing) for both.
There was a problem hiding this comment.
Code Review
This pull request fixes an issue where lockfile platform information was being overwritten instead of merged, causing loss of URLs and sha256 checksums. The changes introduce a merge_with method to PlatformInfo to handle this merging logic correctly, preferring sha256 checksums and preserving existing URLs. The implementation looks solid and is accompanied by a new e2e test that verifies the fix.
I have a couple of suggestions to improve the new test's robustness and simplify some of the new logic for better maintainability.
| assert_contains "cat mise.lock" 'sha256:' | ||
|
|
||
| # Verify URL is preserved | ||
| assert_contains "cat mise.lock" 'url = "https://github.com/jqlang/jq/releases' |
There was a problem hiding this comment.
The assertion for the URL is a bit loose. It could be made more specific to match the full URL, similar to the assertion on line 35. This will make the test more robust against unintended changes.
assert_contains "cat mise.lock" 'url = "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-amd64"'
| let checksum = match (&self.checksum, &other.checksum) { | ||
| (Some(self_cs), Some(other_cs)) => { | ||
| let self_is_sha256 = self_cs.starts_with("sha256:"); | ||
| let other_is_sha256 = other_cs.starts_with("sha256:"); | ||
| match (self_is_sha256, other_is_sha256) { | ||
| (true, _) => Some(self_cs.clone()), | ||
| (false, true) => Some(other_cs.clone()), | ||
| (false, false) => Some(self_cs.clone()), // both blake3, use self | ||
| } | ||
| } | ||
| (Some(cs), None) | (None, Some(cs)) => Some(cs.clone()), | ||
| (None, None) => None, | ||
| }; |
There was a problem hiding this comment.
The logic for selecting the checksum is correct, but it could be simplified for better readability and maintainability. You can use or_else and a simple if condition to achieve the same result more concisely.
let checksum = if let (Some(self_cs), Some(other_cs)) = (&self.checksum, &other.checksum) {
if !self_cs.starts_with("sha256:") && other_cs.starts_with("sha256:") {
Some(other_cs.clone())
} else {
Some(self_cs.clone())
}
} else {
self.checksum.clone().or_else(|| other.checksum.clone())
};
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.12 x -- echo |
20.2 ± 0.3 | 19.5 | 22.6 | 1.00 |
mise x -- echo |
20.8 ± 0.7 | 19.6 | 25.7 | 1.03 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.12 env |
19.7 ± 0.7 | 18.7 | 25.6 | 1.01 ± 0.05 |
mise env |
19.5 ± 0.6 | 18.6 | 21.9 | 1.00 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.12 hook-env |
19.7 ± 0.6 | 19.0 | 28.8 | 1.00 |
mise hook-env |
19.8 ± 0.4 | 19.1 | 23.7 | 1.01 ± 0.04 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.12 ls |
17.4 ± 0.3 | 16.8 | 18.5 | 1.00 |
mise ls |
17.8 ± 0.5 | 17.0 | 20.1 | 1.02 ± 0.03 |
xtasks/test/perf
| Command | mise-2026.1.12 | mise | Variance |
|---|---|---|---|
| install (cached) | 110ms | 109ms | +0% |
| ls (cached) | 69ms | 69ms | +0% |
| bin-paths (cached) | 73ms | 73ms | +0% |
| task-ls (cached) | 2489ms | ✅ 529ms | +370% |
✅ Performance improvement: task-ls cached is 370%
### 🚀 Features - **(edit)** add interactive config editor (`mise edit`) by @jdx in [#7930](#7930) - **(lockfile)** graduate lockfiles from experimental by @jdx in [#7929](#7929) - **(task)** add support for usage values in task confirm dialog by @roele in [#7924](#7924) - **(task)** improve source freshness checking with edge case handling by @jdx in [#7932](#7932) ### 🐛 Bug Fixes - **(activate)** preserve ordering of paths appended after mise activate by @jdx in [#7919](#7919) - **(install)** sort failed installations for deterministic error output by @jdx in [#7936](#7936) - **(lockfile)** preserve URL and prefer sha256 when merging platform info by @jdx in [#7923](#7923) - **(lockfile)** add atomic writes and cache invalidation by @jdx in [#7927](#7927) - **(templates)** use sha256 for hash filter instead of blake3 by @jdx in [#7925](#7925) - **(upgrade)** respect tracked configs when pruning old versions by @jdx in [#7926](#7926) ### 🚜 Refactor - **(progress)** migrate from indicatif to clx by @jdx in [#7928](#7928) ### 📚 Documentation - improve clarity on uvx and pipx dependencies by @ygormutti in [#7878](#7878) ### ⚡ Performance - **(install)** use Kahn's algorithm for dependency scheduling by @jdx in [#7933](#7933) - use Aho-Corasick for efficient redaction by @jdx in [#7931](#7931) ### 🧪 Testing - remove flaky test_http_version_list test by @jdx in [#7934](#7934) ### Chore - use github backend instead of ubi in mise.lock by @jdx in [#7922](#7922) ### New Contributors - @ygormutti made their first contribution in [#7878](#7878)
…nfo (jdx#7923) ## Summary - Fix lockfile platform info merging to preserve URLs when new entries don't have them - Prefer sha256 checksums over blake3 (sha256 comes from official releases and is more portable/verifiable) - Add `merge_with` method to `PlatformInfo` for proper merging logic ## Problem When running `mise upgrade` on a platform (e.g., macos-arm64), the lockfile entry for that platform would lose its URL and have its sha256 checksum replaced with a locally-generated blake3 checksum. This happened because: 1. During installation, a blake3 checksum is generated locally (no URL available) 2. The merge logic used `or_insert` which doesn't merge when both old and new entries exist for the same platform key 3. The new entry (with blake3, no URL) would simply overwrite the existing entry (with sha256 and URL) ## Solution Added a `merge_with` method to `PlatformInfo` that: - Prefers sha256 checksums over blake3 when both are present - Preserves URLs from existing entries when new entry doesn't have one - Properly combines all fields Updated `merge_tool_entries_with_env` to use `and_modify` with `merge_with` instead of just `or_insert`. ## Test plan - [x] Added e2e test `test_lock_platform_merge` that verifies URLs and sha256 checksums are preserved during lockfile updates - [x] Existing lockfile tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk because it changes lockfile merge semantics for per-platform entries; incorrect precedence could cause stale or wrong checksums/URLs to persist across updates. > > **Overview** > Fixes lockfile platform merging so updating a tool doesn’t drop existing per-platform metadata. Adds `PlatformInfo::merge_with` and updates `merge_tool_entries_with_env` to merge entries (preserving `url`/`url_api` and preferring `sha256:` checksums over `blake3:`). > > Adds an e2e CLI test (`test_lock_platform_merge`) covering URL preservation, sha256 preference, and retention of other platforms during `mise lock --platform …` updates. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit f34d3ee. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
### 🚀 Features - **(edit)** add interactive config editor (`mise edit`) by @jdx in [jdx#7930](jdx#7930) - **(lockfile)** graduate lockfiles from experimental by @jdx in [jdx#7929](jdx#7929) - **(task)** add support for usage values in task confirm dialog by @roele in [jdx#7924](jdx#7924) - **(task)** improve source freshness checking with edge case handling by @jdx in [jdx#7932](jdx#7932) ### 🐛 Bug Fixes - **(activate)** preserve ordering of paths appended after mise activate by @jdx in [jdx#7919](jdx#7919) - **(install)** sort failed installations for deterministic error output by @jdx in [jdx#7936](jdx#7936) - **(lockfile)** preserve URL and prefer sha256 when merging platform info by @jdx in [jdx#7923](jdx#7923) - **(lockfile)** add atomic writes and cache invalidation by @jdx in [jdx#7927](jdx#7927) - **(templates)** use sha256 for hash filter instead of blake3 by @jdx in [jdx#7925](jdx#7925) - **(upgrade)** respect tracked configs when pruning old versions by @jdx in [jdx#7926](jdx#7926) ### 🚜 Refactor - **(progress)** migrate from indicatif to clx by @jdx in [jdx#7928](jdx#7928) ### 📚 Documentation - improve clarity on uvx and pipx dependencies by @ygormutti in [jdx#7878](jdx#7878) ### ⚡ Performance - **(install)** use Kahn's algorithm for dependency scheduling by @jdx in [jdx#7933](jdx#7933) - use Aho-Corasick for efficient redaction by @jdx in [jdx#7931](jdx#7931) ### 🧪 Testing - remove flaky test_http_version_list test by @jdx in [jdx#7934](jdx#7934) ### Chore - use github backend instead of ubi in mise.lock by @jdx in [jdx#7922](jdx#7922) ### New Contributors - @ygormutti made their first contribution in [jdx#7878](jdx#7878)
Summary
merge_withmethod toPlatformInfofor proper merging logicProblem
When running
mise upgradeon a platform (e.g., macos-arm64), the lockfile entry for that platform would lose its URL and have its sha256 checksum replaced with a locally-generated blake3 checksum. This happened because:or_insertwhich doesn't merge when both old and new entries exist for the same platform keySolution
Added a
merge_withmethod toPlatformInfothat:Updated
merge_tool_entries_with_envto useand_modifywithmerge_withinstead of justor_insert.Test plan
test_lock_platform_mergethat verifies URLs and sha256 checksums are preserved during lockfile updates🤖 Generated with Claude Code
Note
Medium Risk
Medium risk because it changes lockfile merge semantics for per-platform entries; incorrect precedence could cause stale or wrong checksums/URLs to persist across updates.
Overview
Fixes lockfile platform merging so updating a tool doesn’t drop existing per-platform metadata. Adds
PlatformInfo::merge_withand updatesmerge_tool_entries_with_envto merge entries (preservingurl/url_apiand preferringsha256:checksums overblake3:).Adds an e2e CLI test (
test_lock_platform_merge) covering URL preservation, sha256 preference, and retention of other platforms duringmise lock --platform …updates.Written by Cursor Bugbot for commit f34d3ee. This will update automatically on new commits. Configure here.