Skip to content

fix(lockfile): resolve version aliases before lockfile lookup#8194

Merged
jdx merged 1 commit intomainfrom
fix/lockfile-alias-resolution
Feb 16, 2026
Merged

fix(lockfile): resolve version aliases before lockfile lookup#8194
jdx merged 1 commit intomainfrom
fix/lockfile-alias-resolution

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 16, 2026

Summary

  • Fixes lockfile not being respected when using version aliases like node = "lts"
  • The lockfile check was happening before alias resolution, so prefix matching "24.13.0".starts_with("lts") would fail
  • After alias resolution (e.g., "lts""24"), the lockfile is now re-checked so "24.13.0".starts_with("24") matches correctly

Fixes #8175

Test plan

  • Added e2e/lockfile/test_lockfile_alias — verifies lockfile is respected with custom aliases
  • All existing lockfile e2e tests pass (test_lockfile_install, test_lockfile_exec, test_lockfile_use, test_lockfile_local, test_lockfile_env_resolution)

🤖 Generated with Claude Code


Note

Medium Risk
Touches core version resolution and lockfile matching paths, which can affect which versions get installed/selected across tools; the change is small and covered by a new e2e test but could alter behavior for alias/prefix edge cases.

Overview
Fixes a bug where lockfiles were ignored when a tool version was specified via an alias (e.g. lts/custom alias), by adding a second lockfile lookup after alias resolution using the resolved version as the prefix.

Refactors lockfile resolution to support a custom prefix via ToolRequest::lockfile_resolve_with_prefix, and centralizes lockfile-to-ToolVersion construction. Adds an e2e test (e2e/lockfile/test_lockfile_alias) to verify the requested version remains the alias while the installed/resolved version comes from mise.lock.

Written by Cursor Bugbot for commit 84df394. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings February 16, 2026 22:29
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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 addresses a critical bug where mise failed to respect lockfile entries when tool versions were specified using aliases. Previously, the lockfile lookup happened too early, preventing alias-resolved versions from matching. The fix involves introducing a mechanism to re-evaluate the lockfile after aliases have been fully resolved, ensuring that the correct locked version is always used.

Highlights

  • Lockfile Resolution: Fixed an issue where the lockfile was not correctly applied when using version aliases (e.g., "lts"), because the lockfile check occurred before alias resolution.
  • Alias Resolution: Introduced a re-check of the lockfile after aliases are resolved, allowing for correct matching of versions (e.g., "lts" -> "24" -> "24.13.0").
Changelog
  • e2e/lockfile/test_lockfile_alias
    • Added a new end-to-end test to verify that lockfiles are correctly respected when using version aliases.
  • src/toolset/tool_request.rs
    • Implemented a new public function lockfile_resolve_with_prefix to allow lockfile resolution using a custom prefix, specifically for post-alias resolution.
  • src/toolset/tool_version.rs
    • Modified the resolve method to incorporate a secondary lockfile check using the newly resolved version alias, ensuring the lockfile is honored.
Activity
  • Added a new end-to-end test e2e/lockfile/test_lockfile_alias to validate the fix for lockfile alias resolution.
  • Confirmed that all existing lockfile end-to-end tests continue to pass after the changes.
  • The pull request was generated with Claude Code.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses an issue where version aliases were not being resolved before checking the lockfile. The introduction of lockfile_resolve_with_prefix and its usage after alias resolution is a logical fix. The new e2e test provides good coverage for this scenario.

I've left a couple of comments regarding code duplication. These are suggestions to improve maintainability and are not critical to the functionality of this fix.

Comment on lines +304 to +319
match self.source() {
ToolSource::MiseToml(path) => lockfile::get_locked_version(
config,
Some(path),
&self.ba().short,
prefix,
&request_options,
),
_ => lockfile::get_locked_version(
config,
None,
&self.ba().short,
prefix,
&request_options,
),
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This match statement can be simplified to avoid duplicating the call to lockfile::get_locked_version. This also makes the code more DRY and easier to maintain. A similar refactoring could be applied to lockfile_resolve.

        let path = match self.source() {
            ToolSource::MiseToml(path) => Some(path),
            _ => None,
        };
        lockfile::get_locked_version(
            config,
            path,
            &self.ba().short,
            prefix,
            &request_options,
        )

Comment on lines +207 to +209
let mut tv = Self::new(request.clone(), lt.version);
tv.lock_platforms = lt.platforms;
return Ok(tv);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This logic for creating a ToolVersion from a LockfileTool is duplicated from ToolVersion::resolve. To improve maintainability and reduce code duplication, consider extracting this into a private helper function like ToolVersion::from_lockfile(request, lockfile_tool).

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON, but a Cloud Agent failed to start.

When using version aliases like `node = "lts"`, the lockfile was not
being respected because the lockfile check happened before alias
resolution. The prefix "lts" would never match a lockfile entry like
"24.13.0".starts_with("lts"). After alias resolution ("lts" → "24"),
the lockfile is now re-checked so "24.13.0".starts_with("24") matches.

Fixes #8175

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the fix/lockfile-alias-resolution branch from f276b05 to 84df394 Compare February 16, 2026 22:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes lockfile resolution when tool versions are specified via aliases (e.g., node = "lts"), by re-checking the lockfile after alias resolution so prefix matching works as intended.

Changes:

  • Re-check the lockfile after resolving a version alias during tool version resolution.
  • Add ToolRequest::lockfile_resolve_with_prefix to support lockfile lookups using a custom prefix.
  • Add an e2e test ensuring lockfiles are respected when using custom version aliases.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/toolset/tool_version.rs Adds a post-alias lockfile re-check during version resolution to ensure lockfile prefix matches work after alias expansion.
src/toolset/tool_request.rs Introduces a lockfile lookup helper that accepts an explicit prefix (used post-alias resolution).
e2e/lockfile/test_lockfile_alias New e2e coverage validating alias → version resolution still respects lockfile-pinned versions.
Comments suppressed due to low confidence (1)

src/toolset/tool_request.rs:301

  • lockfile_resolve_with_prefix duplicates the request_options + match self.source() logic from lockfile_resolve. Consider extracting a single private helper (e.g., taking an explicit prefix) and having both methods delegate to it to avoid future drift.
        if let Some(backend) = backend::get(self.ba()) {
            let matches = backend.list_installed_versions_matching(v);
            if matches.iter().any(|m| m == v) {
                return Ok(Some(v.to_string()));
            }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

&& !has_linked_version(request.ba())
&& let Some(lt) = request.lockfile_resolve_with_prefix(config, &v)?
{
return Ok(Self::from_lockfile(request.clone(), lt));
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

In this new lockfile-return path, request.clone() is unnecessary because the function returns immediately; you can move request into ToolVersion::new to avoid an extra clone/allocation.

Suggested change
return Ok(Self::from_lockfile(request.clone(), lt));
return Ok(Self::from_lockfile(request, lt));

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 x -- echo 24.5 ± 0.8 23.1 27.0 1.00
mise x -- echo 25.3 ± 1.7 23.4 46.4 1.03 ± 0.08

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 env 25.2 ± 1.0 22.8 29.1 1.02 ± 0.05
mise env 24.6 ± 0.9 22.8 26.8 1.00

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 hook-env 26.3 ± 0.8 23.7 29.2 1.00 ± 0.05
mise hook-env 26.2 ± 0.9 23.5 27.7 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 ls 23.3 ± 1.0 21.6 25.6 1.00
mise ls 23.7 ± 1.0 22.1 26.2 1.02 ± 0.06

xtasks/test/perf

Command mise-2026.2.13 mise Variance
install (cached) 131ms 136ms -3%
ls (cached) 83ms 85ms -2%
bin-paths (cached) 87ms 89ms -2%
task-ls (cached) 849ms 863ms -1%

@jdx jdx merged commit c39cd1e into main Feb 16, 2026
35 checks passed
@jdx jdx deleted the fix/lockfile-alias-resolution branch February 16, 2026 23:02
mise-en-dev added a commit that referenced this pull request Feb 17, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[#8164](#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion #7479) by @mackwic in
[#8183](#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [#8189](#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [#8190](#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [#8194](#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [#8173](#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [#8169](#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [#8192](#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [#8165](#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [#8193](#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[#8195](#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[#8180](#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [#8191](#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[#8181](#8181)

### New Contributors

- @jean-humann made their first contribution in
[#8173](#8173)
- @mackwic made their first contribution in
[#8183](#8183)
- @rooperuu made their first contribution in
[#8169](#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Summary
- Fixes lockfile not being respected when using version aliases like
`node = "lts"`
- The lockfile check was happening before alias resolution, so prefix
matching `"24.13.0".starts_with("lts")` would fail
- After alias resolution (e.g., `"lts"` → `"24"`), the lockfile is now
re-checked so `"24.13.0".starts_with("24")` matches correctly

Fixes jdx#8175

## Test plan
- [x] Added `e2e/lockfile/test_lockfile_alias` — verifies lockfile is
respected with custom aliases
- [x] All existing lockfile e2e tests pass (`test_lockfile_install`,
`test_lockfile_exec`, `test_lockfile_use`, `test_lockfile_local`,
`test_lockfile_env_resolution`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core version resolution and lockfile matching paths, which can
affect which versions get installed/selected across tools; the change is
small and covered by a new e2e test but could alter behavior for
alias/prefix edge cases.
> 
> **Overview**
> Fixes a bug where lockfiles were ignored when a tool version was
specified via an alias (e.g. `lts`/custom alias), by adding a second
lockfile lookup after alias resolution using the resolved version as the
prefix.
> 
> Refactors lockfile resolution to support a custom prefix via
`ToolRequest::lockfile_resolve_with_prefix`, and centralizes
lockfile-to-`ToolVersion` construction. Adds an e2e test
(`e2e/lockfile/test_lockfile_alias`) to verify the requested version
remains the alias while the installed/resolved version comes from
`mise.lock`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
84df394. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[jdx#8164](jdx#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion jdx#7479) by @mackwic in
[jdx#8183](jdx#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [jdx#8189](jdx#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [jdx#8190](jdx#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [jdx#8194](jdx#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [jdx#8173](jdx#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [jdx#8169](jdx#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [jdx#8192](jdx#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [jdx#8165](jdx#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [jdx#8193](jdx#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[jdx#8195](jdx#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[jdx#8180](jdx#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [jdx#8191](jdx#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[jdx#8181](jdx#8181)

### New Contributors

- @jean-humann made their first contribution in
[jdx#8173](jdx#8173)
- @mackwic made their first contribution in
[jdx#8183](jdx#8183)
- @rooperuu made their first contribution in
[jdx#8169](jdx#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[jdx#8164](jdx#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion jdx#7479) by @mackwic in
[jdx#8183](jdx#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [jdx#8189](jdx#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [jdx#8190](jdx#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [jdx#8194](jdx#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [jdx#8173](jdx#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [jdx#8169](jdx#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [jdx#8192](jdx#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [jdx#8165](jdx#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [jdx#8193](jdx#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[jdx#8195](jdx#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[jdx#8180](jdx#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [jdx#8191](jdx#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[jdx#8181](jdx#8181)

### New Contributors

- @jean-humann made their first contribution in
[jdx#8173](jdx#8173)
- @mackwic made their first contribution in
[jdx#8183](jdx#8183)
- @rooperuu made their first contribution in
[jdx#8169](jdx#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants