nx 19.7.3 → 19.8.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/package.json +12 -12
  2. package/schemas/nx-schema.json +2 -2
  3. package/src/command-line/generate/generator-utils.d.ts +2 -1
  4. package/src/command-line/import/command-object.js +1 -1
  5. package/src/command-line/import/import.js +9 -4
  6. package/src/command-line/import/utils/prepare-source-repo.js +7 -35
  7. package/src/command-line/init/init-v2.d.ts +1 -1
  8. package/src/command-line/init/init-v2.js +14 -4
  9. package/src/command-line/release/command-object.d.ts +2 -2
  10. package/src/command-line/release/config/config.js +10 -3
  11. package/src/command-line/release/utils/git.d.ts +2 -2
  12. package/src/command-line/release/utils/git.js +12 -2
  13. package/src/command-line/release/utils/shared.d.ts +1 -1
  14. package/src/command-line/release/version.js +4 -0
  15. package/src/command-line/sync/command-object.js +2 -2
  16. package/src/config/nx-json.d.ts +13 -5
  17. package/src/core/graph/main.js +1 -1
  18. package/src/core/graph/styles.css +1 -1
  19. package/src/daemon/client/client.d.ts +2 -1
  20. package/src/daemon/client/client.js +7 -0
  21. package/src/daemon/message-types/task-history.d.ts +9 -3
  22. package/src/daemon/message-types/task-history.js +10 -2
  23. package/src/daemon/server/handle-task-history.d.ts +5 -1
  24. package/src/daemon/server/handle-task-history.js +11 -9
  25. package/src/daemon/server/server.js +5 -2
  26. package/src/hasher/hash-task.js +2 -2
  27. package/src/native/index.d.ts +1 -0
  28. package/src/native/nx.wasi-browser.js +42 -54
  29. package/src/native/nx.wasi.cjs +42 -54
  30. package/src/native/nx.wasm32-wasi.wasm +0 -0
  31. package/src/plugins/js/utils/register.js +7 -0
  32. package/src/tasks-runner/cache.js +2 -1
  33. package/src/tasks-runner/init-tasks-runner.d.ts +1 -1
  34. package/src/tasks-runner/init-tasks-runner.js +5 -3
  35. package/src/tasks-runner/life-cycles/invoke-runner-terminal-output-life-cycle.d.ts +0 -2
  36. package/src/tasks-runner/life-cycles/invoke-runner-terminal-output-life-cycle.js +0 -5
  37. package/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.d.ts +2 -6
  38. package/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.d.ts +2 -6
  39. package/src/tasks-runner/life-cycles/store-run-information-life-cycle.d.ts +2 -7
  40. package/src/tasks-runner/life-cycles/task-history-life-cycle.js +1 -1
  41. package/src/tasks-runner/life-cycles/task-profiling-life-cycle.d.ts +2 -7
  42. package/src/tasks-runner/life-cycles/task-results-life-cycle.d.ts +6 -0
  43. package/src/tasks-runner/life-cycles/task-results-life-cycle.js +17 -0
  44. package/src/tasks-runner/life-cycles/task-timings-life-cycle.d.ts +2 -7
  45. package/src/tasks-runner/run-command.d.ts +12 -2
  46. package/src/tasks-runner/run-command.js +53 -60
  47. package/src/tasks-runner/task-orchestrator.d.ts +0 -1
  48. package/src/tasks-runner/task-orchestrator.js +7 -7
  49. package/src/tasks-runner/tasks-schedule.d.ts +3 -0
  50. package/src/tasks-runner/tasks-schedule.js +26 -4
  51. package/src/utils/git-utils.d.ts +4 -2
  52. package/src/utils/git-utils.index-filter.d.ts +0 -0
  53. package/src/utils/git-utils.index-filter.js +20 -0
  54. package/src/utils/git-utils.js +48 -13
  55. package/src/utils/git-utils.tree-filter.d.ts +11 -0
  56. package/src/utils/git-utils.tree-filter.js +43 -0
  57. package/src/utils/task-history.d.ts +12 -1
  58. package/src/utils/task-history.js +23 -0
@@ -39,7 +39,7 @@ async function initTasksRunner(nxArgs) {
39
39
  return acc;
40
40
  }, {}),
41
41
  };
42
- const status = await (0, run_command_1.invokeTasksRunner)({
42
+ const taskResults = await (0, run_command_1.invokeTasksRunner)({
43
43
  tasks: opts.tasks,
44
44
  projectGraph,
45
45
  taskGraph,
@@ -50,9 +50,11 @@ async function initTasksRunner(nxArgs) {
50
50
  initiatingProject: null,
51
51
  });
52
52
  return {
53
- status,
53
+ status: Object.values(taskResults).some((taskResult) => taskResult.status === 'failure' || taskResult.status === 'skipped')
54
+ ? 1
55
+ : 0,
54
56
  taskGraph,
55
- taskResults: lifeCycle.getTaskResults(),
57
+ taskResults,
56
58
  };
57
59
  },
58
60
  };
@@ -5,11 +5,9 @@ export declare class InvokeRunnerTerminalOutputLifeCycle implements LifeCycle {
5
5
  private readonly tasks;
6
6
  failedTasks: Task[];
7
7
  cachedTasks: Task[];
8
- private taskResults;
9
8
  constructor(tasks: Task[]);
10
9
  startCommand(): void;
11
10
  endCommand(): void;
12
11
  endTasks(taskResults: TaskResult[]): void;
13
12
  printTaskTerminalOutput(task: Task, cacheStatus: TaskStatus, terminalOutput: string): void;
14
- getTaskResults(): Record<string, TaskResult>;
15
13
  }
@@ -8,7 +8,6 @@ class InvokeRunnerTerminalOutputLifeCycle {
8
8
  this.tasks = tasks;
9
9
  this.failedTasks = [];
10
10
  this.cachedTasks = [];
11
- this.taskResults = {};
12
11
  }
13
12
  startCommand() {
14
13
  output_1.output.log({
@@ -46,7 +45,6 @@ class InvokeRunnerTerminalOutputLifeCycle {
46
45
  }
47
46
  endTasks(taskResults) {
48
47
  for (let t of taskResults) {
49
- this.taskResults[t.task.id] = t;
50
48
  if (t.status === 'failure') {
51
49
  this.failedTasks.push(t.task);
52
50
  }
@@ -65,8 +63,5 @@ class InvokeRunnerTerminalOutputLifeCycle {
65
63
  const args = (0, utils_1.getPrintableCommandArgsForTask)(task);
66
64
  output_1.output.logCommandOutput(args.join(' '), cacheStatus, terminalOutput);
67
65
  }
68
- getTaskResults() {
69
- return this.taskResults;
70
- }
71
66
  }
72
67
  exports.InvokeRunnerTerminalOutputLifeCycle = InvokeRunnerTerminalOutputLifeCycle;
@@ -1,5 +1,5 @@
1
1
  import { TaskStatus } from '../tasks-runner';
2
- import type { LifeCycle } from '../life-cycle';
2
+ import type { LifeCycle, TaskResult } from '../life-cycle';
3
3
  import { Task } from '../../config/task-graph';
4
4
  /**
5
5
  * The following life cycle's outputs are static, meaning no previous content
@@ -24,10 +24,6 @@ export declare class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
24
24
  startCommand(): void;
25
25
  endCommand(): void;
26
26
  private skippedTasks;
27
- endTasks(taskResults: {
28
- task: Task;
29
- status: TaskStatus;
30
- code: number;
31
- }[]): void;
27
+ endTasks(taskResults: TaskResult[]): void;
32
28
  printTaskTerminalOutput(task: Task, cacheStatus: TaskStatus, terminalOutput: string): void;
33
29
  }
@@ -1,5 +1,5 @@
1
1
  import { TaskStatus } from '../tasks-runner';
2
- import type { LifeCycle } from '../life-cycle';
2
+ import type { LifeCycle, TaskResult } from '../life-cycle';
3
3
  import { Task } from '../../config/task-graph';
4
4
  /**
5
5
  * The following life cycle's outputs are static, meaning no previous content
@@ -23,10 +23,6 @@ export declare class StaticRunOneTerminalOutputLifeCycle implements LifeCycle {
23
23
  });
24
24
  startCommand(): void;
25
25
  endCommand(): void;
26
- endTasks(taskResults: {
27
- task: Task;
28
- status: TaskStatus;
29
- code: number;
30
- }[]): void;
26
+ endTasks(taskResults: TaskResult[]): void;
31
27
  printTaskTerminalOutput(task: Task, status: TaskStatus, terminalOutput: string): void;
32
28
  }
@@ -1,6 +1,5 @@
1
- import { LifeCycle } from '../../tasks-runner/life-cycle';
1
+ import { LifeCycle, TaskResult } from '../../tasks-runner/life-cycle';
2
2
  import { Task } from '../../config/task-graph';
3
- import { TaskStatus } from '../../tasks-runner/tasks-runner';
4
3
  export declare class StoreRunInformationLifeCycle implements LifeCycle {
5
4
  private readonly command;
6
5
  private readonly storeFile;
@@ -10,11 +9,7 @@ export declare class StoreRunInformationLifeCycle implements LifeCycle {
10
9
  private taskResults;
11
10
  constructor(command?: string, storeFile?: typeof storeFileFunction, now?: () => string);
12
11
  startTasks(tasks: Task[]): void;
13
- endTasks(taskResults: Array<{
14
- task: Task;
15
- status: TaskStatus;
16
- code: number;
17
- }>): void;
12
+ endTasks(taskResults: TaskResult[]): void;
18
13
  startCommand(): void;
19
14
  endCommand(): any;
20
15
  }
@@ -8,7 +8,7 @@ class TaskHistoryLifeCycle {
8
8
  constructor() {
9
9
  this.startTimings = {};
10
10
  this.taskRuns = new Map();
11
- this.taskHistory = new task_history_1.TaskHistory();
11
+ this.taskHistory = (0, task_history_1.getTaskHistory)();
12
12
  }
13
13
  startTasks(tasks) {
14
14
  for (let task of tasks) {
@@ -1,5 +1,4 @@
1
- import { LifeCycle, TaskMetadata } from '../life-cycle';
2
- import { TaskStatus } from '../tasks-runner';
1
+ import { LifeCycle, TaskMetadata, TaskResult } from '../life-cycle';
3
2
  import { Task } from '../../config/task-graph';
4
3
  export declare class TaskProfilingLifeCycle implements LifeCycle {
5
4
  private timings;
@@ -8,11 +7,7 @@ export declare class TaskProfilingLifeCycle implements LifeCycle {
8
7
  private registeredGroups;
9
8
  constructor(_profileFile: string);
10
9
  startTasks(tasks: Task[], { groupId }: TaskMetadata): void;
11
- endTasks(taskResults: Array<{
12
- task: Task;
13
- status: TaskStatus;
14
- code: number;
15
- }>, metadata: TaskMetadata): void;
10
+ endTasks(taskResults: TaskResult[], metadata: TaskMetadata): void;
16
11
  endCommand(): void;
17
12
  private recordTaskCompletions;
18
13
  private registerGroup;
@@ -0,0 +1,6 @@
1
+ import type { LifeCycle, TaskResult } from '../life-cycle';
2
+ export declare class TaskResultsLifeCycle implements LifeCycle {
3
+ private taskResults;
4
+ endTasks(taskResults: TaskResult[]): void;
5
+ getTaskResults(): Record<string, TaskResult>;
6
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskResultsLifeCycle = void 0;
4
+ class TaskResultsLifeCycle {
5
+ constructor() {
6
+ this.taskResults = {};
7
+ }
8
+ endTasks(taskResults) {
9
+ for (let t of taskResults) {
10
+ this.taskResults[t.task.id] = t;
11
+ }
12
+ }
13
+ getTaskResults() {
14
+ return this.taskResults;
15
+ }
16
+ }
17
+ exports.TaskResultsLifeCycle = TaskResultsLifeCycle;
@@ -1,13 +1,8 @@
1
1
  import { Task } from '../../config/task-graph';
2
- import { LifeCycle } from '../life-cycle';
3
- import { TaskStatus } from '../tasks-runner';
2
+ import { LifeCycle, TaskResult } from '../life-cycle';
4
3
  export declare class TaskTimingsLifeCycle implements LifeCycle {
5
4
  private timings;
6
5
  startTasks(tasks: Task[]): void;
7
- endTasks(taskResults: Array<{
8
- task: Task;
9
- status: TaskStatus;
10
- code: number;
11
- }>): void;
6
+ endTasks(taskResults: TaskResult[]): void;
12
7
  endCommand(): void;
13
8
  }
@@ -3,7 +3,7 @@ import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
3
3
  import { Task, TaskGraph } from '../config/task-graph';
4
4
  import { TargetDependencyConfig } from '../config/workspace-json-project-json';
5
5
  import { NxArgs } from '../utils/command-line-utils';
6
- import { LifeCycle } from './life-cycle';
6
+ import { LifeCycle, TaskResult } from './life-cycle';
7
7
  import { TasksRunner } from './tasks-runner';
8
8
  export declare function runCommand(projectsToRun: ProjectGraphProjectNode[], currentProjectGraph: ProjectGraph, { nxJson }: {
9
9
  nxJson: NxJsonConfiguration;
@@ -11,6 +11,14 @@ export declare function runCommand(projectsToRun: ProjectGraphProjectNode[], cur
11
11
  excludeTaskDependencies: boolean;
12
12
  loadDotEnvFiles: boolean;
13
13
  }): Promise<NodeJS.Process['exitCode']>;
14
+ export declare function runCommandForTasks(projectsToRun: ProjectGraphProjectNode[], currentProjectGraph: ProjectGraph, { nxJson }: {
15
+ nxJson: NxJsonConfiguration;
16
+ }, nxArgs: NxArgs, overrides: any, initiatingProject: string | null, extraTargetDependencies: Record<string, (TargetDependencyConfig | string)[]>, extraOptions: {
17
+ excludeTaskDependencies: boolean;
18
+ loadDotEnvFiles: boolean;
19
+ }): Promise<{
20
+ [id: string]: TaskResult;
21
+ }>;
14
22
  export declare function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nxJson, nxArgs, loadDotEnvFiles, initiatingProject, }: {
15
23
  tasks: Task[];
16
24
  projectGraph: ProjectGraph;
@@ -20,7 +28,9 @@ export declare function invokeTasksRunner({ tasks, projectGraph, taskGraph, life
20
28
  nxArgs: NxArgs;
21
29
  loadDotEnvFiles: boolean;
22
30
  initiatingProject: string | null;
23
- }): Promise<1 | 0>;
31
+ }): Promise<{
32
+ [id: string]: TaskResult;
33
+ }>;
24
34
  export declare function getRunner(nxArgs: NxArgs, nxJson: NxJsonConfiguration): {
25
35
  tasksRunner: TasksRunner;
26
36
  runnerOptions: any;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runCommand = runCommand;
4
+ exports.runCommandForTasks = runCommandForTasks;
4
5
  exports.invokeTasksRunner = invokeTasksRunner;
5
6
  exports.getRunner = getRunner;
6
7
  exports.getRunnerOptions = getRunnerOptions;
@@ -31,6 +32,7 @@ const task_history_life_cycle_1 = require("./life-cycles/task-history-life-cycle
31
32
  const task_history_life_cycle_old_1 = require("./life-cycles/task-history-life-cycle-old");
32
33
  const task_profiling_life_cycle_1 = require("./life-cycles/task-profiling-life-cycle");
33
34
  const task_timings_life_cycle_1 = require("./life-cycles/task-timings-life-cycle");
35
+ const task_results_life_cycle_1 = require("./life-cycles/task-results-life-cycle");
34
36
  const task_graph_utils_1 = require("./task-graph-utils");
35
37
  const utils_1 = require("./utils");
36
38
  const chalk = require("chalk");
@@ -99,25 +101,31 @@ function createTaskGraphAndRunValidations(projectGraph, extraTargetDependencies,
99
101
  }
100
102
  async function runCommand(projectsToRun, currentProjectGraph, { nxJson }, nxArgs, overrides, initiatingProject, extraTargetDependencies, extraOptions) {
101
103
  const status = await (0, handle_errors_1.handleErrors)(process.env.NX_VERBOSE_LOGGING === 'true', async () => {
102
- const projectNames = projectsToRun.map((t) => t.name);
103
- const { projectGraph, taskGraph } = await ensureWorkspaceIsInSyncAndGetGraphs(currentProjectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions);
104
- const tasks = Object.values(taskGraph.tasks);
105
- const { lifeCycle, renderIsDone } = await getTerminalOutputLifeCycle(initiatingProject, projectNames, tasks, nxArgs, nxJson, overrides);
106
- const status = await invokeTasksRunner({
107
- tasks,
108
- projectGraph,
109
- taskGraph,
110
- lifeCycle,
111
- nxJson,
112
- nxArgs,
113
- loadDotEnvFiles: extraOptions.loadDotEnvFiles,
114
- initiatingProject,
115
- });
116
- await renderIsDone;
117
- return status;
104
+ const taskResults = await runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, nxArgs, overrides, initiatingProject, extraTargetDependencies, extraOptions);
105
+ return Object.values(taskResults).some((taskResult) => taskResult.status === 'failure' || taskResult.status === 'skipped')
106
+ ? 1
107
+ : 0;
118
108
  });
119
109
  return status;
120
110
  }
111
+ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, nxArgs, overrides, initiatingProject, extraTargetDependencies, extraOptions) {
112
+ const projectNames = projectsToRun.map((t) => t.name);
113
+ const { projectGraph, taskGraph } = await ensureWorkspaceIsInSyncAndGetGraphs(currentProjectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions);
114
+ const tasks = Object.values(taskGraph.tasks);
115
+ const { lifeCycle, renderIsDone } = await getTerminalOutputLifeCycle(initiatingProject, projectNames, tasks, nxArgs, nxJson, overrides);
116
+ const taskResults = await invokeTasksRunner({
117
+ tasks,
118
+ projectGraph,
119
+ taskGraph,
120
+ lifeCycle,
121
+ nxJson,
122
+ nxArgs,
123
+ loadDotEnvFiles: extraOptions.loadDotEnvFiles,
124
+ initiatingProject,
125
+ });
126
+ await renderIsDone;
127
+ return taskResults;
128
+ }
121
129
  async function ensureWorkspaceIsInSyncAndGetGraphs(projectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions) {
122
130
  let taskGraph = createTaskGraphAndRunValidations(projectGraph, extraTargetDependencies ?? {}, projectNames, nxArgs, overrides, extraOptions);
123
131
  if (nxArgs.skipSync) {
@@ -359,9 +367,14 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
359
367
  // to submit everything that is known in advance to Nx Cloud to run in
360
368
  // a distributed fashion
361
369
  await (0, hash_task_1.hashTasksThatDoNotDependOnOutputsOfOtherTasks)(hasher, projectGraph, taskGraph, nxJson);
362
- const promiseOrObservable = tasksRunner(tasks, {
370
+ const taskResultsLifecycle = new task_results_life_cycle_1.TaskResultsLifeCycle();
371
+ const compositedLifeCycle = new life_cycle_1.CompositeLifeCycle([
372
+ ...constructLifeCycles(lifeCycle),
373
+ taskResultsLifecycle,
374
+ ]);
375
+ let promiseOrObservable = tasksRunner(tasks, {
363
376
  ...runnerOptions,
364
- lifeCycle: new life_cycle_1.CompositeLifeCycle(constructLifeCycles(lifeCycle)),
377
+ lifeCycle: compositedLifeCycle,
365
378
  }, {
366
379
  initiatingProject: nxArgs.outputStyle === 'compact' ? null : initiatingProject,
367
380
  projectGraph,
@@ -418,15 +431,11 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
418
431
  },
419
432
  daemon: client_1.daemonClient,
420
433
  });
421
- let anyFailures;
422
434
  if (promiseOrObservable.subscribe) {
423
- anyFailures = await anyFailuresInObservable(promiseOrObservable);
424
- }
425
- else {
426
- // simply await the promise
427
- anyFailures = await anyFailuresInPromise(promiseOrObservable);
435
+ promiseOrObservable = convertObservableToPromise(promiseOrObservable);
428
436
  }
429
- return anyFailures ? 1 : 0;
437
+ await promiseOrObservable;
438
+ return taskResultsLifecycle.getTaskResults();
430
439
  }
431
440
  function constructLifeCycles(lifeCycle) {
432
441
  const lifeCycles = [];
@@ -439,45 +448,29 @@ function constructLifeCycles(lifeCycle) {
439
448
  lifeCycles.push(new task_profiling_life_cycle_1.TaskProfilingLifeCycle(process.env.NX_PROFILE));
440
449
  }
441
450
  if (!(0, nx_cloud_utils_1.isNxCloudUsed)((0, nx_json_1.readNxJson)())) {
442
- lifeCycles.push(!native_1.IS_WASM ? new task_history_life_cycle_1.TaskHistoryLifeCycle() : new task_history_life_cycle_old_1.LegacyTaskHistoryLifeCycle());
451
+ lifeCycles.push(process.env.NX_DISABLE_DB !== 'true' && !native_1.IS_WASM
452
+ ? new task_history_life_cycle_1.TaskHistoryLifeCycle()
453
+ : new task_history_life_cycle_old_1.LegacyTaskHistoryLifeCycle());
443
454
  }
444
455
  return lifeCycles;
445
456
  }
446
- function mergeTargetDependencies(defaults, deps) {
447
- const res = {};
448
- Object.keys(defaults ?? {}).forEach((k) => {
449
- res[k] = defaults[k].dependsOn;
450
- });
451
- if (deps) {
452
- Object.keys(deps).forEach((k) => {
453
- if (res[k]) {
454
- res[k] = [...res[k], deps[k]];
455
- }
456
- else {
457
- res[k] = deps[k];
458
- }
459
- });
460
- return res;
461
- }
462
- }
463
- async function anyFailuresInPromise(promise) {
464
- return Object.values(await promise).some((v) => v === 'failure' || v === 'skipped');
465
- }
466
- async function anyFailuresInObservable(obs) {
457
+ async function convertObservableToPromise(obs) {
467
458
  return await new Promise((res) => {
468
- let anyFailures = false;
469
- obs.subscribe((t) => {
470
- if (!t.success) {
471
- anyFailures = true;
472
- }
473
- }, (error) => {
474
- output_1.output.error({
475
- title: 'Unhandled error in task executor',
476
- });
477
- console.error(error);
478
- res(true);
479
- }, () => {
480
- res(anyFailures);
459
+ let tasksResults = {};
460
+ obs.subscribe({
461
+ next: (t) => {
462
+ tasksResults[t.task.id] = t.success ? 'success' : 'failure';
463
+ },
464
+ error: (error) => {
465
+ output_1.output.error({
466
+ title: 'Unhandled error in task executor',
467
+ });
468
+ console.error(error);
469
+ res(tasksResults);
470
+ },
471
+ complete: () => {
472
+ res(tasksResults);
473
+ },
481
474
  });
482
475
  });
483
476
  }
@@ -40,7 +40,6 @@ export declare class TaskOrchestrator {
40
40
  private runTaskInForkedProcess;
41
41
  private preRunSteps;
42
42
  private postRunSteps;
43
- private scheduleNextTasks;
44
43
  private complete;
45
44
  private pipeOutputCapture;
46
45
  private shouldCacheTaskResult;
@@ -41,9 +41,12 @@ class TaskOrchestrator {
41
41
  }
42
42
  async run() {
43
43
  // Init the ForkedProcessTaskRunner
44
- await this.forkedProcessTaskRunner.init();
44
+ await Promise.all([
45
+ this.forkedProcessTaskRunner.init(),
46
+ this.tasksSchedule.init(),
47
+ ]);
45
48
  // initial scheduling
46
- await this.scheduleNextTasks();
49
+ await this.tasksSchedule.scheduleNextTasks();
47
50
  perf_hooks_1.performance.mark('task-execution:start');
48
51
  const threads = [];
49
52
  process.stdout.setMaxListeners(this.options.parallel + events_1.defaultMaxListeners);
@@ -65,6 +68,7 @@ class TaskOrchestrator {
65
68
  }
66
69
  const doNotSkipCache = this.options.skipNxCache === false ||
67
70
  this.options.skipNxCache === undefined;
71
+ this.processAllScheduledTasks();
68
72
  const batch = this.tasksSchedule.nextBatch();
69
73
  if (batch) {
70
74
  const groupId = this.closeGroup();
@@ -373,15 +377,11 @@ class TaskOrchestrator {
373
377
  status,
374
378
  };
375
379
  }));
376
- await this.scheduleNextTasks();
380
+ await this.tasksSchedule.scheduleNextTasks();
377
381
  // release blocked threads
378
382
  this.waitingForTasks.forEach((f) => f(null));
379
383
  this.waitingForTasks.length = 0;
380
384
  }
381
- async scheduleNextTasks() {
382
- await this.tasksSchedule.scheduleNextTasks();
383
- this.processAllScheduledTasks();
384
- }
385
385
  complete(taskResults) {
386
386
  this.tasksSchedule.complete(taskResults.map(({ taskId }) => taskId));
387
387
  for (const { taskId, status } of taskResults) {
@@ -12,12 +12,15 @@ export declare class TasksSchedule {
12
12
  private notScheduledTaskGraph;
13
13
  private reverseTaskDeps;
14
14
  private reverseProjectGraph;
15
+ private taskHistory;
15
16
  private scheduledBatches;
16
17
  private scheduledTasks;
17
18
  private runningTasks;
18
19
  private completedTasks;
19
20
  private scheduleRequestsExecutionChain;
21
+ private estimatedTaskTimings;
20
22
  constructor(projectGraph: ProjectGraph, taskGraph: TaskGraph, options: DefaultTasksRunnerOptions);
23
+ init(): Promise<void>;
21
24
  scheduleNextTasks(): Promise<void>;
22
25
  hasTasks(): boolean;
23
26
  complete(taskIds: string[]): void;
@@ -4,6 +4,7 @@ exports.TasksSchedule = void 0;
4
4
  const utils_1 = require("./utils");
5
5
  const project_graph_utils_1 = require("../utils/project-graph-utils");
6
6
  const operators_1 = require("../project-graph/operators");
7
+ const task_history_1 = require("../utils/task-history");
7
8
  class TasksSchedule {
8
9
  constructor(projectGraph, taskGraph, options) {
9
10
  this.projectGraph = projectGraph;
@@ -12,11 +13,19 @@ class TasksSchedule {
12
13
  this.notScheduledTaskGraph = this.taskGraph;
13
14
  this.reverseTaskDeps = (0, utils_1.calculateReverseDeps)(this.taskGraph);
14
15
  this.reverseProjectGraph = (0, operators_1.reverse)(this.projectGraph);
16
+ this.taskHistory = process.env.NX_DISABLE_DB !== 'true' ? (0, task_history_1.getTaskHistory)() : null;
15
17
  this.scheduledBatches = [];
16
18
  this.scheduledTasks = [];
17
19
  this.runningTasks = new Set();
18
20
  this.completedTasks = new Set();
19
21
  this.scheduleRequestsExecutionChain = Promise.resolve();
22
+ this.estimatedTaskTimings = {};
23
+ }
24
+ async init() {
25
+ if (this.taskHistory) {
26
+ this.estimatedTaskTimings =
27
+ await this.taskHistory.getEstimatedTaskTimings(Object.values(this.taskGraph.tasks).map((t) => t.target));
28
+ }
20
29
  }
21
30
  async scheduleNextTasks() {
22
31
  this.scheduleRequestsExecutionChain =
@@ -81,10 +90,23 @@ class TasksSchedule {
81
90
  // Most likely tasks with no dependencies such as test
82
91
  const project1 = this.taskGraph.tasks[taskId1].target.project;
83
92
  const project2 = this.taskGraph.tasks[taskId2].target.project;
84
- return ((0, project_graph_utils_1.findAllProjectNodeDependencies)(project2, this.reverseProjectGraph)
85
- .length -
86
- (0, project_graph_utils_1.findAllProjectNodeDependencies)(project1, this.reverseProjectGraph)
87
- .length);
93
+ const project1NodeDependencies = (0, project_graph_utils_1.findAllProjectNodeDependencies)(project1, this.reverseProjectGraph).length;
94
+ const project2NodeDependencies = (0, project_graph_utils_1.findAllProjectNodeDependencies)(project2, this.reverseProjectGraph).length;
95
+ const dependenciesDiff = project2NodeDependencies - project1NodeDependencies;
96
+ if (dependenciesDiff !== 0) {
97
+ return dependenciesDiff;
98
+ }
99
+ const task1Timing = this.estimatedTaskTimings[taskId1];
100
+ if (!task1Timing) {
101
+ // if no timing or 0, put task1 at beginning
102
+ return -1;
103
+ }
104
+ const task2Timing = this.estimatedTaskTimings[taskId2];
105
+ if (!task2Timing) {
106
+ // if no timing or 0, put task2 at beginning
107
+ return 1;
108
+ }
109
+ return task2Timing - task1Timing;
88
110
  });
89
111
  this.runningTasks.add(taskId);
90
112
  }
@@ -7,6 +7,7 @@ export declare class GitRepository {
7
7
  root: string;
8
8
  constructor(directory: string);
9
9
  getGitRootPath(cwd: string): string;
10
+ hasUncommittedChanges(): Promise<boolean>;
10
11
  addFetchRemote(remoteName: string, branch: string): Promise<string>;
11
12
  showStat(): Promise<string>;
12
13
  listBranches(): Promise<string[]>;
@@ -25,10 +26,11 @@ export declare class GitRepository {
25
26
  deleteGitRemote(name: string): Promise<string>;
26
27
  addGitRemote(name: string, url: string): Promise<string>;
27
28
  hasFilterRepoInstalled(): Promise<boolean>;
28
- filterRepo(subdirectory: string): Promise<string>;
29
- filterBranch(subdirectory: string, branchName: string): Promise<string>;
29
+ filterRepo(source: string, destination: string): Promise<void>;
30
+ filterBranch(source: string, destination: string, branchName: string): Promise<void>;
30
31
  private execAsync;
31
32
  private quotePath;
33
+ private quoteArg;
32
34
  }
33
35
  /**
34
36
  * This is currently duplicated in Nx Console. Please let @MaxKless know if you make changes here.
File without changes
@@ -0,0 +1,20 @@
1
+ /**
2
+ * This is meant to be used with `git filter-branch --index-filter` to rewrite
3
+ * history such that only commits related to the subdirectory is kept.
4
+ *
5
+ * Example:
6
+ * NX_IMPORT_SOURCE=<source> git filter-branch --index-filter 'node git-utils.index-filter.js' --prune-empty -- --all
7
+ */
8
+ try {
9
+ const { execSync } = require('child_process');
10
+ // NOTE: Using env vars because Windows PowerShell has its own handling of quotes (") messes up quotes in args, even if escaped.
11
+ const src = process.env.NX_IMPORT_SOURCE;
12
+ execSync('git read-tree --empty', { stdio: 'inherit' });
13
+ execSync(`git reset ${process.env.GIT_COMMIT} -- "${src}"`, {
14
+ stdio: 'inherit',
15
+ });
16
+ }
17
+ catch (error) {
18
+ console.error(`Error executing Git commands: ${error}`);
19
+ process.exit(1);
20
+ }
@@ -40,6 +40,10 @@ class GitRepository {
40
40
  .toString()
41
41
  .trim();
42
42
  }
43
+ async hasUncommittedChanges() {
44
+ const data = await this.execAsync(`git status --porcelain`);
45
+ return data.trim() !== '';
46
+ }
43
47
  async addFetchRemote(remoteName, branch) {
44
48
  return await this.execAsync(`git config --add remote.${remoteName}.fetch "+refs/heads/${branch}:refs/remotes/${remoteName}/${branch}"`);
45
49
  }
@@ -105,32 +109,63 @@ class GitRepository {
105
109
  }
106
110
  // git-filter-repo is much faster than filter-branch, but needs to be installed by user
107
111
  // Use `hasFilterRepoInstalled` to check if it's installed
108
- async filterRepo(subdirectory) {
109
- // filter-repo requires POSIX path to work
110
- const posixPath = subdirectory.split(path_1.sep).join(path_1.posix.sep);
111
- return await this.execAsync(`git filter-repo -f --subdirectory-filter ${this.quotePath(posixPath)}`);
112
- }
113
- async filterBranch(subdirectory, branchName) {
114
- // filter-repo requires POSIX path to work
115
- const posixPath = subdirectory.split(path_1.sep).join(path_1.posix.sep);
112
+ async filterRepo(source, destination) {
113
+ // NOTE: filter-repo requires POSIX path to work
114
+ const sourcePosixPath = source.split(path_1.sep).join(path_1.posix.sep);
115
+ const destinationPosixPath = destination.split(path_1.sep).join(path_1.posix.sep);
116
+ await this.execAsync(`git filter-repo -f ${source !== '' ? `--path ${this.quotePath(sourcePosixPath)}` : ''} ${source !== destination
117
+ ? `--path-rename ${this.quotePath(sourcePosixPath, true)}:${this.quotePath(destinationPosixPath, true)}`
118
+ : ''}`);
119
+ }
120
+ async filterBranch(source, destination, branchName) {
116
121
  // We need non-ASCII file names to not be quoted, or else filter-branch will exclude them.
117
122
  await this.execAsync(`git config core.quotepath false`);
118
- return await this.execAsync(`git filter-branch --subdirectory-filter ${this.quotePath(posixPath)} -- ${branchName}`);
123
+ // NOTE: filter-repo requires POSIX path to work
124
+ const sourcePosixPath = source.split(path_1.sep).join(path_1.posix.sep);
125
+ const destinationPosixPath = destination.split(path_1.sep).join(path_1.posix.sep);
126
+ // First, if the source is not a root project, then only include commits relevant to the subdirectory.
127
+ if (source !== '') {
128
+ const indexFilterCommand = this.quoteArg(`node ${(0, path_1.join)(__dirname, 'git-utils.index-filter.js')}`);
129
+ await this.execAsync(`git filter-branch -f --index-filter ${indexFilterCommand} --prune-empty -- ${branchName}`, {
130
+ NX_IMPORT_SOURCE: sourcePosixPath,
131
+ NX_IMPORT_DESTINATION: destinationPosixPath,
132
+ });
133
+ }
134
+ // Then, move files to their new location if necessary.
135
+ if (source === '' || source !== destination) {
136
+ const treeFilterCommand = this.quoteArg(`node ${(0, path_1.join)(__dirname, 'git-utils.tree-filter.js')}`);
137
+ await this.execAsync(`git filter-branch -f --tree-filter ${treeFilterCommand} -- ${branchName}`, {
138
+ NX_IMPORT_SOURCE: sourcePosixPath,
139
+ NX_IMPORT_DESTINATION: destinationPosixPath,
140
+ });
141
+ }
119
142
  }
120
- execAsync(command) {
143
+ execAsync(command, env) {
121
144
  return execAsync(command, {
122
145
  cwd: this.root,
123
146
  maxBuffer: 10 * 1024 * 1024,
147
+ env: {
148
+ ...process.env,
149
+ ...env,
150
+ },
124
151
  });
125
152
  }
126
- quotePath(path) {
153
+ quotePath(path, ensureTrailingSlash) {
154
+ return this.quoteArg(ensureTrailingSlash && path !== '' && !path.endsWith('/')
155
+ ? `${path}/`
156
+ : path);
157
+ }
158
+ quoteArg(arg) {
127
159
  return process.platform === 'win32'
128
160
  ? // Windows/CMD only understands double-quotes, single-quotes are treated as part of the file name
129
161
  // Bash and other shells will substitute `$` in file names with a variable value.
130
- `"${path}"`
162
+ `"${arg
163
+ // Need to keep two slashes for Windows or else the path will be invalid.
164
+ // e.g. 'C:\Users\bob\projects\repo' is invalid, but 'C:\\Users\\bob\\projects\\repo' is valid
165
+ .replaceAll('\\', '\\\\')}"`
131
166
  : // e.g. `git mv "$$file.txt" "libs/a/$$file.txt"` will not work since `$$` is swapped with the PID of the last process.
132
167
  // Using single-quotes prevents this substitution.
133
- `'${path}'`;
168
+ `'${arg}'`;
134
169
  }
135
170
  }
136
171
  exports.GitRepository = GitRepository;