nx 21.0.0-rc.2 → 21.0.0-rc.4

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
@@ -17,3 +17,4 @@ export declare function verifyOrUpdateNxCloudClient(options: CloudTaskRunnerOpti
17
17
  nxCloudClient: NxCloudClient;
18
18
  version: string;
19
19
  } | null>;
20
+ export declare function getBundleInstallDefaultLocation(): string;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NxCloudClientUnavailableError = exports.NxCloudEnterpriseOutdatedError = void 0;
4
4
  exports.verifyOrUpdateNxCloudClient = verifyOrUpdateNxCloudClient;
5
+ exports.getBundleInstallDefaultLocation = getBundleInstallDefaultLocation;
5
6
  const fs_1 = require("fs");
6
7
  const zlib_1 = require("zlib");
7
8
  const path_1 = require("path");
@@ -5,6 +5,6 @@ export declare class UnknownCommandError extends Error {
5
5
  constructor(command: string, availableCommands: string[]);
6
6
  }
7
7
  export declare function getCloudClient(options: CloudTaskRunnerOptions): Promise<{
8
- invoke: (command: string) => void;
8
+ invoke: (command: string, exit?: boolean) => void;
9
9
  availableCommands: string[];
10
10
  }>;
@@ -17,13 +17,20 @@ async function getCloudClient(options) {
17
17
  const paths = (0, resolution_helpers_1.findAncestorNodeModules)(__dirname, []);
18
18
  nxCloudClient.configureLightClientRequire()(paths);
19
19
  return {
20
- invoke: (command) => {
20
+ invoke: (command, exit = true) => {
21
21
  if (command in nxCloudClient.commands) {
22
22
  nxCloudClient.commands[command]()
23
- .then(() => process.exit(0))
23
+ .then(() => {
24
+ if (exit) {
25
+ process.exit(0);
26
+ }
27
+ })
24
28
  .catch((e) => {
25
29
  console.error(e);
26
- process.exit(1);
30
+ if (exit) {
31
+ process.exit(1);
32
+ }
33
+ throw e;
27
34
  });
28
35
  }
29
36
  else {
@@ -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 (totalCompletedTasks + stoppedTasks.size === totalTasks) {
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`;
@@ -122,35 +138,30 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
122
138
  .forEach((arg) => taskOverridesLines.push(arg));
123
139
  }
124
140
  const viewLogs = (0, view_logs_utils_1.viewLogsFooterRows)(totalFailedTasks);
125
- lines = [
126
- output_1.output.colors.red([
127
- output_1.output.applyNxPrefix('red', output_1.output.colors.red(text) + output_1.output.dim(` (${timeTakenText})`)),
128
- ...taskOverridesLines,
129
- '',
130
- `${LEFT_PAD}${output_1.output.colors.red(figures.cross)}${SPACER}${totalFailedTasks}${`/${totalCompletedTasks}`} failed`,
131
- `${LEFT_PAD}${output_1.output.dim(figures.tick)}${SPACER}${totalSuccessfulTasks}${`/${totalCompletedTasks}`} succeeded ${output_1.output.dim(`[${totalCachedTasks} read from cache]`)}`,
132
- ...viewLogs,
133
- ].join(node_os_1.EOL)),
134
- ];
141
+ lines.push(output_1.output.colors.red([
142
+ output_1.output.applyNxPrefix('red', output_1.output.colors.red(text) + output_1.output.dim(` (${timeTakenText})`)),
143
+ ...taskOverridesLines,
144
+ '',
145
+ `${LEFT_PAD}${output_1.output.colors.red(figures.cross)}${SPACER}${totalFailedTasks}${`/${totalCompletedTasks}`} failed`,
146
+ `${LEFT_PAD}${output_1.output.dim(figures.tick)}${SPACER}${totalSuccessfulTasks}${`/${totalCompletedTasks}`} succeeded ${output_1.output.dim(`[${totalCachedTasks} read from cache]`)}`,
147
+ ...viewLogs,
148
+ ].join(node_os_1.EOL)));
135
149
  }
136
150
  else {
137
- lines = [
138
- ...output_1.output.getVerticalSeparatorLines('red'),
139
- output_1.output.applyNxPrefix('red', output_1.output.colors.red(`Cancelled running target ${output_1.output.bold(targets[0])} for project ${output_1.output.bold(initiatingProject)}`) + output_1.output.dim(` (${timeTakenText})`)),
140
- ];
151
+ lines.push(output_1.output.applyNxPrefix('red', output_1.output.colors.red(`Cancelled running target ${output_1.output.bold(targets[0])} for project ${output_1.output.bold(initiatingProject)}`) + output_1.output.dim(` (${timeTakenText})`)));
141
152
  }
142
153
  // adds some vertical space after the summary to avoid bunching against terminal
143
154
  lines.push('');
144
155
  console.log(lines.join(node_os_1.EOL));
145
156
  };
146
- const printRunManySummary = () => {
157
+ const printRunManySummary = ({ failure, cancelled, }) => {
147
158
  console.log('');
148
- const lines = [];
149
- const failure = totalSuccessfulTasks + stoppedTasks.size !== totalTasks;
159
+ const lines = [''];
150
160
  for (const taskId of taskIdsInOrderOfCompletion) {
151
161
  const { terminalOutput, taskStatus } = tasksToTerminalOutputs[taskId];
152
162
  if (taskStatus === 'failure') {
153
163
  output_1.output.logCommandOutput(taskId, taskStatus, terminalOutput);
164
+ output_1.output.addNewline();
154
165
  lines.push(`${LEFT_PAD}${output_1.output.colors.red(figures.cross)}${SPACER}${output_1.output.colors.gray('nx run ')}${taskId}`);
155
166
  }
156
167
  else {
@@ -179,7 +190,7 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, args, overrides,
179
190
  lines.push(successSummaryRows.join(node_os_1.EOL));
180
191
  }
181
192
  else {
182
- 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)}`;
183
194
  const taskOverridesRows = [];
184
195
  if (Object.keys(overrides).length > 0) {
185
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,
@@ -168,20 +170,14 @@ async function getTerminalOutputLifeCycle(initiatingProject, initiatingTasks, pr
168
170
  // The cloud client calls console.log when NX_VERBOSE_LOGGING is set to true
169
171
  console.log = createPatchedConsoleMethod(originalConsoleLog);
170
172
  console.error = createPatchedConsoleMethod(originalConsoleError);
171
- renderIsDone = new Promise((resolve) => {
172
- appLifeCycle.__init(() => {
173
- resolve();
174
- });
175
- })
176
- .then(() => {
173
+ globalThis.tuiOnProcessExit = () => {
177
174
  restoreTerminal();
178
- })
179
- .finally(() => {
180
175
  // Revert the patched methods
181
176
  process.stdout.write = originalStdoutWrite;
182
177
  process.stderr.write = originalStderrWrite;
183
178
  console.log = originalConsoleLog;
184
179
  console.error = originalConsoleError;
180
+ process.stdout.write('\n');
185
181
  // Print the intercepted Nx Cloud logs
186
182
  for (const log of interceptedNxCloudLogs) {
187
183
  const logString = log.toString().trimStart();
@@ -190,6 +186,18 @@ async function getTerminalOutputLifeCycle(initiatingProject, initiatingTasks, pr
190
186
  process.stdout.write('\n');
191
187
  }
192
188
  }
189
+ };
190
+ renderIsDone = new Promise((resolve) => {
191
+ appLifeCycle.__init(() => {
192
+ resolve();
193
+ });
194
+ }).finally(() => {
195
+ restoreTerminal();
196
+ // Revert the patched methods
197
+ process.stdout.write = originalStdoutWrite;
198
+ process.stderr.write = originalStderrWrite;
199
+ console.log = originalConsoleLog;
200
+ console.error = originalConsoleError;
193
201
  });
194
202
  }
195
203
  return {
@@ -271,21 +279,23 @@ async function runCommand(projectsToRun, currentProjectGraph, { nxJson }, nxArgs
271
279
  workspaceRoot: workspace_root_1.workspaceRoot,
272
280
  nxJsonConfiguration: nxJson,
273
281
  });
274
- const taskResults = await runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, {
282
+ const { taskResults, completed } = await runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, {
275
283
  ...nxArgs,
276
284
  skipNxCache: nxArgs.skipNxCache ||
277
285
  process.env.NX_SKIP_NX_CACHE === 'true' ||
278
286
  process.env.NX_DISABLE_NX_CACHE === 'true',
279
287
  }, overrides, initiatingProject, extraTargetDependencies, extraOptions);
280
- const result = Object.values(taskResults).some((taskResult) => taskResult.status === 'failure' || taskResult.status === 'skipped')
281
- ? 1
282
- : 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;
283
293
  await (0, tasks_execution_hooks_1.runPostTasksExecution)({
284
294
  taskResults,
285
295
  workspaceRoot: workspace_root_1.workspaceRoot,
286
296
  nxJsonConfiguration: nxJson,
287
297
  });
288
- return result;
298
+ return exitCode;
289
299
  });
290
300
  return status;
291
301
  }
@@ -314,7 +324,10 @@ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }
314
324
  printSummary();
315
325
  }
316
326
  await (0, nx_key_1.printNxKey)();
317
- return taskResults;
327
+ return {
328
+ taskResults,
329
+ completed: didCommandComplete(tasks, taskGraph, taskResults),
330
+ };
318
331
  }
319
332
  catch (e) {
320
333
  if (restoreTerminal) {
@@ -323,6 +336,30 @@ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }
323
336
  throw e;
324
337
  }
325
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
+ }
326
363
  async function ensureWorkspaceIsInSyncAndGetGraphs(projectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions) {
327
364
  let taskGraph = createTaskGraphAndRunValidations(projectGraph, extraTargetDependencies ?? {}, projectNames, nxArgs, overrides, extraOptions);
328
365
  if (nxArgs.skipSync || (0, is_ci_1.isCI)()) {
@@ -575,7 +612,7 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
575
612
  ...runnerOptions,
576
613
  lifeCycle: compositedLifeCycle,
577
614
  }, {
578
- initiatingProject: nxArgs.outputStyle === 'compact' ? null : initiatingProject,
615
+ initiatingProject,
579
616
  initiatingTasks,
580
617
  projectGraph,
581
618
  nxJson,
@@ -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();