fix(ruby): support build revisions for precompiled binaries in mise.lock#8900
fix(ruby): support build revisions for precompiled binaries in mise.lock#8900
Conversation
When resolving precompiled Ruby binaries from jdx/ruby, prefer the latest build revision tag (e.g., 3.3.11-1 over 3.3.11). This prevents mise.lock from breaking when binaries are rebuilt with different checksums while keeping the same upstream Ruby version. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to fetch the latest build revision for a GitHub release, specifically handling tags with incrementing integer suffixes (e.g., version-N). This is implemented in src/github.rs and integrated into the Ruby plugin to support precompiled binaries that might be rebuilt. Feedback focuses on optimizing the selection logic by avoiding redundant string allocations and refactoring duplicated code between the production function and the test suite into a shared helper.
| pub async fn get_release_with_build_revision(repo: &str, version: &str) -> Result<GithubRelease> { | ||
| let releases = list_releases(repo).await?; | ||
| let best = releases | ||
| .into_iter() | ||
| .filter(|r| { | ||
| r.tag_name == version | ||
| || r.tag_name | ||
| .strip_prefix(&format!("{version}-")) | ||
| .is_some_and(|suffix| suffix.parse::<u32>().is_ok()) | ||
| }) | ||
| .max_by_key(|r| { | ||
| r.tag_name | ||
| .strip_prefix(&format!("{version}-")) | ||
| .and_then(|s| s.parse::<u32>().ok()) | ||
| .unwrap_or(0) | ||
| }); | ||
| match best { | ||
| Some(release) => Ok(release), | ||
| None => get_release(repo, version).await, | ||
| } | ||
| } |
There was a problem hiding this comment.
The current implementation performs redundant string formatting and allocations inside the filter and max_by_key closures. Pre-calculating the version prefix outside the iteration improves efficiency.
Additionally, this selection logic is duplicated in the test module (lines 741-759). Consider refactoring it into a shared private helper function within this file to improve maintainability and ensure tests are validating the production logic.
pub async fn get_release_with_build_revision(repo: &str, version: &str) -> Result<GithubRelease> {
let releases = list_releases(repo).await?;
let prefix = format!("{version}-");
let best = releases
.into_iter()
.filter(|r| {
r.tag_name == version
|| r.tag_name
.strip_prefix(&prefix)
.is_some_and(|suffix| suffix.parse::<u32>().is_ok())
})
.max_by_key(|r| {
r.tag_name
.strip_prefix(&prefix)
.and_then(|s| s.parse::<u32>().ok())
.unwrap_or(0)
});
match best {
Some(release) => Ok(release),
None => get_release(repo, version).await,
}
}
src/github.rs
Outdated
| fn select_best_build_revision( | ||
| releases: Vec<GithubRelease>, | ||
| version: &str, | ||
| ) -> Option<GithubRelease> { | ||
| releases | ||
| .into_iter() | ||
| .filter(|r| { | ||
| r.tag_name == version | ||
| || r.tag_name | ||
| .strip_prefix(&format!("{version}-")) | ||
| .is_some_and(|suffix| suffix.parse::<u32>().is_ok()) | ||
| }) | ||
| .max_by_key(|r| { | ||
| r.tag_name | ||
| .strip_prefix(&format!("{version}-")) | ||
| .and_then(|s| s.parse::<u32>().ok()) | ||
| .unwrap_or(0) | ||
| }) | ||
| } |
There was a problem hiding this comment.
This function is a literal duplicate of the logic implemented in get_release_with_build_revision. Since mod tests is a child module, it can directly access private helper functions in the parent module. Refactoring the selection logic into a single shared function in the parent module would reduce code duplication and ensure that the tests are actually exercising the code used in production.
Greptile SummaryThis PR fixes Both prior review concerns are addressed: the test helper no longer duplicates production logic (tests call the extracted Confidence Score: 5/5Safe to merge — no P0/P1 issues found and both previous review concerns are resolved. All remaining findings are P2 or lower. The shared No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant User as mise lock / install
participant Ruby as RubyPlugin
participant Lock as ToolVersion.lock_platforms
participant GH as github::*
User->>Ruby: resolve_lock_info / install_precompiled
Ruby->>Lock: extract_build_revision_from_lock_platforms(tv, version)
alt locked revision found (e.g. 3.3.11-1)
Lock-->>Ruby: Some("3.3.11-1")
Ruby->>GH: get_release(repo, "3.3.11-1")
alt release exists
GH-->>Ruby: GithubRelease { tag: "3.3.11-1", ... }
else release deleted
GH-->>Ruby: Err
Ruby->>GH: get_release_with_build_revision(repo, "3.3.11")
GH->>GH: list_releases → pick_best_build_revision
GH-->>Ruby: GithubRelease { tag: "3.3.11-2", ... }
end
else no locked revision (first run or base tag locked)
Lock-->>Ruby: None
Ruby->>GH: get_release_with_build_revision(repo, "3.3.11")
GH->>GH: list_releases → pick_best_build_revision
GH-->>Ruby: GithubRelease { tag: "3.3.11-1", ... }
end
Ruby-->>User: url + checksum (locked to specific build revision)
Reviews (4): Last reviewed commit: "refactor(ruby): extract shared build rev..." | Re-trigger 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 9caa901. Configure here.
When a build revision is already locked in the lockfile (extracted from the stored URL), reuse that exact revision instead of always resolving to the latest. Falls back to latest build revision if the locked one is no longer available. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…feedback - Extract `pick_best_build_revision` as shared helper used by both production code and unit tests (no more duplicated logic) - Pre-compute version prefix string outside filter/max_by_key closures - Document pagination limitation in doc comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.3 x -- echo |
22.3 ± 0.4 | 21.5 | 25.9 | 1.00 |
mise x -- echo |
23.0 ± 0.8 | 22.1 | 31.3 | 1.03 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.3 env |
22.3 ± 0.6 | 21.4 | 29.1 | 1.00 |
mise env |
23.0 ± 0.7 | 21.7 | 27.3 | 1.03 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.3 hook-env |
22.9 ± 0.7 | 21.8 | 28.8 | 1.00 |
mise hook-env |
23.4 ± 0.9 | 22.4 | 35.4 | 1.02 ± 0.05 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.3 ls |
20.3 ± 0.6 | 19.1 | 22.5 | 1.00 |
mise ls |
20.7 ± 0.5 | 19.7 | 22.3 | 1.02 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.4.3 | mise | Variance |
|---|---|---|---|
| install (cached) | 151ms | 151ms | +0% |
| ls (cached) | 80ms | 79ms | +1% |
| bin-paths (cached) | 84ms | 83ms | +1% |
| task-ls (cached) | 2751ms | ✅ 810ms | +239% |
✅ Performance improvement: task-ls cached is 239%
### 🚀 Features - **(ci)** auto-convert external PRs to draft mode by @jdx in [#8896](#8896) - **(deps)** add `depends` field for user-specified tool dependencies by @cprecioso in [#8776](#8776) - **(dotnet)** support runtime-only installs by @fragon10 in [#8524](#8524) - **(npm)** apply install_before to transitive dependencies by @risu729 in [#8851](#8851) - **(task)** allow passing arguments to task dependencies via {{usage.*}} templates by @jdx in [#8893](#8893) - add options field to BackendListVersionsCtx by @esteve in [#8875](#8875) ### 🐛 Bug Fixes - **(backend)** filter PEP 440 .dev versions in fuzzy version matching by @richardthe3rd in [#8849](#8849) - **(ci)** update COPR BuildRequires rust version to match MSRV 1.88 by @jdx in [#8911](#8911) - **(ci)** add Ruby build dependencies to e2e Docker image by @jdx in [#8910](#8910) - **(ci)** add missing build dependencies to e2e Docker image by @jdx in [#8912](#8912) - **(ci)** add missing build dependencies to e2e Docker image by @jdx in [#8914](#8914) - **(ci)** use Node 24 LTS for corepack e2e test by @jdx in [#8915](#8915) - **(ci)** add libxml2 and pkg-config to e2e Docker image by @jdx in [#8917](#8917) - **(ci)** add libxml2-dev to e2e image and disable Swift SPM tests by @jdx in [#8918](#8918) - **(docs)** use sans-serif font for badges by @jdx in [#8887](#8887) - **(env)** parse --env=VALUE and -E=VALUE flag forms correctly by @jdx in [#8889](#8889) - **(exec)** use i64::from() for seccomp syscall numbers to survive autofix by @jdx in [#8882](#8882) - **(github)** preserve tool options like filter_bins when version specified via CLI by @jdx in [#8888](#8888) - **(github)** use alias-specific options when tool_alias has its own config by @jdx in [#8892](#8892) - **(install)** add locked_verify_provenance setting and detect github attestations at lock time by @jdx in [#8901](#8901) - **(lock)** prune stale version entries during filtered `mise lock <tool>` runs by @altendky in [#8599](#8599) - **(python)** use lockfile URL for precompiled installs by @hehaoqian in [#8750](#8750) - **(release)** verify all build targets succeed before releasing by @jdx in [#8886](#8886) - **(ruby)** support build revisions for precompiled binaries in mise.lock by @jdx in [#8900](#8900) - **(swift)** fall back to Ubuntu 24.04 for unsupported Ubuntu versions by @jdx in [#8916](#8916) - **(zsh)** avoid duplicate trust warning after cd by @timothysparg in [#8898](#8898) - update flake.lock and add fix for rust-bindgen to default.nix by @esteve in [#8874](#8874) - when direnv diff is empty, do not try to parse it by @yaleman in [#8857](#8857) - skip trust check for plain .tool-versions in task list by @dportalesr in [#8876](#8876) ### 🚜 Refactor - **(go)** rename go_* settings to go.* namespace by @jdbruijn in [#8598](#8598) ### 📚 Documentation - **(tasks)** clarify task_config.includes behavior by @risu729 in [#8905](#8905) ### 🧪 Testing - **(ci)** run e2e tests inside Docker containers by @jdx in [#8899](#8899) ### 📦️ Dependency Updates - bump ubi from 0.8 to 0.9 by @jdx in [#8906](#8906) - bump zip from 3 to 8 by @jdx in [#8908](#8908) - update lockfile deps (hold back rattler) by @jdx in [#8909](#8909) - update bun.lock by @jdx in [#8913](#8913) ### 📦 Registry - add turso ([github:tursodatabase/turso-cli](https://github.com/tursodatabase/turso-cli)) by @kenn in [#8884](#8884) - remove carp test by @jdx in [#8894](#8894) ### Chore - **(ci)** add workflow to warn PRs modifying vendored aqua-registry by @jdx in [#8897](#8897) - **(ci)** use github.token for draft conversion in auto-draft workflow by @jdx in [#8903](#8903) - remove deprecated settings older than 12 months by @jdx in [#8904](#8904) ### New Contributors - @dportalesr made their first contribution in [#8876](#8876) - @timothysparg made their first contribution in [#8898](#8898) - @hehaoqian made their first contribution in [#8750](#8750) - @jdbruijn made their first contribution in [#8598](#8598) - @cprecioso made their first contribution in [#8776](#8776) - @yaleman made their first contribution in [#8857](#8857) - @kenn made their first contribution in [#8884](#8884) - @fragon10 made their first contribution in [#8524](#8524) ## 📦 Aqua Registry Updates #### New Packages (6) - [`ahkohd/oyo`](https://github.com/ahkohd/oyo) - [`bellicose100xp/jiq`](https://github.com/bellicose100xp/jiq) - [`kurama/dealve-tui`](https://github.com/kurama/dealve-tui) - [`micahkepe/jsongrep`](https://github.com/micahkepe/jsongrep) - [`textfuel/lazyjira`](https://github.com/textfuel/lazyjira) - [`ubugeeei/vize`](https://github.com/ubugeeei/vize) #### Updated Packages (1) - [`sigstore/cosign`](https://github.com/sigstore/cosign)

Summary
jdx/ruby, prefer the latest build revision tag (e.g.,3.3.11-1over3.3.11){version}-{N}where N is an incrementing integer, representing rebuilds of the same upstream Ruby versionmise.lock, subsequentmise lockruns preserve it instead of upgrading to a newer build revisionmise.lockfrom breaking when binaries are rebuilt with different checksumsBackground
The
jdx/rubyrepo now publishes build revision tags (e.g.,3.3.11and3.3.11-1). When binaries are rebuilt, the old release's checksums become stale. Previously, mise always fetched the exact version tag, so a lockfile created against3.3.11would break if the binaries were later republished as3.3.11-1.Changes
src/github.rs: Addedget_release_with_build_revision()+ sharedpick_best_build_revision()helper that finds the highest{version}-Ntag from releases; unit tests exercise the shared helper directlysrc/plugins/core/ruby.rs: Addedextract_build_revision_from_lock_platforms()to parse existing lockfile URLs for build revision tags; threadslocked_build_revisionthroughfind_precompiled_asset_in_reposo locked revisions are preserved acrossmise lockruns, falling back to latest if the locked revision is deletedTest plan
mise use ruby@3.3.11installs from the-1build revision releasemise lockcaptures the build revision URL in the lockfilemise lockpreserves the existing build revision🤖 Generated with Claude Code
Note
Medium Risk
Changes precompiled Ruby resolution to prefer and optionally pin GitHub release tags with
{version}-Nbuild revisions, which can affect which binaries/checksums are downloaded and locked. Risk is moderated by fallback behavior when a locked revision can’t be found and by added unit tests for revision selection.Overview
Precompiled Ruby installs/locks now support GitHub build revision tags (e.g.
3.3.11-2) in repos likejdx/ruby, preferring the highest numeric-Ntag over the baseversion.Adds
github::get_release_with_build_revision()(backed bypick_best_build_revision()) and unit tests, and updates the Ruby plugin to (1) extract an existing build revision frommise.lockURLs, (2) use that exact tag when present, and (3) fall back to the latest revision or base tag if the locked revision no longer exists.Reviewed by Cursor Bugbot for commit 1b63f1e. Bugbot is set up for automated code reviews on this repo. Configure here.