Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 180 additions & 9 deletions src/plugins/core/ruby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ use crate::ui::progress_report::SingleReport;
use crate::{file, hash, plugins, timeout};

const RUBY_INDEX_URL: &str = "https://cache.ruby-lang.org/pub/ruby/index.txt";
const DEFAULT_RUBY_PRECOMPILED_URL: &str = "jdx/ruby";
const DEFAULT_RUBY_BUILD_REPO: &str = "https://github.com/rbenv/ruby-build.git";
const DEFAULT_RUBY_INSTALL_REPO: &str = "https://github.com/postmodern/ruby-install.git";
const ATTESTATION_HELP: &str = "To disable attestation verification, set MISE_RUBY_GITHUB_ATTESTATIONS=false\n\
or add `ruby.github_attestations = false` under [settings] in mise.toml";

Expand Down Expand Up @@ -1023,17 +1026,54 @@ impl Backend for RubyPlugin {
) -> BTreeMap<String, String> {
let mut opts = BTreeMap::new();
let settings = Settings::get();
let ruby = &settings.ruby;
let is_current_platform = target.is_current();

// 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 is_current_platform {
if let Some(compile) = ruby.compile {
opts.insert("compile".to_string(), compile.to_string());
} else if settings.experimental {
opts.insert("compile".to_string(), "false".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.
if ruby.ruby_install {
opts.insert("ruby_install".to_string(), "true".to_string());
if let Some(ruby_install_opts) = ruby.ruby_install_opts.clone() {
opts.insert("ruby_install_opts".to_string(), ruby_install_opts);
}
if ruby.ruby_install_repo != DEFAULT_RUBY_INSTALL_REPO {
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);
}
if ruby.ruby_build_repo != DEFAULT_RUBY_BUILD_REPO {
opts.insert("ruby_build_repo".to_string(), ruby.ruby_build_repo.clone());
}
}

if let Some(apply_patches) = ruby.apply_patches.clone() {
opts.insert("apply_patches".to_string(), apply_patches);
}
}

if ruby.compile == Some(false) || (settings.experimental && ruby.compile.is_none()) {
if ruby.precompiled_url != DEFAULT_RUBY_PRECOMPILED_URL {
opts.insert("precompiled_url".to_string(), ruby.precompiled_url.clone());
}
if let Some(precompiled_arch) = ruby.precompiled_arch.clone() {
opts.insert("precompiled_arch".to_string(), precompiled_arch);
}
if let Some(precompiled_os) = ruby.precompiled_os.clone() {
opts.insert("precompiled_os".to_string(), precompiled_os);
}
}

opts
Expand Down Expand Up @@ -1119,9 +1159,38 @@ fn parse_gemfile(body: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use crate::config::settings::SettingsPartial;
use crate::toolset::ToolSource;
use confique::Layer;
use indoc::indoc;
use pretty_assertions::assert_eq;

static TEST_SETTINGS_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());

struct SettingsResetGuard {
_lock: std::sync::MutexGuard<'static, ()>,
}

impl Drop for SettingsResetGuard {
fn drop(&mut self) {
Settings::reset(None);
}
}

fn resolve_ruby_lockfile_options(
configure_settings: impl FnOnce(&mut SettingsPartial),
) -> BTreeMap<String, String> {
let lock = TEST_SETTINGS_LOCK.lock().unwrap();
let mut settings = SettingsPartial::empty();
configure_settings(&mut settings);
Settings::reset(Some(settings));
let _guard = SettingsResetGuard { _lock: lock };

let backend = RubyPlugin::new();
let request = ToolRequest::new(backend.ba().clone(), "3.3.0", ToolSource::Unknown).unwrap();
backend.resolve_lockfile_options(&request, &PlatformTarget::from_current())
}

#[test]
fn test_tag_to_version() {
// Standard versions
Expand Down Expand Up @@ -1188,4 +1257,106 @@ mod tests {
""
);
}

#[test]
fn test_ruby_lockfile_options_include_precompiled_inputs() {
let opts = resolve_ruby_lockfile_options(|settings| {
settings.ruby.compile = Some(false);
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()),
])
);
}

#[test]
fn test_ruby_lockfile_options_include_source_build_inputs() {
let opts = resolve_ruby_lockfile_options(|settings| {
settings.ruby.compile = Some(true);
settings.ruby.ruby_build_opts = Some("--enable-yjit".to_string());
settings.ruby.apply_patches = Some("https://example.com/ruby.patch".to_string());
});

assert_eq!(
opts,
BTreeMap::from([
(
"apply_patches".to_string(),
"https://example.com/ruby.patch".to_string(),
),
("compile".to_string(), "true".to_string()),
("ruby_build_opts".to_string(), "--enable-yjit".to_string()),
])
);
}

#[test]
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()),
])
);
}

#[test]
fn test_ruby_lockfile_options_include_source_fallback_inputs() {
let opts = resolve_ruby_lockfile_options(|settings| {
settings.ruby.compile = Some(false);
settings.ruby.ruby_build_opts = Some("--enable-yjit".to_string());
settings.ruby.apply_patches = Some("https://example.com/ruby.patch".to_string());
});

assert_eq!(
opts,
BTreeMap::from([
(
"apply_patches".to_string(),
"https://example.com/ruby.patch".to_string(),
),
("compile".to_string(), "false".to_string()),
("ruby_build_opts".to_string(), "--enable-yjit".to_string()),
])
);
}

#[test]
fn test_ruby_lockfile_options_include_ruby_install_inputs() {
let opts = resolve_ruby_lockfile_options(|settings| {
settings.ruby.compile = Some(true);
settings.ruby.ruby_install = Some(true);
settings.ruby.ruby_install_opts = Some("--no-reinstall".to_string());
});

assert_eq!(
opts,
BTreeMap::from([
("compile".to_string(), "true".to_string()),
("ruby_install".to_string(), "true".to_string()),
(
"ruby_install_opts".to_string(),
"--no-reinstall".to_string()
),
])
);
}
}
Loading