nx 22.7.10 → 22.7.11
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/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +3 -0
- package/dist/src/tasks-runner/forked-process-task-runner.js +15 -6
- package/dist/src/tasks-runner/init-tasks-runner.js +32 -2
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle.js +16 -11
- package/dist/src/tasks-runner/running-tasks/batch-process.js +3 -0
- package/dist/src/tasks-runner/running-tasks/shared-running-task.d.ts +1 -0
- package/dist/src/tasks-runner/running-tasks/shared-running-task.js +12 -1
- package/dist/src/tasks-runner/task-orchestrator.d.ts +3 -0
- package/dist/src/tasks-runner/task-orchestrator.js +40 -6
- package/package.json +11 -11
|
Binary file
|
|
Binary file
|
|
@@ -11,6 +11,8 @@ export declare class ForkedProcessTaskRunner {
|
|
|
11
11
|
cliPath: string;
|
|
12
12
|
private readonly verbose;
|
|
13
13
|
private processes;
|
|
14
|
+
private processMessageHandler;
|
|
15
|
+
private processExitHandler;
|
|
14
16
|
private finishedProcesses;
|
|
15
17
|
private pseudoTerminals;
|
|
16
18
|
constructor(options: DefaultTasksRunnerOptions, tuiEnabled: boolean);
|
|
@@ -39,4 +41,5 @@ export declare class ForkedProcessTaskRunner {
|
|
|
39
41
|
private writeTerminalOutput;
|
|
40
42
|
cleanup(signal?: NodeJS.Signals): void;
|
|
41
43
|
private setupProcessEventListeners;
|
|
44
|
+
removeProcessEventListeners(): void;
|
|
42
45
|
}
|
|
@@ -271,7 +271,7 @@ class ForkedProcessTaskRunner {
|
|
|
271
271
|
this.cleanUpBatchProcesses();
|
|
272
272
|
}
|
|
273
273
|
setupProcessEventListeners() {
|
|
274
|
-
|
|
274
|
+
this.processMessageHandler = (message) => {
|
|
275
275
|
this.pseudoTerminals.forEach((p) => {
|
|
276
276
|
p.sendMessageToChildren(message);
|
|
277
277
|
});
|
|
@@ -281,14 +281,23 @@ class ForkedProcessTaskRunner {
|
|
|
281
281
|
}
|
|
282
282
|
});
|
|
283
283
|
};
|
|
284
|
+
this.processExitHandler = () => {
|
|
285
|
+
this.cleanup();
|
|
286
|
+
process.off('message', this.processMessageHandler);
|
|
287
|
+
};
|
|
284
288
|
// When the nx process gets a message, it will be sent into the task's process
|
|
285
|
-
process.on('message',
|
|
289
|
+
process.on('message', this.processMessageHandler);
|
|
286
290
|
// Terminate any task processes on exit
|
|
287
|
-
process.once('exit',
|
|
288
|
-
this.cleanup();
|
|
289
|
-
process.off('message', messageHandler);
|
|
290
|
-
});
|
|
291
|
+
process.once('exit', this.processExitHandler);
|
|
291
292
|
// Signal handlers removed - TaskOrchestrator owns signal handling
|
|
292
293
|
}
|
|
294
|
+
// Long-lived processes (Nx Cloud agents) create a runner per invocation;
|
|
295
|
+
// without this each instance stays reachable from `process` forever.
|
|
296
|
+
// Call only after cleanup(): the exit handler is the last-resort child
|
|
297
|
+
// kill, so it must not be removed while children may still be alive.
|
|
298
|
+
removeProcessEventListeners() {
|
|
299
|
+
process.off('message', this.processMessageHandler);
|
|
300
|
+
process.off('exit', this.processExitHandler);
|
|
301
|
+
}
|
|
293
302
|
}
|
|
294
303
|
exports.ForkedProcessTaskRunner = ForkedProcessTaskRunner;
|
|
@@ -14,6 +14,7 @@ const life_cycle_1 = require("./life-cycle");
|
|
|
14
14
|
const task_orchestrator_1 = require("./task-orchestrator");
|
|
15
15
|
const create_task_hasher_1 = require("../hasher/create-task-hasher");
|
|
16
16
|
const client_1 = require("../daemon/client/client");
|
|
17
|
+
const shared_running_task_1 = require("./running-tasks/shared-running-task");
|
|
17
18
|
const task_results_life_cycle_1 = require("./life-cycles/task-results-life-cycle");
|
|
18
19
|
/**
|
|
19
20
|
* This function is deprecated. Do not use this
|
|
@@ -110,6 +111,11 @@ async function createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJs
|
|
|
110
111
|
orchestrator.processAllScheduledTasks();
|
|
111
112
|
return orchestrator;
|
|
112
113
|
}
|
|
114
|
+
// Nothing awaits the dispose chains below, so an unhandled rejection would take
|
|
115
|
+
// down a long-lived agent process.
|
|
116
|
+
function logDisposeFailure(e) {
|
|
117
|
+
console.error('Failed to dispose the task orchestrator:', e);
|
|
118
|
+
}
|
|
113
119
|
async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
|
|
114
120
|
const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
|
|
115
121
|
let groupId = 0;
|
|
@@ -138,12 +144,36 @@ async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson
|
|
|
138
144
|
return [hit];
|
|
139
145
|
return [await orchestrator.runTaskDirectly(true, task, groupId++)];
|
|
140
146
|
});
|
|
141
|
-
|
|
147
|
+
const results = [...batchResults, ...taskResults];
|
|
148
|
+
// Callers like Nx Cloud agents create an orchestrator per invocation in a
|
|
149
|
+
// long-lived process; release its process-level listeners once all tasks
|
|
150
|
+
// settle, otherwise every invocation's orchestrator stays reachable forever.
|
|
151
|
+
// Not awaited, so callers keep consuming handles as they settle; the forked
|
|
152
|
+
// runner's exit handler still reaps children until dispose() runs.
|
|
153
|
+
Promise.allSettled(results)
|
|
154
|
+
.then(() => orchestrator.dispose())
|
|
155
|
+
.catch(logDisposeFailure);
|
|
156
|
+
return results;
|
|
142
157
|
}
|
|
143
158
|
async function runContinuousTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
|
|
144
159
|
const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
|
|
145
|
-
|
|
160
|
+
const runningTasks = tasks.reduce((current, task, index) => {
|
|
146
161
|
current[task.id] = orchestrator.startContinuousTask(task, index);
|
|
147
162
|
return current;
|
|
148
163
|
}, {});
|
|
164
|
+
// Unlike runDiscreteTasks, this must resolve at task start: callers keep
|
|
165
|
+
// the RunningTask handles to kill later, so disposal has to be deferred
|
|
166
|
+
// until every task actually exits.
|
|
167
|
+
Promise.allSettled(Object.entries(runningTasks).map(async ([taskId, promise]) => {
|
|
168
|
+
const runningTask = await promise;
|
|
169
|
+
// A shared task is owned by another nx process; this orchestrator has
|
|
170
|
+
// no child to protect for it, so disposal does not wait on it.
|
|
171
|
+
if (runningTask instanceof shared_running_task_1.SharedRunningTask) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
await orchestrator.waitForContinuousTaskExit(taskId);
|
|
175
|
+
}))
|
|
176
|
+
.then(() => orchestrator.dispose())
|
|
177
|
+
.catch(logDisposeFailure);
|
|
178
|
+
return runningTasks;
|
|
149
179
|
}
|
|
@@ -22,6 +22,8 @@ function getTasksHistoryLifeCycle() {
|
|
|
22
22
|
class TaskHistoryLifeCycle {
|
|
23
23
|
constructor() {
|
|
24
24
|
this.startTimings = {};
|
|
25
|
+
// Holds no terminalOutput: this singleton outlives every run in the
|
|
26
|
+
// process, and agents complete thousands of tasks in one process.
|
|
25
27
|
this.pendingResults = new Map();
|
|
26
28
|
this.taskRuns = new Map();
|
|
27
29
|
this.taskHistory = (0, task_history_1.getTaskHistory)();
|
|
@@ -36,25 +38,28 @@ class TaskHistoryLifeCycle {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
async endTasks(taskResults) {
|
|
39
|
-
for (const
|
|
40
|
-
this.pendingResults.set(
|
|
41
|
+
for (const { task, code, status } of taskResults) {
|
|
42
|
+
this.pendingResults.set(task.id, { task, code, status });
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
async endCommand() {
|
|
46
|
+
const pendingResults = Array.from(this.pendingResults.values());
|
|
47
|
+
this.pendingResults.clear();
|
|
44
48
|
if (!this.taskHistory) {
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
// Build TaskRun objects now — task.hash is guaranteed to be set by this point
|
|
48
|
-
for (const
|
|
49
|
-
this.taskRuns.set(
|
|
50
|
-
hash:
|
|
51
|
-
target:
|
|
52
|
-
code
|
|
53
|
-
status
|
|
54
|
-
start:
|
|
55
|
-
end:
|
|
56
|
-
cacheable:
|
|
52
|
+
for (const { task, code, status } of pendingResults) {
|
|
53
|
+
this.taskRuns.set(task.hash, {
|
|
54
|
+
hash: task.hash,
|
|
55
|
+
target: task.target,
|
|
56
|
+
code,
|
|
57
|
+
status,
|
|
58
|
+
start: task.startTime ?? this.startTimings[task.id],
|
|
59
|
+
end: task.endTime ?? Date.now(),
|
|
60
|
+
cacheable: task.cache === true,
|
|
57
61
|
});
|
|
62
|
+
delete this.startTimings[task.id];
|
|
58
63
|
}
|
|
59
64
|
const runs = [];
|
|
60
65
|
// Only check for flaky tasks among cacheable tasks
|
|
@@ -82,6 +82,9 @@ class BatchProcess {
|
|
|
82
82
|
onOutput(cb) {
|
|
83
83
|
this.outputCallbacks.push(cb);
|
|
84
84
|
}
|
|
85
|
+
// Resolves on the CompleteBatchExecution message, not process exit: the
|
|
86
|
+
// child can outlive its results (open handles keep it alive), so callers
|
|
87
|
+
// must not treat a settled batch as an exited process.
|
|
85
88
|
async getResults() {
|
|
86
89
|
return Promise.race([
|
|
87
90
|
new Promise((_, rej) => {
|
|
@@ -3,6 +3,7 @@ import { RunningTasksService } from '../../native';
|
|
|
3
3
|
export declare class SharedRunningTask implements RunningTask {
|
|
4
4
|
private runningTasksService;
|
|
5
5
|
private exitCallbacks;
|
|
6
|
+
private detached;
|
|
6
7
|
constructor(runningTasksService: RunningTasksService, taskId: string);
|
|
7
8
|
getResults(): Promise<{
|
|
8
9
|
code: number;
|
|
@@ -5,7 +5,11 @@ class SharedRunningTask {
|
|
|
5
5
|
constructor(runningTasksService, taskId) {
|
|
6
6
|
this.runningTasksService = runningTasksService;
|
|
7
7
|
this.exitCallbacks = [];
|
|
8
|
+
this.detached = false;
|
|
8
9
|
this.waitForTaskToFinish(taskId).then(() => {
|
|
10
|
+
if (this.detached) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
// notify exit callbacks
|
|
10
14
|
this.exitCallbacks.forEach((cb) => cb(0));
|
|
11
15
|
});
|
|
@@ -13,8 +17,14 @@ class SharedRunningTask {
|
|
|
13
17
|
async getResults() {
|
|
14
18
|
throw new Error('Results cannot be retrieved from a shared task');
|
|
15
19
|
}
|
|
20
|
+
// Detaches from the shared task; the owning process keeps running it.
|
|
16
21
|
kill() {
|
|
22
|
+
if (this.detached) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.detached = true;
|
|
17
26
|
this.exitCallbacks.forEach((cb) => cb(0));
|
|
27
|
+
this.exitCallbacks = [];
|
|
18
28
|
}
|
|
19
29
|
onExit(cb) {
|
|
20
30
|
this.exitCallbacks.push(cb);
|
|
@@ -24,7 +34,8 @@ class SharedRunningTask {
|
|
|
24
34
|
// wait for the running task to finish
|
|
25
35
|
do {
|
|
26
36
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
27
|
-
} while (this.
|
|
37
|
+
} while (!this.detached &&
|
|
38
|
+
this.runningTasksService.getRunningTasks([taskId]).length);
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
exports.SharedRunningTask = SharedRunningTask;
|
|
@@ -47,6 +47,7 @@ export declare class TaskOrchestrator {
|
|
|
47
47
|
private discreteTaskExitHandled;
|
|
48
48
|
private continuousTaskExitHandled;
|
|
49
49
|
private cleanupPromise;
|
|
50
|
+
private signalHandlers;
|
|
50
51
|
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);
|
|
51
52
|
init(): Promise<void>;
|
|
52
53
|
run(): Promise<{
|
|
@@ -170,6 +171,8 @@ export declare class TaskOrchestrator {
|
|
|
170
171
|
private cleanup;
|
|
171
172
|
private performCleanup;
|
|
172
173
|
private setupSignalHandlers;
|
|
174
|
+
waitForContinuousTaskExit(taskId: string): Promise<void>;
|
|
175
|
+
dispose(): Promise<void>;
|
|
173
176
|
private cleanUpUnneededContinuousTasks;
|
|
174
177
|
}
|
|
175
178
|
export declare function getThreadPoolSize(options: NxArgs & DefaultTasksRunnerOptions, taskGraph: TaskGraph): {
|
|
@@ -86,6 +86,7 @@ class TaskOrchestrator {
|
|
|
86
86
|
this.discreteTaskExitHandled = new Map();
|
|
87
87
|
this.continuousTaskExitHandled = new Map();
|
|
88
88
|
this.cleanupPromise = null;
|
|
89
|
+
this.signalHandlers = [];
|
|
89
90
|
}
|
|
90
91
|
async init() {
|
|
91
92
|
this.setupSignalHandlers();
|
|
@@ -139,6 +140,7 @@ class TaskOrchestrator {
|
|
|
139
140
|
this.cache.removeOldCacheRecords();
|
|
140
141
|
}
|
|
141
142
|
await this.cleanup();
|
|
143
|
+
await this.dispose();
|
|
142
144
|
// Public API (defaultTasksRunner) returns a plain object keyed by
|
|
143
145
|
// task id. Internal state is a Map for faster lookup.
|
|
144
146
|
return Object.fromEntries(this.completedTasks);
|
|
@@ -1184,7 +1186,7 @@ class TaskOrchestrator {
|
|
|
1184
1186
|
await Promise.all(this.discreteTaskExitHandled.values());
|
|
1185
1187
|
}
|
|
1186
1188
|
setupSignalHandlers() {
|
|
1187
|
-
|
|
1189
|
+
const sigintHandler = () => {
|
|
1188
1190
|
this.stopRequested = true;
|
|
1189
1191
|
if (!this.tuiEnabled) {
|
|
1190
1192
|
// Synchronously remove DB entries before async cleanup to prevent
|
|
@@ -1216,23 +1218,55 @@ class TaskOrchestrator {
|
|
|
1216
1218
|
process.exit((0, exit_codes_1.signalToCode)('SIGINT'));
|
|
1217
1219
|
}
|
|
1218
1220
|
});
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
+
};
|
|
1222
|
+
const sigtermHandler = () => {
|
|
1221
1223
|
this.stopRequested = true;
|
|
1222
1224
|
this.cleanup().finally(() => {
|
|
1223
1225
|
if (this.resolveStopPromise) {
|
|
1224
1226
|
this.resolveStopPromise();
|
|
1225
1227
|
}
|
|
1226
1228
|
});
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
+
};
|
|
1230
|
+
const sighupHandler = () => {
|
|
1229
1231
|
this.stopRequested = true;
|
|
1230
1232
|
this.cleanup().finally(() => {
|
|
1231
1233
|
if (this.resolveStopPromise) {
|
|
1232
1234
|
this.resolveStopPromise();
|
|
1233
1235
|
}
|
|
1234
1236
|
});
|
|
1235
|
-
}
|
|
1237
|
+
};
|
|
1238
|
+
for (const [signal, handler] of [
|
|
1239
|
+
['SIGINT', sigintHandler],
|
|
1240
|
+
['SIGTERM', sigtermHandler],
|
|
1241
|
+
['SIGHUP', sighupHandler],
|
|
1242
|
+
]) {
|
|
1243
|
+
this.signalHandlers.push([signal, handler]);
|
|
1244
|
+
process.once(signal, handler);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
// Registered at child creation, so unlike subscribing to RunningTask.onExit
|
|
1248
|
+
// after the fact it cannot miss an exit that already happened.
|
|
1249
|
+
waitForContinuousTaskExit(taskId) {
|
|
1250
|
+
return this.continuousTaskExitHandled.get(taskId) ?? Promise.resolve();
|
|
1251
|
+
}
|
|
1252
|
+
// Releases the process-level listeners registered by setupSignalHandlers.
|
|
1253
|
+
// Each closes over `this`, so a long-lived caller (an Nx Cloud agent creates
|
|
1254
|
+
// an orchestrator per invocation) leaks whole orchestrators until they run.
|
|
1255
|
+
async dispose() {
|
|
1256
|
+
// The forked runner's exit handler is the last-resort kill for child
|
|
1257
|
+
// processes, and a batch child can outlive its results message. Signal the
|
|
1258
|
+
// children first so removing that handler cannot orphan a live one.
|
|
1259
|
+
try {
|
|
1260
|
+
await this.forkedProcessTaskRunner.cleanup();
|
|
1261
|
+
}
|
|
1262
|
+
catch (e) {
|
|
1263
|
+
console.error('Failed to clean up child processes on dispose:', e);
|
|
1264
|
+
}
|
|
1265
|
+
for (const [signal, handler] of this.signalHandlers) {
|
|
1266
|
+
process.off(signal, handler);
|
|
1267
|
+
}
|
|
1268
|
+
this.signalHandlers = [];
|
|
1269
|
+
this.forkedProcessTaskRunner.removeProcessEventListeners();
|
|
1236
1270
|
}
|
|
1237
1271
|
cleanUpUnneededContinuousTasks() {
|
|
1238
1272
|
const incompleteTasks = this.tasksSchedule.getIncompleteTasks();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nx",
|
|
3
|
-
"version": "22.7.
|
|
3
|
+
"version": "22.7.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
|
|
@@ -175,16 +175,16 @@
|
|
|
175
175
|
}
|
|
176
176
|
},
|
|
177
177
|
"optionalDependencies": {
|
|
178
|
-
"@nx/nx-darwin-arm64": "22.7.
|
|
179
|
-
"@nx/nx-darwin-x64": "22.7.
|
|
180
|
-
"@nx/nx-freebsd-x64": "22.7.
|
|
181
|
-
"@nx/nx-linux-arm-gnueabihf": "22.7.
|
|
182
|
-
"@nx/nx-linux-arm64-gnu": "22.7.
|
|
183
|
-
"@nx/nx-linux-arm64-musl": "22.7.
|
|
184
|
-
"@nx/nx-linux-x64-gnu": "22.7.
|
|
185
|
-
"@nx/nx-linux-x64-musl": "22.7.
|
|
186
|
-
"@nx/nx-win32-arm64-msvc": "22.7.
|
|
187
|
-
"@nx/nx-win32-x64-msvc": "22.7.
|
|
178
|
+
"@nx/nx-darwin-arm64": "22.7.11",
|
|
179
|
+
"@nx/nx-darwin-x64": "22.7.11",
|
|
180
|
+
"@nx/nx-freebsd-x64": "22.7.11",
|
|
181
|
+
"@nx/nx-linux-arm-gnueabihf": "22.7.11",
|
|
182
|
+
"@nx/nx-linux-arm64-gnu": "22.7.11",
|
|
183
|
+
"@nx/nx-linux-arm64-musl": "22.7.11",
|
|
184
|
+
"@nx/nx-linux-x64-gnu": "22.7.11",
|
|
185
|
+
"@nx/nx-linux-x64-musl": "22.7.11",
|
|
186
|
+
"@nx/nx-win32-arm64-msvc": "22.7.11",
|
|
187
|
+
"@nx/nx-win32-x64-msvc": "22.7.11"
|
|
188
188
|
},
|
|
189
189
|
"nx-migrations": {
|
|
190
190
|
"migrations": "./migrations.json",
|