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
Empty file modified e2e/backend/test_aqua_cosign
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_aqua_github_attestations
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_aqua_slsa
100755 → 100644
Empty file.
130 changes: 130 additions & 0 deletions e2e/backend/test_backend_missing_deps
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env bash

# Test that package manager backends show helpful errors when their dependencies are missing

# Create a PATH that has mise but not package managers
# ROOT is set by run_test script
MISE_DIR="${CARGO_TARGET_DIR:-$ROOT/target}/debug"
export PATH="$MISE_DIR:/usr/bin:/bin:/usr/sbin:/sbin"

# Track overall test status
TEST_FAILED=0

# Helper function to check if a command exists in PATH
check_command_missing() {
local cmd=$1
if command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: $cmd is found in PATH, test cannot verify missing dependency behavior"
exit 1
fi
}

# Helper function to test an expected error message
test_error_message() {
local test_name=$1
local output=$2
local expected=$3

if [[ $output == *"$expected"* ]]; then
echo "✓ $test_name"
else
echo "ERROR: $test_name failed"
echo "Expected: $expected"
echo "Got: $output"
TEST_FAILED=1
return 1
fi
}

# Helper function to test a command that should fail with expected error
test_backend_error() {
local backend_name=$1
local command=$2
local expected_error=$3

local output
if output=$(eval "$command" 2>&1); then
echo "ERROR: $backend_name command should have failed but succeeded"
TEST_FAILED=1
return 1
else
output=$(eval "$command" 2>&1 || true)
test_error_message "$backend_name shows correct error" "$output" "$expected_error"
fi
}

# Test npm backend
test_npm() {
echo "Testing npm backend with missing npm..."
check_command_missing "npm"

# Test ls-remote
test_backend_error "npm" "mise ls-remote npm:test-package" "npm is required but not found"

# Check for helpful install instructions
local output
output=$(mise ls-remote npm:test-package 2>&1 || true)
test_error_message "npm suggests installing node" "$output" "mise use node@latest"
test_error_message "npm mentions npm is needed for queries" "$output" "npm is required for querying package information"

# Test install
test_backend_error "npm" "mise install npm:test-package@latest" "npm is required but not found"

# Test with bun mode enabled but npm still missing
echo "Testing npm backend with bun mode enabled but npm missing..."
export MISE_NPM_BUN=true
test_backend_error "npm (bun mode)" "mise ls-remote npm:test-package" "npm is required but not found"
output=$(mise ls-remote npm:test-package 2>&1 || true)
test_error_message "npm (bun mode) shows npm is required for queries" "$output" "npm is required for querying package information"
unset MISE_NPM_BUN
}

# Test cargo backend
test_cargo() {
echo "Testing cargo backend with missing cargo..."

# Remove cargo from PATH if it exists
local new_path=""
local IFS=':'
read -ra PATHS <<<"$PATH"
for p in "${PATHS[@]}"; do
if [[ ! -x "$p/cargo" ]]; then
[[ -z $new_path ]] && new_path="$p" || new_path="$new_path:$p"
fi
done
export PATH="$new_path"

check_command_missing "cargo"
test_backend_error "cargo" "mise install cargo:tiny@latest" "cargo is required but not found"
}

# Test go backend
test_go() {
echo "Testing go backend with missing go..."

# Remove go from PATH if it exists
local new_path=""
local IFS=':'
read -ra PATHS <<<"$PATH"
for p in "${PATHS[@]}"; do
if [[ ! -x "$p/go" ]]; then
[[ -z $new_path ]] && new_path="$p" || new_path="$new_path:$p"
fi
done
export PATH="$new_path"

check_command_missing "go"
test_backend_error "go" "mise ls-remote go:github.com/test/test" "go is required but not found"
}

# Run all tests
test_npm
test_cargo
test_go

if [ $TEST_FAILED -eq 0 ]; then
echo "✓ backend dependency error test passed"
else
echo "✗ Some tests failed"
exit 1
fi
Empty file modified e2e/backend/test_dotnet
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_github_docker_compose
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_github_tools
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_github_url_tracking
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_http_binary_clean
100755 → 100644
Empty file.
Empty file modified e2e/backend/test_http_caching
100755 → 100644
Empty file.
84 changes: 76 additions & 8 deletions e2e/backend/test_npm_bun
Original file line number Diff line number Diff line change
@@ -1,12 +1,80 @@
#!/usr/bin/env bash

cat >mise.toml <<EOF
[settings.npm]
bun = true
EOF
# Test that npm backend properly uses bun when configured
# This test primarily verifies behavior when npm and/or bun are available

mise use bun node
# Ensure npm is available by installing node if needed
if ! command -v npm >/dev/null 2>&1; then
echo "npm not available in test environment, installing node..."
# Disable GPG verification for faster test
export MISE_GPG_VERIFY=false
assert_succeed "mise use node@20"
fi

assert "mise x npm:prettier@3.1.0 -- prettier -v" "3.1.0"
assert "FORCE_COLOR=0 mise x npm:@antfu/ni@0.21.12 -- ni -v 2>/dev/null | head -n1" "@antfu/ni v0.21.12"
assert_succeed "mise install npm:tldr@3.4.0"
# Ensure bun is available
if ! command -v bun >/dev/null 2>&1; then
echo "bun not available in test environment, installing..."
assert_succeed "mise use bun@latest"
fi

# Test 1: With npm available but bun mode disabled (default)
echo "Testing npm backend with npm available..."
unset MISE_NPM_BUN

# Test listing versions - should succeed
assert_succeed "mise ls-remote npm:tiny >/dev/null"
echo "✓ npm backend lists versions using npm"

# Test latest version - should succeed
assert_succeed "mise latest npm:tiny >/dev/null"
echo "✓ npm backend gets latest version using npm"

# Test 2: With bun mode enabled - npm backend should still use npm for version queries
echo "Testing npm backend with bun mode enabled..."
export MISE_NPM_BUN=true

# The npm backend always uses npm for version queries (bun info requires package.json)
# This is documented in the TODOs in src/backend/npm.rs
assert_succeed "mise ls-remote npm:tiny >/dev/null"
echo "✓ npm backend uses npm for version queries even in bun mode"

# Test latest version - should succeed
assert_succeed "mise latest npm:tiny >/dev/null"
echo "✓ npm backend gets latest version using npm even in bun mode"

unset MISE_NPM_BUN

# Test 3: Test installation with bun mode
echo "Testing npm package installation with bun..."
export MISE_NPM_BUN=true

# Clean up any previous installation
mise uninstall npm:tiny@latest >/dev/null 2>&1 || true

# Install a small package using bun
assert_succeed "mise install npm:tiny@latest >/dev/null 2>&1"
echo "✓ npm backend successfully installs package using bun"

# Verify the package was installed by executing it
assert_succeed "mise exec npm:tiny@latest -- tiny --version >/dev/null"
echo "✓ Installed package can be executed"

# Clean up
mise uninstall npm:tiny@latest >/dev/null 2>&1 || true

unset MISE_NPM_BUN

# Test 4: Test installation with npm mode (default)
echo "Testing npm package installation with npm (default)..."

# Clean up any previous installation
mise uninstall npm:tiny@latest >/dev/null 2>&1 || true

# Install using npm (default)
assert_succeed "mise install npm:tiny@latest >/dev/null 2>&1"
echo "✓ npm backend successfully installs package using npm"

# Clean up
mise uninstall npm:tiny@latest >/dev/null 2>&1 || true

echo "✓ npm bun behavior test completed"
Empty file modified e2e/backend/test_vfox_backend_npm
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_env_redacted_flags
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_error_display
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_generate_tool_stub
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_install_dry_run
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_install_parallel_failure
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_lock
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_lock_creation
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_lock_future
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_mcp
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_mcp_protocol
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_nonexistent_cwd
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_set_env
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_tool
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_tool_stub_basic
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_tool_stub_errors
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_tool_stub_http
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_upgrade_parallel_failure
100755 → 100644
Empty file.
Empty file modified e2e/cli/test_use_dry_run
100755 → 100644
Empty file.
Empty file modified e2e/core/test_java_url_tracking
100755 → 100644
Empty file.
Empty file modified e2e/env/test_env_path_node_regression
100755 → 100644
Empty file.
Empty file modified e2e/env/test_env_path_resolution
100755 → 100644
Empty file.
Empty file modified e2e/env/test_poetry_path
100755 → 100644
Empty file.
Empty file modified e2e/generate/test_generate_devcontainer
100755 → 100644
Empty file.
Empty file modified e2e/generate/test_generate_task_docs
100755 → 100644
Empty file.
Empty file modified e2e/generate/test_generate_tool_stub
100755 → 100644
Empty file.
Empty file modified e2e/generate/test_generate_tool_stub_jq
100755 → 100644
Empty file.
Empty file modified e2e/secrets/test_secrets_non_strict
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_depends_post_multiple
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_display_truncation
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_env_directives
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_ls_usage
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_parallel_execution
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_remote_git_https
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_remote_git_ssh
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_sequence_failure
100755 → 100644
Empty file.
Empty file modified e2e/tasks/test_task_timeout
100755 → 100644
Empty file.
10 changes: 10 additions & 0 deletions src/backend/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ impl Backend for CargoBackend {
}

async fn install_version_(&self, ctx: &InstallContext, tv: ToolVersion) -> Result<ToolVersion> {
// Check if cargo is available
self.ensure_dependency(
&ctx.config,
"cargo",
"To use cargo packages with mise, you need to install Rust first:\n\
mise use rust@latest\n\n\
Or install Rust via https://rustup.rs/",
)
.await?;

let config = ctx.config.clone();
let install_arg = format!("{}@{}", self.tool_name(), tv.version);
let registry_name = &Settings::get().cargo.registry_name;
Expand Down
10 changes: 10 additions & 0 deletions src/backend/dotnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ impl Backend for DotnetBackend {
) -> eyre::Result<crate::toolset::ToolVersion> {
Settings::get().ensure_experimental("dotnet backend")?;

// Check if dotnet is available
self.ensure_dependency(
&ctx.config,
"dotnet",
"To use dotnet tools with mise, you need to install .NET SDK first:\n\
mise use dotnet@latest\n\n\
Or install .NET SDK via https://dotnet.microsoft.com/download",
)
.await?;

let mut cli = CmdLineRunner::new("dotnet")
.arg("tool")
.arg("install")
Expand Down
9 changes: 9 additions & 0 deletions src/backend/gem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ impl Backend for GemBackend {
async fn install_version_(&self, ctx: &InstallContext, tv: ToolVersion) -> Result<ToolVersion> {
Settings::get().ensure_experimental("gem backend")?;

// Check if gem is available
self.ensure_dependency(
&ctx.config,
"gem",
"To use gem packages with mise, you need to install Ruby first:\n\
mise use ruby@latest",
)
.await?;

CmdLineRunner::new("gem")
.arg("install")
.arg(self.tool_name())
Expand Down
24 changes: 23 additions & 1 deletion src/backend/go.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::backend::Backend;
use crate::backend::backend_type::BackendType;
use crate::cli::args::BackendArg;
use crate::cmd::CmdLineRunner;
use crate::config::Config;
use crate::config::Settings;
use crate::install_context::InstallContext;
use crate::timeout;
use crate::toolset::ToolVersion;
use crate::{backend::Backend, config::Config};
use async_trait::async_trait;
use itertools::Itertools;
use std::{fmt::Debug, sync::Arc};
Expand All @@ -31,6 +32,16 @@ impl Backend for GoBackend {
}

async fn _list_remote_versions(&self, config: &Arc<Config>) -> eyre::Result<Vec<String>> {
// Check if go is available
self.ensure_dependency(
config,
"go",
"To use go packages with mise, you need to install Go first:\n\
mise use go@latest\n\n\
Or install Go via https://go.dev/dl/",
)
.await?;

timeout::run_with_timeout_async(
async || {
let tool_name = self.tool_name();
Expand Down Expand Up @@ -87,6 +98,17 @@ impl Backend for GoBackend {
tv: ToolVersion,
) -> eyre::Result<ToolVersion> {
Settings::get().ensure_experimental("go backend")?;

// Check if go is available
self.ensure_dependency(
&ctx.config,
"go",
"To use go packages with mise, you need to install Go first:\n\
mise use go@latest\n\n\
Or install Go via https://go.dev/dl/",
)
.await?;

let opts = self.ba.opts();

let install = async |v| {
Expand Down
18 changes: 18 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,24 @@ pub trait Backend: Debug + Send + Sync {
b.which(config, &tv, bin).await.ok().flatten()
}

/// Check if a required dependency is available and return a helpful error if not.
/// This provides a consistent error message format across all backends.
async fn ensure_dependency(
&self,
config: &Arc<Config>,
program: &str,
install_instructions: &str,
) -> eyre::Result<()> {
if self.dependency_which(config, program).await.is_none() {
bail!(
"{} is required but not found.\n\n{}",
program,
install_instructions
);
}
Ok(())
}

async fn dependency_env(&self, config: &Arc<Config>) -> eyre::Result<BTreeMap<String, String>> {
self.dependency_toolset(config)
.await?
Expand Down
Loading
Loading