Repository navigation
perf(install): reuse cached version lists for the tool's release-age window - #14484
Conversation
…window Commands that only need a matching version (install, use, run, ...) now keep a cached remote version list for the tool's effective minimum_release_age, clamped to 1h..24h, when the list dates every version. A release newer than the list could not be picked before it reached that age anyway. outdated, upgrade, ls-remote, lock and latest keep fetch_remote_versions_cache (1h), as does any setup where the user sets fetch_remote_versions_cache explicitly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Entire-Checkpoint: 01M4YH8N3VW166BB0WSXAGG4XA
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
[Medium impact] The PR appears safe to merge; no new blocking issues remain. SummaryThis PR reuses fully dated version lists for the tool's release-age window while keeping shorter refresh periods for commands that report newest versions.
Reviews (3) · Last reviewed commit: "fix(install): only drop in-memory versio..." · Reviewed by Greptile |
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.
Reviewed by Cursor Bugbot for commit 278ce34. Configure here.
…st reuse Subtract the versions-host snapshot lag from the release-age window, and drop a list held in memory when a later request in the same command allows a shorter window than the one it was adopted under. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Entire-Checkpoint: 01M4YJ00N4J167BY3NFEG6GW5R
…m disk Track when an adopted cache file was written instead of reading the file's age later, so a list fetched in this process is not refetched when writing it to disk failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Entire-Checkpoint: 01M4YJEK465QQAHYHPRWS5W2BK
…the release-age window (#14485) <!-- entire-trail-link-start --> https://entire.io/gh/jdx/mise/trails/763 <!-- entire-trail-link-end --> #14484 lets `mise install` / `mise use` keep a dated version list for the tool's release-age window (up to 24h) instead of refetching it after `fetch_remote_versions_cache` (1h). Two lookups that sit beside that list still expired after an hour, so install kept making requests the window was meant to remove: - **The mise-versions index.** When a command resolves several tools, the index answers for all but the first, so those tools' own lists are never cached. Two hours later, `mise install` revalidated the index (about 140 KB when it has changed) even though every list it stands in for was within its window. - **pipx's latest release.** `pipx:black = "latest"` resolves through the registry's latest-release lookup, not the shared list, so `mise install` fetched `https://pypi.org/pypi/black/json` again after an hour. Both now follow the same window. Traced with `MISE_TRACE=1 mise install --dry-run`, with caches backdated 2h: ``` # before # after GET https://mise-versions.jdx.dev/data/index.v1.json (none) GET https://pypi.org/pypi/black/json (none) ``` Commands that report what is newest (`outdated`, `upgrade`, `ls-remote`, `lock`, `latest`) still refetch after 1h, and a `fetch_remote_versions_cache` you set yourself still applies everywhere. ### Why these two, and not the others The window is safe for a cached answer when the cutoff hides everything a fresh fetch could add: - **Versions index**: every entry is dated (mise-versions' builder rejects one without `created_at`), and it is built from the same snapshots as the per-tool lists. The window is the asking tool's own, so a tool with a shorter cutoff, or no cutoff, still revalidates a copy another tool kept. - **pipx latest**: mise computes it as the newest stable release in the registry's own dated list, so it moves forward only when a newer version is uploaded. The cutoff hides that upload for the window, and resolution then takes the newest release past the cutoff, which is the cached answer again. It moves backwards only when that release is yanked, which a version list cached under #14484 shows just as late. An undated answer (a simple index without upload times) keeps the 1h refresh. Left on `fetch_remote_versions_cache`: - **npm's `latest` dist-tag**: install reads it and refetches it hourly, but a maintainer can point it at any version at any time, including back to an older release after a bad publish. The release-age cutoff dates releases, not tag moves, so it cannot cover that. - **asdf `latest-stable`**: a plugin script's undated output; asdf lists carry no dates either, so #14484 does not extend them. - **aqua's tag list**: install reads it only to map an already chosen version to its tag, and only when the versions host's release URL cannot be confirmed. It stores no dates, and an exact pin newer than the cached list is not filtered by the cutoff, so a longer window could turn a right tag into a `v`-prefix guess. ### Tests - Unit tests for the index's per-tool window (`Loaded::usable`, `is_usable`) and for pipx (`adopt_latest_within_release_age`, plus a mocked registry that expects two requests across a fresh read, a 2h-old read, and a read with `fetch_remote_versions_cache` set). - `e2e/cli/test_versions_index_release_age_window` and `e2e/backend/test_pipx_latest_release_age_window` serve the versions host and registry locally and log requests. Both show no request 2h later, and a request again with `MISE_FETCH_REMOTE_VERSIONS_CACHE=1h`, a 90m cutoff, past 24h, or under `mise outdated`. All three tests fail when the window is disabled. *AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5-5; version: 2.1.295.* 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes when remote version metadata is refetched during install resolution; behavior is gated on dated releases and release-age cutoffs, with explicit exceptions for refresh-style commands. > > **Overview** > Extends the **release-age cache window** (from dated per-tool version lists) to two more lookups so `mise install` / `mise use` stop refetching them after the default `fetch_remote_versions_cache` (1h) when `minimum_release_age` applies. > > The **mise-versions index** now takes the asking tool’s release-age window in `versions_index::get`; disk copies past the 1h TTL stay valid for that window when every entry is dated. **pipx `latest`** reuses a cached, dated registry “newest stable” answer via `latest_stable_version_info_before` and `adopt_latest_within_release_age` under the same rules. > > **`outdated`** and other “what’s newest” flows still refresh on the shorter cache; user-set `fetch_remote_versions_cache` still wins. Docs note that movable `latest` pointers (npm dist-tags, asdf `latest-stable`) are unchanged. > > Adds unit tests plus e2e scripts for the index and pipx registry behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d9b8eae. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Version lookups can reuse cached release information within the applicable freshness window, reducing unnecessary registry requests. * Configured cache durations and minimum release-age settings affect when version information is refreshed. This applies to version indexes and the newest dated release reported by a `pipx:` registry. * Movable `latest` pointers, including npm’s `latest` tag, continue to be refetched. * **Documentation** * Updated guidance to clarify how freshness windows apply to version indexes and dated releases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
<!-- entire-trail-link-start --> https://entire.io/gh/jdx/mise/trails/726 <!-- entire-trail-link-end --> ### 🚀 Features - **(bootstrap)** add --upgrade to upgrade packages and tools by @jdx in [#14433](#14433) - **(env)** add --paths to list the PATH entries mise manages by @jdx in [#14429](#14429) - **(install)** download pinned versions from mise.jdx.dev with GitHub fallback by @jdx in [#14446](#14446) ### 🐛 Bug Fixes - **(aqua)** read GitHub-hosted aqua registries from raw.githubusercontent.com by @jdx in [#14470](#14470) - **(backend)** build packslip after versions_host_applies was removed by @jdx in [8bdf9ae](8bdf9ae) - **(backend)** resolve packslip prefixes like `latest` by @jdx in [#14467](#14467) - **(bootstrap)** render incoming direction for ufw interface rules by @jdx in [#14480](#14480) - **(dotfiles)** show planned file actions before the apply prompt by @jdx in [#14430](#14430) - **(dotfiles)** speed up flaky objects_at test by @jdx in [#14439](#14439) - **(dotfiles)** capture changed files of copy-mode groups with dot add --changed by @jdx in [#14436](#14436) - **(exec)** ask mise-versions for GitHub releases when auto-installing from exec and shims by @jdx in [#14458](#14458) - **(github)** stop falling back to api.github.com when mise-versions misses by @jdx in [#14460](#14460) - **(github)** read the checksum-mismatch hint's digest from mise-versions by @jdx in [#14481](#14481) - **(github)** list git tags from mise-versions instead of api.github.com by @jdx in [#14461](#14461) - **(github)** verify attestations from mise-versions without api.github.com fallbacks by @jdx in [#14469](#14469) - **(lock)** ignore the host's implicit compile default for other lock platforms by @jdx in [#14432](#14432) - **(lock)** download artifacts for provenance checks from github.com, not the API by @jdx in [#14465](#14465) - **(oci)** require root for apt bootstrap packages and show dpkg output on failure by @jdx in [#14447](#14447) - **(pipx)** preserve uv graph locking during global upgrades by @wislertt in [#14426](#14426) - **(ruby)** look up the latest ruby-build release through mise-versions by @jdx in [#14471](#14471) - **(self-update)** read mise releases from mise-versions and download from github.com by @jdx in [#14464](#14464) - **(shim)** allow a few repeated fallback handoffs in one process by @jdx in [#14475](#14475) - **(upgrade)** leave lazy tools that are not installed to their first run by @ryanrhughes in [#14422](#14422) - **(upgrade)** skip version lookups for lazy tools a bare upgrade leaves alone by @jdx in [#14444](#14444) - avoid false "stack limit reached" on musl builds by @jdx in [#14450](#14450) ### 📚 Documentation - recommend committing mise.lock so CI and Docker skip version lookups by @jdx in [#14453](#14453) ### ⚡ Performance - **(backend)** take release tags from the versions host instead of listing or guessing them by @jdx in [#14442](#14442) - **(backend)** read the registry tool's version list for an explicit backend arg by @jdx in [#14459](#14459) - **(backend)** stop re-downloading what packslip resolution already verified by @jdx in [#14466](#14466) - **(github)** reuse mirrored attestations and skip mise-versions for asset-less latest releases by @jdx in [#14443](#14443) - **(github)** list releases and tags from mise-versions' whole lists by @jdx in [#14477](#14477) - **(install)** skip refetching version lists a cached copy already answers by @jdx in [#14457](#14457) - **(install)** reuse cached version lists for the tool's release-age window by @jdx in [#14484](#14484) - **(install)** keep the versions index and pipx's latest release for the release-age window by @jdx in [#14485](#14485) - **(outdated)** resolve `latest` without refetching the version list by @jdx in [#14456](#14456) - **(outdated)** resolve many registry tools with one request to the mise-versions index by @jdx in [#14473](#14473) - **(python)** list python versions from mise-versions instead of cloning pyenv by @jdx in [#14455](#14455) - **(versions-host)** send install tracking and python lists to static assets by @jdx in [#14441](#14441) ### 📦️ Dependency Updates - bump jactionlint to 2.2.0 and mbx to 1.24.0 by @jdx in [#14448](#14448) ### 📦 Registry - install native-sdk from npm so the native command works by @jdx in [#14431](#14431) - add gdal (conda:libgdal-core) by @ivan-ivkovic in [#14440](#14440) ### Ci - prune unused release folders from the mise.jdx.dev CDN bucket by @jdx in [#14445](#14445) - check R2 credentials on every prune run by @jdx in [#14451](#14451) - stop aube-lock from re-committing on Cargo-only Renovate PRs by @jdx in [#14482](#14482) ### Security - **(deps)** update rustls to 0.23.46 by @jdx in [#14434](#14434) - **(task)** require trust before rendering a remote task file by @JamBalaya56562 in [#14416](#14416) ### New Contributors - @ivan-ivkovic made their first contribution in [#14440](#14440) ## 📦 Aqua Registry Updates ### New Packages (2) - [`btwld/fvm`](https://github.com/btwld/fvm) - [`tailscale/tailcat`](https://github.com/tailscale/tailcat) ### Updated Packages (1) - [`k1LoW/tbls`](https://github.com/k1LoW/tbls)
Instruction counts
No instruction-count regression above 1%. Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run. Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.
|

https://entire.io/gh/jdx/mise/trails/762
mise install,mise use,mise runand other commands that only need a version matching a request used to refetch a tool's version list whenever the cached copy was more than an hour old (fetch_remote_versions_cache). Most of those refetches can't change the answer: with the default 24hminimum_release_age, a release published after the list was fetched can't be installed until it is 24h old anyway.These commands now keep a cached list for the tool's effective release-age cutoff less an hour (the most a versions-host snapshot lags the cache file), clamped to between 1 and 24 hours:
minimum_release_age"7d"or an absolute date"6h"(setting, per-tool option or--minimum-release-age)"2h"or less,"0s", or no cutoff for the toolThe cutoff is the one resolution uses: the global setting, a per-tool
minimum_release_age(or the deprecatedinstall_before),minimum_release_age_excludes, the built-in default, and the--minimum-release-ageflag oninstall/use.For example, with the default cutoff:
What keeps the 1h window
mise outdated,mise upgrade,mise ls-remote,mise lockandmise latest.latestjoins the other four in the remote-fetch command list, so underprefer_offlineit also gets the fullfetch_remote_versions_timeoutbudget, like they do.fetch_remote_versions_cacheyourself. An explicit value applies to every command, as it does today. Its default is now documented as1hrather than stored as one, so mise can tell an explicit1hfrom the default.Prefer-offline commands (shims,
activate,exec,env,ls) and--offlineare unchanged. They still use any cached list, whatever its age.The install-time refresh for
latestwhen nothing installed matches (#14457) uses the same window. Before, it only took the cutoff shortcut for a list less than 1h old. Now it takes it for a list less than the window old, and the snapshot-lag margin from #14457 still applies. Within one command, a list adopted for a request with a long window is not reused for a later request whose window is shorter (for example--tool-option minimum_release_age=0s).Not changed: backend-specific caches that hold a single "latest" pointer or non-list data (npm dist-tags, PyPI latest, asdf
latest-stable, aqua tags and bin paths, the tool catalog), GitHub/GitLab/Forgejo release caches, and the multi-tool versions index all stay onfetch_remote_versions_cache. Most aren't read when the outer list is fresh. A pointer isn't covered by the dated-list argument, so extending those is left to a separate change.Tests
0s, and an explicitfetch_remote_versions_cache. Also list reuse for dated, undated, short-cutoff and explicit-setting lists, a later request with a shorter window in the same process, and theCacheManagerhelpers.test_version_list_release_age_window: against a local versions host,mise install --dry-runreuses a 2h-old list. It refetches underMISE_FETCH_REMOTE_VERSIONS_CACHE=1h,MISE_MINIMUM_RELEASE_AGE=90m, or when the list is 25h old.mise outdatedrefetches a 2h-old list.🤖 Generated with Claude Code
AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5-5; version: 2.1.295.
Note
Medium Risk
Changes when remote version lists are considered fresh affect install resolution and network behavior across commands; logic is gated by release-age settings and command type with extensive tests.
Overview
Install-style commands (
mise install,use,run, etc.) no longer refetch remote version lists every hour when the default applies. They can reuse a cached list for up to the tool’s effectiveminimum_release_ageminus one hour (clamped 1–24h, e.g. 23h with the default 24h cutoff), but only when every version in the list has parseable release dates.“Newest” commands (
outdated,upgrade,ls-remote,lock, and newly classifiedlatest) still refresh afterfetch_remote_versions_cache(documented default 1h; the setting is now optional so mise can distinguish unset from an explicit1h). Settingfetch_remote_versions_cacheyourself applies that TTL everywhere.CacheManagergainsadopt_file_if_fresh_forandforget_memory_older_thanso a list adopted for a long window isn’t served from memory to a later request with a shorter cutoff.install/userecord--minimum-release-agebefore listing versions.Docs, schema, and an e2e test cover the split refresh behavior.
Reviewed by Cursor Bugbot for commit 94f58da. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
minimum_release_age, while commands that report the newest versions refresh lists after the configured cache duration (one hour by default).fetch_remote_versions_cacheapplies its duration across commands.