nx 21.0.0-beta.2 → 21.0.0-beta.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.
@@ -40,6 +40,7 @@ function nodeProcess(command, cwd, env) {
40
40
  }
41
41
  async function ptyProcess(command, cwd, env) {
42
42
  const terminal = (0, pseudo_terminal_1.createPseudoTerminal)();
43
+ await terminal.init();
43
44
  return new Promise((res, rej) => {
44
45
  const cp = terminal.runCommand(command, { cwd, jsEnv: env });
45
46
  cp.onExit((code) => {
@@ -1,5 +1,5 @@
1
1
  const { join, basename } = require('path');
2
- const { copyFileSync, existsSync, mkdirSync } = require('fs');
2
+ const { copyFileSync, existsSync, mkdirSync, renameSync } = require('fs');
3
3
  const Module = require('module');
4
4
  const { nxVersion } = require('../utils/versions');
5
5
  const { getNativeFileCacheLocation } = require('./native-file-cache-location');
@@ -71,14 +71,28 @@ Module._load = function (request, parent, isMain) {
71
71
 
72
72
  // we copy the file to a workspace-scoped tmp directory and prefix with nxVersion to avoid stale files being loaded
73
73
  const nativeFileCacheLocation = getNativeFileCacheLocation();
74
+ // This is a path to copy to, not the one that gets loaded
75
+ const tmpTmpFile = join(
76
+ nativeFileCacheLocation,
77
+ nxVersion + '-' + Math.random() + fileName
78
+ );
79
+ // This is the path that will get loaded
74
80
  const tmpFile = join(nativeFileCacheLocation, nxVersion + '-' + fileName);
81
+
82
+ // If the file to be loaded already exists, just load it
75
83
  if (existsSync(tmpFile)) {
76
84
  return originalLoad.apply(this, [tmpFile, parent, isMain]);
77
85
  }
78
86
  if (!existsSync(nativeFileCacheLocation)) {
79
87
  mkdirSync(nativeFileCacheLocation, { recursive: true });
80
88
  }
81
- copyFileSync(nativeLocation, tmpFile);
89
+ // First copy to a unique location for each process
90
+ copyFileSync(nativeLocation, tmpTmpFile);
91
+
92
+ // Then rename to the final location
93
+ renameSync(tmpTmpFile, tmpFile);
94
+
95
+ // Load from the final location
82
96
  return originalLoad.apply(this, [tmpFile, parent, isMain]);
83
97
  } else {
84
98
  // call the original _load function for everything else
Binary file
@@ -1,5 +1,5 @@
1
1
  import { NxJsonConfiguration } from '../../config/nx-json';
2
- import { ProjectConfiguration } from '../../config/workspace-json-project-json';
2
+ import type { ProjectConfiguration } from '../../config/workspace-json-project-json';
3
3
  import { PackageJson } from '../../utils/package-json';
4
4
  import { CreateNodesV2 } from '../../project-graph/plugins';
5
5
  import { PackageJsonConfigurationCache } from '../../../plugins/package-json';
@@ -11,10 +11,12 @@ export declare class ForkedProcessTaskRunner {
11
11
  cliPath: string;
12
12
  private readonly verbose;
13
13
  private processes;
14
+ private finishedProcesses;
14
15
  private pseudoTerminals;
15
16
  constructor(options: DefaultTasksRunnerOptions, tuiEnabled: boolean);
16
17
  init(): Promise<void>;
17
18
  forkProcessForBatch({ executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv): Promise<BatchProcess>;
19
+ cleanUpBatchProcesses(): void;
18
20
  forkProcessLegacy(task: Task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }: {
19
21
  temporaryOutputPath: string;
20
22
  streamOutput: boolean;
@@ -22,6 +22,7 @@ class ForkedProcessTaskRunner {
22
22
  this.cliPath = (0, utils_1.getCliPath)();
23
23
  this.verbose = process.env.NX_VERBOSE_LOGGING === 'true';
24
24
  this.processes = new Set();
25
+ this.finishedProcesses = new Set();
25
26
  this.pseudoTerminals = new Set();
26
27
  }
27
28
  async init() {
@@ -56,6 +57,14 @@ class ForkedProcessTaskRunner {
56
57
  });
57
58
  return cp;
58
59
  }
60
+ cleanUpBatchProcesses() {
61
+ if (this.finishedProcesses.size > 0) {
62
+ this.finishedProcesses.forEach((p) => {
63
+ p.kill();
64
+ });
65
+ this.finishedProcesses.clear();
66
+ }
67
+ }
59
68
  async forkProcessLegacy(task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }) {
60
69
  return pipeOutput
61
70
  ? this.forkProcessWithPrefixAndNotTTY(task, {
@@ -221,41 +230,41 @@ class ForkedProcessTaskRunner {
221
230
  (0, fs_1.writeFileSync)(outputPath, content);
222
231
  }
223
232
  setupProcessEventListeners() {
224
- // When the nx process gets a message, it will be sent into the task's process
225
- process.on('message', (message) => {
233
+ const messageHandler = (message) => {
226
234
  this.pseudoTerminals.forEach((p) => {
227
235
  p.sendMessageToChildren(message);
228
236
  });
229
237
  this.processes.forEach((p) => {
230
- if ('send' in p) {
238
+ if ('connected' in p && p.connected && 'send' in p) {
231
239
  p.send(message);
232
240
  }
233
241
  });
234
- });
235
- // Terminate any task processes on exit
236
- process.on('exit', () => {
242
+ };
243
+ // When the nx process gets a message, it will be sent into the task's process
244
+ process.on('message', messageHandler);
245
+ const cleanUp = (signal) => {
237
246
  this.processes.forEach((p) => {
238
- p.kill();
247
+ p.kill(signal);
239
248
  });
249
+ process.off('message', messageHandler);
250
+ this.cleanUpBatchProcesses();
251
+ };
252
+ // Terminate any task processes on exit
253
+ process.once('exit', () => {
254
+ cleanUp();
240
255
  });
241
- process.on('SIGINT', () => {
242
- this.processes.forEach((p) => {
243
- p.kill('SIGTERM');
244
- });
256
+ process.once('SIGINT', () => {
257
+ cleanUp('SIGTERM');
245
258
  // we exit here because we don't need to write anything to cache.
246
259
  process.exit((0, exit_codes_1.signalToCode)('SIGINT'));
247
260
  });
248
- process.on('SIGTERM', () => {
249
- this.processes.forEach((p) => {
250
- p.kill('SIGTERM');
251
- });
261
+ process.once('SIGTERM', () => {
262
+ cleanUp('SIGTERM');
252
263
  // no exit here because we expect child processes to terminate which
253
264
  // will store results to the cache and will terminate this process
254
265
  });
255
- process.on('SIGHUP', () => {
256
- this.processes.forEach((p) => {
257
- p.kill('SIGTERM');
258
- });
266
+ process.once('SIGHUP', () => {
267
+ cleanUp('SIGTERM');
259
268
  // no exit here because we expect child processes to terminate which
260
269
  // will store results to the cache and will terminate this process
261
270
  });
@@ -75,12 +75,16 @@ class TaskOrchestrator {
75
75
  }
76
76
  await Promise.race([
77
77
  Promise.all(threads),
78
- new Promise((resolve) => {
79
- this.options.lifeCycle.registerForcedShutdownCallback(() => {
80
- // The user force quit the TUI with ctrl+c, so proceed onto cleanup
81
- resolve(undefined);
82
- });
83
- }),
78
+ ...(this.tuiEnabled
79
+ ? [
80
+ new Promise((resolve) => {
81
+ this.options.lifeCycle.registerForcedShutdownCallback(() => {
82
+ // The user force quit the TUI with ctrl+c, so proceed onto cleanup
83
+ resolve(undefined);
84
+ });
85
+ }),
86
+ ]
87
+ : []),
84
88
  ]);
85
89
  perf_hooks_1.performance.mark('task-execution:end');
86
90
  perf_hooks_1.performance.measure('task-execution', 'task-execution:start', 'task-execution:end');
@@ -201,6 +205,7 @@ class TaskOrchestrator {
201
205
  results.push(...batchResults);
202
206
  }
203
207
  await this.postRunSteps(tasks, results, doNotSkipCache, { groupId });
208
+ this.forkedProcessTaskRunner.cleanUpBatchProcesses();
204
209
  const tasksCompleted = taskEntries.filter(([taskId]) => this.completedTasks[taskId]);
205
210
  // Batch is still not done, run it again
206
211
  if (tasksCompleted.length !== taskEntries.length) {