From 160c50983ffd8e05a6529ac2c674299f6a5643dc Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:09:11 -0600 Subject: [PATCH] fix(install): sort failed installations for deterministic error output With parallel tool installation, failures complete in arbitrary order. This caused test_error_display to fail intermittently because the error message listed tools in completion order rather than a consistent order. Sort failed tools alphabetically in the error message to ensure deterministic output regardless of parallel execution order. Co-Authored-By: Claude Opus 4.5 --- e2e/cli/test_error_display | 2 +- src/errors.rs | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/e2e/cli/test_error_display b/e2e/cli/test_error_display index c3cef32c0a..92d3ff99d4 100644 --- a/e2e/cli/test_error_display +++ b/e2e/cli/test_error_display @@ -108,7 +108,7 @@ python = ["also", "not", "valid"] EOF local_assert_fail "MISE_CONFIG_FILE=test_invalid_config.toml mise install" \ - "0: Failed to install tools: core:python@also, core:python@not, core:python@valid, core:node@this is not valid" + "0: Failed to install tools: core:node@this is not valid, core:python@also, core:python@not, core:python@valid" rm -f test_invalid_config.toml echo "" diff --git a/src/errors.rs b/src/errors.rs index 8eb53a8bca..8b3fc39188 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -60,10 +60,17 @@ fn format_install_failures(failed_installations: &[(ToolRequest, Report)]) -> St } // For multiple failures, show a summary and then each error + // Sort by tool name for deterministic output (parallel installs complete in arbitrary order) + let mut sorted_failures: Vec<_> = failed_installations + .iter() + .map(|(tr, err)| (format!("{}@{}", tr.ba().full(), tr.version()), err)) + .collect(); + sorted_failures.sort_by(|a, b| a.0.cmp(&b.0)); + let mut output = vec![]; - let failed_tools: Vec = failed_installations + let failed_tools: Vec<&str> = sorted_failures .iter() - .map(|(tr, _)| format!("{}@{}", tr.ba().full(), tr.version())) + .map(|(name, _)| name.as_str()) .collect(); output.push(format!( @@ -71,15 +78,10 @@ fn format_install_failures(failed_installations: &[(ToolRequest, Report)]) -> St failed_tools.join(", ") )); - // Show detailed errors for each failure + // Show detailed errors for each failure (in sorted order) // Use {:#} to show full error chain (includes wrapped errors) - for (tr, error) in failed_installations.iter() { - output.push(format!( - "\n{}@{}: {:#}", - tr.ba().full(), - tr.version(), - error - )); + for (name, error) in sorted_failures.iter() { + output.push(format!("\n{}: {:#}", name, error)); } output.join("\n")