fix(env): skip remote version fetching for "latest" in prefer-offline mode#8500
fix(env): skip remote version fetching for "latest" in prefer-offline mode#8500
Conversation
… mode In prefer-offline mode (used by `mise env`, `hook-env`, `activate`, `exec`), "latest" versions were not covered by the prefer_offline guard, causing unnecessary network calls to package registries. This is especially problematic with private npm registries where the call can hang indefinitely. The existing prefer_offline guard (added in #7976) only covered fully-qualified versions (e.g. "2.3.2"). This adds the same guard for "latest" versions: if a version is already installed, it's returned; otherwise "latest" is returned as-is without a network fetch. Fixes #8499 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello, 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 enhances the Highlights
Changelog
Activity
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.
Code Review
This pull request introduces a prefer_offline guard when resolving the 'latest' version, specifically in src/toolset/tool_version.rs, to skip remote version fetching when prefer-offline mode is enabled, enhancing performance and reliability. A security audit found no vulnerabilities meeting reporting criteria. Additionally, a suggestion has been made to simplify the logic by removing a now redundant check.
src/toolset/tool_version.rs
Outdated
| if settings.prefer_offline() { | ||
| return build(v); | ||
| } |
There was a problem hiding this comment.
This change makes the !is_offline check on line 269 redundant. If settings.prefer_offline() is false, then settings.offline() must also be false, which means is_offline is false and !is_offline is always true in that code path.
Consider simplifying this logic by removing the redundant check. The block from line 266 to 275 could be simplified to:
if settings.prefer_offline() {
return build(v);
}
if let Some(v) = backend
.latest_version_with_opts(config, None, opts.before_date)
.await?
{
return build(v);
} if settings.prefer_offline() {
return build(v);
}
if let Some(v) = backend
.latest_version_with_opts(config, None, opts.before_date)
.await?
{
return build(v);
}
Greptile SummaryThis PR adds a one-line Key issues found:
Confidence Score: 2/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["resolve_version(v, opts)"] --> B{v == 'latest'?}
B -- Yes --> C{!opts.latest_versions AND installed version exists?}
C -- Yes --> D[return installed version ✅]
C -- No --> E{"!is_offline AND\n(!prefer_offline OR opts.latest_versions)?\n[NEW GUARD]"}
E -- Yes --> F["latest_version_with_opts()\n🌐 network call"]
F --> G[return remote latest ✅]
E -- No --> H["⚠️ Falls through without returning"]
B -- No --> I{!opts.latest_versions?}
H --> I
I -- Yes --> J["list_installed_versions_matching(v)\n(local, no network)"]
J --> K{match found?}
K -- Yes --> L[return installed match ✅]
K -- No --> M{is_offline?}
M -- Yes --> N[return v as-is ✅]
M -- No --> O{"prefer_offline AND\nv.matches('.') >= 2?"}
O -- Yes --> P["return v as-is ✅\n(fully-qualified only)"]
O -- No --> Q["list_versions_matching_with_opts()\n🌐 NETWORK CALL\n⚠️ 'latest' reaches here in prefer_offline mode!"]
Q --> R{exact match?}
R -- Yes --> S[return match ✅]
R -- No --> T["resolve_prefix()\n🌐 ANOTHER network call\n⚠️ also reached in prefer_offline mode"]
style H fill:#ffcccc,stroke:#cc0000
style Q fill:#ffcccc,stroke:#cc0000
style T fill:#ffcccc,stroke:#cc0000
|
Move the prefer_offline guard for "latest" versions inline with the existing is_offline check rather than using an early return. This ensures list_installed_versions_matching() still runs, so a user with pkg@1.2.3 installed but configured as "latest" (no latest symlink) still gets the installed version instead of the literal string "latest". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
When no version is installed and v is still "latest", it falls through past the first prefer_offline guard (in the "latest" block) and past list_installed_versions_matching (no matches). The second prefer_offline guard at line 291 only covered fully-qualified versions (2+ dots), so "latest" would still reach list_versions_matching_with_opts and make a network call. Add "latest" to the condition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the user explicitly passes @latest (e.g. `mise x dummy@latest`), opts.latest_versions is true — they want the actual latest, not just whatever is installed. The prefer_offline guards should not block network resolution in that case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The second prefer_offline guard (for list_versions_matching_with_opts) should treat "latest" the same as prefix versions like "2" — both need remote resolution. Only the first guard (for latest_version_with_opts inside the "latest" block) needs to block network calls, since that's the npm-specific path that hangs with private registries. Restore the original dot-count heuristic at the second guard, keeping "latest" and prefix versions consistent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7ac3426 to
1ecf269
Compare
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.4 x -- echo |
23.9 ± 0.9 | 23.1 | 35.5 | 1.00 |
mise x -- echo |
24.1 ± 0.7 | 23.3 | 35.6 | 1.01 ± 0.05 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.4 env |
23.5 ± 0.8 | 22.6 | 28.9 | 1.00 |
mise env |
23.5 ± 0.3 | 22.7 | 25.0 | 1.00 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.4 hook-env |
24.0 ± 0.4 | 23.2 | 26.8 | 1.00 |
mise hook-env |
24.3 ± 0.5 | 23.4 | 29.1 | 1.01 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.4 ls |
23.2 ± 0.6 | 22.4 | 32.7 | 1.00 |
mise ls |
23.6 ± 0.5 | 22.8 | 26.3 | 1.02 ± 0.03 |
xtasks/test/perf
| Command | mise-2026.3.4 | mise | Variance |
|---|---|---|---|
| install (cached) | 151ms | 151ms | +0% |
| ls (cached) | 83ms | 83ms | +0% |
| bin-paths (cached) | 87ms | 87ms | +0% |
| task-ls (cached) | 847ms | 825ms | +2% |
### 🚀 Features - **(vfox)** add `RUNTIME.envType` for libc variant detection by @malept in [#8493](#8493) - store provenance verification results in lockfile by @jdx in [#8495](#8495) ### 🐛 Bug Fixes - **(env)** skip remote version fetching for "latest" in prefer-offline mode by @jdx in [#8500](#8500) - **(tasks)** deduplicate shared deps across task delegation by @vadimpiven in [#8497](#8497) - **(windows)** correctly identify mise binary without extension by @jdx in [#8503](#8503) ### 🚜 Refactor - **(core)** migrate cmd! callers to async with kill_on_drop by @jdx in [a63f7d2](a63f7d2) ### Chore - **(ci)** temporarily disable `mise up` in release-plz by @jdx in [#8504](#8504) - consolidate all linters into hk.pkl by @jdx in [#8498](#8498) ## 📦 Aqua Registry Updates #### New Packages (1) - [`apache/ant`](https://github.com/apache/ant)
The GitHub API endpoint for the latest release is `/releases/latest`, not `/releases/tags/latest` (which looks for a tag literally named "latest"). This fixes a regression in v2026.3.5 where `github:` backend tools with `@latest` would fail with a 404 during install/exec. Two fixes: 1. `get_release_` in github.rs now uses `/releases/latest` when tag is "latest" 2. Revert the prefer-offline guard on `latest_version_with_opts` from #8500 — "latest" must always resolve to a concrete version via remote lookup when no installed version is found, otherwise it propagates as the literal string "latest" into the install path Fixes #8530 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes for the regression in v2026.3.5 where `github:` backend tools with `@latest` fail with a 404 during install/exec (#8530): 1. `get_release_()` in github.rs now uses `/releases/latest` when tag is "latest", instead of `/releases/tags/latest` (which looks for a tag literally named "latest") 2. In prefer-offline mode, when "latest" can't be resolved locally, return early with "latest" so the backend's install path can handle it via the correct API endpoint — rather than falling through to general version matching which fails to find it. This preserves the prefer-offline guard from #8500 that prevents hangs with private registries. Fixes #8530 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The prefer-offline guard was added in #8500 to prevent npm hangs, but that was properly fixed in bdaf470 by making subprocess calls async with kill_on_drop. The guard is now redundant and was causing "latest" to not resolve to a concrete version in prefer-offline mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Summary - Fix GitHub API URL for "latest" releases: use `/releases/latest` instead of `/releases/tags/latest` (which looks for a tag literally named "latest") - Fully revert the prefer-offline guard on `latest_version_with_opts` from #8500 — the npm hang it aimed to fix was properly addressed in bdaf470 by making subprocess calls async with `kill_on_drop`, making the guard redundant - Add e2e test for `github:` backend with `@latest` version ## Context Regression in v2026.3.5 where `github:` backend tools configured with `@latest` fail with a 404 error during install/exec. Two issues combined: 1. `get_release_()` in `github.rs` always constructed `/releases/tags/{tag}`, but "latest" isn't a tag — GitHub's API uses `/releases/latest` for the latest release endpoint 2. The prefer-offline guard in #8500 prevented "latest" from being resolved to a concrete version number during `exec`/`env`/`hook-env`, causing the literal string "latest" to propagate into the install path. This guard was added to prevent npm hangs with private registries, but that issue was already properly fixed in bdaf470 by using async subprocess calls with `kill_on_drop` Fixes #8530 ## Test plan - [x] New e2e test `test_github_latest` verifies install and exec with `@latest` - [x] Existing e2e tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches core GitHub release fetching and generic tool version resolution, which can affect installs/execs across backends, though the change is narrowly scoped and covered by a new E2E regression test. > > **Overview** > Fixes `github:` tools configured with `version = "latest"` by calling GitHub’s correct endpoint (`/releases/latest`) instead of treating `latest` as a tag (`/releases/tags/latest`). > > Adjusts tool version resolution so `latest` is still resolved via remote lookup when not offline (avoiding propagation of the literal `latest` string), and adds an E2E regression test (`e2e/backend/test_github_latest`) that installs/execs a GitHub fixture and asserts `mise ls` shows a concrete semver-like version. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 5698a9a. 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>
Summary
prefer_offlineguard for"latest"versions inresolve_version(), matching the existing guard for fully-qualified versions added in fix(hook-env): skip remote version fetching for uninstalled tools in prefer-offline mode #7976mise env,hook-env,activate, andexecfrom making network calls when resolving"latest"versions — avoiding hangs with private npm registriesContext
After lockfiles graduated from experimental in v2026.2.0 (#7929),
mise env --jsonwithnpm:pkg = "latest"and no lockfile entry falls through to a network fetch thatprefer_offlinemode was intended to prevent. If the private registry holds the TCP connection open (waiting for credentials), mise hangs indefinitely.PR #7976 fixed this for fully-qualified versions (e.g.
"2.3.2") but the"latest"path was not covered. This one-liner closes that gap.See #8499 for the full root cause analysis.
Fixes #8499
Test plan
mise env --jsonwithnpm:pkg = "latest"and no registry auth no longer makes network calls or hangsprefer_offlineis not set (e.g.mise installstill resolves latest from registry)🤖 Generated with Claude Code
Note
Low Risk
Small conditional change to version resolution logic that only affects
prefer_offlinebehavior forlatest; low risk but could change expectations for users relying on remote latest lookup in that mode.Overview
Prevents network access when resolving
"latest"inToolVersion::resolve_versionwhileprefer_offlineis enabled, aligninglatestbehavior with the existing prefer-offline skip for fully-qualified versions.This adds a guard so
hook-env/activate/exec/envwon’t calllatest_version_with_optsunless explicitly doing a latest-version lookup (opts.latest_versions), avoiding hangs against registries that keep connections open.Written by Cursor Bugbot for commit 1ecf269. This will update automatically on new commits. Configure here.