-
fix(core): propagate continuous task failures to dependent tasks (#33492)
发布于
2025-11-25 14:49:11 +00:00 Current Behavior
When a continuous task depends on another continuous task and the
dependent task exits with an error, the parent task continues running
indefinitely. The task execution never terminates, leaving processes
running in the background.For example, if task
a(continuous) depends on taskb(continuous),
and taskbexits with error code 1, taskawill continue running
even though its dependency failed.Expected Behavior
When a continuous task exits (with any exit code), the failure should be
propagated to dependent tasks:- The failed continuous task should be marked as failed
- Dependent continuous tasks should be marked as skipped
- All affected continuous tasks should be killed
- Task execution should terminate with an error
Changes Made
1. Restore Error Handling in Continuous Task Exit Handlers
- Re-added the
cleaningUpflag that was removed in a previous
TUI-related commit - Modified
onExithandlers for both regular and shared continuous
tasks to:- Check if the task exited during normal cleanup vs. unexpectedly
- Call
complete()with 'failure' status for unexpected exits - Log error messages for debugging
2. Fix
cleanUpUnneededContinuousTasks()LogicThe previous implementation always added
initializingTaskIdsto the
needed set, even when those tasks were already completed. This prevented
dependency tasks from being killed when the top-level task exited.Fixed by:
- Only adding tasks from
initializingTaskIdsif they are still
incomplete - Keeping dependencies of incomplete tasks alive
- This ensures continuous tasks are killed when no longer needed,
whether a dependency fails or a top-level task exits
3. Prevent Status Overwrites
Added a check in
onExithandlers to only set status toStoppedif
the task hasn't already been completed. This prevents the asynconExit
callback from overwriting the correct status (like 'skipped' or
'failure') with 'Stopped'.4. Fix Signal Handling in
PseudoTtyProcess.kill()The Rust pseudo-terminal defaults to SIGINT when no signal is provided,
which does not reliably terminate child processes in PTY sessions.Fixed by:
- Defaulting to SIGTERM in the JavaScript wrapper (rather than changing
the Rust default) - The JS wrapper is the API boundary that should match Node.js
semantics, wherechildProcess.kill(undefined)defaults to SIGTERM - The Rust default of SIGINT is appropriate for interactive use
(Ctrl-C), while programmatic cleanup needs SIGTERM - This ensures child processes are properly killed when
runningTask.kill()is called
File:
packages/nx/src/tasks-runner/pseudo-terminal.tsTechnical Details
The fix leverages the existing task failure propagation mechanism in
complete()instead of usingprocess.exit(1), which:- Allows proper cleanup through normal execution flow
- Respects the
--bailflag configuration - Works correctly with both TUI and non-TUI modes
- Maintains consistency with how other task failures are handled
下载附件