nx 23.2.1 → 23.3.0-beta.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.
- package/dist/src/command-line/format/format.js +2 -18
- package/dist/src/command-line/graph/graph.js +42 -3
- package/dist/src/command-line/init/implementation/format.js +2 -3
- package/dist/src/command-line/migrate/agentic/definitions.js +75 -19
- package/dist/src/command-line/migrate/agentic/format-command.d.ts +14 -0
- package/dist/src/command-line/migrate/agentic/format-command.js +44 -0
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +3 -3
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +3 -3
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +27 -17
- package/dist/src/command-line/migrate/agentic/handoff.js +107 -24
- package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/instruction-files.js +36 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +16 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +70 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +4 -3
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +25 -1
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +57 -34
- package/dist/src/command-line/migrate/agentic/run-step.js +28 -4
- package/dist/src/command-line/migrate/agentic/runner.d.ts +17 -8
- package/dist/src/command-line/migrate/agentic/runner.js +110 -55
- package/dist/src/command-line/migrate/agentic/types.d.ts +23 -8
- package/dist/src/command-line/migrate/agentic/types.js +10 -7
- package/dist/src/command-line/migrate/migrate-commits.d.ts +1 -0
- package/dist/src/command-line/migrate/migrate-commits.js +9 -0
- package/dist/src/command-line/migrate/migrate-output.d.ts +3 -7
- package/dist/src/command-line/migrate/migrate-output.js +3 -7
- package/dist/src/command-line/migrate/migrate.js +21 -8
- package/dist/src/command-line/migrate/run/agent-output.d.ts +10 -0
- package/dist/src/command-line/migrate/run/agent-output.js +16 -2
- package/dist/src/command-line/migrate/run/agent-work-payload.d.ts +37 -0
- package/dist/src/command-line/migrate/run/agent-work-payload.js +142 -0
- package/dist/src/command-line/migrate/run/atomic-write.d.ts +8 -0
- package/dist/src/command-line/migrate/run/atomic-write.js +17 -0
- package/dist/src/command-line/migrate/run/index.d.ts +1 -1
- package/dist/src/command-line/migrate/run/issues.d.ts +106 -0
- package/dist/src/command-line/migrate/run/issues.js +980 -0
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +1 -0
- package/dist/src/command-line/migrate/run/orchestrator.js +662 -105
- package/dist/src/command-line/migrate/run/run-state.d.ts +43 -8
- package/dist/src/command-line/migrate/run/run-state.js +184 -20
- package/dist/src/command-line/migrate/run/runbook.d.ts +17 -0
- package/dist/src/command-line/migrate/run/runbook.js +97 -0
- package/dist/src/command-line/migrate/run/state-lock.d.ts +2 -2
- package/dist/src/command-line/migrate/run/state-lock.js +2 -2
- package/dist/src/command-line/migrate/run/state-machine.d.ts +5 -1
- package/dist/src/command-line/migrate/run/state-machine.js +27 -1
- package/dist/src/command-line/migrate/run/worker.js +206 -56
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +5 -1
- package/dist/src/command-line/release/publish.js +11 -4
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -0
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +35 -0
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/dist/src/command-line/yargs-utils/shared-options.js +22 -4
- package/dist/src/config/workspace-json-project-json.d.ts +2 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +4 -2
- package/dist/src/daemon/client/client.js +20 -11
- package/dist/src/daemon/message-types/run-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/handle-hash-tasks.d.ts +8 -2
- package/dist/src/daemon/server/handle-hash-tasks.js +16 -3
- package/dist/src/daemon/server/handle-outputs-changes.d.ts +2 -2
- package/dist/src/daemon/server/handle-outputs-changes.js +21 -6
- package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/outputs-tracking.d.ts +6 -0
- package/dist/src/daemon/server/outputs-tracking.js +18 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +22 -7
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +99 -69
- package/dist/src/daemon/server/restart-checks.d.ts +22 -0
- package/dist/src/daemon/server/restart-checks.js +77 -0
- package/dist/src/daemon/server/server.js +28 -18
- package/dist/src/daemon/server/shutdown-utils.d.ts +0 -5
- package/dist/src/daemon/server/shutdown-utils.js +3 -26
- package/dist/src/hasher/hash-task.js +20 -16
- package/dist/src/hasher/native-task-hasher-impl.d.ts +8 -0
- package/dist/src/hasher/native-task-hasher-impl.js +46 -3
- package/dist/src/hasher/task-hasher.d.ts +9 -0
- package/dist/src/hasher/task-hasher.js +14 -0
- package/dist/src/native/index.d.ts +202 -21
- package/dist/src/native/index.js +3 -3
- package/dist/src/native/native-bindings.js +3 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +1 -1
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +2 -0
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +22 -0
- package/dist/src/plugins/js/utils/register.js +2 -1
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +2 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +3 -2
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +5 -1
- package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +3 -2
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +5 -1
- package/dist/src/project-graph/plugins/task-results-stub.d.ts +50 -0
- package/dist/src/project-graph/plugins/task-results-stub.js +111 -0
- package/dist/src/project-graph/plugins/tasks-execution-hooks.d.ts +3 -2
- package/dist/src/project-graph/plugins/tasks-execution-hooks.js +2 -1
- package/dist/src/project-graph/project-graph.js +2 -0
- package/dist/src/tasks-runner/batch/run-batch.js +15 -0
- package/dist/src/tasks-runner/cache.d.ts +35 -0
- package/dist/src/tasks-runner/cache.js +40 -0
- package/dist/src/tasks-runner/create-task-graph.js +1 -0
- package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.js +8 -7
- package/dist/src/tasks-runner/init-tasks-runner.js +5 -1
- package/dist/src/tasks-runner/is-tui-enabled.js +4 -3
- package/dist/src/tasks-runner/life-cycle.d.ts +7 -0
- package/dist/src/tasks-runner/life-cycle.js +7 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.d.ts +35 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +146 -0
- package/dist/src/tasks-runner/prune-task-graph.d.ts +17 -0
- package/dist/src/tasks-runner/prune-task-graph.js +45 -0
- package/dist/src/tasks-runner/run-command.js +14 -3
- package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +93 -19
- package/dist/src/tasks-runner/running-tasks/batch-process.js +184 -88
- package/dist/src/tasks-runner/task-orchestrator.d.ts +64 -2
- package/dist/src/tasks-runner/task-orchestrator.js +232 -46
- package/dist/src/tasks-runner/terminal-output-path.d.ts +11 -0
- package/dist/src/tasks-runner/terminal-output-path.js +18 -0
- package/dist/src/tasks-runner/utils.js +5 -2
- package/dist/src/utils/ab-testing.d.ts +3 -0
- package/dist/src/utils/ab-testing.js +11 -3
- package/dist/src/utils/chunkify.d.ts +1 -7
- package/dist/src/utils/chunkify.js +2 -8
- package/dist/src/utils/command-line-utils.d.ts +19 -0
- package/dist/src/utils/command-line-utils.js +10 -3
- package/dist/src/utils/formatters/index.d.ts +6 -0
- package/dist/src/utils/formatters/index.js +5 -0
- package/dist/src/utils/formatters/prettier.d.ts +0 -8
- package/dist/src/utils/formatters/prettier.js +4 -26
- package/dist/src/utils/installed-nx-version.js +2 -48
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/output.d.ts +22 -3
- package/dist/src/utils/output.js +28 -5
- package/dist/src/utils/package-json.d.ts +25 -0
- package/dist/src/utils/package-json.js +41 -6
- package/dist/src/utils/package-manager.d.ts +6 -0
- package/dist/src/utils/package-manager.js +10 -0
- package/dist/src/utils/provenance.js +20 -4
- package/dist/src/utils/workspace-context.d.ts +47 -3
- package/dist/src/utils/workspace-context.js +162 -10
- package/package.json +12 -12
- package/schemas/nx-schema.json +4 -0
- package/schemas/project-schema.json +4 -0
- package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +0 -9
- package/dist/src/daemon/server/file-watching/route-workspace-changes.js +0 -76
- package/dist/src/daemon/server/watcher.d.ts +0 -18
- package/dist/src/daemon/server/watcher.js +0 -126
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stubTerminalOutputs = stubTerminalOutputs;
|
|
4
|
+
exports.rehydrateTerminalOutputs = rehydrateTerminalOutputs;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const promises_1 = require("node:fs/promises");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const terminal_output_path_1 = require("../../tasks-runner/terminal-output-path");
|
|
9
|
+
const logger_1 = require("../../utils/logger");
|
|
10
|
+
function isStubbed(context) {
|
|
11
|
+
return !!context.stubbedTerminalOutputs;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Swaps outputs out for paths ahead of a transport. Idempotent, so a context
|
|
15
|
+
* already stubbed for the daemon can be passed on to a plugin worker as-is.
|
|
16
|
+
*/
|
|
17
|
+
function stubTerminalOutputs(context) {
|
|
18
|
+
if (!context.taskResults) {
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
const stubbedTerminalOutputs = {
|
|
22
|
+
...(isStubbed(context) ? context.stubbedTerminalOutputs : {}),
|
|
23
|
+
};
|
|
24
|
+
const taskResults = {};
|
|
25
|
+
let swapped = false;
|
|
26
|
+
for (const [id, result] of Object.entries(context.taskResults)) {
|
|
27
|
+
const path = id in stubbedTerminalOutputs ? null : stubbablePath(result);
|
|
28
|
+
if (path) {
|
|
29
|
+
const { terminalOutput, ...withoutOutput } = result;
|
|
30
|
+
stubbedTerminalOutputs[id] = path;
|
|
31
|
+
taskResults[id] = withoutOutput;
|
|
32
|
+
swapped = true;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
taskResults[id] = result;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Nothing to swap: hand back the context as it came, so a run with no
|
|
39
|
+
// outputs on disk sends exactly what it sent before. Covers the
|
|
40
|
+
// already-stubbed case too, which keeps its own map by staying untouched.
|
|
41
|
+
if (!swapped) {
|
|
42
|
+
return context;
|
|
43
|
+
}
|
|
44
|
+
return { ...context, taskResults, stubbedTerminalOutputs };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reads the outputs back. A no-op on a context that was never stubbed, which
|
|
48
|
+
* is every in-process plugin run without the daemon.
|
|
49
|
+
*
|
|
50
|
+
* The reads are issued together rather than awaited one at a time: a run has
|
|
51
|
+
* one file per task, they are independent, and this sits between the tasks
|
|
52
|
+
* finishing and the first plugin seeing the results - so serializing it would
|
|
53
|
+
* add up every file's latency in front of that.
|
|
54
|
+
*/
|
|
55
|
+
async function rehydrateTerminalOutputs(context) {
|
|
56
|
+
if (!isStubbed(context)) {
|
|
57
|
+
return context;
|
|
58
|
+
}
|
|
59
|
+
const taskResults = { ...context.taskResults };
|
|
60
|
+
const reads = Object.entries(context.stubbedTerminalOutputs)
|
|
61
|
+
// A stub whose result is gone has nothing to attach the bytes to, so it is
|
|
62
|
+
// dropped before the read rather than after it.
|
|
63
|
+
.filter(([id]) => taskResults[id])
|
|
64
|
+
.map(async ([id, path]) => {
|
|
65
|
+
const terminalOutput = await readTerminalOutput(path);
|
|
66
|
+
taskResults[id] = { ...taskResults[id], terminalOutput };
|
|
67
|
+
});
|
|
68
|
+
await Promise.all(reads);
|
|
69
|
+
const { stubbedTerminalOutputs, ...rehydrated } = {
|
|
70
|
+
...context,
|
|
71
|
+
taskResults,
|
|
72
|
+
};
|
|
73
|
+
return rehydrated;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The path to swap in, or null to leave the output inline. A task that failed
|
|
77
|
+
* before it was hashed has no path, and a task that never ran has no file —
|
|
78
|
+
* both keep whatever they already carry rather than losing it.
|
|
79
|
+
*/
|
|
80
|
+
function stubbablePath(result) {
|
|
81
|
+
if (result.terminalOutput === undefined) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const hash = result.task?.hash;
|
|
85
|
+
if (!hash) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// `getCustomHasher` lets a plugin return any string, and this one reaches a
|
|
89
|
+
// read, so the sink is kept inside the cache dir here rather than trusted to
|
|
90
|
+
// the hasher. A hash that needed stripping resolves to a file that does not
|
|
91
|
+
// exist, which falls back to sending the output inline.
|
|
92
|
+
const path = (0, terminal_output_path_1.terminalOutputPathForHash)((0, node_path_1.basename)(hash));
|
|
93
|
+
return (0, node_fs_1.existsSync)(path) ? path : null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A path that no longer resolves means the bytes are gone. Saying so with
|
|
97
|
+
* `undefined` — already a legal value, skipped tasks have it — beats leaving
|
|
98
|
+
* the path, which would have plugins treat a filename as the output.
|
|
99
|
+
*/
|
|
100
|
+
async function readTerminalOutput(path) {
|
|
101
|
+
if (!path) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await (0, promises_1.readFile)(path, 'utf-8');
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
logger_1.logger.warn(`Nx could not read a task's terminal output from ${path}: ${e.message}`);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PreTasksExecutionContext } from './public-api';
|
|
2
|
+
import { type MaybeStubbedPostTasksExecutionContext } from './task-results-stub';
|
|
2
3
|
export declare function runPreTasksExecution(pluginContext: PreTasksExecutionContext): Promise<NodeJS.ProcessEnv[]>;
|
|
3
|
-
export declare function runPostTasksExecution(context:
|
|
4
|
+
export declare function runPostTasksExecution(context: MaybeStubbedPostTasksExecutionContext): Promise<void>;
|
|
@@ -7,6 +7,7 @@ const get_plugins_1 = require("./get-plugins");
|
|
|
7
7
|
const is_on_daemon_1 = require("../../daemon/is-on-daemon");
|
|
8
8
|
const client_1 = require("../../daemon/client/client");
|
|
9
9
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
10
|
+
const task_results_stub_1 = require("./task-results-stub");
|
|
10
11
|
async function runPreTasksExecution(pluginContext) {
|
|
11
12
|
if ((0, is_on_daemon_1.isOnDaemon)() || !(0, client_1.isDaemonEnabled)()) {
|
|
12
13
|
performance.mark(`preTasksExecution:start`);
|
|
@@ -62,6 +63,6 @@ async function runPostTasksExecution(context) {
|
|
|
62
63
|
performance.measure(`postTasksExecution`, `postTasksExecution:start`, `postTasksExecution:end`);
|
|
63
64
|
}
|
|
64
65
|
else {
|
|
65
|
-
await client_1.daemonClient.runPostTasksExecution(context);
|
|
66
|
+
await client_1.daemonClient.runPostTasksExecution((0, task_results_stub_1.stubTerminalOutputs)(context));
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -26,6 +26,7 @@ const logger_1 = require("../utils/logger");
|
|
|
26
26
|
const output_1 = require("../utils/output");
|
|
27
27
|
const strip_indents_1 = require("../utils/strip-indents");
|
|
28
28
|
const workspace_root_1 = require("../utils/workspace-root");
|
|
29
|
+
const workspace_context_1 = require("../utils/workspace-context");
|
|
29
30
|
const build_project_graph_1 = require("./build-project-graph");
|
|
30
31
|
const error_types_1 = require("./error-types");
|
|
31
32
|
const nx_deps_cache_1 = require("./nx-deps-cache");
|
|
@@ -87,6 +88,7 @@ async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
|
|
87
88
|
preventRecursionInGraphConstruction();
|
|
88
89
|
global.NX_GRAPH_CREATION = true;
|
|
89
90
|
const nxJson = (0, nx_json_1.readNxJson)();
|
|
91
|
+
(0, workspace_context_1.refreshWorkspaceContext)(workspace_root_1.workspaceRoot);
|
|
90
92
|
perf_hooks_1.performance.mark('retrieve-project-configurations:start');
|
|
91
93
|
let configurationResult;
|
|
92
94
|
let projectConfigurationsError;
|
|
@@ -62,13 +62,28 @@ async function runTasks(executorName, projectGraph, batchTaskGraph, fullTaskGrap
|
|
|
62
62
|
catch (e) {
|
|
63
63
|
const isVerbose = tasks[0].overrides.verbose;
|
|
64
64
|
console.error(isVerbose ? e : e.message);
|
|
65
|
+
// `process.exit` does not wait for pipe writes, so the reason this worker
|
|
66
|
+
// died could otherwise be the one line that never arrives.
|
|
67
|
+
await flushStdio();
|
|
65
68
|
process.exit(1);
|
|
66
69
|
}
|
|
67
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Waits until everything written to stdout and stderr so far has reached the
|
|
73
|
+
* pipe. Writes to a pipe are asynchronous, and the parent learns the batch is
|
|
74
|
+
* over through a separate IPC channel - so without this it can be told the batch
|
|
75
|
+
* finished, or see the process exit, while the end of its output is still in
|
|
76
|
+
* memory here. That tail is where a tool reports what went wrong.
|
|
77
|
+
*/
|
|
78
|
+
function flushStdio() {
|
|
79
|
+
const flush = (stream) => new Promise((resolve) => stream.write('', () => resolve()));
|
|
80
|
+
return Promise.all([flush(process.stdout), flush(process.stderr)]).then(() => undefined);
|
|
81
|
+
}
|
|
68
82
|
process.on('message', async (message) => {
|
|
69
83
|
switch (message.type) {
|
|
70
84
|
case batch_messages_1.BatchMessageType.RunTasks: {
|
|
71
85
|
const results = await runTasks(message.executorName, message.projectGraph, message.batchTaskGraph, message.fullTaskGraph);
|
|
86
|
+
await flushStdio();
|
|
72
87
|
process.send({
|
|
73
88
|
type: batch_messages_1.BatchMessageType.CompleteBatchExecution,
|
|
74
89
|
results,
|
|
@@ -13,6 +13,23 @@ export type TaskWithCachedResult = {
|
|
|
13
13
|
};
|
|
14
14
|
export declare function dbCacheEnabled(): boolean;
|
|
15
15
|
export declare function getCache(options: DefaultTasksRunnerOptions): DbCache | Cache;
|
|
16
|
+
/**
|
|
17
|
+
* Collects the batch worker logs written by `batchOutputPathForKey`.
|
|
18
|
+
*
|
|
19
|
+
* Guarded rather than called straight through: `cache.rs` is
|
|
20
|
+
* `#[cfg(not(target_arch = "wasm32"))]`, so this export does not exist in the
|
|
21
|
+
* WASM binding and calling it there is a `TypeError`. Every other native cache
|
|
22
|
+
* entry point is kept off that path by `dbCacheEnabled()`; this one is a free
|
|
23
|
+
* function, so it needs its own guard. The logs are collected on the next
|
|
24
|
+
* non-WASM run.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sweepBatchOutputs(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Where a batch worker's own log lives. Mirrors `batch_outputs_path` in
|
|
29
|
+
* cache.rs, which is what sweeps the directory — the same mirroring
|
|
30
|
+
* `terminalOutputPathForHash` does for `get_task_outputs_path_internal`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function batchOutputPathForKey(key: string): string;
|
|
16
33
|
export declare class DbCache {
|
|
17
34
|
private readonly options;
|
|
18
35
|
private nxJson;
|
|
@@ -32,6 +49,16 @@ export declare class DbCache {
|
|
|
32
49
|
getUsedCacheSpace(): number;
|
|
33
50
|
private applyRemoteCacheResults;
|
|
34
51
|
put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Register terminal outputs that were written outside of `put` — uncacheable
|
|
54
|
+
* tasks, and cacheable ones run with `--skip-nx-cache`. `removeOldCacheRecords`
|
|
55
|
+
* only collects hashes it finds in the database, so without this the files
|
|
56
|
+
* would never be cleaned up.
|
|
57
|
+
*/
|
|
58
|
+
recordTerminalOutputs(records: {
|
|
59
|
+
hash: string;
|
|
60
|
+
size: number;
|
|
61
|
+
}[]): void;
|
|
35
62
|
copyFilesFromCache(_: string, cachedResult: CachedResult, outputs: string[]): Promise<number>;
|
|
36
63
|
removeOldCacheRecords(): void;
|
|
37
64
|
temporaryOutputPath(task: Task): string;
|
|
@@ -63,6 +90,14 @@ export declare class Cache {
|
|
|
63
90
|
put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
|
|
64
91
|
copyFilesFromCache(hash: string, cachedResult: CachedResult, outputs: string[]): Promise<void>;
|
|
65
92
|
temporaryOutputPath(task: Task): string;
|
|
93
|
+
/**
|
|
94
|
+
* No-op: the legacy cache has no database to record against, and its
|
|
95
|
+
* collection is directory-based rather than driven by cache records.
|
|
96
|
+
*/
|
|
97
|
+
recordTerminalOutputs(_records: {
|
|
98
|
+
hash: string;
|
|
99
|
+
size: number;
|
|
100
|
+
}[]): void;
|
|
66
101
|
private expandOutputsInWorkspace;
|
|
67
102
|
private expandOutputsInCache;
|
|
68
103
|
private _expandOutputs;
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Cache = exports.DbCache = void 0;
|
|
4
4
|
exports.dbCacheEnabled = dbCacheEnabled;
|
|
5
5
|
exports.getCache = getCache;
|
|
6
|
+
exports.sweepBatchOutputs = sweepBatchOutputs;
|
|
7
|
+
exports.batchOutputPathForKey = batchOutputPathForKey;
|
|
6
8
|
exports.resolveMaxCacheSize = resolveMaxCacheSize;
|
|
7
9
|
exports.parseMaxCacheSize = parseMaxCacheSize;
|
|
8
10
|
exports.formatCacheSize = formatCacheSize;
|
|
@@ -54,6 +56,30 @@ function getCache(options) {
|
|
|
54
56
|
})
|
|
55
57
|
: new Cache(options);
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Collects the batch worker logs written by `batchOutputPathForKey`.
|
|
61
|
+
*
|
|
62
|
+
* Guarded rather than called straight through: `cache.rs` is
|
|
63
|
+
* `#[cfg(not(target_arch = "wasm32"))]`, so this export does not exist in the
|
|
64
|
+
* WASM binding and calling it there is a `TypeError`. Every other native cache
|
|
65
|
+
* entry point is kept off that path by `dbCacheEnabled()`; this one is a free
|
|
66
|
+
* function, so it needs its own guard. The logs are collected on the next
|
|
67
|
+
* non-WASM run.
|
|
68
|
+
*/
|
|
69
|
+
function sweepBatchOutputs() {
|
|
70
|
+
if (native_1.IS_WASM) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
(0, native_1.sweepBatchOutputs)(cache_directory_1.cacheDir);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Where a batch worker's own log lives. Mirrors `batch_outputs_path` in
|
|
77
|
+
* cache.rs, which is what sweeps the directory — the same mirroring
|
|
78
|
+
* `terminalOutputPathForHash` does for `get_task_outputs_path_internal`.
|
|
79
|
+
*/
|
|
80
|
+
function batchOutputPathForKey(key) {
|
|
81
|
+
return (0, path_1.join)(cache_directory_1.cacheDir, 'batchOutputs', `${key}.log`);
|
|
82
|
+
}
|
|
57
83
|
class DbCache {
|
|
58
84
|
constructor(options) {
|
|
59
85
|
this.options = options;
|
|
@@ -161,6 +187,15 @@ class DbCache {
|
|
|
161
187
|
}
|
|
162
188
|
});
|
|
163
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Register terminal outputs that were written outside of `put` — uncacheable
|
|
192
|
+
* tasks, and cacheable ones run with `--skip-nx-cache`. `removeOldCacheRecords`
|
|
193
|
+
* only collects hashes it finds in the database, so without this the files
|
|
194
|
+
* would never be cleaned up.
|
|
195
|
+
*/
|
|
196
|
+
recordTerminalOutputs(records) {
|
|
197
|
+
this.cache.recordTerminalOutputs(records);
|
|
198
|
+
}
|
|
164
199
|
copyFilesFromCache(_, cachedResult, outputs) {
|
|
165
200
|
return tryAndRetry(async () => this.cache.copyFilesFromCache(cachedResult, outputs));
|
|
166
201
|
}
|
|
@@ -421,6 +456,11 @@ class Cache {
|
|
|
421
456
|
temporaryOutputPath(task) {
|
|
422
457
|
return (0, path_1.join)(this.terminalOutputsDir, task.hash);
|
|
423
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* No-op: the legacy cache has no database to record against, and its
|
|
461
|
+
* collection is directory-based rather than driven by cache records.
|
|
462
|
+
*/
|
|
463
|
+
recordTerminalOutputs(_records) { }
|
|
424
464
|
async expandOutputsInWorkspace(outputs) {
|
|
425
465
|
return this._expandOutputs(outputs, workspace_root_1.workspaceRoot);
|
|
426
466
|
}
|
|
@@ -172,6 +172,7 @@ class ProcessTasks {
|
|
|
172
172
|
dependencyConfig.target +
|
|
173
173
|
DUMMY_TASK_TARGET, undefined);
|
|
174
174
|
this.dependencies[task.id].push(dummyId);
|
|
175
|
+
this.continuousDependencies[task.id].push(dummyId);
|
|
175
176
|
this.dependencies[dummyId] ??= [];
|
|
176
177
|
this.continuousDependencies[dummyId] ??= [];
|
|
177
178
|
const noopTask = this.createDummyTask(dummyId, task);
|
|
@@ -68,7 +68,7 @@ const defaultTasksRunner = async (tasks, options, context) => {
|
|
|
68
68
|
};
|
|
69
69
|
exports.defaultTasksRunner = defaultTasksRunner;
|
|
70
70
|
async function runAllTasks(options, context) {
|
|
71
|
-
const orchestrator = new task_orchestrator_1.TaskOrchestrator(context.hasher, context.initiatingProject, context.initiatingTasks, context.projectGraph, context.taskGraph, context.nxJson, options, context.nxArgs?.nxBail, context.daemon, context.nxArgs?.
|
|
71
|
+
const orchestrator = new task_orchestrator_1.TaskOrchestrator(context.hasher, context.initiatingProject, context.initiatingTasks, context.projectGraph, context.taskGraph, context.nxJson, options, context.nxArgs?.nxBail, context.daemon, context.nxArgs?.specifiedOutputStyle, context.nxArgs?.resolvedOutputStyle ?? 'static-failures-only');
|
|
72
72
|
return orchestrator.run();
|
|
73
73
|
}
|
|
74
74
|
exports.default = exports.defaultTasksRunner;
|
|
@@ -17,7 +17,7 @@ export declare class ForkedProcessTaskRunner {
|
|
|
17
17
|
private pseudoTerminals;
|
|
18
18
|
constructor(options: DefaultTasksRunnerOptions, tuiEnabled: boolean);
|
|
19
19
|
init(): Promise<void>;
|
|
20
|
-
forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv): Promise<BatchProcess>;
|
|
20
|
+
forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv, printsOutput?: boolean): Promise<BatchProcess>;
|
|
21
21
|
cleanUpBatchProcesses(): void;
|
|
22
22
|
forkProcessLegacy(task: Task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }: {
|
|
23
23
|
temporaryOutputPath: string;
|
|
@@ -5,6 +5,7 @@ const child_process_1 = require("child_process");
|
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const output_1 = require("../utils/output");
|
|
8
|
+
const prune_task_graph_1 = require("./prune-task-graph");
|
|
8
9
|
const strip_indents_1 = require("../utils/strip-indents");
|
|
9
10
|
const batch_messages_1 = require("./batch/batch-messages");
|
|
10
11
|
const process_metrics_service_1 = require("./process-metrics-service");
|
|
@@ -29,7 +30,7 @@ class ForkedProcessTaskRunner {
|
|
|
29
30
|
this.setupProcessEventListeners();
|
|
30
31
|
}
|
|
31
32
|
// TODO: vsavkin delegate terminal output printing
|
|
32
|
-
async forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }, projectGraph, fullTaskGraph, env) {
|
|
33
|
+
async forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }, projectGraph, fullTaskGraph, env, printsOutput = true) {
|
|
33
34
|
const count = Object.keys(batchTaskGraph.tasks).length;
|
|
34
35
|
if (count > 1) {
|
|
35
36
|
output_1.output.logSingleLine(`Running ${output_1.output.bold(count)} ${output_1.output.bold('tasks')} with ${output_1.output.bold(executorName)}`);
|
|
@@ -50,7 +51,7 @@ class ForkedProcessTaskRunner {
|
|
|
50
51
|
const taskIds = Object.keys(batchTaskGraph.tasks);
|
|
51
52
|
(0, process_metrics_service_1.getProcessMetricsService)().registerBatch(batchId, taskIds, p.pid);
|
|
52
53
|
}
|
|
53
|
-
const cp = new batch_process_1.BatchProcess(p, executorName);
|
|
54
|
+
const cp = new batch_process_1.BatchProcess(p, executorName, printsOutput, batchId);
|
|
54
55
|
this.processes.add(cp);
|
|
55
56
|
cp.onExit(() => {
|
|
56
57
|
this.processes.delete(cp);
|
|
@@ -60,8 +61,8 @@ class ForkedProcessTaskRunner {
|
|
|
60
61
|
type: batch_messages_1.BatchMessageType.RunTasks,
|
|
61
62
|
executorName,
|
|
62
63
|
projectGraph,
|
|
63
|
-
batchTaskGraph,
|
|
64
|
-
fullTaskGraph,
|
|
64
|
+
batchTaskGraph: (0, prune_task_graph_1.pruneTaskGraph)(batchTaskGraph),
|
|
65
|
+
fullTaskGraph: (0, prune_task_graph_1.pruneTaskGraph)(fullTaskGraph),
|
|
65
66
|
});
|
|
66
67
|
return cp;
|
|
67
68
|
}
|
|
@@ -148,7 +149,7 @@ class ForkedProcessTaskRunner {
|
|
|
148
149
|
p.send({
|
|
149
150
|
targetDescription: task.target,
|
|
150
151
|
overrides: task.overrides,
|
|
151
|
-
taskGraph,
|
|
152
|
+
taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
|
|
152
153
|
isVerbose: this.verbose,
|
|
153
154
|
});
|
|
154
155
|
this.processes.add(p);
|
|
@@ -186,7 +187,7 @@ class ForkedProcessTaskRunner {
|
|
|
186
187
|
p.send({
|
|
187
188
|
targetDescription: task.target,
|
|
188
189
|
overrides: task.overrides,
|
|
189
|
-
taskGraph,
|
|
190
|
+
taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
|
|
190
191
|
isVerbose: this.verbose,
|
|
191
192
|
});
|
|
192
193
|
const cp = new node_child_process_1.NodeChildProcessWithNonDirectOutput(p, {
|
|
@@ -231,7 +232,7 @@ class ForkedProcessTaskRunner {
|
|
|
231
232
|
p.send({
|
|
232
233
|
targetDescription: task.target,
|
|
233
234
|
overrides: task.overrides,
|
|
234
|
-
taskGraph,
|
|
235
|
+
taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
|
|
235
236
|
isVerbose: this.verbose,
|
|
236
237
|
});
|
|
237
238
|
cp.onExit((code, signal) => {
|
|
@@ -43,7 +43,11 @@ async function createOrchestrator(tasks, projectGraph, fullTaskGraph, nxJson, li
|
|
|
43
43
|
lifeCycle: compositedLifeCycle,
|
|
44
44
|
};
|
|
45
45
|
(0, run_command_1.setEnvVarsBasedOnArgs)(nxArgs, true);
|
|
46
|
-
const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient,
|
|
46
|
+
const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient,
|
|
47
|
+
// No argv on this path, so the output-style middleware never ran and both
|
|
48
|
+
// fields are unset - this always renders `static-failures-only`. `nx.json`'s
|
|
49
|
+
// `outputStyle` does not reach them; it merges under its own name.
|
|
50
|
+
nxArgs.specifiedOutputStyle, nxArgs.resolvedOutputStyle ?? 'static-failures-only', fullTaskGraph);
|
|
47
51
|
await orchestrator.init();
|
|
48
52
|
orchestrator.processAllScheduledTasks();
|
|
49
53
|
return orchestrator;
|
|
@@ -39,12 +39,13 @@ function shouldUseTui(nxJson, nxArgs, skipCapabilityCheck = process.env.NX_TUI_S
|
|
|
39
39
|
if (!isCapable) {
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
|
-
if ((0, output_1.isStaticOutputStyle)(nxArgs.
|
|
43
|
-
['stream', 'stream-without-prefixes', 'dynamic-legacy'].includes(nxArgs.
|
|
42
|
+
if ((0, output_1.isStaticOutputStyle)(nxArgs.specifiedOutputStyle) ||
|
|
43
|
+
['summary', 'stream', 'stream-without-prefixes', 'dynamic-legacy'].includes(nxArgs.specifiedOutputStyle)) {
|
|
44
44
|
// If the user has specified a non-TUI output style, we disable the TUI
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
|
-
if (nxArgs.
|
|
47
|
+
if (nxArgs.specifiedOutputStyle === 'dynamic' ||
|
|
48
|
+
nxArgs.specifiedOutputStyle === 'tui') {
|
|
48
49
|
return true;
|
|
49
50
|
}
|
|
50
51
|
// The environment variable takes precedence over the nx.json config, but
|
|
@@ -53,6 +53,12 @@ export interface LifeCycle {
|
|
|
53
53
|
setEstimatedTaskTimings?(timings: Record<string, number>): void;
|
|
54
54
|
registerRunningBatch?(batchId: string, batchInfo: BatchInfo): void;
|
|
55
55
|
appendBatchOutput?(batchId: string, output: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* The batch worker's own log is on disk and worth reading — its tasks did not
|
|
58
|
+
* all succeed. One log per worker, so this is addressed once for the batch
|
|
59
|
+
* rather than copied into any task's output.
|
|
60
|
+
*/
|
|
61
|
+
batchOutputAvailable?(batchId: string, path: string): void;
|
|
56
62
|
setBatchStatus?(batchId: string, status: BatchStatus): void;
|
|
57
63
|
/**
|
|
58
64
|
* Set a clickable Nx Cloud link in the terminal UI: `label` is the text
|
|
@@ -81,6 +87,7 @@ export declare class CompositeLifeCycle implements LifeCycle {
|
|
|
81
87
|
setEstimatedTaskTimings(timings: Record<string, number>): void;
|
|
82
88
|
registerRunningBatch(batchId: string, batchInfo: BatchInfo): void;
|
|
83
89
|
appendBatchOutput(batchId: string, output: string): void;
|
|
90
|
+
batchOutputAvailable(batchId: string, path: string): void;
|
|
84
91
|
setBatchStatus(batchId: string, status: BatchStatus): void;
|
|
85
92
|
setCloudLink(label: string, url: string): Promise<void>;
|
|
86
93
|
}
|
|
@@ -130,6 +130,13 @@ class CompositeLifeCycle {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
batchOutputAvailable(batchId, path) {
|
|
134
|
+
for (let l of this.lifeCycles) {
|
|
135
|
+
if (l.batchOutputAvailable) {
|
|
136
|
+
l.batchOutputAvailable(batchId, path);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
133
140
|
setBatchStatus(batchId, status) {
|
|
134
141
|
for (let l of this.lifeCycles) {
|
|
135
142
|
if (l.setBatchStatus) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Task } from '../../config/task-graph';
|
|
2
|
+
import type { LifeCycle, TaskResult } from '../life-cycle';
|
|
3
|
+
/**
|
|
4
|
+
* Prints a run as a handful of lines rather than a transcript: counts, then one
|
|
5
|
+
* line per failure naming the file that holds its output. Task output stops
|
|
6
|
+
* being a stream to read and becomes an artifact to address, so the size of this
|
|
7
|
+
* renderer's output depends on how many tasks failed, never on how loud they
|
|
8
|
+
* were.
|
|
9
|
+
*
|
|
10
|
+
* Selected by `--output-style=summary`, and by default when Nx is driven by an
|
|
11
|
+
* AI agent, which otherwise reads thousands of lines of passing output to find
|
|
12
|
+
* the one failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SummaryTerminalOutputLifeCycle implements LifeCycle {
|
|
15
|
+
private readonly tasks;
|
|
16
|
+
private readonly failed;
|
|
17
|
+
private readonly stopped;
|
|
18
|
+
private readonly completed;
|
|
19
|
+
private succeeded;
|
|
20
|
+
private cached;
|
|
21
|
+
private cloudLink;
|
|
22
|
+
private readonly batchLogs;
|
|
23
|
+
constructor(tasks: Task[]);
|
|
24
|
+
setCloudLink(label: string, url: string): void;
|
|
25
|
+
batchOutputAvailable(batchId: string, path: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Nothing is printed as tasks complete. A summary that interleaved with
|
|
28
|
+
* progress would be a transcript again.
|
|
29
|
+
*/
|
|
30
|
+
printTaskTerminalOutput(): void;
|
|
31
|
+
endTasks(taskResults: TaskResult[]): void;
|
|
32
|
+
endCommand(): void;
|
|
33
|
+
private countsLine;
|
|
34
|
+
private failureBlock;
|
|
35
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SummaryTerminalOutputLifeCycle = void 0;
|
|
4
|
+
const output_1 = require("../../utils/output");
|
|
5
|
+
const terminal_output_path_1 = require("../terminal-output-path");
|
|
6
|
+
/**
|
|
7
|
+
* Prints a run as a handful of lines rather than a transcript: counts, then one
|
|
8
|
+
* line per failure naming the file that holds its output. Task output stops
|
|
9
|
+
* being a stream to read and becomes an artifact to address, so the size of this
|
|
10
|
+
* renderer's output depends on how many tasks failed, never on how loud they
|
|
11
|
+
* were.
|
|
12
|
+
*
|
|
13
|
+
* Selected by `--output-style=summary`, and by default when Nx is driven by an
|
|
14
|
+
* AI agent, which otherwise reads thousands of lines of passing output to find
|
|
15
|
+
* the one failure.
|
|
16
|
+
*/
|
|
17
|
+
class SummaryTerminalOutputLifeCycle {
|
|
18
|
+
constructor(tasks) {
|
|
19
|
+
this.tasks = tasks;
|
|
20
|
+
this.failed = [];
|
|
21
|
+
this.stopped = [];
|
|
22
|
+
this.completed = new Set();
|
|
23
|
+
this.succeeded = 0;
|
|
24
|
+
this.cached = 0;
|
|
25
|
+
this.batchLogs = new Map();
|
|
26
|
+
}
|
|
27
|
+
setCloudLink(label, url) {
|
|
28
|
+
this.cloudLink = { label, url };
|
|
29
|
+
}
|
|
30
|
+
batchOutputAvailable(batchId, path) {
|
|
31
|
+
this.batchLogs.set(batchId, path);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Nothing is printed as tasks complete. A summary that interleaved with
|
|
35
|
+
* progress would be a transcript again.
|
|
36
|
+
*/
|
|
37
|
+
printTaskTerminalOutput() { }
|
|
38
|
+
endTasks(taskResults) {
|
|
39
|
+
for (const { task, status, terminalOutput } of taskResults) {
|
|
40
|
+
this.completed.add(task.id);
|
|
41
|
+
switch (status) {
|
|
42
|
+
case 'failure':
|
|
43
|
+
// Whether a log file exists is decided here, not at render time.
|
|
44
|
+
// `persistTerminalOutputs` skips only an undefined `terminalOutput`,
|
|
45
|
+
// so an empty one still writes a 0-byte file - truthiness, not
|
|
46
|
+
// definedness, is what says a reader will find something there.
|
|
47
|
+
this.failed.push({
|
|
48
|
+
task,
|
|
49
|
+
hasOutput: !!terminalOutput,
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
case 'stopped':
|
|
53
|
+
// Every task of a stopped batch carries `''`: the worker's partial
|
|
54
|
+
// log is announced as the batch's own artifact now, not folded into
|
|
55
|
+
// any task. So these address a file only when something else wrote
|
|
56
|
+
// one, and the batch log is addressed once below.
|
|
57
|
+
this.stopped.push({
|
|
58
|
+
task,
|
|
59
|
+
hasOutput: !!terminalOutput,
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
case 'local-cache':
|
|
63
|
+
case 'local-cache-kept-existing':
|
|
64
|
+
case 'remote-cache':
|
|
65
|
+
this.cached++;
|
|
66
|
+
this.succeeded++;
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
this.succeeded++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
endCommand() {
|
|
74
|
+
const skipped = this.tasks.filter((t) => !this.completed.has(t.id)).length;
|
|
75
|
+
const bodyLines = [];
|
|
76
|
+
for (const { task, hasOutput } of this.failed) {
|
|
77
|
+
bodyLines.push('', ...this.failureBlock(task, hasOutput));
|
|
78
|
+
}
|
|
79
|
+
if (this.stopped.length > 0) {
|
|
80
|
+
bodyLines.push('', output_1.output.dim('Stopped before finishing:'), ...this.stopped.flatMap(({ task, hasOutput }) => {
|
|
81
|
+
const line = `${output_1.output.dim('-')} ${task.id}`;
|
|
82
|
+
return task.hash && hasOutput
|
|
83
|
+
? [
|
|
84
|
+
line,
|
|
85
|
+
output_1.output.dim(` full log: ${(0, terminal_output_path_1.terminalOutputPathForHash)(task.hash)}`),
|
|
86
|
+
]
|
|
87
|
+
: [line];
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
if (this.batchLogs.size > 0) {
|
|
91
|
+
// One line per worker, not per task: a batch's log explains the whole
|
|
92
|
+
// batch, and no task's own output file contains it.
|
|
93
|
+
bodyLines.push('', output_1.output.dim('Batch worker logs:'));
|
|
94
|
+
for (const [batchId, path] of this.batchLogs) {
|
|
95
|
+
bodyLines.push(`${output_1.output.dim('-')} ${output_1.output.dim(`${batchId}: ${path}`)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (this.cloudLink) {
|
|
99
|
+
bodyLines.push('', `${this.cloudLink.label} ${this.cloudLink.url}`);
|
|
100
|
+
}
|
|
101
|
+
bodyLines.push('', output_1.output.dim('Re-run with --output-style=static to inline logs.'));
|
|
102
|
+
const title = this.countsLine(skipped);
|
|
103
|
+
if (this.failed.length > 0 || this.stopped.length > 0) {
|
|
104
|
+
output_1.output.error({ title, bodyLines });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
output_1.output.success({ title, bodyLines });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
countsLine(skipped) {
|
|
111
|
+
const parts = [`${this.succeeded} succeeded`];
|
|
112
|
+
if (this.cached > 0) {
|
|
113
|
+
parts.push(`${this.cached} cached`);
|
|
114
|
+
}
|
|
115
|
+
if (this.failed.length > 0) {
|
|
116
|
+
parts.push(`${this.failed.length} failed`);
|
|
117
|
+
}
|
|
118
|
+
if (this.stopped.length > 0) {
|
|
119
|
+
parts.push(`${this.stopped.length} stopped`);
|
|
120
|
+
}
|
|
121
|
+
if (skipped > 0) {
|
|
122
|
+
parts.push(`${skipped} skipped`);
|
|
123
|
+
}
|
|
124
|
+
return `${this.tasks.length} ${this.tasks.length === 1 ? 'task' : 'tasks'}: ${parts.join(', ')}`;
|
|
125
|
+
}
|
|
126
|
+
failureBlock(task, hasOutput) {
|
|
127
|
+
// No exit code: `completeTasks` rebuilds `TaskResult.code` from the status,
|
|
128
|
+
// so every failure arrives as 1 and a task killed with 137 or a missing
|
|
129
|
+
// binary's 127 would all render `(exit 1)`. Printing a number that is
|
|
130
|
+
// always 1 to a reader whose job is to machine-read this line is worse than
|
|
131
|
+
// printing none. formatCommand is what every other renderer uses, so a task
|
|
132
|
+
// reads the same here as it does under --output-style=static.
|
|
133
|
+
const lines = [
|
|
134
|
+
`${output_1.output.colors.red('✖')} ${output_1.output.formatCommand(task.id)}`,
|
|
135
|
+
];
|
|
136
|
+
// The log is addressed, not reproduced. `hash` is optional on the type but
|
|
137
|
+
// always set by the time a task has run — processTask hashes before it
|
|
138
|
+
// schedules — so that half only guards against printing a bogus path.
|
|
139
|
+
// `hasOutput` is the load-bearing half: no output, no file, no address.
|
|
140
|
+
if (task.hash && hasOutput) {
|
|
141
|
+
lines.push(output_1.output.dim(` full log: ${(0, terminal_output_path_1.terminalOutputPathForHash)(task.hash)}`));
|
|
142
|
+
}
|
|
143
|
+
return lines;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.SummaryTerminalOutputLifeCycle = SummaryTerminalOutputLifeCycle;
|