nx 20.8.0-rc.0 → 20.8.1

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.
@@ -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
@@ -8,10 +8,12 @@ export declare class ForkedProcessTaskRunner {
8
8
  cliPath: string;
9
9
  private readonly verbose;
10
10
  private processes;
11
+ private finishedProcesses;
11
12
  private pseudoTerminal;
12
13
  constructor(options: DefaultTasksRunnerOptions);
13
14
  init(): Promise<void>;
14
15
  forkProcessForBatch({ executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv): Promise<BatchResults>;
16
+ cleanUpBatchProcesses(): void;
15
17
  forkProcessLegacy(task: Task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }: {
16
18
  temporaryOutputPath: string;
17
19
  streamOutput: boolean;
@@ -20,6 +20,7 @@ class ForkedProcessTaskRunner {
20
20
  this.cliPath = (0, utils_1.getCliPath)();
21
21
  this.verbose = process.env.NX_VERBOSE_LOGGING === 'true';
22
22
  this.processes = new Set();
23
+ this.finishedProcesses = new Set();
23
24
  this.pseudoTerminal = pseudo_terminal_1.PseudoTerminal.isSupported()
24
25
  ? (0, pseudo_terminal_1.getPseudoTerminal)()
25
26
  : null;
@@ -33,6 +34,7 @@ class ForkedProcessTaskRunner {
33
34
  // TODO: vsavkin delegate terminal output printing
34
35
  forkProcessForBatch({ executorName, taskGraph: batchTaskGraph }, projectGraph, fullTaskGraph, env) {
35
36
  return new Promise((res, rej) => {
37
+ let p;
36
38
  try {
37
39
  const count = Object.keys(batchTaskGraph.tasks).length;
38
40
  if (count > 1) {
@@ -42,7 +44,7 @@ class ForkedProcessTaskRunner {
42
44
  const args = (0, utils_1.getPrintableCommandArgsForTask)(Object.values(batchTaskGraph.tasks)[0]);
43
45
  output_1.output.logCommand(args.join(' '));
44
46
  }
45
- const p = (0, child_process_1.fork)(workerPath, {
47
+ p = (0, child_process_1.fork)(workerPath, {
46
48
  stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
47
49
  env,
48
50
  });
@@ -52,20 +54,18 @@ class ForkedProcessTaskRunner {
52
54
  if (code === null)
53
55
  code = (0, exit_codes_1.signalToCode)(signal);
54
56
  if (code !== 0) {
55
- const results = {};
56
- for (const rootTaskId of batchTaskGraph.roots) {
57
- results[rootTaskId] = {
58
- success: false,
59
- terminalOutput: '',
60
- };
61
- }
62
57
  rej(new Error(`"${executorName}" exited unexpectedly with code: ${code}`));
63
58
  }
64
59
  });
60
+ p.on('error', (err) => {
61
+ this.processes.delete(p);
62
+ rej(err || new Error(`"${executorName}" exited unexpectedly`));
63
+ });
65
64
  p.on('message', (message) => {
66
65
  switch (message.type) {
67
66
  case batch_messages_1.BatchMessageType.CompleteBatchExecution: {
68
67
  res(message.results);
68
+ this.finishedProcesses.add(p);
69
69
  break;
70
70
  }
71
71
  case batch_messages_1.BatchMessageType.RunTasks: {
@@ -90,9 +90,23 @@ class ForkedProcessTaskRunner {
90
90
  }
91
91
  catch (e) {
92
92
  rej(e);
93
+ if (p) {
94
+ this.processes.delete(p);
95
+ p.kill();
96
+ }
93
97
  }
94
98
  });
95
99
  }
100
+ cleanUpBatchProcesses() {
101
+ if (this.finishedProcesses.size > 0) {
102
+ this.finishedProcesses.forEach((p) => {
103
+ if ('connected' in p ? p.connected : p.isAlive) {
104
+ p.kill();
105
+ }
106
+ });
107
+ this.finishedProcesses.clear();
108
+ }
109
+ }
96
110
  async forkProcessLegacy(task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }) {
97
111
  return pipeOutput
98
112
  ? await this.forkProcessPipeOutputCapture(task, {
@@ -157,6 +171,7 @@ class ForkedProcessTaskRunner {
157
171
  });
158
172
  return new Promise((res) => {
159
173
  p.onExit((code) => {
174
+ this.processes.delete(p);
160
175
  // If the exit code is greater than 128, it's a special exit code for a signal
161
176
  if (code >= 128) {
162
177
  process.exit(code);
@@ -227,7 +242,7 @@ class ForkedProcessTaskRunner {
227
242
  p.stderr.on('data', (chunk) => {
228
243
  outWithErr.push(chunk.toString());
229
244
  });
230
- p.on('exit', (code, signal) => {
245
+ p.once('exit', (code, signal) => {
231
246
  this.processes.delete(p);
232
247
  if (code === null)
233
248
  code = (0, exit_codes_1.signalToCode)(signal);
@@ -272,7 +287,8 @@ class ForkedProcessTaskRunner {
272
287
  taskGraph,
273
288
  isVerbose: this.verbose,
274
289
  });
275
- p.on('exit', (code, signal) => {
290
+ p.once('exit', (code, signal) => {
291
+ this.processes.delete(p);
276
292
  if (code === null)
277
293
  code = (0, exit_codes_1.signalToCode)(signal);
278
294
  // we didn't print any output as we were running the command
@@ -317,8 +333,7 @@ class ForkedProcessTaskRunner {
317
333
  process.send(message);
318
334
  });
319
335
  }
320
- // When the nx process gets a message, it will be sent into the task's process
321
- process.on('message', (message) => {
336
+ const messageHandler = (message) => {
322
337
  // this.publisher.publish(message.toString());
323
338
  if (this.pseudoTerminal) {
324
339
  this.pseudoTerminal.sendMessageToChildren(message);
@@ -328,39 +343,34 @@ class ForkedProcessTaskRunner {
328
343
  p.send(message);
329
344
  }
330
345
  });
331
- });
332
- // Terminate any task processes on exit
333
- process.on('exit', () => {
346
+ };
347
+ // When the nx process gets a message, it will be sent into the task's process
348
+ process.on('message', messageHandler);
349
+ const cleanUp = (signal) => {
334
350
  this.processes.forEach((p) => {
335
351
  if ('connected' in p ? p.connected : p.isAlive) {
336
- p.kill();
352
+ p.kill(signal);
337
353
  }
338
354
  });
355
+ process.off('message', messageHandler);
356
+ this.cleanUpBatchProcesses();
357
+ };
358
+ // Terminate any task processes on exit
359
+ process.once('exit', () => {
360
+ cleanUp();
339
361
  });
340
- process.on('SIGINT', () => {
341
- this.processes.forEach((p) => {
342
- if ('connected' in p ? p.connected : p.isAlive) {
343
- p.kill('SIGTERM');
344
- }
345
- });
362
+ process.once('SIGINT', () => {
363
+ cleanUp('SIGTERM');
346
364
  // we exit here because we don't need to write anything to cache.
347
365
  process.exit((0, exit_codes_1.signalToCode)('SIGINT'));
348
366
  });
349
- process.on('SIGTERM', () => {
350
- this.processes.forEach((p) => {
351
- if ('connected' in p ? p.connected : p.isAlive) {
352
- p.kill('SIGTERM');
353
- }
354
- });
367
+ process.once('SIGTERM', () => {
368
+ cleanUp('SIGTERM');
355
369
  // no exit here because we expect child processes to terminate which
356
370
  // will store results to the cache and will terminate this process
357
371
  });
358
- process.on('SIGHUP', () => {
359
- this.processes.forEach((p) => {
360
- if ('connected' in p ? p.connected : p.isAlive) {
361
- p.kill('SIGTERM');
362
- }
363
- });
372
+ process.once('SIGHUP', () => {
373
+ cleanUp('SIGTERM');
364
374
  // no exit here because we expect child processes to terminate which
365
375
  // will store results to the cache and will terminate this process
366
376
  });
@@ -171,6 +171,7 @@ class TaskOrchestrator {
171
171
  results.push(...batchResults);
172
172
  }
173
173
  await this.postRunSteps(tasks, results, doNotSkipCache, { groupId });
174
+ this.forkedProcessTaskRunner.cleanUpBatchProcesses();
174
175
  const tasksCompleted = taskEntries.filter(([taskId]) => this.completedTasks[taskId]);
175
176
  // Batch is still not done, run it again
176
177
  if (tasksCompleted.length !== taskEntries.length) {
@@ -362,6 +362,8 @@ function getSerializedArgsForTask(task, isVerbose) {
362
362
  function shouldStreamOutput(task, initiatingProject) {
363
363
  if (process.env.NX_STREAM_OUTPUT === 'true')
364
364
  return true;
365
+ if (process.env.NX_STREAM_OUTPUT === 'false')
366
+ return false;
365
367
  if (longRunningTask(task))
366
368
  return true;
367
369
  if (task.target.project === initiatingProject)