Skip to content

fix(ruby): lock resolved install options#9992

Draft
risu729 wants to merge 6 commits into
jdx:mainfrom
risu729:fix/ruby-build-lock-options
Draft

fix(ruby): lock resolved install options#9992
risu729 wants to merge 6 commits into
jdx:mainfrom
risu729:fix/ruby-build-lock-options

Conversation

@risu729
Copy link
Copy Markdown
Contributor

@risu729 risu729 commented May 19, 2026

Summary

  • include resolved Ruby install options in lock identity, including defaults such as compile mode, installer choice, the active build repo, and precompiled URL
  • include source-build options and patch inputs that can change compiled output or fallback output
  • add backend-level regression coverage for precompiled, experimental-default, resolved-default, source-build, fallback, and ruby-install paths

Rationale

Lock option values are part of the artifact identity. Recording resolved defaults keeps existing lockfiles stable if mise defaults change later, including Ruby's planned move toward precompiled binaries by default.

Classification

Mixed actual lock/install identity and stale-lock invalidation fix.

Actual identity: source-build settings such as installer choice, build repositories, build options, and patch inputs can change the compiled Ruby tree for the same version. Those inputs are not represented by a locked artifact URL.

Stale-lock invalidation: precompiled URL/platform selector settings mostly choose the binary artifact URL/checksum. Once the lock entry is selected, the locked URL identifies the binary, but the selector still belongs in the exact-options key so changing precompiled source or platform settings does not reuse an older lock entry.

Verification

  • cargo fmt --check
  • git diff --check
  • cargo test ruby_lockfile_options
  • GitHub Actions test workflow on 0d98b51f1 passed, including unit tests, lint, e2e shards, Windows, and test-ci
  • GitHub Actions hyperfine, registry, and release workflows on 0d98b51f1 passed or skipped only expected matrix jobs

Summary by CodeRabbit

  • Improvements

    • Enhanced Ruby installation lockfile resolution to intelligently handle both precompiled and source-build strategies based on configuration options.
  • Tests

    • Added comprehensive test coverage for lockfile option resolution scenarios, including precompiled selection, source builds with patches, and fallback behaviors.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 19, 2026

Greptile Summary

This PR fixes the Ruby backend's lock identity to include all inputs that can change the compiled or downloaded artifact: compile mode, installer choice (ruby-build vs ruby-install), their repo URLs and extra options, patch inputs, and precompiled URL/platform selectors. Six new unit tests — guarded by a Mutex to serialise global Settings mutations — cover the precompiled, source-build, fallback, experimental-default, and ruby-install paths.

  • resolve_lockfile_options is rewritten to emit compile, ruby_install, repo, opts, and patch keys when running on the current platform, and to emit precompiled URL/arch/os keys when try_precompiled is true.
  • try_precompiled in resolve_lockfile_options duplicates the logic from should_try_precompiled() verbatim; the latter has a versioned debug_assert! reminder to update it at 2026.8.0, but the inline copy will not get that reminder.

Confidence Score: 4/5

Safe to merge; the lock identity expansion is correct and well-tested, with one minor duplication that could drift when the precompiled-default TODO is resolved.

The logic change is straightforward and backed by six targeted tests. The only concern is that try_precompiled is inlined rather than delegated to should_try_precompiled(), which carries a versioned reminder comment. If that method is updated at 2026.8.0 and the inline copy is missed, lock entries will diverge from installation behaviour.

src/plugins/core/ruby.rs — the try_precompiled duplication on lines 1028-1029 relative to should_try_precompiled() at line 441.

Important Files Changed

Filename Overview
src/plugins/core/ruby.rs Expands resolve_lockfile_options to include compile mode, installer choice, repo URLs, build options, patch inputs, and precompiled URL/platform selectors in the lock identity; adds six unit tests with mutex-based global-settings isolation covering each major installation path.

Reviews (7): Last reviewed commit: "fix(ruby): lock resolved install options" | Re-trigger Greptile

Comment thread src/plugins/core/ruby.rs Outdated
Comment thread src/plugins/core/ruby.rs Outdated
Copy link
Copy Markdown
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 ToolOptionSource::InstallManifest to track and apply tool options from installation manifests, updating BackendArg and configuration resolution logic accordingly. The Ruby plugin's lockfile options were refactored into ruby_lockfile_options to include a broader set of settings such as compilation flags, installer repositories, and precompiled binary metadata. Feedback suggests simplifying a match block in the Ruby plugin using a more idiomatic if let pattern to improve readability.

Comment thread src/plugins/core/ruby.rs Outdated
@risu729

This comment was marked as outdated.

@risu729 risu729 force-pushed the fix/ruby-build-lock-options branch from 556e50e to 7fb77b1 Compare May 31, 2026 08:19
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

resolve_lockfile_options now conditionally builds lockfile inputs based on platform and precompiled Ruby settings. It derives a try_precompiled flag, inserts compile mode and installer configuration (ruby_install or ruby_build), and optionally includes precompiled URLs and patch application. Tests provide deterministic scaffolding and validate the resulting option maps across multiple scenarios.

Changes

Ruby Lockfile Options Resolution

Layer / File(s) Summary
Core option-building logic
src/plugins/core/ruby.rs
resolve_lockfile_options derives try_precompiled from ruby.compile and experimental settings, then conditionally inserts compile, installer choice, repo/opts pairs, patches, and precompiled URLs into the lockfile options map.
Test infrastructure and validation suite
src/plugins/core/ruby.rs
Test harness adds mutex-protected reset guard, default constants, and helper function to apply settings and construct RubyPlugin. Unit tests validate lockfile option maps under precompiled opt-in, source builds with patches, experimental defaults, resolved defaults, fallback inputs, and ruby_install scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A Ruby plugin hops with care,
Precompiled or built from ware,
Options map with logic neat,
Tests that make the change complete! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: locking resolved Ruby install options into the lock identity to prevent lockfile instability.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@risu729 risu729 changed the title fix(ruby): include build source options in lock identity fix(ruby): lock resolved install options Jun 1, 2026
@risu729 risu729 marked this pull request as ready for review June 2, 2026 08:59
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/plugins/core/ruby.rs (1)

1175-1179: ⚡ Quick win

Avoid poisoning the shared test mutex.

A single panic while holding TEST_SETTINGS_LOCK will poison the mutex and make later tests fail during setup instead of surfacing the original assertion failure.

Suggested change
-        let lock = TEST_SETTINGS_LOCK.lock().unwrap();
+        let lock = TEST_SETTINGS_LOCK
+            .lock()
+            .unwrap_or_else(|poisoned| poisoned.into_inner());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/core/ruby.rs` around lines 1175 - 1179, The TEST_SETTINGS_LOCK
acquisition can poison the mutex if a panic occurs while held; change the lock
call to recover from poisoning by using
TEST_SETTINGS_LOCK.lock().unwrap_or_else(|poison| poison.into_inner()) so the
guard still obtains the inner mutex guard, then proceed to call
SettingsPartial::empty, configure_settings, Settings::reset and create the
SettingsResetGuard as before; this ensures a poisoned mutex won't make
subsequent test setup fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/core/ruby.rs`:
- Around line 1305-1325: The tests in
test_ruby_lockfile_options_include_experimental_precompiled_default incorrectly
assume precompiled-by-default; update the test cases that call
resolve_ruby_lockfile_options so they mirror the runtime gating used by
should_try_precompiled() and install_version_: for the “experimental default”
variant explicitly set settings.experimental = true (so precompiled behavior is
enabled), and for the “resolved defaults” variant either set
settings.ruby.compile = Some(false) to opt into precompiled behavior or else
change the expected BTreeMap to the current (non-precompiled) defaults; locate
these changes around the test function
test_ruby_lockfile_options_include_experimental_precompiled_default and the
sibling test referenced at lines 1329-1346 and adjust expectations to match the
presence or absence of settings.experimental and settings.ruby.compile.
- Around line 1031-1056: When building the options map for non-current targets
(i.e. where is_current_platform is false) ensure you still insert the resolved
source-build defaults so lockfile identity is complete: always set "compile" to
(!try_precompiled).to_string(), set "ruby_install" to
ruby.ruby_install.to_string(), and populate the installer-specific keys by
reading ruby.ruby_install_opts/ruby.ruby_install_repo when ruby.ruby_install is
true or ruby.ruby_build_opts/ruby.ruby_build_repo when false; also insert
"apply_patches" from ruby.apply_patches when present. Update the code around
opts insertion (the block that currently runs only under if is_current_platform)
to perform these same inserts for non-current targets so the options-map
includes compile, installer choice, repo, build opts, and patches for both
current and non-current platforms.

---

Nitpick comments:
In `@src/plugins/core/ruby.rs`:
- Around line 1175-1179: The TEST_SETTINGS_LOCK acquisition can poison the mutex
if a panic occurs while held; change the lock call to recover from poisoning by
using TEST_SETTINGS_LOCK.lock().unwrap_or_else(|poison| poison.into_inner()) so
the guard still obtains the inner mutex guard, then proceed to call
SettingsPartial::empty, configure_settings, Settings::reset and create the
SettingsResetGuard as before; this ensures a poisoned mutex won't make
subsequent test setup fail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a2d20b8e-0413-45d2-88f4-54d5eff4391c

📥 Commits

Reviewing files that changed from the base of the PR and between 5cdba15 and 0d98b51.

📒 Files selected for processing (1)
  • src/plugins/core/ruby.rs

Comment thread src/plugins/core/ruby.rs
Comment on lines +1031 to +1056
if is_current_platform {
opts.insert("compile".to_string(), (!try_precompiled).to_string());

// Ruby uses ruby-install vs ruby-build. The installer and its options
// can affect the source-built output, including fallback after a
// missing precompiled binary.
opts.insert("ruby_install".to_string(), ruby.ruby_install.to_string());
if ruby.ruby_install {
if let Some(ruby_install_opts) = ruby.ruby_install_opts.clone() {
opts.insert("ruby_install_opts".to_string(), ruby_install_opts);
}
opts.insert(
"ruby_install_repo".to_string(),
ruby.ruby_install_repo.clone(),
);
} else {
if let Some(ruby_build_opts) = ruby.ruby_build_opts.clone() {
opts.insert("ruby_build_opts".to_string(), ruby_build_opts);
}
opts.insert("ruby_build_repo".to_string(), ruby.ruby_build_repo.clone());
}

// Ruby uses ruby-install vs ruby-build (ruby compiles from source either way)
// Only include if using non-default ruby-install tool
let ruby_install = if is_current_platform {
settings.ruby.ruby_install
} else {
false
};
if ruby_install {
opts.insert("ruby_install".to_string(), "true".to_string());
if let Some(apply_patches) = ruby.apply_patches.clone() {
opts.insert("apply_patches".to_string(), apply_patches);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Include resolved source-build defaults for non-current targets too.

For non-current targets this now drops compile, installer choice, repo, build opts, and patches entirely. Those values still affect the fallback/source artifact, and lockfile lookup later requires an exact options-map match, so a cross-platform lock entry can be generated with too little identity and then miss once that platform resolves locally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/core/ruby.rs` around lines 1031 - 1056, When building the options
map for non-current targets (i.e. where is_current_platform is false) ensure you
still insert the resolved source-build defaults so lockfile identity is
complete: always set "compile" to (!try_precompiled).to_string(), set
"ruby_install" to ruby.ruby_install.to_string(), and populate the
installer-specific keys by reading ruby.ruby_install_opts/ruby.ruby_install_repo
when ruby.ruby_install is true or ruby.ruby_build_opts/ruby.ruby_build_repo when
false; also insert "apply_patches" from ruby.apply_patches when present. Update
the code around opts insertion (the block that currently runs only under if
is_current_platform) to perform these same inserts for non-current targets so
the options-map includes compile, installer choice, repo, build opts, and
patches for both current and non-current platforms.

Comment thread src/plugins/core/ruby.rs
Comment on lines +1305 to +1325
fn test_ruby_lockfile_options_include_experimental_precompiled_default() {
let opts = resolve_ruby_lockfile_options(|settings| {
settings.ruby.precompiled_url = Some("acme/ruby".to_string());
settings.ruby.precompiled_arch = Some("arm64".to_string());
settings.ruby.precompiled_os = Some("linux".to_string());
});

assert_eq!(
opts,
BTreeMap::from([
("compile".to_string(), "false".to_string()),
("precompiled_arch".to_string(), "arm64".to_string()),
("precompiled_os".to_string(), "linux".to_string()),
("precompiled_url".to_string(), "acme/ruby".to_string()),
(
"ruby_build_repo".to_string(),
DEFAULT_RUBY_BUILD_REPO.to_string(),
),
("ruby_install".to_string(), "false".to_string()),
])
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

These assertions encode precompiled-by-default behavior too early.

Both tests expect compile=false/precompiled_url with ruby.compile unset, but should_try_precompiled() and install_version_() still require either ruby.compile = Some(false) or settings.experimental = true. As written, the “experimental default” case never enables experimental, and the “resolved defaults” case asserts the future default instead of the current one.

Also applies to: 1329-1346

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/core/ruby.rs` around lines 1305 - 1325, The tests in
test_ruby_lockfile_options_include_experimental_precompiled_default incorrectly
assume precompiled-by-default; update the test cases that call
resolve_ruby_lockfile_options so they mirror the runtime gating used by
should_try_precompiled() and install_version_: for the “experimental default”
variant explicitly set settings.experimental = true (so precompiled behavior is
enabled), and for the “resolved defaults” variant either set
settings.ruby.compile = Some(false) to opt into precompiled behavior or else
change the expected BTreeMap to the current (non-precompiled) defaults; locate
these changes around the test function
test_ruby_lockfile_options_include_experimental_precompiled_default and the
sibling test referenced at lines 1329-1346 and adjust expectations to match the
presence or absence of settings.experimental and settings.ruby.compile.

@risu729 risu729 marked this pull request as draft June 2, 2026 09:12
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.

1 participant