Skip to content

feat: auto-select no-YJIT Ruby on older glibc systems#8069

Merged
jdx merged 2 commits intomainfrom
ruby-glibc-noyjit
Feb 9, 2026
Merged

feat: auto-select no-YJIT Ruby on older glibc systems#8069
jdx merged 2 commits intomainfrom
ruby-glibc-noyjit

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 9, 2026

Summary

  • On Linux with glibc < 2.35, automatically download the no-YJIT precompiled Ruby variant from jdx/ruby
  • YJIT builds require glibc 2.35+ (Ubuntu 22.04+, Debian 12+, Fedora 36+)
  • No-YJIT builds target glibc 2.17, supporting older distros (RHEL 7, Amazon Linux 2, CentOS 7)
  • Detects glibc version via ldd --version, cached for the process lifetime
  • Falls back to standard YJIT build if no no-YJIT variant exists for the requested version

Depends on jdx/ruby#11 which adds no-YJIT Linux variants to the release pipeline.

Test plan

  • On glibc 2.35+ system: installs standard YJIT build (unchanged behavior)
  • On glibc < 2.35 system: installs .no_yjit. variant
  • On glibc < 2.35 with version that has no no-YJIT variant: falls back to standard build
  • On macOS: unchanged behavior (always YJIT)

🤖 Generated with Claude Code


Note

Medium Risk
Changes runtime platform detection and alters which Ruby tarball is downloaded on Linux, which could cause install failures on edge cases (missing ldd, non-glibc systems, or unexpected ldd --version output).

Overview
Precompiled Ruby installs now auto-select the .no_yjit release asset on Linux when detected glibc is older than 2.35, with fallback to the standard precompiled build if the variant isn’t available.

This adds a process-wide glibc version detector (LINUX_GLIBC_VERSION via ldd --version) and updates the Ruby plugin’s GitHub release asset selection and download filename handling to support the no-YJIT artifact naming.

Written by Cursor Bugbot for commit 8804fe6. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings February 9, 2026 14:19
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the Ruby installation process by automatically selecting the appropriate Ruby variant based on the system's glibc version. This ensures compatibility with older Linux distributions that do not support YJIT builds, while still utilizing YJIT on newer systems. The changes involve detecting the glibc version, preferring the no-YJIT variant when necessary, and falling back to the standard build if the preferred variant is unavailable.

Highlights

  • Automatic Ruby Variant Selection: This PR introduces automatic selection of the no-YJIT Ruby variant on Linux systems with glibc versions older than 2.35. This ensures compatibility with older distributions like RHEL 7 and Amazon Linux 2.
  • glibc Version Detection: The code detects the glibc version using ldd --version and caches the result for the process lifetime. This avoids repeated calls to the command.
  • Fallback Mechanism: If a no-YJIT variant is not available for the detected glibc version, the system falls back to the standard YJIT build, maintaining functionality.
Changelog
  • src/plugins/core/ruby.rs
    • Detects glibc version on Linux systems.
    • Prefers no-YJIT Ruby variant for glibc versions older than 2.35.
    • Falls back to standard YJIT build if no-YJIT variant is not found.
    • Caches glibc version for the process lifetime using OnceLock.
    • Modifies the asset filename resolution to account for the .no_yjit. suffix.
Activity
  • jdx created this pull request to add support for auto-selecting no-YJIT Ruby on older glibc systems.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Linux glibc-version detection to automatically prefer the “no-YJIT” precompiled Ruby artifacts on older glibc systems, while preserving existing behavior on newer systems and non-Linux platforms.

Changes:

  • Detect glibc version (cached via OnceLock) and decide when to prefer no-YJIT assets.
  • Update GitHub release asset selection to prefer .no_yjit artifacts on glibc < 2.35 with fallback to standard assets.
  • Derive downloaded tarball filename from the URL rather than always constructing the standard name.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 634 to 640
let filename = url
.rsplit('/')
.next()
.unwrap_or(&format!("ruby-{}.{}.tar.gz", tv.version, platform))
.to_string();
let tarball_path = tv.download_path().join(&filename);

Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap_or(&format!(...)) attempts to return a reference tied to the url string slice lifetime, but format!(...) creates a temporary String that does not live long enough. This will fail to compile due to borrowing a temporary. Restructure this to return an owned String from both branches (e.g., match on url.rsplit('/').next() and to_string() the slice, otherwise format!(...)).

Suggested change
let filename = url
.rsplit('/')
.next()
.unwrap_or(&format!("ruby-{}.{}.tar.gz", tv.version, platform))
.to_string();
let tarball_path = tv.download_path().join(&filename);
let filename = match url.rsplit('/').next() {
Some(name) => name.to_string(),
None => format!("ruby-{}.{}.tar.gz", tv.version, platform),
};
let tarball_path = tv.download_path().join(&filename);

Copilot uses AI. Check for mistakes.
}
match Self::glibc_version() {
Some((major, minor)) => major < 2 || (major == 2 && minor < 35),
None => false, // can't detect, assume modern system
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When glibc detection fails (None), this defaults to the standard (YJIT) build. That undermines the PR’s goal on older/minimal Linux environments where ldd may be missing or its output may not parse, and can lead to selecting an incompatible YJIT artifact. A safer behavior would be to treat None as 'prefer no-YJIT' (and rely on the existing fallback to standard when the .no_yjit asset doesn’t exist).

Suggested change
None => false, // can't detect, assume modern system
// If detection fails on Linux (e.g. no `ldd` or unparsable output),
// conservatively prefer the no-YJIT variant and rely on the existing
// fallback to the standard build when the `.no_yjit` asset is missing.
None => true,

Copilot uses AI. Check for mistakes.
fn glibc_version() -> Option<(u32, u32)> {
static GLIBC_VERSION: OnceLock<Option<(u32, u32)>> = OnceLock::new();
*GLIBC_VERSION.get_or_init(|| {
let output = std::process::Command::new("ldd")
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoking ldd via PATH (Command::new(\"ldd\")) allows PATH hijacking (running an unexpected binary). Consider using a more robust approach: (1) prefer absolute paths like /usr/bin/ldd (optionally probing known locations), or (2) avoid spawning a process by using a libc-based glibc version query when available and falling back to a command only if needed.

Suggested change
let output = std::process::Command::new("ldd")
// Prefer absolute ldd paths to avoid PATH hijacking.
// Try a small set of common locations and fall back to None if none exist.
const LDD_CANDIDATES: [&str; 2] = ["/usr/bin/ldd", "/bin/ldd"];
let ldd_path = LDD_CANDIDATES
.iter()
.map(Path::new)
.find(|p| p.is_file())?;
let output = std::process::Command::new(ldd_path)

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a feature to automatically select a no-YJIT precompiled Ruby variant on Linux systems with glibc versions older than 2.35. The implementation correctly detects the glibc version and modifies the asset selection logic. The changes are well-structured. I've provided a couple of suggestions to improve the robustness and performance of the new logic.

Comment on lines +536 to 547
for release in &releases {
for asset in &release.assets {
if asset.name == no_yjit_name && no_yjit_asset.is_none() {
no_yjit_asset =
Some((asset.browser_download_url.clone(), asset.digest.clone()));
}
if asset.name == standard_name && standard_asset.is_none() {
standard_asset =
Some((asset.browser_download_url.clone(), asset.digest.clone()));
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This loop can be slightly optimized. By using if/else if, we avoid a second string comparison for each asset. Also, we can break out of the loop once both no_yjit_asset and standard_asset have been found, which avoids iterating through all remaining releases unnecessarily.

        for release in &releases {
            for asset in &release.assets {
                if no_yjit_asset.is_none() && asset.name == no_yjit_name {
                    no_yjit_asset =
                        Some((asset.browser_download_url.clone(), asset.digest.clone()));
                } else if standard_asset.is_none() && asset.name == standard_name {
                    standard_asset =
                        Some((asset.browser_download_url.clone(), asset.digest.clone()));
                }
            }
            if no_yjit_asset.is_some() && standard_asset.is_some() {
                break;
            }
        }

Comment on lines +634 to +638
let filename = url
.rsplit('/')
.next()
.unwrap_or(&format!("ruby-{}.{}.tar.gz", tv.version, platform))
.to_string();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current logic to extract the filename from the URL can result in an empty string if the URL ends with a /. Using find(|s| !s.is_empty()) will correctly find the last non-empty path segment, making this more robust.

        let filename = url
            .rsplit('/')
            .find(|s| !s.is_empty())
            .unwrap_or(&format!("ruby-{}.{}.tar.gz", tv.version, platform))
            .to_string();

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON, but a Cloud Agent failed to start.

@github-actions
Copy link

github-actions bot commented Feb 9, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.8 x -- echo 23.5 ± 0.6 22.3 29.8 1.00
mise x -- echo 24.0 ± 0.7 22.0 26.1 1.02 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.8 env 22.6 ± 0.7 21.0 27.8 1.00
mise env 24.2 ± 0.5 21.4 26.1 1.07 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.8 hook-env 24.0 ± 0.7 22.1 32.2 1.00
mise hook-env 24.8 ± 0.6 22.9 27.3 1.04 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.8 ls 21.9 ± 0.7 20.3 23.8 1.00
mise ls 22.8 ± 0.8 20.8 24.8 1.04 ± 0.05

xtasks/test/perf

Command mise-2026.2.8 mise Variance
install (cached) 126ms 126ms +0%
ls (cached) 76ms 77ms -1%
bin-paths (cached) 80ms 82ms -2%
task-ls (cached) 558ms 553ms +0%

On Linux with glibc < 2.35, automatically prefer the no-YJIT
precompiled Ruby variant from jdx/ruby. YJIT builds require
glibc 2.35+ while no-YJIT builds target glibc 2.17, supporting
older distros like RHEL 7, Amazon Linux 2, and CentOS 7.

Falls back to the standard YJIT build if no no-YJIT variant is
available for the requested version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the ruby-glibc-noyjit branch from 3296770 to a8a7b55 Compare February 9, 2026 15:20
- Fix borrowing temporary in filename extraction using match
- Optimize asset search loop with if/else if and early break
- Fix lockfile determinism by passing prefer_no_yjit parameter instead
  of reading host glibc in resolve_lock_info

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx merged commit 9a027fd into main Feb 9, 2026
48 of 85 checks passed
@jdx jdx deleted the ruby-glibc-noyjit branch February 9, 2026 21:15
mise-en-dev added a commit that referenced this pull request Feb 10, 2026
### 🚀 Features

- auto-select no-YJIT Ruby on older glibc systems by @jdx in
[#8069](#8069)

### 🐛 Bug Fixes

- **(shim)** update mise-shim.exe during self-update on Windows by @jdx
in [#8075](#8075)
- Bump xx to 2.5 by @erickt in
[#8077](#8077)

### 📚 Documentation

- **(ruby)** remove experimental language for precompiled binaries by
@jdx in [#8073](#8073)

### New Contributors

- @erickt made their first contribution in
[#8077](#8077)

## 📦 Aqua Registry Updates

#### Updated Packages (1)

- [`carthage-software/mago`](https://github.com/carthage-software/mago)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Summary

- On Linux with glibc < 2.35, automatically download the no-YJIT
precompiled Ruby variant from jdx/ruby
- YJIT builds require glibc 2.35+ (Ubuntu 22.04+, Debian 12+, Fedora
36+)
- No-YJIT builds target glibc 2.17, supporting older distros (RHEL 7,
Amazon Linux 2, CentOS 7)
- Detects glibc version via `ldd --version`, cached for the process
lifetime
- Falls back to standard YJIT build if no no-YJIT variant exists for the
requested version

Depends on jdx/ruby#11 which adds no-YJIT Linux variants to the release
pipeline.

## Test plan

- [ ] On glibc 2.35+ system: installs standard YJIT build (unchanged
behavior)
- [ ] On glibc < 2.35 system: installs `.no_yjit.` variant
- [ ] On glibc < 2.35 with version that has no no-YJIT variant: falls
back to standard build
- [ ] On macOS: unchanged behavior (always YJIT)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes runtime platform detection and alters which Ruby tarball is
downloaded on Linux, which could cause install failures on edge cases
(missing `ldd`, non-glibc systems, or unexpected `ldd --version`
output).
> 
> **Overview**
> Precompiled Ruby installs now *auto-select* the `.no_yjit` release
asset on Linux when detected glibc is older than 2.35, with fallback to
the standard precompiled build if the variant isn’t available.
> 
> This adds a process-wide glibc version detector (`LINUX_GLIBC_VERSION`
via `ldd --version`) and updates the Ruby plugin’s GitHub release asset
selection and download filename handling to support the no-YJIT artifact
naming.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8804fe6. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- auto-select no-YJIT Ruby on older glibc systems by @jdx in
[jdx#8069](jdx#8069)

### 🐛 Bug Fixes

- **(shim)** update mise-shim.exe during self-update on Windows by @jdx
in [jdx#8075](jdx#8075)
- Bump xx to 2.5 by @erickt in
[jdx#8077](jdx#8077)

### 📚 Documentation

- **(ruby)** remove experimental language for precompiled binaries by
@jdx in [jdx#8073](jdx#8073)

### New Contributors

- @erickt made their first contribution in
[jdx#8077](jdx#8077)

## 📦 Aqua Registry Updates

#### Updated Packages (1)

- [`carthage-software/mago`](https://github.com/carthage-software/mago)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants