feat: add --before flag for date-based version filtering#7298
Conversation
…ugins Add created_at timestamp support to the following core plugins: - Node.js: extracts date from nodejs.org/dist/index.json - Zig: extracts date from ziglang.org/download/index.json - Rust: extracts created_at from GitHub releases - Bun: extracts created_at from GitHub releases - Swift: extracts created_at from GitHub releases - Erlang: extracts created_at from GitHub releases (when compile=false) - Elixir: extracts timestamp from builds.hex.pm/builds/elixir/builds.txt Each plugin now implements _list_remote_versions_with_info() to provide version metadata. This allows `mise ls-remote --json` to include created_at timestamps for these tools. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The versions host has a .toml endpoint that includes created_at timestamps. Use this instead of plain text to ensure timestamps are available when the versions host succeeds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add the ability to filter tool versions by release date, similar to npm's --before flag. This enables reproducible builds and supply-chain security by ensuring only versions released before a certain date are considered. Features: - New `install_before` setting (env: MISE_INSTALL_BEFORE) - New `--before` CLI flag for install, upgrade, and use commands - Supports absolute dates (2024-06-01) and relative durations (90d, 1y) - Only filters fuzzy version requests (node@20, latest) - explicit versions like node@22.5.0 are not filtered 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| trace!("Failed to parse timestamp: {}", ts); | ||
| true | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: Date-only timestamps fail to parse in version filtering
The VersionInfo::filter_by_date method only tries parsing timestamps with ts.parse::<Timestamp>(), which requires RFC3339 format. However, when falling back from versions host to native APIs, Node and Zig backends provide date-only strings like "2024-01-09" which cannot be parsed by Timestamp. These versions silently bypass the filter (returning true on parse failure). This is inconsistent with parse_into_timestamp in duration.rs which correctly handles date-only strings by parsing as jiff::civil::Date first. The --before filter will not work for Node and Zig when the versions host is unavailable.
Additional Locations (2)
There was a problem hiding this comment.
Bug: Before-date filter not applied in resolve_sub function
The resolve_sub function receives opts: &ResolveOptions containing before_date but ignores it when resolving "latest". It calls backend.latest_version(config, None) instead of backend.latest_version_with_opts(config, None, opts.before_date). This means when using version subtraction syntax like mise install python@sub-0.1:latest --before 2024-01-01, the --before filter is silently ignored, and the base version is resolved from the unfiltered latest instead of the latest version before the specified date.
src/toolset/tool_version.rs#L254-L255
mise/src/toolset/tool_version.rs
Lines 254 to 255 in 94522fa
There was a problem hiding this comment.
Pull request overview
This PR adds a --before flag that filters tool versions by release date, enabling reproducible builds by ensuring only versions released before a specific date are considered. This functionality is similar to npm's --before flag.
Key changes:
- New
--beforeCLI flag forinstall,upgrade, andusecommands - New
MISE_INSTALL_BEFOREsetting (env:MISE_INSTALL_BEFORE) - Support for absolute dates (
2024-06-01) and relative durations (90d,1y) - Modified versions host endpoint to use
.tomlformat for timestamp data - Updated core plugins to provide
VersionInfowith timestamps instead of just version strings
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/backend/mod.rs | Core filtering logic using VersionInfo::filter_by_date(), new list_versions_matching_with_opts() and latest_version_with_opts() methods |
| src/versions_host.rs | Switch from plain text to TOML endpoint for timestamp data, simplified API to use tool name directly |
| src/duration.rs | New parse_into_timestamp() function supporting multiple date/duration formats |
| src/cli/{install,upgrade,use}.rs | Added --before flag and get_before_date() helper method |
| src/plugins/core/*.rs | Updated plugins (node, bun, rust, swift, zig, elixir, erlang) to return VersionInfo with timestamps |
| src/toolset/tool_version.rs | Updated resolution to pass before_date option through version matching calls |
| settings.toml | New install_before setting with documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| In "soft" mode (default), this only affects fuzzy version matches like "20" or "latest". | ||
| Explicitly pinned versions like "22.5.0" are not filtered. | ||
| Use `install_before_mode = "hard"` setting to filter all versions strictly. |
There was a problem hiding this comment.
The documentation references an install_before_mode setting that doesn't exist in the codebase. This setting is not defined in settings.toml or implemented anywhere. Either remove this line or implement the setting.
|
|
||
| In "soft" mode (default), this only affects fuzzy version matches like "20" or "latest". | ||
| Explicitly pinned versions like "22.5.0" are not filtered. | ||
| Use `install_before_mode = "hard"` setting to filter all versions strictly. |
There was a problem hiding this comment.
The man page references an install_before_mode setting that doesn't exist in the codebase. This setting is not defined in settings.toml or implemented anywhere. Either remove this line or implement the setting.
| Use `install_before_mode = "hard"` setting to filter all versions strictly. |
| } | ||
| flag "-n --dry-run" help="Just print what would be done, don't actually do it" | ||
| flag --before help="Only upgrade to versions released before this date" { | ||
| long_help "Only upgrade to versions released before this date\n\nSupports absolute dates like \"2024-06-01\" and relative durations like \"90d\" or \"1y\".\nThis can be useful for reproducibility or security purposes.\n\nIn \"soft\" mode (default), this only affects fuzzy version matches like \"20\" or \"latest\".\nExplicitly pinned versions like \"22.5.0\" are not filtered.\nUse `install_before_mode = \"hard\"` setting to filter all versions strictly." |
There was a problem hiding this comment.
The usage documentation references an install_before_mode setting that doesn't exist in the codebase. This setting is not defined in settings.toml or implemented anywhere. Either remove this line or implement the setting.
| long_help "Only upgrade to versions released before this date\n\nSupports absolute dates like \"2024-06-01\" and relative durations like \"90d\" or \"1y\".\nThis can be useful for reproducibility or security purposes.\n\nIn \"soft\" mode (default), this only affects fuzzy version matches like \"20\" or \"latest\".\nExplicitly pinned versions like \"22.5.0\" are not filtered.\nUse `install_before_mode = \"hard\"` setting to filter all versions strictly." | |
| long_help "Only upgrade to versions released before this date\n\nSupports absolute dates like \"2024-06-01\" and relative durations like \"90d\" or \"1y\".\nThis can be useful for reproducibility or security purposes.\n\nIn \"soft\" mode (default), this only affects fuzzy version matches like \"20\" or \"latest\".\nExplicitly pinned versions like \"22.5.0\" are not filtered." |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2025.12.7 x -- echo |
20.0 ± 0.4 | 19.5 | 25.5 | 1.00 |
mise x -- echo |
20.3 ± 0.2 | 19.8 | 21.2 | 1.01 ± 0.02 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2025.12.7 env |
19.6 ± 0.3 | 19.1 | 22.1 | 1.00 |
mise env |
19.9 ± 0.6 | 19.4 | 25.4 | 1.02 ± 0.03 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2025.12.7 hook-env |
19.7 ± 0.2 | 19.2 | 21.0 | 1.00 |
mise hook-env |
19.9 ± 0.6 | 19.4 | 28.6 | 1.01 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2025.12.7 ls |
16.5 ± 0.2 | 16.0 | 18.0 | 1.00 |
mise ls |
16.8 ± 0.2 | 16.3 | 18.3 | 1.02 ± 0.02 |
xtasks/test/perf
| Command | mise-2025.12.7 | mise | Variance |
|---|---|---|---|
| install (cached) | 107ms | 109ms | -1% |
| ls (cached) | 64ms | 65ms | -1% |
| bin-paths (cached) | 71ms | 71ms | +0% |
| task-ls (cached) | 433ms | 433ms | +0% |
|
@copilot can you fix cursor's comment? |
There was a problem hiding this comment.
Bug: Upgrade dry-run output ignores before-date filter
The upgrade command's --dry-run output doesn't reflect the --before filter. The list_outdated_versions call on line 80 determines the latest version without considering before_date, which is only applied later in InstallOptions on line 169. When --dry-run is used, the output shows "Would install {tool}@{o.latest}" using the unfiltered latest version. The actual upgrade (without dry-run) would correctly apply the --before filter and potentially install a different version, making the dry-run output misleading.
src/cli/upgrade.rs#L141-L157
Lines 141 to 157 in 9f6eaee
src/cli/upgrade.rs#L79-L80
Lines 79 to 80 in 9f6eaee
|
@copilot Bug: Upgrade dry-run output ignores before-date filter src/cli/upgrade.rs#L141-L15 |
The resolve_sub function was calling backend.latest_version() without the before_date filter, causing date-based version filtering to be ignored when resolving "latest" versions with sub-version syntax. Now uses latest_version_with_opts() with opts.before_date to properly respect the install_before setting and --before CLI flag. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
I've fixed the bug identified in the review comments: Bug: The Fixed by changing to This fix also addresses the "upgrade dry-run output" concern - the The e2e test confirms this is working correctly:
|
1. Fix date-only timestamp parsing in filter_by_date - Use parse_into_timestamp() instead of direct Timestamp parsing - This handles date-only formats like "2024-01-09" from Node/Zig backends 2. Add before_date support to outdated_info trait method - Updated Backend::outdated_info signature to include ResolveOptions - Updated Rust plugin's outdated_info to use opts.before_date - Updated call site in list_outdated_versions to pass opts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Additional fixes pushedI've addressed the remaining PR review comments: 1. Date-only timestamp parsing in
2. Rust plugin
All unit tests (377) and e2e tests pass. |
1. Fix potential panic in resolve_sub when date filter excludes all versions - Replace .unwrap() with .ok_or_else() for proper error handling - Provide descriptive error message mentioning date filter when applicable 2. Restore mise.toml that was accidentally removed in earlier commit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| let now = Timestamp::now(); | ||
| let past = now.checked_sub(span)?; | ||
| return Ok(past); | ||
| } |
There was a problem hiding this comment.
Bug: Negative duration inputs produce unexpected future timestamps
The parse_into_timestamp function doesn't validate that parsed spans are positive before subtracting from now. If a user accidentally provides a negative duration like -90d, jiff parses it as a negative span, and checked_sub on a negative value effectively adds, resulting in a future timestamp. This causes the --before filter to include all versions rather than filtering them, since all versions are released "before" a future date. The existing parse_duration function in the same file explicitly checks for and rejects negative durations, but this new function lacks that validation.
There was a problem hiding this comment.
Bug: Pinned versions incorrectly filtered despite documentation claims
The documentation in settings.toml states that "Explicitly pinned versions like node@22.5.0 are not filtered", but the implementation does filter them. In resolve_version, the check for installed versions (lines 238-246) only runs when !opts.latest_versions. Since the install, upgrade, and use commands all set latest_versions: true, this check is skipped, and list_versions_matching_with_opts is called with before_date which applies the date filter to all versions including exact pinned ones. If a user has MISE_INSTALL_BEFORE set and tries to install an exact version released after that date that isn't already installed, the version won't be found because it's filtered out.
src/toolset/tool_version.rs#L246-L253
mise/src/toolset/tool_version.rs
Lines 246 to 253 in 5698c26
settings.toml#L597-L600
Lines 597 to 600 in 5698c26
1. Validate negative duration inputs in parse_into_timestamp
- Reject negative durations like "-90d" which would create future timestamps
- Matches existing validation in parse_duration function
2. Preserve exact/pinned version matching regardless of date filter
- Only apply date filter for fuzzy/prefix version resolution
- Explicit versions like "22.5.0" are not filtered by --before flag
- Optimized to only fetch unfiltered list when needed (date filter active
and exact version not in filtered results)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When using relative durations like "90d", each call to get_before_date() computes a new timestamp using Timestamp::now(). This caused inconsistency between the outdated version check and the actual installation. Now computes the timestamp once at the start and passes it through to both list_outdated_versions and InstallOptions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
bugbot run |
### 🚀 Features - **(conda)** add dependency resolution for conda packages by @jdx in [#7280](#7280) - **(go)** add created_at support to ls-remote --json by @jdx in [#7305](#7305) - **(hook-env)** add hook_env.cache_ttl and hook_env.chpwd_only settings for NFS optimization by @jdx in [#7312](#7312) - **(hooks)** add MISE_TOOL_NAME and MISE_TOOL_VERSION to preinstall/postinstall hooks by @jdx in [#7311](#7311) - **(shell_alias)** add shell_alias support for cross-shell aliases by @jdx in [#7316](#7316) - **(tool)** add security field to mise tool --json by @jdx in [#7303](#7303) - add --before flag for date-based version filtering by @jdx in [#7298](#7298) ### 🐛 Bug Fixes - **(aqua)** support cosign v3 bundle verification by @jdx in [#7314](#7314) - **(config)** use correct config_root in tera context for hooks by @jdx in [#7309](#7309) - **(nu)** fix nushell deactivation script on Windows by @fu050409 in [#7213](#7213) - **(python)** apply uv_venv_create_args in auto-venv code path by @jdx in [#7310](#7310) - **(shell)** escape exe path in activation scripts for paths with spaces by @jdx in [#7315](#7315) - **(task)** parallelize exec_env loading to fix parallel task execution by @jdx in [#7313](#7313) - track downloads for python and java by @jdx in [#7304](#7304) - include full tool ID in download track by @jdx in [#7320](#7320) ### 📚 Documentation - Switch `postinstall` code to be shell-agnostic by @thejcannon in [#7317](#7317) ### 🧪 Testing - **(e2e)** disable debug mode by default for windows-e2e by @jdx in [#7318](#7318) ### New Contributors - @fu050409 made their first contribution in [#7213](#7213)
Summary
Add the ability to filter tool versions by release date, similar to npm's
--beforeflag. This enables reproducible builds and supply-chain security by ensuring only versions released before a certain date are considered.Features:
install_beforesetting (env:MISE_INSTALL_BEFORE)--beforeCLI flag forinstall,upgrade, andusecommands2024-06-01) and relative durations (90d,1y)node@20,latest) - explicit versions likenode@22.5.0are not filteredUsage examples:
Depends on: #7294 (for versions_host timestamps)
Test plan
test_install_before)🤖 Generated with Claude Code
Note
Adds date-aware version resolution/filtering across install/upgrade/use via
--beforeandMISE_INSTALL_BEFORE, with docs, schema, man, completions, and tests updated.ResolveOptions.before_dateand date parsing (parse_into_timestamp).list_versions_matching_with_optsandlatest_version_with_optsusingVersionInfo.created_at.ToolVersion::resolve,latest_version_with_opts, and outdated checks.--before <BEFORE>toinstall,upgrade, anduse; wire through install/upgrade flows.settings.install_before(env:MISE_INSTALL_BEFORE) insettings.tomlandschema/mise.json.docs/cli/*,man/man1/mise.1,mise.usage.kdl, and Fig completions to document--before.VersionInfo::filter_by_date; core Rust plugin and outdated logic acceptResolveOptions.e2e/cli/test_install_beforefor flag and env behavior.Written by Cursor Bugbot for commit c1f3020. This will update automatically on new commits. Configure here.