fix(backend): mark -nightly/-canary/-experimental as prereleases#9523
fix(backend): mark -nightly/-canary/-experimental as prereleases#9523
Conversation
When `minimum_release_age` is set, latest version resolution skips the backend's `latest_stable_version` fast path (e.g. npm dist-tags) and falls back to the shared version-list path, which relies on `VERSION_REGEX` to flag pre-releases. The regex did not catch `-nightly`, `-canary`, `-experimental`, `-insider`, or `-edge` channel tags, so npm packages like `@google/gemini-cli` resolved `latest` to a `-nightly` tarball instead of the stable maintainer-tagged release. Add those channel tags to `VERSION_REGEX` so they are filtered out of `latest` resolution and `ls-remote` (without `--prerelease`) on every backend that opts into pattern-based prerelease detection. Fixes #9503 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Greptile SummaryThis PR fixes a bug where npm packages like Confidence Score: 5/5Safe to merge — targeted, well-tested regex and helper changes with no risk to stable version resolution. Both change sites are additive: the regex adds new filter terms that won't match any realistic stable version string, and the npm helper correctly follows semver semantics. All five new channel tags now have test coverage (addressing the prior review comment), and the stable-version negative assertions are preserved. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[list_remote_versions called] --> B{minimum_release_age set?}
B -- No --> C[latest_stable_version fast path\ne.g. npm dist-tag latest]
B -- Yes --> D[full version list path]
D --> E[VersionInfo populated\nprerelease = is_semver_prerelease version]
E --> F{version contains '-'\nafter stripping '+' build meta?}
F -- Yes --> G[prerelease = true\nfiltered from latest]
F -- No --> H[prerelease = false\nincluded in latest]
D --> I[VERSION_REGEX also checked\nfor non-npm backends]
I --> J{matches -nightly/-canary/\n-experimental/-insider/-edge/\nor other known pre-tag?}
J -- Yes --> K[filtered as prerelease]
J -- No --> L[kept as stable]
Reviews (3): Last reviewed commit: "fix(npm): treat any hyphen-suffixed vers..." | Re-trigger Greptile |
There was a problem hiding this comment.
Code Review
This pull request expands the VERSION_REGEX to include additional pre-release tags (nightly, canary, experimental, insider, edge) and adds corresponding unit tests. The reviewer suggests allowing dot separators for the new tags for consistency and robustness, and recommends adding test cases for the insider and edge tags.
| pub static VERSION_REGEX: Lazy<regex::Regex> = Lazy::new(|| { | ||
| Regex::new( | ||
| r"(?i)(^Available versions:|-src|[-\\.]dev|-latest|-stm|[-\\.]rc|-milestone|-alpha|-beta|[-\\.]pre|-next|-test|([abc])[0-9]+|snapshot|SNAPSHOT|master)" | ||
| r"(?i)(^Available versions:|-src|[-\\.]dev|-latest|-stm|[-\\.]rc|-milestone|-alpha|-beta|[-\\.]pre|-next|-test|-nightly|-canary|-experimental|-insider|-edge|([abc])[0-9]+|snapshot|SNAPSHOT|master)" |
There was a problem hiding this comment.
The newly added pre-release identifiers (nightly, canary, experimental, insider, edge) are currently only matched when preceded by a hyphen. For consistency with other identifiers in this regex (like dev, rc, and pre) which allow both hyphens and dots as separators (e.g., 1.0.0.dev), consider using [-\\.] for these new tags as well. This improves robustness for backends that might use different versioning conventions.
| r"(?i)(^Available versions:|-src|[-\\.]dev|-latest|-stm|[-\\.]rc|-milestone|-alpha|-beta|[-\\.]pre|-next|-test|-nightly|-canary|-experimental|-insider|-edge|([abc])[0-9]+|snapshot|SNAPSHOT|master)" | |
| r"(?i)(^Available versions:|-src|[-\\.]dev|-latest|-stm|[-\\.]rc|-milestone|-alpha|-beta|[-\\.]pre|-next|-test|[-\\.]nightly|[-\\.]canary|[-\\.]experimental|[-\\.]insider|[-\\.]edge|([abc])[0-9]+|snapshot|SNAPSHOT|master)" |
| // npm prerelease channels (GitHub discussion #9503) | ||
| assert!( | ||
| VERSION_REGEX.is_match("0.42.0-nightly.20260429.g6d9911393"), | ||
| "npm -nightly tag should be filtered" | ||
| ); | ||
| assert!( | ||
| VERSION_REGEX.is_match("13.0.0-canary.1"), | ||
| "npm -canary tag should be filtered" | ||
| ); | ||
| assert!( | ||
| VERSION_REGEX.is_match("18.0.0-experimental-abc1234"), | ||
| "npm -experimental tag should be filtered" | ||
| ); |
There was a problem hiding this comment.
The test suite is missing cases for the -insider and -edge tags that were added to the regex. It's recommended to include test cases for all newly added patterns to ensure they are correctly identified and to prevent regressions.
// npm prerelease channels (GitHub discussion #9503)
assert!(
VERSION_REGEX.is_match("0.42.0-nightly.20260429.g6d9911393"),
"npm -nightly tag should be filtered"
);
assert!(
VERSION_REGEX.is_match("13.0.0-canary.1"),
"npm -canary tag should be filtered"
);
assert!(
VERSION_REGEX.is_match("18.0.0-experimental-abc1234"),
"npm -experimental tag should be filtered"
);
assert!(
VERSION_REGEX.is_match("1.2.3-insider"),
"npm -insider tag should be filtered"
);
assert!(
VERSION_REGEX.is_match("1.2.3-edge"),
"npm -edge tag should be filtered"
);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 fe83614. Configure here.
The original `18.0.0-experimental-abc1234` fixture was already matched by the pre-existing `([abc])[0-9]+` alternative (`c1234`), so the assertion would pass even if `-experimental` was dropped from the regex. Switch to suffixes that exercise only the channel-tag alternative under test, and add the missing `-insider` / `-edge` cases for full coverage of #9523. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
Addressed review feedback in e8e9b00:
🤖 Generated with Claude Code |
The shared `VERSION_REGEX` channel-tag list will always lag behind whatever new pre-release tag a maintainer invents (`-nightly`, `-canary`, `-experimental`, ...). For npm specifically we don't have to play whack-a-mole: npm enforces strict semver, so any hyphen-introduced identifier on a version is — by rule 9 — a pre-release. Stamp `prerelease = true` directly in `NPMBackend::_list_remote_versions` based on a structural semver check, so the prerelease filter catches maintainer-chosen tags that aren't in the regex (`-foo`, `-internal-...`, future-named channels) without requiring a code change for each one. The general regex addition from the previous commits is kept as a backstop for backends without a strict semver guarantee. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 x -- echo |
24.2 ± 1.0 | 22.9 | 33.0 | 1.00 ± 0.04 |
mise x -- echo |
24.1 ± 0.3 | 23.5 | 29.0 | 1.00 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 env |
23.6 ± 2.0 | 22.3 | 43.6 | 1.00 |
mise env |
23.9 ± 1.5 | 23.0 | 40.9 | 1.01 ± 0.11 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 hook-env |
23.9 ± 0.7 | 23.1 | 28.4 | 1.00 |
mise hook-env |
24.6 ± 0.5 | 23.8 | 29.5 | 1.03 ± 0.04 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 ls |
20.6 ± 0.2 | 20.1 | 22.0 | 1.00 |
mise ls |
21.5 ± 0.3 | 20.8 | 22.8 | 1.04 ± 0.02 |
xtasks/test/perf
| Command | mise-2026.4.28 | mise | Variance |
|---|---|---|---|
| install (cached) | 156ms | 162ms | -3% |
| ls (cached) | 78ms | 81ms | -3% |
| bin-paths (cached) | 83ms | 85ms | -2% |
| task-ls (cached) | 797ms | 788ms | +1% |
### 🚀 Features - **(conda)** graduate conda backend out of experimental by @jdx in [#9544](#9544) - **(deps)** Add dart and flutter providers by @tjarvstrand in [#9505](#9505) - **(registry)** add neo4j by @mnm364 in [#9525](#9525) - **(registry)** add rustfs by @mnm364 in [#9530](#9530) - **(task)** support exclusion patterns in task sources by @jlarmstrongiv in [#9496](#9496) - **(vfox)** add stat function to lua file module by @esteve in [#9497](#9497) ### 🐛 Bug Fixes - **(backend)** flag regex prerelease versions by @jdx in [#9500](#9500) - **(backend)** mark -nightly/-canary/-experimental as prereleases by @jdx in [#9523](#9523) - **(backend)** suppress no-versions warning for unresolved-latest backends by @jdx in [#9548](#9548) - **(backend)** include dotnet prereleases from package flags by @jdx in [#9551](#9551) - **(backend)** scope PEP 440 prerelease detection to Python backends by @jdx in [#9558](#9558) - **(cargo)** Apply install_env during cargo install by @c22 in [#9502](#9502) - **(copr)** drop epel-9 chroots since rust >= 1.91 is unavailable by @jdx in [#9484](#9484) - **(github)** skip attestations on non-default api_url by @jdx in [#9486](#9486) - **(github)** retry ip allow list errors without auth by @risu729 in [#9506](#9506) - **(http)** update versions host tracking endpoint by @jdx in [#9527](#9527) - **(install)** don't warn for configured tools when version is passed via CLI by @jdx in [#9522](#9522) - **(install)** refresh latest before installing missing tools by @jdx in [#9545](#9545) - **(install)** don't cache nonexistent install paths by @jdx in [#9553](#9553) - **(lockfile)** don't propagate ad-hoc CLI overrides into the project lockfile by @jdx in [#9562](#9562) - **(plugin)** detect plugin types after cloning by @risu729 in [#9540](#9540) - **(release)** pass --no-git-checks to aube publish by @jdx in [#9483](#9483) - **(task)** convert PATH to MSYS Unix form when spawning POSIX shells on Windows by @JamBalaya56562 in [#9547](#9547) ### 📚 Documentation - **(contributing)** require popularity check for registry PRs by @jdx in [7bbeebe](7bbeebe) - **(watch)** update pitchfork domain to en.dev by @risu729 in [#9536](#9536) - document ghtkn GitHub token setup by @jdx in [#9546](#9546) - clarify registry backend acceptance policy by @jdx in [#9543](#9543) - Change exec command to use bash for variable echo by @kuboon in [#9567](#9567) ### 🧪 Testing - **(e2e)** run test-tool targets in parallel by @jdx in [#9564](#9564) - **(e2e)** run tests in parallel by @jdx in [#9563](#9563) - **(e2e)** bind-mount /tmp on disk and surface failed tests in CI summary by @jdx in [#9570](#9570) - **(tasks)** migrate test_task_help atask to usage field by @jdx in [#9549](#9549) ### 📦️ Dependency Updates - update fedora:45 docker digest to 8b838b3 by @renovate[bot] in [#9507](#9507) - update ghcr.io/jdx/mise:deb docker digest to f02194c by @renovate[bot] in [#9509](#9509) - update taiki-e/install-action digest to 7769b73 by @renovate[bot] in [#9512](#9512) - update ghcr.io/jdx/mise:alpine docker digest to 581f8a8 by @renovate[bot] in [#9508](#9508) - update rust crate ctor to v0.10.1 by @renovate[bot] in [#9515](#9515) - update ghcr.io/jdx/mise:rpm docker digest to a5c9655 by @renovate[bot] in [#9510](#9510) - update rust docker digest to a9cfb75 by @renovate[bot] in [#9511](#9511) - update rust crate age to v0.11.3 by @renovate[bot] in [#9514](#9514) - update rust crate jiff to v0.2.24 by @renovate[bot] in [#9516](#9516) - update dependency vitepress-plugin-tabs to ^0.9.0 by @renovate[bot] in [#9518](#9518) - update autofix-ci/action action to v1.3.4 by @renovate[bot] in [#9513](#9513) - update rust crate usage-lib to v3.2.1 by @renovate[bot] in [#9517](#9517) - update apple-actions/import-codesign-certs action to v7 by @renovate[bot] in [#9519](#9519) - update taiki-e/install-action digest to 51cd0b8 by @renovate[bot] in [#9531](#9531) - exclude taiki-e/install-action from renovate by @jdx in [#9532](#9532) - update rust crate blake3 to v1.8.5 by @renovate[bot] in [#9533](#9533) ### 📦 Registry - enable shellcheck on windows by @zeitlinger in [#9487](#9487) - add google-java-format by @zeitlinger in [#9488](#9488) - add expert ([aqua:expert-lsp/expert](https://github.com/expert-lsp/expert)) by @AlternateRT in [#9498](#9498) - update entry for checkmake by @eread in [#9504](#9504) - add systemctl-tui ([aqua:rgwood/systemctl-tui](https://github.com/rgwood/systemctl-tui)) by @2xdevv in [#9521](#9521) - add codon by @3w36zj6 in [#9538](#9538) - add tool yr (backend:github:VirusTotal/yara-x) by @adam-moss in [#9542](#9542) - add tool betterleaks (backend:aqua/betterleaks/betterleaks) by @adam-moss in [#9541](#9541) - add `git-filter-repo` by @garysassano in [#9550](#9550) - add umoci ([aqua:opencontainers/umoci](https://github.com/opencontainers/umoci)) by @2xdevv in [#9555](#9555) - add aqua backend for elixir-ls by @AlternateRT in [#9557](#9557) - deny inline backend options by @risu729 in [#9565](#9565) ### Chore - **(ci)** fail registry tests without summary by @jdx in [#9559](#9559) - **(ci)** use !cancelled() instead of always() for test-ci aggregator by @jdx in [#9569](#9569) - **(ci)** use namespace runners for ci jobs by @jdx in [#9561](#9561) - **(config)** deprecate shorthands_file setting by @risu729 in [#9534](#9534) - **(docs)** remove shrill.en.dev analytics script by @jdx in [#9539](#9539) - **(release)** replace bc with awk in release-plz star formatting by @jdx in [d7f177f](d7f177f) - bump hk to 1.44.3 by @jdx in [#9493](#9493) - invert CLAUDE.md/AGENTS.md so AGENTS.md is canonical by @jdx in [#9560](#9560) - set dev profile debug to 1 by @jdx in [#9572](#9572) ### New Contributors - @kuboon made their first contribution in [#9567](#9567) - @AlternateRT made their first contribution in [#9557](#9557) - @2xdevv made their first contribution in [#9555](#9555) - @adam-moss made their first contribution in [#9541](#9541) - @jlarmstrongiv made their first contribution in [#9496](#9496) - @tjarvstrand made their first contribution in [#9505](#9505)

Summary
When
minimum_release_ageis set, latest version resolution skips the backend'slatest_stable_versionfast path (e.g. npm dist-tags) and falls back to the shared version-list path, which relies onVERSION_REGEXto flag pre-releases. The regex did not catch-nightly,-canary,-experimental,-insider, or-edgechannel tags, so npm packages like@google/gemini-cliresolvedlatestto a-nightlytarball instead of the stable maintainer-tagged release.This adds those channel tags to
VERSION_REGEXso they are filtered out oflatestresolution andls-remote(without--prerelease) on every backend that opts into pattern-based prerelease detection.Fixes #9503
Test plan
cargo test test_version_regex_filters_prereleasecargo test(full unit suite, 796 passing)mise install npm:@google/gemini-cli@latestwithMISE_MINIMUM_RELEASE_AGE=0dno longer picks the-nightlytarball🤖 Generated with Claude Code
Note
Medium Risk
Changes prerelease detection used in version listing/resolution, which may alter which versions are considered installable as “stable” (especially for npm packages) and could inadvertently filter edge-case version strings.
Overview
Improves prerelease filtering during version resolution so npm channel builds like
-nightly,-canary,-experimental,-insider, and-edgeare no longer treated as stable whenVERSION_REGEX-based filtering is used.The npm backend now explicitly marks versions as prereleases based on strict semver rule 9 (hyphen suffix, ignoring
+buildmetadata), and adds focused unit tests covering both the new regex channels and semver prerelease parsing.Reviewed by Cursor Bugbot for commit 9246794. Bugbot is set up for automated code reviews on this repo. Configure here.