Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions e2e/backend/test_disable_backends
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#!/usr/bin/env bash
export MISE_EXPERIMENTAL=1

assert_fail "MISE_DISABLE_BACKENDS=ubi mise install ubi:example/example@1.0.0" "backend ubi is disabled by disable_backends"
assert_fail "MISE_DISABLE_BACKENDS=ubi mise use -g ubi:example/example@1.0.0" "backend ubi is disabled by disable_backends"
assert_fail "MISE_DISABLE_BACKENDS=asdf mise install asdf:dummy@1.0.0" "backend asdf is disabled by disable_backends"

eval "$(mise activate bash)" && _mise_hook
assert_contains "MISE_DISABLE_BACKENDS=asdf mise doctor" "dummy"
assert_contains "MISE_DISABLE_BACKENDS=asdf mise doctor -J" '"dummy"'
Comment thread
risu729 marked this conversation as resolved.
Outdated

assert "mise registry age" "aqua:FiloSottile/age asdf:threkk/asdf-age"

mise install age
Expand All @@ -9,3 +17,4 @@ mise uninstall age

MISE_DISABLE_BACKENDS=aqua mise install age
ls "$MISE_DATA_DIR/plugins/age"
assert "MISE_DISABLE_BACKENDS=asdf mise tool age --backend" "aqua:FiloSottile/age"
23 changes: 22 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,29 @@ pub fn remove(short: &str) {
}
}

pub fn is_disabled_backend_type(backend_type: &BackendType) -> bool {
if matches!(backend_type, BackendType::Unknown) {
return false;
}
Settings::get()
.disable_backends
.contains(&backend_type.to_string())
}
Comment thread
risu729 marked this conversation as resolved.
Outdated

pub fn is_disabled_backend(full: &str) -> bool {
full.split_once(':').is_some_and(|(backend, _)| {
Settings::get()
.disable_backends
.contains(&backend.to_string())
})
}

pub fn arg_to_backend(ba: BackendArg) -> Option<ABackend> {
match ba.backend_type() {
let backend_type = ba.backend_type();
if is_disabled_backend_type(&backend_type) {
return None;
}
match backend_type {
BackendType::Core => {
CORE_PLUGINS
.get(&ba.short)
Expand Down
30 changes: 25 additions & 5 deletions src/cli/args/backend_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,20 @@ fn parse_backend_components_fallible(
Ok((short, tool_name.to_string(), opts))
}

fn plugin_backend_type(plugin_name: &str, plugin_type: PluginType) -> BackendType {
match plugin_type {
PluginType::Asdf => BackendType::Asdf,
PluginType::Vfox => BackendType::Vfox,
PluginType::VfoxBackend => BackendType::VfoxBackend(plugin_name.to_string()),
}
}

fn enabled_installed_plugin_type(plugin_name: &str) -> Option<PluginType> {
install_state::get_plugin_type(plugin_name).filter(|plugin_type| {
!backend::is_disabled_backend_type(&plugin_backend_type(plugin_name, *plugin_type))
})
}

impl BackendArg {
#[requires(!short.is_empty())]
pub fn new(short: String, full: Option<String>) -> Self {
Expand Down Expand Up @@ -228,8 +242,11 @@ impl BackendArg {
// }
// })?;
// Ok(backend.clone())
let backend_type = self.backend_type();
if let Some(backend) = backend::get(self) {
Ok(backend)
} else if backend::is_disabled_backend_type(&backend_type) {
bail!("backend {backend_type} is disabled by disable_backends");
} else if let Some((plugin_name, tool_name)) = self.short.split_once(':') {
// Check if the plugin exists first
if let Some(plugin_type) = install_state::get_plugin_type(plugin_name) {
Expand Down Expand Up @@ -325,6 +342,7 @@ impl BackendArg {
// Only check install state for non-plugin:tool format entries
if !self.short.contains(':')
&& let Ok(Some(backend_type)) = install_state::backend_type(&self.short)
&& !backend::is_disabled_backend_type(&backend_type)
{
return backend_type;
}
Expand Down Expand Up @@ -380,7 +398,7 @@ impl BackendArg {
// backend if available. This allows tools to automatically switch backends when
// the registry changes (e.g., when a tool moves from one maintainer to another).
if !self.resolution.explicit
&& install_state::get_plugin_type(short).is_none()
&& enabled_installed_plugin_type(short).is_none()
&& let Some(registry_full) = REGISTRY
.get(short)
.and_then(|rt| rt.backends().first().cloned())
Expand All @@ -397,14 +415,16 @@ impl BackendArg {

if let Some(full) = &self.full {
full.clone()
} else if let Some(full) = install_state::get_tool_full(short) {
} else if let Some(full) =
install_state::get_tool_full(short).filter(|full| !backend::is_disabled_backend(full))
{
full
} else if let Some((plugin_name, _tool_name)) = short.split_once(':') {
// Check if this is a plugin:tool format
if BackendType::guess(short) != BackendType::Unknown {
// Handle built-in backends
short.to_string()
} else if let Some(pt) = install_state::get_plugin_type(plugin_name) {
} else if let Some(pt) = enabled_installed_plugin_type(plugin_name) {
match pt {
PluginType::Asdf => {
// For asdf plugins, plugin:tool format is invalid
Expand All @@ -422,7 +442,7 @@ impl BackendArg {
} else {
short.to_string()
}
} else if let Some(pt) = install_state::get_plugin_type(short) {
} else if let Some(pt) = enabled_installed_plugin_type(short) {
match pt {
PluginType::Asdf => format!("asdf:{short}"),
PluginType::Vfox => format!("vfox:{short}"),
Expand Down Expand Up @@ -621,7 +641,7 @@ impl BackendArg {
}

pub fn uses_plugin(&self) -> bool {
install_state::get_plugin_type(&self.short).is_some()
enabled_installed_plugin_type(&self.short).is_some()
}
Comment thread
risu729 marked this conversation as resolved.
}

Expand Down
94 changes: 70 additions & 24 deletions src/cli/doctor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ impl Doctor {
.map(|p| p.to_string_lossy().to_string())
.collect(),
);
data.insert("plugins".into(), render_plugins_json());

let tools = ts.list_versions_by_plugin().into_iter().map(|(f, tv)| {
let versions: serde_json::Value = tv
Expand Down Expand Up @@ -645,45 +646,90 @@ fn render_backends() -> String {
}

fn render_plugins() -> String {
let plugins = backend::list()
.into_iter()
.filter(|b| {
b.plugin()
.is_some_and(|p| p.is_installed() && b.get_type() == BackendType::Asdf)
})
.collect::<Vec<_>>();
let plugins = installed_plugins();
let max_plugin_name_len = plugins
.iter()
.map(|p| p.id().len())
.map(|p| p.name().len())
.max()
.unwrap_or(0)
.min(40);
plugins
.into_iter()
.filter(|b| b.plugin().is_some())
.map(|p| {
let p = p.plugin().unwrap();
let padded_name = pad_str(p.name(), max_plugin_name_len, Alignment::Left, None);
let extra = match p {
PluginEnum::Asdf(_) | PluginEnum::Vfox(_) | PluginEnum::VfoxBackend(_) => {
let git = Git::new(dirs::PLUGINS.join(p.name()));
match git.get_remote_url() {
Some(url) => {
let sha = git
.current_sha_short()
.unwrap_or_else(|_| "(unknown)".to_string());
format!("{url}#{sha}")
}
None => "".to_string(),
}
} // TODO: PluginType::Core => "(core)".to_string(),
};
let extra = plugin_extra(&p);
format!("{padded_name} {}", style::ndim(extra))
})
.collect::<Vec<_>>()
.join("\n")
}

fn render_plugins_json() -> serde_json::Value {
installed_plugins()
.into_iter()
.map(|plugin| {
let mut value = serde_json::Map::new();
value.insert(
"type".into(),
serde_json::Value::String(plugin_type_name(plugin.get_plugin_type()).to_string()),
);
if let Some(git) = plugin_git(&plugin)
&& let Some(url) = git.get_remote_url()
{
value.insert("url".into(), serde_json::Value::String(url));
if let Ok(ref_) = git.current_abbrev_ref() {
value.insert("ref".into(), serde_json::Value::String(ref_));
}
if let Ok(sha) = git.current_sha_short() {
value.insert("sha".into(), serde_json::Value::String(sha));
}
}
(plugin.name().to_string(), serde_json::Value::Object(value))
})
.collect::<serde_json::Map<_, _>>()
.into()
}

fn installed_plugins() -> Vec<PluginEnum> {
install_state::list_plugins()
.iter()
.map(|(name, plugin_type)| plugin_type.plugin(name.clone()))
.filter(|plugin| plugin.is_installed())
.collect()
}

fn plugin_extra(plugin: &PluginEnum) -> String {
let Some(git) = plugin_git(plugin) else {
return "".to_string();
};
match git.get_remote_url() {
Some(url) => {
let sha = git
.current_sha_short()
.unwrap_or_else(|_| "(unknown)".to_string());
format!("{url}#{sha}")
}
None => "".to_string(),
}
}

fn plugin_git(plugin: &PluginEnum) -> Option<Git> {
let path = dirs::PLUGINS.join(plugin.name());
if path.join(".git").exists() {
Some(Git::new(path))
} else {
None
}
}

fn plugin_type_name(plugin_type: PluginType) -> &'static str {
match plugin_type {
PluginType::Asdf => "asdf",
PluginType::Vfox => "vfox",
PluginType::VfoxBackend => "vfox_backend",
}
}

fn build_info() -> IndexMap<String, &'static str> {
let mut s = IndexMap::new();
s.insert("Target".into(), built_info::TARGET);
Expand Down
Loading