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.
- package/package.json +11 -11
- package/src/command-line/init/init-v1.js +1 -1
- package/src/command-line/release/publish.js +3 -3
- package/src/core/graph/main.js +1 -1
- package/src/native/nx.wasm32-wasi.wasm +0 -0
- package/src/tasks-runner/life-cycles/tui-summary-life-cycle.d.ts +3 -2
- package/src/tasks-runner/life-cycles/tui-summary-life-cycle.js +25 -10
- package/src/tasks-runner/run-command.d.ts +4 -1
- package/src/tasks-runner/run-command.js +37 -6
- package/src/tasks-runner/task-graph-utils.d.ts +1 -0
- package/src/tasks-runner/task-graph-utils.js +28 -0
- package/src/tasks-runner/task-orchestrator.d.ts +1 -0
- package/src/tasks-runner/task-orchestrator.js +27 -12
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 (
|
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 = `${
|
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<
|
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
|
287
|
-
?
|
288
|
-
:
|
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
|
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
|
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(
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
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();
|