Skip to content
Merged
Show file tree
Hide file tree
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
20 changes: 20 additions & 0 deletions e2e/cli/test_tool
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail

# Test valid tool information
mise tool node | grep -q "Backend:" || fail "Backend field not found"

# Test JSON output
mise tool node --json | grep -q '"backend"' || fail "JSON backend field not found"

# Test specific field filters
backend_output=$(mise tool node --backend)
[[ -n $backend_output ]] || fail "Backend output is empty"

# Test that invalid tool names should error
assert_fail "mise tool INVALID_TOOL_NAME"

# Test duplicate version bug - install multiple versions of a tool
mise use tiny@1.0.0
mise use tiny@1.1.0
assert "mise tool tiny --installed" "1.0.0 1.1.0"
58 changes: 54 additions & 4 deletions src/cli/tool.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::ui::style;
use eyre::Result;
use itertools::Itertools;
use serde_derive::Serialize;
Expand Down Expand Up @@ -61,7 +62,19 @@ impl Tool {
let tvl = ts.versions.get(&self.tool);
let tv = tvl.map(|tvl| tvl.versions.first().unwrap());
let ba = tv.map(|tv| tv.ba()).unwrap_or_else(|| &self.tool);
let backend = ba.backend().ok();

// Check if the backend exists and fail if it doesn't
let backend = match ba.backend() {
Ok(b) => Some(b),
Err(e) => {
// If no versions are configured for this tool, it's likely invalid
if tvl.is_none() {
return Err(e);
}
None
}
};

let description = if let Some(backend) = backend {
backend.description().await
} else {
Expand All @@ -76,6 +89,7 @@ impl Tool {
.into_iter()
.filter(|(b, _)| b.ba().as_ref() == ba)
.map(|(_, tv)| tv.version)
.unique()
.collect::<Vec<_>>(),
active_versions: tvl.map(|tvl| {
tvl.versions
Expand Down Expand Up @@ -137,7 +151,23 @@ impl Tool {
miseprintln!("[none]");
}
} else if self.filter.installed {
miseprintln!("{}", info.installed_versions.join(" "));
let active_set = info
.active_versions
.as_ref()
.map(|v| v.iter().collect::<std::collections::HashSet<_>>())
.unwrap_or_default();
let installed_with_bold = info
.installed_versions
.iter()
.map(|v| {
if active_set.contains(v) {
style::nbold(v).to_string()
} else {
v.to_string()
}
})
.join(" ");
miseprintln!("{}", installed_with_bold);
} else if self.filter.active {
if let Some(active_versions) = info.active_versions {
miseprintln!("{}", active_versions.join(" "));
Expand Down Expand Up @@ -170,9 +200,29 @@ impl Tool {
if let Some(description) = info.description {
table.push(("Description:", description));
}
table.push(("Installed Versions:", info.installed_versions.join(" ")));
// Bold currently active versions within the installed list for clarity
let active_set = info
.active_versions
.as_ref()
.map(|v| v.iter().collect::<std::collections::HashSet<_>>())
.unwrap_or_default();
let installed_with_bold = info
.installed_versions
.iter()
.map(|v| {
if active_set.contains(v) {
style::nbold(v).to_string()
} else {
v.to_string()
}
})
.join(" ");
table.push(("Installed Versions:", installed_with_bold));
if let Some(active_versions) = info.active_versions {
table.push(("Active Version:", active_versions.join(" ")));
table.push((
"Active Version:",
style::nbold(active_versions.join(" ")).to_string(),
));
}
if let Some(requested_versions) = info.requested_versions {
table.push(("Requested Version:", requested_versions.join(" ")));
Expand Down
9 changes: 5 additions & 4 deletions src/toolset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,12 @@ impl Toolset {
for v in b.list_installed_versions() {
if let Some((p, tv)) = current_versions.get(&(b.id().into(), v.clone())) {
versions.push((p.clone(), tv.clone()));
} else {
let tv = ToolRequest::new(b.ba().clone(), &v, ToolSource::Unknown)?
.resolve(config, &Default::default())
.await?;
versions.push((b.clone(), tv));
}
let tv = ToolRequest::new(b.ba().clone(), &v, ToolSource::Unknown)?
.resolve(config, &Default::default())
.await?;
versions.push((b.clone(), tv));
}
}

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

There appears to be a misplaced closing brace. This brace seems to close the outer loop prematurely, which would prevent processing of subsequent backends. The brace should likely be moved to after line 511 to properly close the inner loop iteration.

Copilot uses AI. Check for mistakes.
Ok(versions)
Expand Down
4 changes: 4 additions & 0 deletions src/ui/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ pub fn ebold<D>(val: D) -> StyledObject<D> {
estyle(val).bold()
}

pub fn nbold<D>(val: D) -> StyledObject<D> {
nstyle(val).bold()
}

pub fn epath(path: &Path) -> StyledObject<String> {
estyle(display_path(path))
}
Expand Down
Loading