fix(backend): scope PEP 440 prerelease detection to Python backends#9558
fix(backend): scope PEP 440 prerelease detection to Python backends#9558
Conversation
Greptile SummaryThis PR fixes two regressions by scoping PEP 440 separator-less prerelease detection ( Confidence Score: 5/5Safe to merge — the fix is tightly scoped, regression-tested, and does not change behaviour for any non-Python backend. No P0/P1 issues found. The regex change is correct and well-anchored, the two-layer filtering (VersionInfo stamp + fuzzy_match_filter override) is complementary rather than duplicative, the exact-match bypass is preserved, and the test suite covers Python prereleases, stable versions, Go pseudo-versions, and bare identifiers. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Backend calls fuzzy_match_filter] --> B{Python-flavored backend?}
B -- "Yes (pipx / python)" --> C[fuzzy_match_versions_pep440]
B -- No --> D[fuzzy_match_versions]
C --> E{filter_prereleases?}
E -- Yes --> F["Pre-filter: drop PEP440_PRERELEASE_REGEX matches\n(except exact query match)"]
E -- No --> G[Keep all versions]
F --> D
G --> D
D --> H{filter_prereleases?}
H -- Yes --> I["Drop VERSION_REGEX matches\n(-rc1, -dev, -alpha …)\nexact match preserved"]
H -- No --> J[Keep all versions]
I --> K[Pattern match against query]
J --> K
K --> L[Return matched versions]
Reviews (7): Last reviewed commit: "fix(backend): scope PEP 440 prerelease d..." | Re-trigger Greptile |
There was a problem hiding this comment.
Code Review
This pull request updates the VERSION_REGEX in src/plugins/mod.rs to prevent false positives when matching version strings, specifically addressing issues where hex hashes in Go pseudo-versions were incorrectly identified as prerelease suffixes. It also adds comprehensive test cases to verify the new regex behavior against Python-style suffixes and Go pseudo-versions. I have no feedback to provide.
cfff9d8 to
a3ba126
Compare
a3ba126 to
2be0505
Compare
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 x -- echo |
23.1 ± 0.8 | 21.6 | 30.5 | 1.00 |
mise x -- echo |
24.0 ± 0.8 | 22.5 | 28.9 | 1.04 ± 0.05 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 env |
22.9 ± 0.9 | 21.2 | 29.6 | 1.00 |
mise env |
23.1 ± 0.6 | 21.8 | 25.6 | 1.01 ± 0.05 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 hook-env |
23.0 ± 0.6 | 22.0 | 29.2 | 1.00 |
mise hook-env |
24.3 ± 0.8 | 22.6 | 27.5 | 1.05 ± 0.05 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.28 ls |
20.7 ± 0.7 | 19.4 | 25.1 | 1.00 |
mise ls |
21.3 ± 0.7 | 19.8 | 25.3 | 1.03 ± 0.05 |
xtasks/test/perf
| Command | mise-2026.4.28 | mise | Variance |
|---|---|---|---|
| install (cached) | 154ms | 160ms | -3% |
| ls (cached) | 78ms | 81ms | -3% |
| bin-paths (cached) | 83ms | 85ms | -2% |
| task-ls (cached) | 856ms | 811ms | +5% |
29cc81a to
0919ec8
Compare
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 0919ec8. Configure here.
The `([abc])[0-9]+` alternative in the shared `VERSION_REGEX` was unanchored, so it false-positived on any `a`/`b`/`c` followed by a digit anywhere in a version string — including the random hex hash inside Go pseudo-versions (e.g. `c1` inside `f149714c1d54`). After #9500 enabled `mark_prereleases_from_version_pattern: true` for the `go` backend, this caused `mise install go:.../@latest` to filter out the only available version for paths whose latest is a pseudo-version, e.g. `go:github.com/go-kratos/kratos/cmd/kratos/v2`. The rule itself is PEP 440-specific (Python's separator-less alpha/beta/rc shorthand) and shouldn't have been in the general regex. Move it into a new `PEP440_PRERELEASE_REGEX` consulted only by the Python-flavored backends — `pipx` and the `python` core plugin. Other backends would otherwise false-positive on hex hashes / short SHAs. The new regex is grounded in the canonical PEP 440 grammar, `[N!]N(.N)*[{a|b|rc}N][.postN][.devN]`: the prerelease segment must follow the release segment, so the regex requires a leading digit. `c` is included as PEP 440's recognized alternate spelling for `rc`. A trailing `(?:$|[^a-z0-9])` boundary keeps it from matching inside hex identifiers even when those start with a digit. Both Python-flavored backends share the logic via a new `fuzzy_match_versions_pep440` helper. `pipx` additionally stamps the flag on its `VersionInfo` cache entries so `prerelease=true` reflects upstream metadata as well as fuzzy-match filtering. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
0919ec8 to
287f01d
Compare
### 🚀 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
Two regressions visible in the release PR (#9485):
mise install go:.../@latestreturns "no versions found" when the latest is a Go pseudo-version (e.g.go:github.com/go-kratos/kratos/cmd/kratos/v2) — the e2e-0 /backend/test_go_install_slowfailure.mise latest pythonreturned3.15.0a8instead of a stable. Fixed below.)Cause
The
([abc])[0-9]+alternative in the sharedVERSION_REGEXwas unanchored, so it false-positived on anya/b/cfollowed by a digit anywhere in a string — including the random hex hash inside Go pseudo-versions (e.g.c1insidef149714c1d54).#9500 enabled
mark_prereleases_from_version_pattern: truefor thegobackend on May 1, which is what made it user-visible — the regex was already over-permissive but no Go-flavored input had been feeding it before.Fix
The rule is PEP 440-specific (Python's separator-less alpha/beta/rc shorthand). Move it out of the general regex into
PEP440_PRERELEASE_REGEXand consult it only from Python-flavored backends.VERSION_REGEX(insrc/plugins/mod.rs): drop the([abc])[0-9]+alternative.PEP440_PRERELEASE_REGEX(new):(?i)[0-9](?:a|b|c|rc)[0-9]+(?:\$|[^a-z0-9])— grounded in PEP 440's canonical grammar[N!]N(.N)*[{a|b|rc}N][.postN][.devN]. The leading digit anchors the prerelease segment to follow the release segment (so it can't trigger inside arbitrary identifiers);cis included as PEP 440's recognized alternate spelling forrc; the trailing boundary stops it from matching inside hex hashes even when those start with a digit.pipx:VersionInfoin_list_remote_versionsso the cache reflects it.fuzzy_match_filterto drop them from match results.pythoncore plugin: samefuzzy_match_filteroverride (python-buildreturns3.15.0a8-style alphas).fuzzy_match_versions_pep440helper insrc/backend/mod.rsso the override is one line each.Test plan
cargo test test_pep440_prerelease_regex(new) — Python suffixes match, Go pseudo-versions and bare-bidentifiers don't,.postdoesn't.cargo test test_version_regex_filters_prerelease— confirms PEP 440 cases now negative on the general regex.cargo test test_mark_prerelease_flags_regex_matches— added a Go pseudo-version case and a PEP 440 case (both should not be flagged by the general path).cargo test test_fuzzy_match_versions_*,test_filter_cached_prereleases_*,pipx::tests::*.mise ls-remote 'go:github.com/go-kratos/kratos/cmd/kratos/v2'returns2.0.0-20260404020628-f149714c1d54.mise latest pythonreturns3.14.4(was3.15.0a8after the earlier rev of this PR).e2e-0 / test_go_install_slowande2e-6 / test_latest.🤖 Generated with Claude Code
Note
Medium Risk
Adjusts prerelease detection and fuzzy version resolution logic, which can change what versions are considered installable/latest across multiple backends; mistakes could hide valid versions or surface prereleases unexpectedly.
Overview
Fixes prerelease filtering regressions by removing the unanchored
([abc])[0-9]+clause from the sharedVERSION_REGEX(which was incorrectly flagging strings like Go pseudo-versions) and introducing a new Python-scopedPEP440_PRERELEASE_REGEX.Python-flavored backends (
pipxand the corepythonplugin) now apply PEP 440 prerelease handling explicitly:pipxstamps PEP 440 alphas/betas/rcs onto cachedVersionInfo.prerelease, and both backends override fuzzy matching via a new shared helperfuzzy_match_versions_pep440solatest/prefix queries skip3.15.0a8-style versions unless explicitly requested.Adds focused tests to ensure Go pseudo-versions are not filtered by the general regex and that PEP 440 prerelease detection works only where intended.
Reviewed by Cursor Bugbot for commit 287f01d. Bugbot is set up for automated code reviews on this repo. Configure here.