发布

  • fix(core): propagate continuous task failures to dependent tasks (#33492)

    frostbyte_neo 发布于 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 task b (continuous),
    and task b exits with error code 1, task a will 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:

    1. The failed continuous task should be marked as failed
    2. Dependent continuous tasks should be marked as skipped
    3. All affected continuous tasks should be killed
    4. Task execution should terminate with an error

    Changes Made

    1. Restore Error Handling in Continuous Task Exit Handlers

    • Re-added the cleaningUp flag that was removed in a previous
      TUI-related commit
    • Modified onExit handlers 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() Logic

    The previous implementation always added initializingTaskIds to 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 initializingTaskIds if 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 onExit handlers to only set status to Stopped if
    the task hasn't already been completed. This prevents the async onExit
    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, where childProcess.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.ts

    Technical Details

    The fix leverages the existing task failure propagation mechanism in
    complete() instead of using process.exit(1), which:

    • Allows proper cleanup through normal execution flow
    • Respects the --bail flag configuration
    • Works correctly with both TUI and non-TUI modes
    • Maintains consistency with how other task failures are handled
    下载附件