nx 21.0.0-rc.3 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -1,8 +1,9 @@
1
- import { Task } from '../../config/task-graph';
1
+ import { Task, TaskGraph } from '../../config/task-graph';
2
2
  import type { LifeCycle } from '../life-cycle';
3
- export declare function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides, initiatingProject, initiatingTasks, resolveRenderIsDonePromise, }: {
3
+ export declare function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, taskGraph, args, overrides, initiatingProject, initiatingTasks, resolveRenderIsDonePromise, }: {
4
4
  projectNames: string[];
5
5
  tasks: Task[];
6
+ taskGraph: TaskGraph;
6
7
  args: {
7
8
  targets?: string[];
8
9
  configuration?: string;
@@ -8,10 +8,11 @@ const pretty_time_1 = require("./pretty-time");
8
8
  const view_logs_utils_1 = require("./view-logs-utils");
9
9
  const figures = require("figures");
10
10
  const task_history_life_cycle_1 = require("./task-history-life-cycle");
11
+ const task_graph_utils_1 = require("../task-graph-utils");
11
12
  const LEFT_PAD = ` `;
12
13
  const SPACER = ` `;
13
14
  const EXTENDED_LEFT_PAD = ` `;
14
- function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides, initiatingProject, initiatingTasks, resolveRenderIsDonePromise, }) {
15
+ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, taskGraph, args, overrides, initiatingProject, initiatingTasks, resolveRenderIsDonePromise, }) {
15
16
  const lifeCycle = {};
16
17
  const start = process.hrtime();
17
18
  const targets = args.targets;
@@ -74,17 +75,32 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
74
75
  console.log(`\n${output_1.output.applyNxPrefix('gray', 'No tasks were run')}\n`);
75
76
  return;
76
77
  }
78
+ const failure = totalSuccessfulTasks + stoppedTasks.size !== totalTasks;
79
+ const cancelled =
80
+ // Some tasks were in progress...
81
+ inProgressTasks.size > 0 ||
82
+ // or some tasks had not started yet
83
+ totalTasks !== tasks.length ||
84
+ // the run had a continous task as a leaf...
85
+ // this is needed because continous tasks get marked as stopped when the process is interrupted
86
+ [...(0, task_graph_utils_1.getLeafTasks)(taskGraph)].filter((t) => taskGraph.tasks[t].continuous)
87
+ .length > 0;
77
88
  if (isRunOne) {
78
- printRunOneSummary();
89
+ printRunOneSummary({
90
+ failure,
91
+ cancelled,
92
+ });
79
93
  }
80
94
  else {
81
- printRunManySummary();
95
+ printRunManySummary({
96
+ failure,
97
+ cancelled,
98
+ });
82
99
  }
83
100
  (0, task_history_life_cycle_1.getTasksHistoryLifeCycle)()?.printFlakyTasksMessage();
84
101
  };
85
- const printRunOneSummary = () => {
102
+ const printRunOneSummary = ({ failure, cancelled, }) => {
86
103
  let lines = [];
87
- const failure = totalSuccessfulTasks + stoppedTasks.size !== totalTasks;
88
104
  // Prints task outputs in the order they were completed
89
105
  // above the summary, since run-one should print all task results.
90
106
  for (const taskId of taskIdsInOrderOfCompletion) {
@@ -92,7 +108,7 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
92
108
  output_1.output.logCommandOutput(taskId, taskStatus, terminalOutput);
93
109
  }
94
110
  lines.push(...output_1.output.getVerticalSeparatorLines(failure ? 'red' : 'green'));
95
- if (!failure) {
111
+ if (!failure && !cancelled) {
96
112
  const text = `Successfully ran ${(0, formatting_utils_1.formatTargetsAndProjects)([initiatingProject], targets, tasks)}`;
97
113
  const taskOverridesLines = [];
98
114
  if (Object.keys(overrides).length > 0) {
@@ -108,7 +124,7 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
108
124
  }
109
125
  lines = [output_1.output.colors.green(lines.join(node_os_1.EOL))];
110
126
  }
111
- else if (inProgressTasks.size === 0) {
127
+ else if (!cancelled) {
112
128
  let text = `Ran target ${output_1.output.bold(targets[0])} for project ${output_1.output.bold(initiatingProject)}`;
113
129
  if (tasks.length > 1) {
114
130
  text += ` and ${output_1.output.bold(tasks.length - 1)} task(s) they depend on`;
@@ -138,10 +154,9 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
138
154
  lines.push('');
139
155
  console.log(lines.join(node_os_1.EOL));
140
156
  };
141
- const printRunManySummary = () => {
157
+ const printRunManySummary = ({ failure, cancelled, }) => {
142
158
  console.log('');
143
159
  const lines = [''];
144
- const failure = totalSuccessfulTasks + stoppedTasks.size !== totalTasks;
145
160
  for (const taskId of taskIdsInOrderOfCompletion) {
146
161
  const { terminalOutput, taskStatus } = tasksToTerminalOutputs[taskId];
147
162
  if (taskStatus === 'failure') {
@@ -175,7 +190,7 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
175
190
  lines.push(successSummaryRows.join(node_os_1.EOL));
176
191
  }
177
192
  else {
178
- const text = `${inProgressTasks.size ? 'Cancelled while running' : 'Ran'} ${(0, formatting_utils_1.formatTargetsAndProjects)(projectNames, targets, tasks)}`;
193
+ const text = `${cancelled ? 'Cancelled while running' : 'Ran'} ${(0, formatting_utils_1.formatTargetsAndProjects)(projectNames, targets, tasks)}`;
179
194
  const taskOverridesRows = [];
180
195
  if (Object.keys(overrides).length > 0) {
181
196
  taskOverridesRows.push('');
@@ -16,7 +16,10 @@ export declare function runCommandForTasks(projectsToRun: ProjectGraphProjectNod
16
16
  }, nxArgs: NxArgs, overrides: any, initiatingProject: string | null, extraTargetDependencies: Record<string, (TargetDependencyConfig | string)[]>, extraOptions: {
17
17
  excludeTaskDependencies: boolean;
18
18
  loadDotEnvFiles: boolean;
19
- }): Promise<TaskResults>;
19
+ }): Promise<{
20
+ taskResults: TaskResults;
21
+ completed: boolean;
22
+ }>;
20
23
  export declare function setEnvVarsBasedOnArgs(nxArgs: NxArgs, loadDotEnvFiles: boolean): void;
21
24
  export declare function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nxJson, nxArgs, loadDotEnvFiles, initiatingProject, initiatingTasks, }: {
22
25
  tasks: Task[];
@@ -40,6 +40,7 @@ const tui_summary_life_cycle_1 = require("./life-cycles/tui-summary-life-cycle")
40
40
  const task_graph_utils_1 = require("./task-graph-utils");
41
41
  const utils_1 = require("./utils");
42
42
  const chalk = require("chalk");
43
+ const exit_codes_1 = require("../utils/exit-codes");
43
44
  const originalStdoutWrite = process.stdout.write.bind(process.stdout);
44
45
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
45
46
  const originalConsoleLog = console.log.bind(console);
@@ -107,6 +108,7 @@ async function getTerminalOutputLifeCycle(initiatingProject, initiatingTasks, pr
107
108
  const { lifeCycle: tsLifeCycle, printSummary } = (0, tui_summary_life_cycle_1.getTuiTerminalSummaryLifeCycle)({
108
109
  projectNames,
109
110
  tasks,
111
+ taskGraph,
110
112
  args: nxArgs,
111
113
  overrides: overridesWithoutHidden,
112
114
  initiatingProject,
@@ -277,21 +279,23 @@ async function runCommand(projectsToRun, currentProjectGraph, { nxJson }, nxArgs
277
279
  workspaceRoot: workspace_root_1.workspaceRoot,
278
280
  nxJsonConfiguration: nxJson,
279
281
  });
280
- const taskResults = await runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, {
282
+ const { taskResults, completed } = await runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, {
281
283
  ...nxArgs,
282
284
  skipNxCache: nxArgs.skipNxCache ||
283
285
  process.env.NX_SKIP_NX_CACHE === 'true' ||
284
286
  process.env.NX_DISABLE_NX_CACHE === 'true',
285
287
  }, overrides, initiatingProject, extraTargetDependencies, extraOptions);
286
- const result = Object.values(taskResults).some((taskResult) => taskResult.status === 'failure' || taskResult.status === 'skipped')
287
- ? 1
288
- : 0;
288
+ const exitCode = !completed
289
+ ? (0, exit_codes_1.signalToCode)('SIGINT')
290
+ : Object.values(taskResults).some((taskResult) => taskResult.status === 'failure' || taskResult.status === 'skipped')
291
+ ? 1
292
+ : 0;
289
293
  await (0, tasks_execution_hooks_1.runPostTasksExecution)({
290
294
  taskResults,
291
295
  workspaceRoot: workspace_root_1.workspaceRoot,
292
296
  nxJsonConfiguration: nxJson,
293
297
  });
294
- return result;
298
+ return exitCode;
295
299
  });
296
300
  return status;
297
301
  }
@@ -320,7 +324,10 @@ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }
320
324
  printSummary();
321
325
  }
322
326
  await (0, nx_key_1.printNxKey)();
323
- return taskResults;
327
+ return {
328
+ taskResults,
329
+ completed: didCommandComplete(tasks, taskGraph, taskResults),
330
+ };
324
331
  }
325
332
  catch (e) {
326
333
  if (restoreTerminal) {
@@ -329,6 +336,30 @@ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }
329
336
  throw e;
330
337
  }
331
338
  }
339
+ function didCommandComplete(tasks, taskGraph, taskResults) {
340
+ // If no tasks, then we can consider it complete
341
+ if (tasks.length === 0) {
342
+ return true;
343
+ }
344
+ let continousLeafTasks = false;
345
+ const leafTasks = (0, task_graph_utils_1.getLeafTasks)(taskGraph);
346
+ for (const task of tasks) {
347
+ if (!task.continuous) {
348
+ // If any discrete task does not have a result then it did not run
349
+ if (!taskResults[task.id]) {
350
+ return false;
351
+ }
352
+ }
353
+ else {
354
+ if (leafTasks.has(task.id)) {
355
+ continousLeafTasks = true;
356
+ }
357
+ }
358
+ }
359
+ // If a leaf task is continous, we must have cancelled it.
360
+ // Otherwise, we've looped through all the discrete tasks and they have results
361
+ return !continousLeafTasks;
362
+ }
332
363
  async function ensureWorkspaceIsInSyncAndGetGraphs(projectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions) {
333
364
  let taskGraph = createTaskGraphAndRunValidations(projectGraph, extraTargetDependencies ?? {}, projectNames, nxArgs, overrides, extraOptions);
334
365
  if (nxArgs.skipSync || (0, is_ci_1.isCI)()) {
@@ -22,3 +22,4 @@ export declare function makeAcyclic(graph: {
22
22
  }): void;
23
23
  export declare function validateNoAtomizedTasks(taskGraph: TaskGraph, projectGraph: ProjectGraph): void;
24
24
  export declare function assertTaskGraphDoesNotContainInvalidTargets(taskGraph: TaskGraph): void;
25
+ export declare function getLeafTasks(taskGraph: TaskGraph): Set<string>;
@@ -5,6 +5,7 @@ exports.findCycles = findCycles;
5
5
  exports.makeAcyclic = makeAcyclic;
6
6
  exports.validateNoAtomizedTasks = validateNoAtomizedTasks;
7
7
  exports.assertTaskGraphDoesNotContainInvalidTargets = assertTaskGraphDoesNotContainInvalidTargets;
8
+ exports.getLeafTasks = getLeafTasks;
8
9
  const output_1 = require("../utils/output");
9
10
  function _findCycle(graph, id, visited, path) {
10
11
  if (visited[id])
@@ -138,3 +139,30 @@ class NonParallelTaskDependsOnContinuousTasksError extends Error {
138
139
  this.name = 'NonParallelTaskDependsOnContinuousTasksError';
139
140
  }
140
141
  }
142
+ function getLeafTasks(taskGraph) {
143
+ const reversed = reverseTaskGraph(taskGraph);
144
+ const leafTasks = new Set();
145
+ for (const [taskId, dependencies] of Object.entries(reversed.dependencies)) {
146
+ if (dependencies.length === 0) {
147
+ leafTasks.add(taskId);
148
+ }
149
+ }
150
+ return leafTasks;
151
+ }
152
+ function reverseTaskGraph(taskGraph) {
153
+ const reversed = {
154
+ tasks: taskGraph.tasks,
155
+ dependencies: Object.fromEntries(Object.entries(taskGraph.tasks).map(([taskId]) => [taskId, []])),
156
+ };
157
+ for (const [taskId, dependencies] of Object.entries(taskGraph.dependencies)) {
158
+ for (const dependency of dependencies) {
159
+ reversed.dependencies[dependency].push(taskId);
160
+ }
161
+ }
162
+ for (const [taskId, dependencies] of Object.entries(taskGraph.continuousDependencies)) {
163
+ for (const dependency of dependencies) {
164
+ reversed.dependencies[dependency].push(taskId);
165
+ }
166
+ }
167
+ return reversed;
168
+ }
@@ -38,6 +38,7 @@ export declare class TaskOrchestrator {
38
38
  private groups;
39
39
  private bailed;
40
40
  private runningContinuousTasks;
41
+ private runningRunCommandsTasks;
41
42
  constructor(hasher: TaskHasher, initiatingProject: string | undefined, initiatingTasks: Task[], projectGraph: ProjectGraph, taskGraph: TaskGraph, nxJson: NxJsonConfiguration, options: NxArgs & DefaultTasksRunnerOptions, bail: boolean, daemon: DaemonClient, outputStyle: string, taskGraphForHashing?: TaskGraph);
42
43
  init(): Promise<void>;
43
44
  run(): Promise<{
@@ -55,6 +55,7 @@ class TaskOrchestrator {
55
55
  this.groups = [];
56
56
  this.bailed = false;
57
57
  this.runningContinuousTasks = new Map();
58
+ this.runningRunCommandsTasks = new Map();
58
59
  }
59
60
  async init() {
60
61
  // Init the ForkedProcessTaskRunner, TasksSchedule, and Cache
@@ -324,6 +325,10 @@ class TaskOrchestrator {
324
325
  const runningTask = await (0, run_commands_impl_1.runCommands)(runCommandsOptions, {
325
326
  root: workspace_root_1.workspaceRoot, // only root is needed in runCommands
326
327
  });
328
+ this.runningRunCommandsTasks.set(task.id, runningTask);
329
+ runningTask.onExit(() => {
330
+ this.runningRunCommandsTasks.delete(task.id);
331
+ });
327
332
  if (this.tuiEnabled) {
328
333
  if (runningTask instanceof pseudo_terminal_1.PseudoTtyProcess) {
329
334
  // This is an external of a the pseudo terminal where a task is running and can be passed to the TUI
@@ -640,18 +645,28 @@ class TaskOrchestrator {
640
645
  }
641
646
  // endregion utils
642
647
  async cleanup() {
643
- await Promise.all(Array.from(this.runningContinuousTasks).map(async ([taskId, t]) => {
644
- try {
645
- await t.kill();
646
- this.options.lifeCycle.setTaskStatus(taskId, 9 /* NativeTaskStatus.Stopped */);
647
- }
648
- catch (e) {
649
- console.error(`Unable to terminate ${taskId}\nError:`, e);
650
- }
651
- finally {
652
- this.runningTasksService.removeRunningTask(taskId);
653
- }
654
- }));
648
+ await Promise.all([
649
+ ...Array.from(this.runningContinuousTasks).map(async ([taskId, t]) => {
650
+ try {
651
+ await t.kill();
652
+ this.options.lifeCycle.setTaskStatus(taskId, 9 /* NativeTaskStatus.Stopped */);
653
+ }
654
+ catch (e) {
655
+ console.error(`Unable to terminate ${taskId}\nError:`, e);
656
+ }
657
+ finally {
658
+ this.runningTasksService.removeRunningTask(taskId);
659
+ }
660
+ }),
661
+ ...Array.from(this.runningRunCommandsTasks).map(async ([taskId, t]) => {
662
+ try {
663
+ await t.kill();
664
+ }
665
+ catch (e) {
666
+ console.error(`Unable to terminate ${taskId}\nError:`, e);
667
+ }
668
+ }),
669
+ ]);
655
670
  }
656
671
  cleanUpUnneededContinuousTasks() {
657
672
  const incompleteTasks = this.tasksSchedule.getIncompleteTasks();