@oisincoveney/pipeline 1.28.1 → 1.29.0
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/README.md +1 -1
- package/dist/argo-graph.js +4 -6
- package/dist/argo-submit.d.ts +1 -1
- package/dist/argo-submit.js +2 -1
- package/dist/argo-workflow.d.ts +10 -0
- package/dist/argo-workflow.js +51 -5
- package/dist/cli/format.js +137 -0
- package/dist/cli/program.d.ts +34 -0
- package/dist/cli/program.js +369 -0
- package/dist/cli/submit-options.js +91 -0
- package/dist/cluster-doctor.js +228 -0
- package/dist/commands/runner-command-command.js +5 -1
- package/dist/config/defaults.d.ts +147 -0
- package/dist/config/defaults.js +433 -0
- package/dist/config/lint.js +72 -0
- package/dist/config/load.d.ts +9 -0
- package/dist/config/load.js +72 -0
- package/dist/config/schemas.d.ts +523 -0
- package/dist/config/schemas.js +585 -0
- package/dist/config/validate.d.ts +6 -0
- package/dist/config/validate.js +218 -0
- package/dist/config.d.ts +5 -670
- package/dist/config.js +4 -1278
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +1 -26
- package/dist/index.js +6 -596
- package/dist/install-commands.js +4 -2
- package/dist/model-resolver.js +1 -12
- package/dist/moka-global-config.js +2 -1
- package/dist/moka-submit.d.ts +3 -3
- package/dist/moka-submit.js +2 -1
- package/dist/package-assets.js +1 -1
- package/dist/pipeline-runtime.d.ts +1 -1
- package/dist/pipeline-runtime.js +66 -163
- package/dist/planned-node.js +10 -0
- package/dist/runner-command/finalize.js +30 -21
- package/dist/runner-command/lifecycle-context.js +76 -0
- package/dist/runner-command/lifecycle.js +55 -0
- package/dist/runner-command/run.js +6 -10
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner.d.ts +8 -3
- package/dist/runner.js +27 -7
- package/dist/{runtime-machines/contracts.d.ts → runtime/actor-ids.d.ts} +3 -5
- package/dist/{runtime-machines/contracts.js → runtime/actor-ids.js} +1 -1
- package/dist/runtime/agent-node/agent-node.js +20 -8
- package/dist/runtime/builtins/builtins.js +1 -1
- package/dist/runtime/context/context.js +4 -16
- package/dist/runtime/contracts/contracts.d.ts +2 -2
- package/dist/runtime/drain-merge/drain-merge.js +1 -1
- package/dist/runtime/events/events.js +10 -7
- package/dist/runtime/gates/gates.js +83 -42
- package/dist/runtime/goal-loop.d.ts +1 -1
- package/dist/runtime/goal-state.js +9 -5
- package/dist/runtime/hooks/hooks.js +117 -45
- package/dist/runtime/json-validation/json-validation.js +2 -9
- package/dist/runtime/node-state-store.js +82 -0
- package/dist/runtime/node-state-tracker.js +107 -0
- package/dist/runtime/parallel-node/parallel-node.js +1 -11
- package/dist/runtime/retry.js +33 -0
- package/dist/runtime/scheduler.js +157 -0
- package/dist/runtime/workflow-lifecycle.js +77 -0
- package/dist/safe-json.js +14 -1
- package/dist/schedule/passes/coverage.js +133 -0
- package/dist/schedule/passes/ids.js +51 -0
- package/dist/schedule/passes/index.js +9 -0
- package/dist/schedule/passes/models.js +39 -0
- package/dist/schedule/passes/references.js +33 -0
- package/dist/schedule/planner.d.ts +1586 -0
- package/dist/schedule/planner.js +771 -0
- package/dist/schedule/prompts.js +144 -0
- package/dist/schedule-planner.d.ts +2 -1586
- package/dist/schedule-planner.js +1 -1067
- package/dist/strings.js +8 -0
- package/dist/workflow-planner.d.ts +1 -1
- package/dist/workflow-planner.js +1 -3
- package/docs/config-architecture.md +0 -1
- package/docs/devspace-runner.md +26 -0
- package/docs/operator-guide.md +15 -0
- package/docs/pipeline-console-runner-contract.md +26 -0
- package/docs/{xstate-runtime-actor-model.md → runtime-actor-model.md} +8 -10
- package/package.json +1 -2
- package/dist/runtime-machines/gate-machine.js +0 -138
- package/dist/runtime-machines/hook-machine.js +0 -167
- package/dist/runtime-machines/node-machine.js +0 -268
- package/dist/runtime-machines/workflow-machine.d.ts +0 -1
- package/dist/runtime-machines/workflow-machine.js +0 -403
- package/dist/runtime-observability-inspection.js +0 -49
- package/dist/runtime-observability.js +0 -22
- /package/dist/{runtime-machines/node-machine.d.ts → runtime/node-state-store.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -223,7 +223,7 @@ single dependency target. Agents may route work to tracks, but the branch
|
|
|
223
223
|
topology stays auditable in YAML or in the generated schedule artifact.
|
|
224
224
|
|
|
225
225
|
See [`docs/config-architecture.md`](docs/config-architecture.md) for the config
|
|
226
|
-
shape and [`docs/
|
|
226
|
+
shape and [`docs/runtime-actor-model.md`](docs/runtime-actor-model.md)
|
|
227
227
|
for the runtime actor model.
|
|
228
228
|
|
|
229
229
|
## Generated Host Resources
|
package/dist/argo-graph.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { uniqueStrings } from "./strings.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
//#region src/argo-graph.ts
|
|
3
4
|
const EXECUTABLE_NODE_KINDS = [
|
|
@@ -63,14 +64,14 @@ var ArgoGraphCompiler = class {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
resolveDependencyTaskNames(nodeIds) {
|
|
66
|
-
return
|
|
67
|
+
return uniqueStrings(nodeIds.flatMap((nodeId) => this.resolveDependencyNodeIds(nodeId).map((id) => argoTaskName(id))));
|
|
67
68
|
}
|
|
68
69
|
resolveDependencyNodeIds(nodeId) {
|
|
69
70
|
const node = this.nodeById.get(nodeId);
|
|
70
71
|
if (!node) return [];
|
|
71
72
|
if (isExecutableNode(node)) return [node.id];
|
|
72
|
-
if (node.kind === "group") return
|
|
73
|
-
if (node.kind === "parallel") return
|
|
73
|
+
if (node.kind === "group") return uniqueStrings([...node.nodes ?? [], ...node.needs].flatMap((id) => this.resolveDependencyNodeIds(id)));
|
|
74
|
+
if (node.kind === "parallel") return uniqueStrings((node.children ?? []).flatMap((child) => this.resolveDependencyNodeIds(child.id)));
|
|
74
75
|
return [];
|
|
75
76
|
}
|
|
76
77
|
terminalTasks() {
|
|
@@ -87,8 +88,5 @@ function argoTaskName(nodeId) {
|
|
|
87
88
|
function argoTemplateName(nodeId) {
|
|
88
89
|
return `task-${nodeId}`;
|
|
89
90
|
}
|
|
90
|
-
function unique(values) {
|
|
91
|
-
return [...new Set(values)];
|
|
92
|
-
}
|
|
93
91
|
//#endregion
|
|
94
92
|
export { compileArgoExecutionGraph };
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PipelineConfig } from "./config.js";
|
|
1
|
+
import { PipelineConfig } from "./config/schemas.js";
|
|
2
2
|
import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { CoreV1Api, CustomObjectsApi, KubeConfig } from "@kubernetes/client-node";
|
package/dist/argo-submit.js
CHANGED
|
@@ -2,7 +2,8 @@ import { compileArgoExecutionGraph } from "./argo-graph.js";
|
|
|
2
2
|
import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
|
|
3
3
|
import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
|
|
4
4
|
import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
|
|
5
|
-
import { compileScheduleArtifact, parseScheduleArtifact } from "./schedule
|
|
5
|
+
import { compileScheduleArtifact, parseScheduleArtifact } from "./schedule/planner.js";
|
|
6
|
+
import "./schedule-planner.js";
|
|
6
7
|
import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
|
|
7
8
|
import { stringify } from "yaml";
|
|
8
9
|
import { z } from "zod";
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -81,6 +81,16 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
|
|
|
81
81
|
}, z.core.$strict>>;
|
|
82
82
|
}, z.core.$strict>>;
|
|
83
83
|
name: z.ZodString;
|
|
84
|
+
retryStrategy: z.ZodOptional<z.ZodObject<{
|
|
85
|
+
expression: z.ZodOptional<z.ZodString>;
|
|
86
|
+
limit: z.ZodOptional<z.ZodString>;
|
|
87
|
+
retryPolicy: z.ZodEnum<{
|
|
88
|
+
Always: "Always";
|
|
89
|
+
OnError: "OnError";
|
|
90
|
+
OnFailure: "OnFailure";
|
|
91
|
+
OnTransientError: "OnTransientError";
|
|
92
|
+
}>;
|
|
93
|
+
}, z.core.$strict>>;
|
|
84
94
|
}, z.core.$strict>>;
|
|
85
95
|
ttlStrategy: z.ZodOptional<z.ZodObject<{
|
|
86
96
|
secondsAfterCompletion: z.ZodOptional<z.ZodNumber>;
|
package/dist/argo-workflow.js
CHANGED
|
@@ -8,9 +8,15 @@ const ARGO_WORKFLOW_KIND = "Workflow";
|
|
|
8
8
|
const RUNNER_WORKFLOW_IMAGE = "ghcr.io/oisin-ee/pipeline-runner:latest";
|
|
9
9
|
const RUNNER_WORKFLOW_SERVICE_ACCOUNT = "pipeline-runner";
|
|
10
10
|
const RUNNER_WORKFLOW_ENTRYPOINT = "pipeline";
|
|
11
|
+
const RUNNER_WORKFLOW_START_TASK = "workflow-start";
|
|
11
12
|
const RUNNER_WORKFLOW_PAYLOAD_PATH = "/etc/pipeline/payload.json";
|
|
12
13
|
const RUNNER_WORKFLOW_SCHEDULE_PATH = "/etc/pipeline/schedule.yaml";
|
|
13
14
|
const RUNNER_GIT_CREDENTIALS_PATH = "/etc/pipeline/git-credentials";
|
|
15
|
+
const RUNNER_STARTUP_RETRY_STRATEGY = {
|
|
16
|
+
expression: "asInt(lastRetry.exitCode) == 70",
|
|
17
|
+
limit: "3",
|
|
18
|
+
retryPolicy: "OnFailure"
|
|
19
|
+
};
|
|
14
20
|
const RUNNER_OPENCODE_ENV = [{
|
|
15
21
|
name: "CODEX_AUTH_PER_PROJECT_ACCOUNTS",
|
|
16
22
|
value: "0"
|
|
@@ -54,6 +60,16 @@ const argoWorkflowArtifactSchema = z.object({
|
|
|
54
60
|
name: z.string().min(1),
|
|
55
61
|
path: z.string().min(1).optional()
|
|
56
62
|
}).strict();
|
|
63
|
+
const argoWorkflowRetryStrategySchema = z.object({
|
|
64
|
+
expression: z.string().min(1).optional(),
|
|
65
|
+
limit: z.string().min(1).optional(),
|
|
66
|
+
retryPolicy: z.enum([
|
|
67
|
+
"Always",
|
|
68
|
+
"OnError",
|
|
69
|
+
"OnFailure",
|
|
70
|
+
"OnTransientError"
|
|
71
|
+
])
|
|
72
|
+
}).strict();
|
|
57
73
|
const argoWorkflowTemplateSchema = z.object({
|
|
58
74
|
container: z.object({
|
|
59
75
|
args: z.array(z.string().min(1)).min(1),
|
|
@@ -83,7 +99,8 @@ const argoWorkflowTemplateSchema = z.object({
|
|
|
83
99
|
parameters: z.array(z.object({ name: z.string().min(1) }).strict()).optional()
|
|
84
100
|
}).strict().optional(),
|
|
85
101
|
outputs: z.object({ artifacts: z.array(argoWorkflowArtifactSchema) }).strict().optional(),
|
|
86
|
-
name: z.string().min(1)
|
|
102
|
+
name: z.string().min(1),
|
|
103
|
+
retryStrategy: argoWorkflowRetryStrategySchema.optional()
|
|
87
104
|
}).strict().refine((template) => template.container !== void 0 || template.dag !== void 0, { message: "Workflow templates must declare container or dag" });
|
|
88
105
|
const runnerArgoWorkflowManifestSchema = z.object({
|
|
89
106
|
apiVersion: z.literal(ARGO_WORKFLOW_API_VERSION),
|
|
@@ -179,13 +196,17 @@ function buildRunnerArgoWorkflowManifest(rawOptions) {
|
|
|
179
196
|
serviceAccountName: options.serviceAccountName,
|
|
180
197
|
templates: [
|
|
181
198
|
{
|
|
182
|
-
dag: { tasks:
|
|
183
|
-
|
|
199
|
+
dag: { tasks: [{
|
|
200
|
+
name: RUNNER_WORKFLOW_START_TASK,
|
|
201
|
+
template: RUNNER_WORKFLOW_START_TASK
|
|
202
|
+
}, ...graph.tasks.map((task) => ({
|
|
203
|
+
dependencies: [RUNNER_WORKFLOW_START_TASK, ...task.dependencies],
|
|
184
204
|
name: task.taskName,
|
|
185
205
|
template: task.templateName
|
|
186
|
-
})) },
|
|
206
|
+
}))] },
|
|
187
207
|
name: RUNNER_WORKFLOW_ENTRYPOINT
|
|
188
208
|
},
|
|
209
|
+
runnerLifecycleTemplate(options, volumeMounts),
|
|
189
210
|
...graph.tasks.map((task) => runnerCommandTemplate(task, options, volumeMounts)),
|
|
190
211
|
runnerFinalizerTemplate(options, volumeMounts)
|
|
191
212
|
],
|
|
@@ -333,6 +354,30 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
333
354
|
volumes: z.array(argoWorkflowVolumeSchema).parse(volumes)
|
|
334
355
|
};
|
|
335
356
|
}
|
|
357
|
+
function runnerLifecycleTemplate(options, volumeMounts) {
|
|
358
|
+
return {
|
|
359
|
+
container: {
|
|
360
|
+
args: [
|
|
361
|
+
"runner-lifecycle",
|
|
362
|
+
"--phase",
|
|
363
|
+
"workflow.start",
|
|
364
|
+
"--payload-file",
|
|
365
|
+
RUNNER_WORKFLOW_PAYLOAD_PATH,
|
|
366
|
+
"--schedule-file",
|
|
367
|
+
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
368
|
+
],
|
|
369
|
+
command: ["moka"],
|
|
370
|
+
env: [...RUNNER_OPENCODE_ENV],
|
|
371
|
+
image: options.image,
|
|
372
|
+
imagePullPolicy: options.imagePullPolicy,
|
|
373
|
+
name: "runner",
|
|
374
|
+
...options.resources ? { resources: options.resources } : {},
|
|
375
|
+
volumeMounts
|
|
376
|
+
},
|
|
377
|
+
name: RUNNER_WORKFLOW_START_TASK,
|
|
378
|
+
retryStrategy: { ...RUNNER_STARTUP_RETRY_STRATEGY }
|
|
379
|
+
};
|
|
380
|
+
}
|
|
336
381
|
function runnerCommandTemplate(task, options, volumeMounts) {
|
|
337
382
|
const taskVolumeMount = {
|
|
338
383
|
mountPath: DEFAULT_RUNNER_TASK_DESCRIPTOR_PATH,
|
|
@@ -357,7 +402,8 @@ function runnerCommandTemplate(task, options, volumeMounts) {
|
|
|
357
402
|
...options.resources ? { resources: options.resources } : {},
|
|
358
403
|
volumeMounts: [...volumeMounts, taskVolumeMount]
|
|
359
404
|
},
|
|
360
|
-
name: task.templateName
|
|
405
|
+
name: task.templateName,
|
|
406
|
+
retryStrategy: { ...RUNNER_STARTUP_RETRY_STRATEGY }
|
|
361
407
|
};
|
|
362
408
|
}
|
|
363
409
|
function runnerFinalizerTemplate(options, volumeMounts) {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//#region src/cli/format.ts
|
|
2
|
+
const LINE_RE = /\r?\n/;
|
|
3
|
+
function createTerminalRuntimeReporter(write = (message) => console.log(message)) {
|
|
4
|
+
const state = { attempts: /* @__PURE__ */ new Map() };
|
|
5
|
+
return (event) => {
|
|
6
|
+
write(formatRuntimeProgressMessage(event, state));
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function formatRuntimeProgressMessage(event, state = { attempts: /* @__PURE__ */ new Map() }) {
|
|
10
|
+
return formatWorkflowProgress(event, state) ?? formatAgentProgress(event, state) ?? formatCheckProgress(event, state) ?? formatObservabilityProgress(event) ?? formatRepairProgress(event);
|
|
11
|
+
}
|
|
12
|
+
function formatWorkflowProgress(event, state) {
|
|
13
|
+
switch (event.type) {
|
|
14
|
+
case "workflow.planned": return `Pipeline planned: ${event.workflowId} (${event.nodes.map((node) => node.id).join(" -> ")})`;
|
|
15
|
+
case "workflow.start": return `Pipeline starting: ${event.workflowId} (${event.nodeIds.join(" -> ")})`;
|
|
16
|
+
case "node.start":
|
|
17
|
+
state.attempts.set(event.nodeId, event.attempt);
|
|
18
|
+
return [
|
|
19
|
+
`Node starting: ${event.nodeId}`,
|
|
20
|
+
event.runnerId ? `runner=${event.runnerId}` : "",
|
|
21
|
+
event.profile ? `profile=${event.profile}` : "",
|
|
22
|
+
`attempt=${event.attempt}`
|
|
23
|
+
].filter(Boolean).join(" ");
|
|
24
|
+
case "node.finish":
|
|
25
|
+
state.attempts.set(event.nodeId, event.attempt);
|
|
26
|
+
return `Node finished: ${event.nodeId} ${event.status} exit=${event.exitCode}`;
|
|
27
|
+
case "node.output.recorded": return [
|
|
28
|
+
`Node output: ${event.nodeId}`,
|
|
29
|
+
`attempt=${event.attempt}`,
|
|
30
|
+
`format=${event.format}`,
|
|
31
|
+
formatRuntimeEventOutput(event.output)
|
|
32
|
+
].filter(Boolean).join(" ");
|
|
33
|
+
case "workflow.finish": return `Pipeline finished: ${event.workflowId} ${event.outcome}`;
|
|
34
|
+
default: return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function formatAgentProgress(event, state) {
|
|
38
|
+
switch (event.type) {
|
|
39
|
+
case "agent.start":
|
|
40
|
+
state.attempts.set(event.nodeId, event.attempt);
|
|
41
|
+
return `Agent starting: ${event.nodeId} runner=${event.runnerId ?? "unknown"} attempt=${event.attempt}`;
|
|
42
|
+
case "agent.finish":
|
|
43
|
+
state.attempts.set(event.nodeId, event.attempt);
|
|
44
|
+
return `Agent finished: ${event.nodeId} runner=${event.runnerId ?? "unknown"} exit=${event.exitCode}`;
|
|
45
|
+
case "hook.start": return `Hook starting: ${event.hookId} event=${event.event}${event.nodeId ? ` node=${event.nodeId}` : ""}`;
|
|
46
|
+
case "hook.finish": return `Hook ${event.passed ? "passed" : "failed"}: ${event.hookId}${event.reason ? ` (${event.reason})` : ""}`;
|
|
47
|
+
case "hook.result": return `Hook result: ${event.hookId} ${event.status}${event.summary ? ` (${event.summary})` : ""}`;
|
|
48
|
+
default: return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function formatCheckProgress(event, state) {
|
|
52
|
+
switch (event.type) {
|
|
53
|
+
case "gate.start": return `Gate starting: ${event.nodeId}/${event.gateId}${formatKnownAttempt(state, event.nodeId)}`;
|
|
54
|
+
case "gate.finish": return [
|
|
55
|
+
`Gate ${event.passed ? "passed" : "failed"}: ${event.nodeId}/${event.gateId}${formatKnownAttempt(state, event.nodeId)}`,
|
|
56
|
+
event.reason ? `reason=${event.reason}` : "",
|
|
57
|
+
...(event.evidence ?? []).map((item) => `evidence=${item}`)
|
|
58
|
+
].filter(Boolean).join(" ");
|
|
59
|
+
case "artifact.check.start": return `Artifact check starting: ${event.nodeId}/${event.path}`;
|
|
60
|
+
case "artifact.check.finish": return `Artifact check ${event.passed ? "passed" : "failed"}: ${event.nodeId}/${event.path}${event.reason ? ` (${event.reason})` : ""}`;
|
|
61
|
+
default: return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function formatKnownAttempt(state, nodeId) {
|
|
65
|
+
const attempt = state.attempts.get(nodeId);
|
|
66
|
+
return attempt === void 0 ? "" : ` attempt=${attempt}`;
|
|
67
|
+
}
|
|
68
|
+
function formatRuntimeEventOutput(output) {
|
|
69
|
+
if (typeof output === "string") return output.trimEnd();
|
|
70
|
+
return JSON.stringify(output);
|
|
71
|
+
}
|
|
72
|
+
function formatRepairProgress(event) {
|
|
73
|
+
switch (event.type) {
|
|
74
|
+
case "output.repair": return `Output repair ${event.passed ? "passed" : "failed"}: ${event.nodeId} attempt=${event.attempt}${event.reason ? ` (${event.reason})` : ""}`;
|
|
75
|
+
default: throw new Error(`Unhandled runtime event: ${event.type}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function formatObservabilityProgress(event) {
|
|
79
|
+
switch (event.type) {
|
|
80
|
+
case "runtime.observability": return `Runtime observed: ${event.name} - ${event.summary}`;
|
|
81
|
+
default: return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function formatRuntimeResult(result) {
|
|
85
|
+
const lines = [
|
|
86
|
+
`Pipeline complete: ${result.outcome}`,
|
|
87
|
+
`Workflow: ${result.plan.workflowId}`,
|
|
88
|
+
`Nodes: ${result.nodes.map((node) => `${node.nodeId}:${node.status}`).join(", ")}`,
|
|
89
|
+
`Agent boundaries: ${result.agentInvocations.length}`
|
|
90
|
+
];
|
|
91
|
+
const outputs = result.nodes.filter((node) => node.output.trim());
|
|
92
|
+
if (outputs.length > 0) {
|
|
93
|
+
lines.push("Node outputs:");
|
|
94
|
+
for (const node of outputs) appendIndentedSection(lines, node.nodeId, [node.output]);
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
function formatRuntimeFailure(result) {
|
|
99
|
+
const lines = ["Pipeline failed."];
|
|
100
|
+
for (const failure of result.failureDetails) {
|
|
101
|
+
lines.push(failure.nodeId ? `- ${failure.nodeId}: ${failure.reason}` : `- ${failure.reason}`);
|
|
102
|
+
appendIndentedSection(lines, "Evidence", failure.evidence);
|
|
103
|
+
const node = failure.nodeId ? result.nodes.find((item) => item.nodeId === failure.nodeId) : void 0;
|
|
104
|
+
if (node) {
|
|
105
|
+
lines.push(` Node: status=${node.status} attempts=${node.attempts} exit=${node.exitCode}`);
|
|
106
|
+
appendIndentedSection(lines, "Node evidence", node.evidence);
|
|
107
|
+
appendIndentedSection(lines, "Node output", [node.output]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (result.gates.length > 0) {
|
|
111
|
+
lines.push("Gates:");
|
|
112
|
+
for (const gate of result.gates) {
|
|
113
|
+
lines.push(` - ${gate.nodeId}/${gate.gateId}: ${gate.passed ? "PASS" : "FAIL"}${gate.reason ? ` (${gate.reason})` : ""}`);
|
|
114
|
+
appendIndentedSection(lines, "Gate evidence", gate.evidence);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
function formatDoctorResult(result) {
|
|
120
|
+
return [`Doctor: ${result.passed ? "PASS" : "FAIL"}`, ...result.checks.map((check) => `- ${check.passed ? "PASS" : "FAIL"} ${check.name}: ${check.detail}`)].join("\n");
|
|
121
|
+
}
|
|
122
|
+
function appendIndentedSection(lines, label, values) {
|
|
123
|
+
const text = values.filter(Boolean).join("\n").trim();
|
|
124
|
+
if (!text) return;
|
|
125
|
+
lines.push(` ${label}:`);
|
|
126
|
+
lines.push(indent(truncateMiddle(text, 4e3), " "));
|
|
127
|
+
}
|
|
128
|
+
function indent(text, prefix) {
|
|
129
|
+
return text.split(LINE_RE).map((line) => `${prefix}${line}`).join("\n");
|
|
130
|
+
}
|
|
131
|
+
function truncateMiddle(text, maxLength) {
|
|
132
|
+
if (text.length <= maxLength) return text;
|
|
133
|
+
const keep = Math.floor((maxLength - 32) / 2);
|
|
134
|
+
return `${text.slice(0, keep)}\n... truncated ...\n${text.slice(-keep)}`;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { createTerminalRuntimeReporter, formatDoctorResult, formatRuntimeFailure, formatRuntimeResult };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/program.d.ts
|
|
5
|
+
interface ExecuteOptions {
|
|
6
|
+
entrypoint?: string;
|
|
7
|
+
pipelineRunner?: typeof runPipelineFromConfig;
|
|
8
|
+
schedule?: string;
|
|
9
|
+
workflow?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Config-driven `execute` entrypoint. Package-owned defaults are the source of
|
|
13
|
+
* truth; repo-local pipeline files are ignored by runtime loading.
|
|
14
|
+
*/
|
|
15
|
+
declare function execute(description: string, options?: ExecuteOptions): Promise<void>;
|
|
16
|
+
declare function quick(description: string, options?: Omit<ExecuteOptions, "entrypoint">): Promise<void>;
|
|
17
|
+
interface DoctorCheck {
|
|
18
|
+
detail: string;
|
|
19
|
+
name: string;
|
|
20
|
+
passed: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface DoctorResult {
|
|
23
|
+
checks: DoctorCheck[];
|
|
24
|
+
passed: boolean;
|
|
25
|
+
}
|
|
26
|
+
interface DoctorFlags {
|
|
27
|
+
cluster?: boolean | string;
|
|
28
|
+
kubeContext?: string;
|
|
29
|
+
}
|
|
30
|
+
declare function createCliProgram(): Command;
|
|
31
|
+
declare function runCli(argv: string[]): Promise<void>;
|
|
32
|
+
declare function runDoctor(cwd: string, options?: DoctorFlags): Promise<DoctorResult>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createCliProgram, execute, quick, runCli, runDoctor };
|