@oisincoveney/pipeline 1.27.12 → 1.27.14
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/argo-submit.js +5 -0
- package/dist/argo-workflow.d.ts +2 -0
- package/dist/argo-workflow.js +3 -0
- package/dist/config.d.ts +2 -2
- package/dist/pipeline-runtime.js +98 -26
- package/package.json +1 -1
package/dist/argo-submit.js
CHANGED
|
@@ -64,6 +64,11 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
|
|
|
64
64
|
"pipeline.oisin.dev/workflow": compiled.workflowId
|
|
65
65
|
};
|
|
66
66
|
const workflow = buildRunnerArgoWorkflowManifest({
|
|
67
|
+
annotations: payload.task.kind === "ticket" ? {
|
|
68
|
+
"pipeline.oisin.dev/ticket-id": payload.task.id,
|
|
69
|
+
"pipeline.oisin.dev/ticket-project": payload.run.project,
|
|
70
|
+
"pipeline.oisin.dev/ticket-title": payload.task.title
|
|
71
|
+
} : {},
|
|
67
72
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
68
73
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
69
74
|
generateName: options.generateName,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
|
|
|
6
6
|
apiVersion: z.ZodLiteral<"argoproj.io/v1alpha1">;
|
|
7
7
|
kind: z.ZodLiteral<"Workflow">;
|
|
8
8
|
metadata: z.ZodObject<{
|
|
9
|
+
annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
9
10
|
generateName: z.ZodOptional<z.ZodString>;
|
|
10
11
|
labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
11
12
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -112,6 +113,7 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
112
113
|
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
113
114
|
generateName: z.ZodOptional<z.ZodString>;
|
|
114
115
|
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
116
|
+
annotations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
|
|
115
117
|
image: z.ZodDefault<z.ZodString>;
|
|
116
118
|
imagePullPolicy: z.ZodDefault<z.ZodEnum<{
|
|
117
119
|
Always: "Always";
|
package/dist/argo-workflow.js
CHANGED
|
@@ -83,6 +83,7 @@ const runnerArgoWorkflowManifestSchema = z.object({
|
|
|
83
83
|
apiVersion: z.literal(ARGO_WORKFLOW_API_VERSION),
|
|
84
84
|
kind: z.literal(ARGO_WORKFLOW_KIND),
|
|
85
85
|
metadata: z.object({
|
|
86
|
+
annotations: z.record(z.string().min(1), z.string().min(1)).optional(),
|
|
86
87
|
generateName: z.string().min(1).optional(),
|
|
87
88
|
labels: z.record(z.string().min(1), labelValueSchema).optional(),
|
|
88
89
|
name: z.string().min(1).optional(),
|
|
@@ -110,6 +111,7 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
110
111
|
eventAuthSecretName: kubernetesNameSchema.optional(),
|
|
111
112
|
generateName: z.string().min(1).optional(),
|
|
112
113
|
githubAuthSecretName: kubernetesNameSchema.optional(),
|
|
114
|
+
annotations: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
|
|
113
115
|
image: z.string().min(1).default(RUNNER_WORKFLOW_IMAGE),
|
|
114
116
|
imagePullPolicy: z.enum([
|
|
115
117
|
"Always",
|
|
@@ -151,6 +153,7 @@ function buildRunnerArgoWorkflowManifest(rawOptions) {
|
|
|
151
153
|
apiVersion: ARGO_WORKFLOW_API_VERSION,
|
|
152
154
|
kind: ARGO_WORKFLOW_KIND,
|
|
153
155
|
metadata: {
|
|
156
|
+
annotations: compactRecord(options.annotations),
|
|
154
157
|
...options.name ? { name: options.name } : {},
|
|
155
158
|
...options.generateName ? { generateName: options.generateName } : {},
|
|
156
159
|
labels: compactRecord({
|
package/dist/config.d.ts
CHANGED
|
@@ -452,7 +452,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
452
452
|
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
453
453
|
timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
454
454
|
tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
455
|
-
task: "task";
|
|
456
455
|
read: "read";
|
|
457
456
|
list: "list";
|
|
458
457
|
grep: "grep";
|
|
@@ -460,6 +459,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
460
459
|
bash: "bash";
|
|
461
460
|
edit: "edit";
|
|
462
461
|
write: "write";
|
|
462
|
+
task: "task";
|
|
463
463
|
}>>>;
|
|
464
464
|
}, z.core.$strict>>>;
|
|
465
465
|
runner_command: z.ZodDefault<z.ZodObject<{
|
|
@@ -511,7 +511,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
511
511
|
rules: z.ZodOptional<z.ZodBoolean>;
|
|
512
512
|
skills: z.ZodOptional<z.ZodBoolean>;
|
|
513
513
|
tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
514
|
-
task: "task";
|
|
515
514
|
read: "read";
|
|
516
515
|
list: "list";
|
|
517
516
|
grep: "grep";
|
|
@@ -519,6 +518,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
519
518
|
bash: "bash";
|
|
520
519
|
edit: "edit";
|
|
521
520
|
write: "write";
|
|
521
|
+
task: "task";
|
|
522
522
|
}>>>;
|
|
523
523
|
}, z.core.$strict>;
|
|
524
524
|
command: z.ZodOptional<z.ZodString>;
|
package/dist/pipeline-runtime.js
CHANGED
|
@@ -28,6 +28,7 @@ function runPipelineFromConfig(options) {
|
|
|
28
28
|
function runScheduledWorkflowTask(options) {
|
|
29
29
|
const { dependencyOutputs, nodeId, ...runtimeOptions } = options;
|
|
30
30
|
const context = createRuntimeContext(runtimeOptions);
|
|
31
|
+
hydrateScheduledDependencyStates(context, nodeId);
|
|
31
32
|
hydrateDependencyOutputs(context, dependencyOutputs);
|
|
32
33
|
recordNodeEvent(context, nodeId, {
|
|
33
34
|
at: now(),
|
|
@@ -208,6 +209,57 @@ function hydrateDependencyOutputs(context, dependencyOutputs) {
|
|
|
208
209
|
});
|
|
209
210
|
}
|
|
210
211
|
}
|
|
212
|
+
function hydrateScheduledDependencyStates(context, nodeId) {
|
|
213
|
+
const finishedAt = now();
|
|
214
|
+
for (const dependencyId of scheduledDependencyNodeIds(context, nodeId)) {
|
|
215
|
+
const existing = context.nodeStates.get(dependencyId);
|
|
216
|
+
context.nodeStates.set(dependencyId, scheduledDependencyState(dependencyId, finishedAt, existing));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function scheduledDependencyState(id, finishedAt, existing) {
|
|
220
|
+
return completedScheduledDependencyState(existing ?? emptyScheduledDependencyState(id), finishedAt);
|
|
221
|
+
}
|
|
222
|
+
function emptyScheduledDependencyState(id) {
|
|
223
|
+
return {
|
|
224
|
+
attempts: 0,
|
|
225
|
+
evidence: [],
|
|
226
|
+
gates: [],
|
|
227
|
+
id,
|
|
228
|
+
status: "pending"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function completedScheduledDependencyState(base, finishedAt) {
|
|
232
|
+
return {
|
|
233
|
+
...base,
|
|
234
|
+
attempts: positiveAttempts(base),
|
|
235
|
+
evidence: dependencyEvidence(base),
|
|
236
|
+
exitCode: base.exitCode ?? 0,
|
|
237
|
+
finishedAt: base.finishedAt ?? finishedAt,
|
|
238
|
+
output: base.output ?? "",
|
|
239
|
+
status: "passed"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function positiveAttempts(state) {
|
|
243
|
+
return state.attempts > 0 ? state.attempts : 1;
|
|
244
|
+
}
|
|
245
|
+
function dependencyEvidence(state) {
|
|
246
|
+
return state.evidence.length > 0 ? state.evidence : ["dependency satisfied by scheduled workflow"];
|
|
247
|
+
}
|
|
248
|
+
function scheduledDependencyNodeIds(context, nodeId) {
|
|
249
|
+
const visited = /* @__PURE__ */ new Set();
|
|
250
|
+
const ordered = [];
|
|
251
|
+
const visit = (candidateId) => {
|
|
252
|
+
if (visited.has(candidateId)) return;
|
|
253
|
+
visited.add(candidateId);
|
|
254
|
+
const candidate = context.plan.graph.node(candidateId);
|
|
255
|
+
if (!candidate) return;
|
|
256
|
+
for (const need of candidate.needs) visit(need);
|
|
257
|
+
ordered.push(candidateId);
|
|
258
|
+
};
|
|
259
|
+
const node = context.plan.graph.node(nodeId);
|
|
260
|
+
for (const need of node?.needs ?? []) visit(need);
|
|
261
|
+
return ordered;
|
|
262
|
+
}
|
|
211
263
|
function cancelledFailure() {
|
|
212
264
|
return {
|
|
213
265
|
evidence: ["pipeline cancelled by AbortSignal"],
|
|
@@ -302,27 +354,22 @@ async function executeNode(node, context) {
|
|
|
302
354
|
return cycle.result;
|
|
303
355
|
}
|
|
304
356
|
retry = retryCandidateForCycle(node, cycle, last, attempt);
|
|
305
|
-
const
|
|
357
|
+
const remediation = await remediateFailedNode({
|
|
306
358
|
attempt,
|
|
307
359
|
context,
|
|
308
360
|
node,
|
|
309
361
|
retry
|
|
310
362
|
});
|
|
311
|
-
if (
|
|
363
|
+
if (remediation?.result) {
|
|
312
364
|
recordNodeEvent(context, node.id, {
|
|
313
365
|
at: now(),
|
|
314
|
-
result:
|
|
366
|
+
result: remediation.result,
|
|
315
367
|
type: "PASSED"
|
|
316
368
|
});
|
|
317
|
-
emitNodeFinish(context,
|
|
318
|
-
return
|
|
369
|
+
emitNodeFinish(context, remediation.result);
|
|
370
|
+
return remediation.result;
|
|
319
371
|
}
|
|
320
|
-
if (
|
|
321
|
-
attempt,
|
|
322
|
-
context,
|
|
323
|
-
node,
|
|
324
|
-
retry
|
|
325
|
-
})) continue;
|
|
372
|
+
if (remediation?.retryNode) continue;
|
|
326
373
|
recordNodeEvent(context, node.id, {
|
|
327
374
|
at: now(),
|
|
328
375
|
attempt,
|
|
@@ -379,6 +426,13 @@ async function executeNode(node, context) {
|
|
|
379
426
|
emitNodeFinish(context, result);
|
|
380
427
|
return result;
|
|
381
428
|
}
|
|
429
|
+
async function remediateFailedNode(input) {
|
|
430
|
+
const selfRemediation = await remediateWritableNodeFailure(input);
|
|
431
|
+
if (selfRemediation) return { result: selfRemediation };
|
|
432
|
+
if (await remediateCoverageFailure(input)) return { retryNode: true };
|
|
433
|
+
if (await remediateUpstreamImplementationFailure(input)) return { retryNode: true };
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
382
436
|
async function remediateWritableNodeFailure(input) {
|
|
383
437
|
if (input.retry.retryReason !== "gate_failure" || isRemediationNode(input.node) || !nodeCanWrite(input.context, input.node)) return null;
|
|
384
438
|
const beforeSnapshot = await snapshotChangedFiles(input.context.worktreePath);
|
|
@@ -421,23 +475,41 @@ async function executeSelfRemediation(input) {
|
|
|
421
475
|
}
|
|
422
476
|
async function remediateCoverageFailure(input) {
|
|
423
477
|
if (input.retry.retryReason !== "gate_failure" || !hasSchedulingRole(input.context, input.node, "coverage")) return false;
|
|
478
|
+
return await remediatePassedImplementationAncestors(input);
|
|
479
|
+
}
|
|
480
|
+
async function remediateUpstreamImplementationFailure(input) {
|
|
481
|
+
if (isRemediationNode(input.node) || nodeCanWrite(input.context, input.node) || hasSchedulingRole(input.context, input.node, "coverage")) return false;
|
|
482
|
+
return await remediatePassedImplementationAncestors(input);
|
|
483
|
+
}
|
|
484
|
+
async function remediatePassedImplementationAncestors(input) {
|
|
424
485
|
const implementationNodes = upstreamImplementationNodes(input.context, input.node).filter((candidate) => input.context.nodeStates.get(candidate.id)?.status === "passed");
|
|
425
486
|
if (implementationNodes.length === 0) return false;
|
|
426
|
-
for (const implementationNode of implementationNodes)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
487
|
+
for (const implementationNode of implementationNodes) if (!await remediateImplementationAncestor(input, implementationNode)) return false;
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
async function remediateImplementationAncestor(input, implementationNode) {
|
|
491
|
+
if (isCancelled(input.context)) return false;
|
|
492
|
+
const beforeSnapshot = await snapshotChangedFiles(input.context.worktreePath);
|
|
493
|
+
const beforeOutput = input.context.lastOutputByNode.get(implementationNode.id);
|
|
494
|
+
const result = await executeImplementationRemediation({
|
|
495
|
+
attempt: input.attempt,
|
|
496
|
+
context: input.context,
|
|
497
|
+
coverageNode: input.node,
|
|
498
|
+
implementationNode,
|
|
499
|
+
retry: input.retry
|
|
500
|
+
});
|
|
501
|
+
if (result.status !== "passed") return false;
|
|
502
|
+
return await recordImplementationRemediationEffect({
|
|
503
|
+
beforeOutput,
|
|
504
|
+
beforeSnapshot,
|
|
505
|
+
context: input.context,
|
|
506
|
+
implementationNode,
|
|
507
|
+
result
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
async function recordImplementationRemediationEffect(input) {
|
|
511
|
+
if (diffChangedFiles(input.beforeSnapshot, await snapshotChangedFiles(input.context.worktreePath), input.context.worktreePath).files.size === 0 && input.result.output === input.beforeOutput) return false;
|
|
512
|
+
input.context.lastOutputByNode.set(input.implementationNode.id, input.result.output);
|
|
441
513
|
return true;
|
|
442
514
|
}
|
|
443
515
|
async function executeImplementationRemediation(input) {
|
package/package.json
CHANGED
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"prepack": "bun run build:cli"
|
|
121
121
|
},
|
|
122
122
|
"type": "module",
|
|
123
|
-
"version": "1.27.
|
|
123
|
+
"version": "1.27.14",
|
|
124
124
|
"description": "Config-driven multi-agent pipeline runner for repository work",
|
|
125
125
|
"main": "./dist/index.js",
|
|
126
126
|
"types": "./dist/index.d.ts",
|