fix(core): handle skipped tasks and trigger finished state (#30864)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

When tasks are skipped due to a dependent task's failure, the TUI does
not recognize that the command is concluded and leaves the user in a
confusing state where Nx seems like it is still waiting for tasks.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

When tasks are skipped due to a dependent task's failure, the TUI shows
that the command has concluded.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Jason Jean 2025-04-25 08:15:43 -04:00 committed by GitHub
parent a65f0f421b
commit dda740fd2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 19 additions and 16 deletions

View File

@ -254,6 +254,8 @@ export interface NxWorkspaceFilesExternals {
allWorkspaceFiles: ExternalObject<Array<FileData>>
}
export declare export declare function parseTaskStatus(stringStatus: string): TaskStatus
export interface Project {
root: string
namedInputs?: Record<string, Array<JsInputs>>

View File

@ -388,6 +388,7 @@ module.exports.getTransformableOutputs = nativeBinding.getTransformableOutputs
module.exports.hashArray = nativeBinding.hashArray
module.exports.hashFile = nativeBinding.hashFile
module.exports.IS_WASM = nativeBinding.IS_WASM
module.exports.parseTaskStatus = nativeBinding.parseTaskStatus
module.exports.remove = nativeBinding.remove
module.exports.restoreTerminal = nativeBinding.restoreTerminal
module.exports.TaskStatus = nativeBinding.TaskStatus

View File

@ -182,7 +182,6 @@ impl App {
&mut self,
task_id: String,
parser_and_writer: External<(ParserArc, WriterArc)>,
task_status: TaskStatus,
) {
if let Some(tasks_list) = self
.components
@ -190,7 +189,7 @@ impl App {
.find_map(|c| c.as_any_mut().downcast_mut::<TasksList>())
{
tasks_list.create_and_register_pty_instance(&task_id, parser_and_writer);
tasks_list.update_task_status(task_id.clone(), task_status);
tasks_list.set_task_status(task_id.clone(), TaskStatus::InProgress);
}
}
@ -203,7 +202,7 @@ impl App {
let (_, parser_and_writer) = TasksList::create_empty_parser_and_noop_writer();
tasks_list.create_and_register_pty_instance(&task_id, parser_and_writer);
tasks_list.update_task_status(task_id.clone(), TaskStatus::InProgress);
tasks_list.set_task_status(task_id.clone(), TaskStatus::InProgress);
let _ = tasks_list.handle_resize(None);
}
}

View File

@ -152,6 +152,11 @@ impl std::str::FromStr for TaskStatus {
}
}
#[napi]
pub fn parse_task_status(string_status: String) -> napi::Result<TaskStatus> {
string_status.as_str().parse().map_err(napi::Error::from_reason)
}
/// A list component that displays and manages tasks in a terminal UI.
/// Provides filtering, sorting, and output display capabilities.
pub struct TasksList {
@ -927,12 +932,12 @@ impl TasksList {
self.sort_tasks();
}
/// Updates their status to InProgress and triggers a sort.
/// Updates a task's status and triggers a sort of the list.
pub fn set_task_status(&mut self, task_id: String, status: TaskStatus) {
if let Some(task_item) = self.tasks.iter_mut().find(|t| t.name == task_id) {
task_item.update_status(status);
self.sort_tasks();
}
self.sort_tasks();
}
pub fn end_tasks(&mut self, task_results: Vec<TaskResult>) {
@ -942,9 +947,6 @@ impl TasksList {
.iter_mut()
.find(|t| t.name == task_result.task.id)
{
let parsed_status = task_result.status.parse().unwrap();
task.update_status(parsed_status);
if task_result.task.start_time.is_some() && task_result.task.end_time.is_some() {
task.start_time = Some(task_result.task.start_time.unwrap() as u128);
task.end_time = Some(task_result.task.end_time.unwrap() as u128);
@ -960,13 +962,6 @@ impl TasksList {
let _ = self.handle_resize(None);
}
pub fn update_task_status(&mut self, task_id: String, status: TaskStatus) {
if let Some(task) = self.tasks.iter_mut().find(|t| t.name == task_id) {
task.update_status(status);
self.sort_tasks();
}
}
/// Toggles the visibility of the task list panel
pub fn toggle_task_list(&mut self) {
// Only allow hiding if at least one pane is visible, otherwise the screen will be blank
@ -1108,6 +1103,7 @@ impl Component for TasksList {
TaskStatus::Success
| TaskStatus::Failure
| TaskStatus::Skipped
| TaskStatus::Stopped
| TaskStatus::LocalCache
| TaskStatus::LocalCacheKeptExisting
| TaskStatus::RemoteCache

View File

@ -238,7 +238,7 @@ impl AppLifeCycle {
parser_and_writer: External<(ParserArc, WriterArc)>,
) {
let mut app = self.app.lock().unwrap();
app.register_running_task(task_id, parser_and_writer, TaskStatus::InProgress)
app.register_running_task(task_id, parser_and_writer)
}
#[napi]

View File

@ -10,6 +10,7 @@ import { runCommands } from '../executors/run-commands/run-commands.impl';
import { getTaskDetails, hashTask } from '../hasher/hash-task';
import { TaskHasher } from '../hasher/task-hasher';
import {
parseTaskStatus,
RunningTasksService,
TaskDetails,
TaskStatus as NativeTaskStatus,
@ -879,6 +880,10 @@ export class TaskOrchestrator {
if (this.completedTasks[taskId] === undefined) {
this.completedTasks[taskId] = status;
if (this.tuiEnabled) {
this.options.lifeCycle.setTaskStatus(taskId, parseTaskStatus(status));
}
if (status === 'failure' || status === 'skipped') {
if (this.bail) {
// mark the execution as bailed which will stop all further execution