diff --git a/e2e/tasks/test_task_failure_hang_depends b/e2e/tasks/test_task_failure_hang_depends new file mode 100755 index 0000000000..2b40d2acab --- /dev/null +++ b/e2e/tasks/test_task_failure_hang_depends @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail +# https://github.com/jdx/mise/discussions/6391 + +cat <mise.toml +[tasks.clean] +run = 'echo "Cleaning..."' + +[tasks.install] +wait_for = "clean" +run = "sleep 1" + +[tasks.lint] +depends = "install" +run = "exit 1" + +[tasks.test] +depends = "install" +run = "exit 1" + +[tasks."build:docker"] +depends = "install" +run = "exit 1" + +[tasks.check] +depends = ["lint", "test", "build:docker"] + +[tasks."ci:check"] +depends = ["clean", "check"] # this variant currently hangs + +# this variant now works after initial fix +# run = [ +# { task = "clean" }, +# { task = "check" } +# ] +EOF + +# Test that task failure with dependencies does not hang +timeout 5s mise run --jobs 1 ci:check 2>&1 && exit_code=0 || exit_code=$? + +# Check if it was a timeout (exit code 124) +if [ "$exit_code" -eq 124 ]; then + echo "FAIL: Task hung after dependency failure (timeout reached)" + exit 1 +fi + +# The command should fail with exit code 1 +if [ "$exit_code" -ne 1 ]; then + echo "Expected exit code 1, got $exit_code" + exit 1 +fi + +echo "Test passed: task with failing dependency did not hang" diff --git a/src/cli/run.rs b/src/cli/run.rs index cc1530d8f7..517cc50afe 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -440,6 +440,19 @@ impl Run { } } + // Check if we should stop early due to failure + if this.is_stopping() && !this.continue_on_error { + trace!("scheduler: stopping early due to failure, cleaning up main deps"); + // Clean up the dependency graph to ensure the main_done signal is sent + let mut deps = main_deps.lock().await; + let tasks_to_remove: Vec = deps.all().cloned().collect(); + for task in tasks_to_remove { + deps.remove(&task); + } + drop(deps); + break; + } + // Exit if main deps finished and nothing is running/queued if *main_done_rx.borrow() && in_flight.load(std::sync::atomic::Ordering::SeqCst) == 0