@oisincoveney/pipeline 3.24.2 → 3.24.3
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 +2 -2
- package/defaults/pipeline.yaml +23 -5
- package/defaults/profiles.yaml +104 -21
- package/dist/argo-graph.js +88 -88
- package/dist/argo-submit.d.ts +109 -137
- package/dist/argo-submit.js +337 -360
- package/dist/argo-workflow.d.ts +156 -176
- package/dist/argo-workflow.js +96 -138
- package/dist/backlog.js +5 -4
- package/dist/bench/eval-report.js +21 -12
- package/dist/claude-settings-config.js +23 -31
- package/dist/claude-user-config.js +6 -16
- package/dist/cli/bootstrap-commands.js +3 -3
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.js +172 -169
- package/dist/cli/factory-commands.js +12 -11
- package/dist/cli/format.js +92 -86
- package/dist/cli/loop-commands.js +21 -21
- package/dist/cli/mcp-gateway-commands.js +12 -12
- package/dist/cli/plan-commands.js +85 -116
- package/dist/cli/program.d.ts +2 -2
- package/dist/cli/program.js +35 -43
- package/dist/cli/run-command.js +25 -25
- package/dist/cli/run-commands.js +25 -25
- package/dist/cli/run-resolver.d.ts +1 -1
- package/dist/cli/run-resolver.js +30 -32
- package/dist/cli/run-service.d.ts +2 -2
- package/dist/cli/run-service.js +226 -239
- package/dist/cli/submit-options.js +78 -82
- package/dist/cluster-doctor.js +147 -175
- package/dist/codex-config.js +24 -30
- package/dist/commands/bench-command.js +9 -7
- package/dist/commands/pipeline-command.js +9 -5
- package/dist/commands/runner-command-command.js +28 -18
- package/dist/commands/ticket/complete.js +29 -33
- package/dist/commands/ticket/create.js +82 -82
- package/dist/commands/ticket/graph-check.js +11 -11
- package/dist/commands/ticket/next.js +36 -36
- package/dist/commands/ticket/sequence.js +10 -12
- package/dist/commands/ticket/shared.d.ts +1 -1
- package/dist/commands/ticket/shared.js +63 -66
- package/dist/commands/ticket/start.js +58 -67
- package/dist/commands/ticket-command.js +2 -2
- package/dist/config/defaults.d.ts +139 -139
- package/dist/config/defaults.js +81 -80
- package/dist/config/lint.js +52 -64
- package/dist/config/load.d.ts +4 -4
- package/dist/config/load.js +62 -74
- package/dist/config/schema/catalog.js +1 -1
- package/dist/config/schema/mcp.js +46 -52
- package/dist/config/schemas.d.ts +375 -525
- package/dist/config/schemas.js +397 -397
- package/dist/config/validate.d.ts +1 -1
- package/dist/config/validate.js +236 -201
- package/dist/context/repo-map.js +102 -119
- package/dist/credentials/broker.d.ts +6 -8
- package/dist/credentials/broker.js +17 -16
- package/dist/credentials/codex-config.js +15 -19
- package/dist/credentials/file-targets.js +9 -15
- package/dist/credentials/local-codex-auth-sync.js +55 -58
- package/dist/credentials/opencode-config.js +28 -32
- package/dist/credentials/runner.js +13 -13
- package/dist/effect-sync-errors.js +28 -0
- package/dist/factory/create-experiment.js +38 -42
- package/dist/factory/exec.js +10 -12
- package/dist/factory/git-credentials.js +4 -4
- package/dist/factory/stamp-answers.js +10 -11
- package/dist/factory/template-update.js +65 -44
- package/dist/factory-lane.d.ts +26 -40
- package/dist/factory-lane.js +26 -25
- package/dist/gates.js +3 -3
- package/dist/git-remote-url.js +17 -19
- package/dist/hooks.d.ts +26 -25
- package/dist/hooks.js +20 -22
- package/dist/index.d.ts +1 -1
- package/dist/index.js +21 -35
- package/dist/install-commands/claude-code.js +96 -92
- package/dist/install-commands/host-selection.js +3 -6
- package/dist/install-commands/opencode.js +263 -259
- package/dist/install-commands/planner.js +159 -180
- package/dist/install-commands/result-format.js +1 -3
- package/dist/install-commands/shared.js +25 -42
- package/dist/install-commands/writer.js +22 -25
- package/dist/install-commands.js +8 -8
- package/dist/jscpd-output.js +29 -0
- package/dist/json-config-merge.js +14 -20
- package/dist/json-line-values.js +17 -6
- package/dist/loop/argo-poll.js +52 -71
- package/dist/loop/backlog-records.js +1 -3
- package/dist/loop/controller-deps.js +146 -159
- package/dist/loop/controller.js +162 -186
- package/dist/loop/gh-checks.js +62 -61
- package/dist/loop/gh-runner.js +22 -25
- package/dist/loop/loop-command.js +66 -58
- package/dist/loop/loop-controller-entrypoint.js +28 -30
- package/dist/loop/merge.js +36 -40
- package/dist/mcp/gateway-config.js +25 -29
- package/dist/mcp/gateway-doctor.js +104 -116
- package/dist/mcp/gateway-reconcile.js +17 -22
- package/dist/mcp/gateway-runtime.js +1 -3
- package/dist/mcp/host-config.js +37 -40
- package/dist/mcp/host-renderers.js +10 -18
- package/dist/mcp/repo-local-backends.js +50 -54
- package/dist/mcp/toolhive-vmcp.js +39 -39
- package/dist/model-resolver.js +29 -33
- package/dist/moka-global-config.d.ts +78 -54
- package/dist/moka-global-config.js +90 -92
- package/dist/moka-submit.d.ts +2163 -864
- package/dist/moka-submit.js +97 -99
- package/dist/opencode-project-config.js +34 -40
- package/dist/package-assets.js +1 -3
- package/dist/path-refs.js +2 -2
- package/dist/pipeline-init.js +25 -25
- package/dist/pipeline-runtime.d.ts +11 -11
- package/dist/pipeline-runtime.js +49 -50
- package/dist/planned-node.js +6 -3
- package/dist/planning/compile.d.ts +3 -3
- package/dist/planning/compile.js +140 -139
- package/dist/planning/dependency-refs.js +5 -5
- package/dist/planning/generate.d.ts +1293 -1293
- package/dist/planning/generate.js +305 -314
- package/dist/planning/graph.js +131 -149
- package/dist/remote/argo/model.d.ts +103 -121
- package/dist/remote/argo/model.js +205 -197
- package/dist/remote/argo/policy.js +39 -47
- package/dist/remote/argo/storage.js +81 -85
- package/dist/remote/submit/argo-submission.js +53 -58
- package/dist/remote/submit/compilation.js +46 -49
- package/dist/remote/submit/event-boundary.js +62 -76
- package/dist/remote/submit/hook-events.d.ts +5 -0
- package/dist/remote/submit/io.js +40 -43
- package/dist/remote/submit/options.d.ts +50 -0
- package/dist/remote/submit/options.js +27 -0
- package/dist/remote/submit/service.js +45 -49
- package/dist/run-control/command-context.js +6 -8
- package/dist/run-control/commands.js +10 -10
- package/dist/run-control/contracts.d.ts +40 -100
- package/dist/run-control/contracts.js +56 -69
- package/dist/run-control/detach.js +66 -72
- package/dist/run-control/file-errors.js +1 -3
- package/dist/run-control/logical-segment.js +3 -3
- package/dist/run-control/next-node.js +66 -78
- package/dist/run-control/postgres/postgres-run-control-store.js +195 -224
- package/dist/run-control/resume-command.js +33 -41
- package/dist/run-control/run-artifacts-command.js +83 -105
- package/dist/run-control/run-command-domain.js +7 -11
- package/dist/run-control/run-control-store.js +9 -9
- package/dist/run-control/run-query-command.js +73 -100
- package/dist/run-control/run-record.js +2 -2
- package/dist/run-control/run-state-lock.js +5 -5
- package/dist/run-control/runtime-event-projection.js +38 -50
- package/dist/run-control/runtime-reporter.js +84 -102
- package/dist/run-control/stop-command.js +42 -53
- package/dist/run-control/store-fs-effects.js +3 -1
- package/dist/run-control/store-manifest.js +38 -13
- package/dist/run-control/store-paths.js +3 -3
- package/dist/run-control/store.js +1 -1
- package/dist/run-control/submit-result.js +21 -21
- package/dist/run-control/supervisor.js +77 -68
- package/dist/run-control/workspace.js +2 -2
- package/dist/run-state/git-refs.js +257 -307
- package/dist/runner/opencode-excludes.js +4 -4
- package/dist/runner/subprocess-result.js +28 -37
- package/dist/runner/subprocess.js +41 -41
- package/dist/runner/timeouts.js +14 -13
- package/dist/runner-command/dynamic-command.js +46 -39
- package/dist/runner-command/finalize.js +174 -153
- package/dist/runner-command/lifecycle-context.js +112 -130
- package/dist/runner-command/lifecycle.js +77 -71
- package/dist/runner-command/pre-schedule.js +201 -222
- package/dist/runner-command/run.js +432 -447
- package/dist/runner-command/schedule-source-options.js +8 -10
- package/dist/runner-command/select-ready-wave.js +38 -29
- package/dist/runner-command/task-descriptor.js +11 -17
- package/dist/runner-command-contract.d.ts +147 -156
- package/dist/runner-command-contract.js +401 -410
- package/dist/runner-event-schema.d.ts +364 -504
- package/dist/runner-event-schema.js +187 -175
- package/dist/runner-event-sink.js +37 -43
- package/dist/runner-output.js +6 -6
- package/dist/runner.d.ts +56 -36
- package/dist/runner.js +175 -127
- package/dist/runtime/actor-ids.js +2 -2
- package/dist/runtime/agent-node/agent-node.js +99 -111
- package/dist/runtime/agent-node/handoff-finalization.js +23 -24
- package/dist/runtime/agent-node/model-selection.js +19 -22
- package/dist/runtime/agent-node/output-finalization.js +174 -205
- package/dist/runtime/agent-node/prompt-rendering.js +207 -251
- package/dist/runtime/agent-node/session-execution.js +34 -38
- package/dist/runtime/builtins/builtins.js +212 -284
- package/dist/runtime/changed-files/changed-files.js +26 -34
- package/dist/runtime/command-executor/command-executor.js +31 -23
- package/dist/runtime/config-error.js +1 -3
- package/dist/runtime/context/context.js +35 -37
- package/dist/runtime/contracts/index.d.ts +1 -1
- package/dist/runtime/detached-race.js +11 -14
- package/dist/runtime/drain-merge/drain-merge.js +137 -163
- package/dist/runtime/durable-store/acquisition.js +5 -3
- package/dist/runtime/durable-store/durable-store.d.ts +1 -1
- package/dist/runtime/durable-store/postgres/migrate-substrate.js +36 -33
- package/dist/runtime/durable-store/postgres/postgres-store.js +17 -18
- package/dist/runtime/events/events.js +255 -323
- package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
- package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
- package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
- package/dist/runtime/gates/adjudicator/index.js +2 -2
- package/dist/runtime/gates/contract.js +5 -9
- package/dist/runtime/gates/gates.js +9 -8
- package/dist/runtime/gates/kinds/acceptance/acceptance.js +66 -70
- package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
- package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
- package/dist/runtime/gates/kinds/artifact/index.js +2 -2
- package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
- package/dist/runtime/gates/kinds/builtin/index.js +2 -2
- package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
- package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
- package/dist/runtime/gates/kinds/command/command.js +3 -3
- package/dist/runtime/gates/kinds/command/index.js +2 -2
- package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
- package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
- package/dist/runtime/gates/kinds/verdict/index.js +2 -2
- package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
- package/dist/runtime/gates/orchestrator.js +72 -86
- package/dist/runtime/gates/registry.js +1 -3
- package/dist/runtime/handoff.d.ts +1 -1
- package/dist/runtime/handoff.js +43 -46
- package/dist/runtime/hooks/command-hook.js +57 -71
- package/dist/runtime/hooks/context.js +21 -25
- package/dist/runtime/hooks/events.js +84 -100
- package/dist/runtime/hooks/execution.js +6 -12
- package/dist/runtime/hooks/hooks.js +34 -23
- package/dist/runtime/hooks/invocation.js +23 -29
- package/dist/runtime/hooks/module-hook.js +35 -35
- package/dist/runtime/hooks/policy.js +32 -29
- package/dist/runtime/hooks/results.js +56 -66
- package/dist/runtime/journal-acquisition.d.ts +1 -1
- package/dist/runtime/journal-acquisition.js +14 -14
- package/dist/runtime/json-validation/json-validation.js +50 -72
- package/dist/runtime/local-scheduler.js +46 -48
- package/dist/runtime/node-execution.js +375 -446
- package/dist/runtime/node-protocol/node-protocol.js +36 -79
- package/dist/runtime/node-state-store.d.ts +1 -1
- package/dist/runtime/node-state-store.js +13 -57
- package/dist/runtime/node-state-tracker.js +46 -54
- package/dist/runtime/open-pull-request/open-pull-request.js +143 -187
- package/dist/runtime/opencode-adapter.js +23 -24
- package/dist/runtime/opencode-agent-name.js +6 -6
- package/dist/runtime/opencode-runtime.js +92 -92
- package/dist/runtime/opencode-server.js +39 -53
- package/dist/runtime/opencode-session-executor.js +315 -334
- package/dist/runtime/parallel-node/parallel-node.js +122 -134
- package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
- package/dist/runtime/protected-paths/protected-paths.js +70 -71
- package/dist/runtime/remediation/remediation.js +202 -231
- package/dist/runtime/retry.js +8 -10
- package/dist/runtime/run-journal.js +7 -7
- package/dist/runtime/runtime-results.js +54 -69
- package/dist/runtime/scheduled-dependencies.d.ts +1 -1
- package/dist/runtime/scheduled-dependencies.js +59 -74
- package/dist/runtime/scheduler.js +177 -202
- package/dist/runtime/services/agent-node-runtime-service.js +3 -3
- package/dist/runtime/services/backlog-service.js +8 -14
- package/dist/runtime/services/command-executor-service.js +1 -1
- package/dist/runtime/services/config-io-service.js +24 -21
- package/dist/runtime/services/drain-merge-git-service.js +1 -1
- package/dist/runtime/services/file-system-service.js +9 -7
- package/dist/runtime/services/git-porcelain-service.js +6 -6
- package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -2
- package/dist/runtime/services/kubernetes-argo-service.js +90 -84
- package/dist/runtime/services/mcp-gateway-service.js +82 -81
- package/dist/runtime/services/open-pull-request-git-service.js +1 -3
- package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
- package/dist/runtime/services/opencode-sdk-service.js +7 -7
- package/dist/runtime/services/repo-io-service.js +10 -12
- package/dist/runtime/services/run-journal-file-service.js +2 -2
- package/dist/runtime/services/runner-command-io-service.js +48 -52
- package/dist/runtime/services/runner-event-sink-http-service.js +31 -39
- package/dist/runtime/step/step-node.js +15 -15
- package/dist/runtime/workflow-execution.js +55 -57
- package/dist/runtime/workflow-lifecycle.js +81 -101
- package/dist/safe-json.js +16 -16
- package/dist/schedule/backlog-context.js +120 -116
- package/dist/schedule/baseline.js +249 -255
- package/dist/schedule/passes/coverage.js +63 -68
- package/dist/schedule/passes/drain-merge.js +34 -40
- package/dist/schedule/passes/ids.js +15 -17
- package/dist/schedule/passes/models.js +40 -37
- package/dist/schedule/passes/open-pull-request.js +12 -18
- package/dist/schedule/passes/references.js +18 -22
- package/dist/schedule/prompts.js +134 -141
- package/dist/schedule/scheduling-roles.js +8 -14
- package/dist/schema-boundary.d.ts +1 -0
- package/dist/schema-boundary.js +145 -0
- package/dist/serialized-write-queue.js +2 -2
- package/dist/standard-output-schemas.js +61 -59
- package/dist/strings.js +12 -12
- package/dist/task-ref.js +5 -5
- package/dist/tickets/apply-ticket-plan.js +51 -61
- package/dist/tickets/backlog-task-store.d.ts +13 -22
- package/dist/tickets/backlog-task-store.js +80 -94
- package/dist/tickets/completion/complete-ticket.js +53 -59
- package/dist/tickets/ticket-graph-dto.d.ts +24 -47
- package/dist/tickets/ticket-graph-dto.js +40 -40
- package/dist/tickets/ticket-graph.d.ts +7 -1
- package/dist/tickets/ticket-graph.js +36 -50
- package/dist/tickets/ticket-plan-command-args.js +6 -10
- package/dist/tickets/ticket-plan-render.js +10 -16
- package/dist/tickets/ticket-plan.d.ts +31 -40
- package/dist/tickets/ticket-plan.js +97 -77
- package/dist/tickets/ticket-selection.d.ts +1 -1
- package/dist/tickets/ticket-selection.js +37 -52
- package/dist/tickets/ticket-task-index.js +13 -17
- package/dist/tickets/validation-error-format.js +3 -10
- package/dist/token-estimator.js +12 -7
- package/dist/unknown-fields.js +21 -0
- package/dist/workflow-submit-contract.d.ts +9 -9
- package/dist/workflow-submit-contract.js +10 -9
- package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
- package/docs/config-architecture.md +13 -13
- package/docs/mcp-host-isolation.md +1 -0
- package/docs/moka-orchestrator-design.md +3 -3
- package/docs/operator-guide.md +1 -1
- package/docs/runtime-actor-model.md +18 -18
- package/docs/slash-command-adapter-contract.md +3 -3
- package/package.json +73 -76
- package/dist/config/schema/reference-validation.js +0 -83
package/dist/argo-submit.js
CHANGED
|
@@ -1,121 +1,321 @@
|
|
|
1
|
+
import { nonEmptyMutableArray, parseStrictWithSchema, parseWithSchema, requiredString, stringRecord, struct, withDefault } from "./schema-boundary.js";
|
|
1
2
|
import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
|
|
2
|
-
import { argoWorkflowActiveDeadlineSecondsSchema, argoWorkflowPodGcSchema, argoWorkflowTtlStrategySchema, dbAuthOptionSchema, mcpGatewayAuthOptionSchema } from "./remote/argo/model.js";
|
|
3
|
-
import { brokerAuthOptionSchema } from "./credentials/broker.js";
|
|
4
3
|
import { appendPullRequestDelivery } from "./schedule/passes/open-pull-request.js";
|
|
5
4
|
import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
|
|
6
5
|
import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
|
|
7
6
|
import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
|
|
8
7
|
import { buildDynamicRunnerArgoWorkflowManifest, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
|
|
9
8
|
import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
|
|
9
|
+
import { runnerPodSubmitOptionShape } from "./remote/submit/options.js";
|
|
10
10
|
import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
|
|
11
11
|
import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
|
|
12
|
+
import * as DateTime from "effect/DateTime";
|
|
13
|
+
import * as Effect from "effect/Effect";
|
|
14
|
+
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
15
|
+
import * as Option$1 from "effect/Option";
|
|
16
|
+
import * as R from "effect/Record";
|
|
17
|
+
import * as Schema from "effect/Schema";
|
|
12
18
|
import { stringify } from "yaml";
|
|
13
|
-
import { Effect } from "effect";
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
import { randomBytes } from "node:crypto";
|
|
16
19
|
//#region src/argo-submit.ts
|
|
17
|
-
const
|
|
18
|
-
const configMapSchema =
|
|
19
|
-
apiVersion:
|
|
20
|
-
data:
|
|
21
|
-
kind:
|
|
22
|
-
metadata:
|
|
23
|
-
labels:
|
|
24
|
-
name:
|
|
25
|
-
namespace:
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
const workflowOwnerReferenceSchema =
|
|
29
|
-
apiVersion:
|
|
30
|
-
kind:
|
|
31
|
-
name:
|
|
32
|
-
uid:
|
|
33
|
-
})
|
|
34
|
-
const configMapOwnerReferencesPatchSchema =
|
|
35
|
-
const createdWorkflowSchema =
|
|
36
|
-
name:
|
|
37
|
-
uid:
|
|
38
|
-
})
|
|
20
|
+
const scheduleId = Schema.String.check(Schema.isPattern(/^[a-z][a-z0-9-]*$/u));
|
|
21
|
+
const configMapSchema = struct({
|
|
22
|
+
apiVersion: Schema.Literal("v1"),
|
|
23
|
+
data: stringRecord,
|
|
24
|
+
kind: Schema.Literal("ConfigMap"),
|
|
25
|
+
metadata: struct({
|
|
26
|
+
labels: Schema.optional(stringRecord),
|
|
27
|
+
name: requiredString,
|
|
28
|
+
namespace: requiredString
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
const workflowOwnerReferenceSchema = struct({
|
|
32
|
+
apiVersion: Schema.Literal("argoproj.io/v1alpha1"),
|
|
33
|
+
kind: Schema.Literal("Workflow"),
|
|
34
|
+
name: requiredString,
|
|
35
|
+
uid: requiredString
|
|
36
|
+
});
|
|
37
|
+
const configMapOwnerReferencesPatchSchema = struct({ metadata: struct({ ownerReferences: Schema.mutable(Schema.Tuple([workflowOwnerReferenceSchema])) }) });
|
|
38
|
+
const createdWorkflowSchema = struct({ metadata: struct({
|
|
39
|
+
name: Schema.optional(requiredString),
|
|
40
|
+
uid: Schema.optional(requiredString)
|
|
41
|
+
}) });
|
|
42
|
+
const runnerTaskDescriptorJson = Schema.fromJsonString(struct({ nodeId: requiredString }));
|
|
43
|
+
const encodeRunnerTaskDescriptorJson = Schema.encodeSync(runnerTaskDescriptorJson);
|
|
44
|
+
const runnerCommandPayloadJson = Schema.fromJsonString(runnerCommandPayloadSchema);
|
|
45
|
+
const encodeRunnerCommandPayloadJson = Schema.encodeSync(runnerCommandPayloadJson);
|
|
46
|
+
const errorMessageShape = struct({ message: Schema.String });
|
|
47
|
+
const randomHex = (byteLength) => globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, byteLength * 2);
|
|
48
|
+
var ArgoSubmitError = class extends Schema.TaggedErrorClass()("ArgoSubmitError", { message: Schema.String }) {};
|
|
39
49
|
const submitRunnerArgoWorkflowBaseOptionShape = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
dbAuth: dbAuthOptionSchema.optional(),
|
|
43
|
-
eventAuthSecretKey: z.string().min(1).optional(),
|
|
44
|
-
eventAuthSecretName: z.string().min(1).optional(),
|
|
45
|
-
generateName: z.string().min(1).optional(),
|
|
46
|
-
gitCredentialsSecretName: z.string().min(1).optional(),
|
|
47
|
-
githubAuthSecretName: z.string().min(1).optional(),
|
|
48
|
-
image: z.string().min(1).optional(),
|
|
49
|
-
imagePullPolicy: z.enum([
|
|
50
|
+
...runnerPodSubmitOptionShape,
|
|
51
|
+
imagePullPolicy: Schema.optional(Schema.Literals([
|
|
50
52
|
"Always",
|
|
51
53
|
"IfNotPresent",
|
|
52
54
|
"Never"
|
|
53
|
-
])
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
kubeconfigPath: z.string().min(1).optional(),
|
|
57
|
-
mcpGatewayAuth: mcpGatewayAuthOptionSchema.optional(),
|
|
58
|
-
name: z.string().min(1).optional(),
|
|
59
|
-
namespace: z.string().min(1),
|
|
60
|
-
npmRegistryAuthSecretName: z.string().min(1).optional(),
|
|
61
|
-
payloadJson: z.string().min(1),
|
|
62
|
-
podGC: argoWorkflowPodGcSchema.optional(),
|
|
63
|
-
serviceAccountName: z.string().min(1).optional(),
|
|
64
|
-
ttlStrategy: argoWorkflowTtlStrategySchema.optional()
|
|
55
|
+
])),
|
|
56
|
+
namespace: requiredString,
|
|
57
|
+
payloadJson: requiredString
|
|
65
58
|
};
|
|
66
|
-
const
|
|
59
|
+
const commandScheduleOptionsSchema = struct({
|
|
60
|
+
command: nonEmptyMutableArray(requiredString),
|
|
61
|
+
deliverPullRequest: withDefault(Schema.Boolean, false),
|
|
62
|
+
generatedAt: Schema.optional(Schema.DateTimeUtcFromDate),
|
|
63
|
+
scheduleId: Schema.optional(scheduleId),
|
|
64
|
+
task: requiredString
|
|
65
|
+
});
|
|
66
|
+
const hasWorkflowName = (options) => options.name !== void 0 || options.generateName !== void 0;
|
|
67
|
+
const submitRunnerArgoWorkflowOptionsSchema = struct({
|
|
67
68
|
...submitRunnerArgoWorkflowBaseOptionShape,
|
|
68
|
-
scheduleYaml:
|
|
69
|
-
}).
|
|
70
|
-
|
|
69
|
+
scheduleYaml: requiredString
|
|
70
|
+
}).check(Schema.makeFilter((options) => hasWorkflowName(options) || "Argo submit options must declare name or generateName", {
|
|
71
|
+
description: "Static Argo submit options must include name or generateName.",
|
|
72
|
+
identifier: "SubmitRunnerArgoWorkflowName",
|
|
73
|
+
title: "Submit runner Argo workflow name"
|
|
74
|
+
}));
|
|
75
|
+
const submitDynamicRunnerArgoWorkflowOptionsSchema = struct({
|
|
71
76
|
...submitRunnerArgoWorkflowBaseOptionShape,
|
|
72
|
-
workflowId:
|
|
73
|
-
}).
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
workflowId: requiredString
|
|
78
|
+
}).check(Schema.makeFilter((options) => hasWorkflowName(options) || "Argo submit options must declare name or generateName", {
|
|
79
|
+
description: "Dynamic Argo submit options must include name or generateName.",
|
|
80
|
+
identifier: "SubmitDynamicRunnerArgoWorkflowName",
|
|
81
|
+
title: "Submit dynamic runner Argo workflow name"
|
|
82
|
+
}));
|
|
83
|
+
const kubernetesArgoRuntime = ManagedRuntime.make(KubernetesArgoServiceLive);
|
|
84
|
+
const runKubernetesArgoEffect = async (effect) => await kubernetesArgoRuntime.runPromise(effect);
|
|
85
|
+
const runConfigMap = (input) => {
|
|
86
|
+
return {
|
|
87
|
+
body: parseStrictWithSchema(configMapSchema, {
|
|
88
|
+
apiVersion: "v1",
|
|
89
|
+
data: input.data,
|
|
90
|
+
kind: "ConfigMap",
|
|
91
|
+
metadata: {
|
|
92
|
+
labels: input.labels,
|
|
93
|
+
name: input.name,
|
|
94
|
+
namespace: input.namespace
|
|
95
|
+
}
|
|
96
|
+
}),
|
|
97
|
+
name: input.name
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
const staticRunConfigMaps = (input) => [
|
|
101
|
+
runConfigMap({
|
|
102
|
+
data: { "payload.json": input.payloadJson },
|
|
103
|
+
labels: input.labels,
|
|
104
|
+
name: input.payloadConfigMapName,
|
|
105
|
+
namespace: input.namespace
|
|
106
|
+
}),
|
|
107
|
+
runConfigMap({
|
|
108
|
+
data: R.fromEntries(input.tasks.map((task) => [`${task.taskName}.json`, `${encodeRunnerTaskDescriptorJson(buildRunnerTaskDescriptor(task.nodeId))}\n`])),
|
|
109
|
+
labels: input.labels,
|
|
110
|
+
name: input.taskDescriptorConfigMapName,
|
|
111
|
+
namespace: input.namespace
|
|
112
|
+
}),
|
|
113
|
+
runConfigMap({
|
|
114
|
+
data: { "schedule.yaml": input.scheduleYaml },
|
|
115
|
+
labels: input.labels,
|
|
116
|
+
name: input.scheduleConfigMapName,
|
|
117
|
+
namespace: input.namespace
|
|
118
|
+
})
|
|
119
|
+
];
|
|
120
|
+
const dynamicRunConfigMaps = (input) => [runConfigMap({
|
|
121
|
+
data: { "payload.json": input.payloadJson },
|
|
122
|
+
labels: input.labels,
|
|
123
|
+
name: input.payloadConfigMapName,
|
|
124
|
+
namespace: input.namespace
|
|
125
|
+
})];
|
|
126
|
+
const runConfigMapEffects = (configMapNames, effectForName) => Effect.all(configMapNames.map(effectForName), { concurrency: "unbounded" });
|
|
127
|
+
const patchRunConfigMapOwnerReferences = (input) => Effect.gen(function* effectBody() {
|
|
128
|
+
const service = yield* KubernetesArgoService;
|
|
129
|
+
yield* runConfigMapEffects(input.configMapNames, (name) => service.patchConfigMapOwnerReferences({
|
|
130
|
+
body: input.body,
|
|
131
|
+
dependencies: input.dependencies,
|
|
132
|
+
name,
|
|
133
|
+
namespace: input.namespace,
|
|
134
|
+
options: input.options
|
|
135
|
+
}));
|
|
136
|
+
});
|
|
137
|
+
const deleteRunConfigMaps = (input) => Effect.gen(function* effectBody() {
|
|
138
|
+
const service = yield* KubernetesArgoService;
|
|
139
|
+
yield* runConfigMapEffects(input.configMapNames, (name) => service.deleteConfigMap({
|
|
140
|
+
dependencies: input.dependencies,
|
|
141
|
+
name,
|
|
142
|
+
namespace: input.namespace,
|
|
143
|
+
options: input.options
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
const workflowOwnerReference = (result) => {
|
|
147
|
+
if (result.workflowUid === void 0) return Option$1.none();
|
|
148
|
+
return Option$1.some(parseStrictWithSchema(workflowOwnerReferenceSchema, {
|
|
149
|
+
apiVersion: "argoproj.io/v1alpha1",
|
|
150
|
+
kind: "Workflow",
|
|
151
|
+
name: result.workflowName,
|
|
152
|
+
uid: result.workflowUid
|
|
153
|
+
}));
|
|
154
|
+
};
|
|
155
|
+
const missingWorkflowUidMessage = (result) => `Created Argo Workflow '${result.workflowName}' did not include metadata.uid; cannot own ConfigMaps`;
|
|
156
|
+
const configMapOwnerReferencesPatch = (ownerReference) => parseStrictWithSchema(configMapOwnerReferencesPatchSchema, { metadata: { ownerReferences: [ownerReference] } });
|
|
157
|
+
const errorMessage = (error) => Option$1.match(Schema.decodeUnknownOption(errorMessageShape)(error), {
|
|
158
|
+
onNone: () => String(error),
|
|
159
|
+
onSome: (value) => value.message
|
|
160
|
+
});
|
|
161
|
+
const warnRunConfigMapOwnershipSkipped = (input) => Effect.sync(() => {
|
|
162
|
+
const reason = errorMessage(input.error);
|
|
163
|
+
const message = `moka submit: failed to set Workflow ownerReference for ConfigMaps after Argo Workflow '${input.result.workflowName}' was created; leaving ConfigMaps for TTL/sweeper cleanup: ${reason}\n`;
|
|
164
|
+
process.stderr.write(message);
|
|
165
|
+
});
|
|
166
|
+
const ownRunConfigMaps = (input) => {
|
|
167
|
+
const ownerReference = workflowOwnerReference(input.result);
|
|
168
|
+
if (Option$1.isNone(ownerReference)) return warnRunConfigMapOwnershipSkipped({
|
|
169
|
+
error: new ArgoSubmitError({ message: missingWorkflowUidMessage(input.result) }),
|
|
170
|
+
result: input.result
|
|
171
|
+
}).pipe(Effect.as(input.result));
|
|
172
|
+
return patchRunConfigMapOwnerReferences({
|
|
173
|
+
body: configMapOwnerReferencesPatch(ownerReference.value),
|
|
174
|
+
configMapNames: input.configMapNames,
|
|
175
|
+
dependencies: input.dependencies,
|
|
176
|
+
namespace: input.namespace,
|
|
177
|
+
options: input.options
|
|
178
|
+
}).pipe(Effect.as(input.result));
|
|
179
|
+
};
|
|
180
|
+
const ownRunConfigMapsBestEffort = (input) => ownRunConfigMaps(input).pipe(Effect.catch((error) => warnRunConfigMapOwnershipSkipped({
|
|
181
|
+
error,
|
|
182
|
+
result: input.result
|
|
183
|
+
}).pipe(Effect.as(input.result))));
|
|
184
|
+
const cleanupRunConfigMapsOnFailure = (input) => deleteRunConfigMaps({
|
|
185
|
+
configMapNames: input.configMapNames,
|
|
186
|
+
dependencies: input.dependencies,
|
|
187
|
+
namespace: input.namespace,
|
|
188
|
+
options: input.options
|
|
189
|
+
}).pipe(Effect.catch((cleanupError) => Effect.fail(new ArgoSubmitError({ message: `Failed to clean up ConfigMaps after submit failure: ${errorMessage(input.error)}; cleanup failed: ${errorMessage(cleanupError)}` }))), Effect.flatMap(() => Effect.fail(input.error)));
|
|
190
|
+
const createRunConfigMaps = (input) => {
|
|
191
|
+
const createdConfigMapNames = [];
|
|
192
|
+
return Effect.gen(function* effectBody() {
|
|
193
|
+
const service = yield* KubernetesArgoService;
|
|
194
|
+
yield* Effect.all(input.configMaps.map((configMap) => service.createConfigMap({
|
|
195
|
+
body: configMap.body,
|
|
196
|
+
dependencies: input.dependencies,
|
|
197
|
+
namespace: input.namespace,
|
|
198
|
+
options: input.options
|
|
199
|
+
}).pipe(Effect.tap(() => Effect.sync(() => {
|
|
200
|
+
createdConfigMapNames.push(configMap.name);
|
|
201
|
+
})))), { concurrency: "unbounded" });
|
|
202
|
+
return createdConfigMapNames;
|
|
203
|
+
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
204
|
+
configMapNames: createdConfigMapNames,
|
|
205
|
+
dependencies: input.dependencies,
|
|
206
|
+
error,
|
|
207
|
+
namespace: input.namespace,
|
|
208
|
+
options: input.options
|
|
209
|
+
})));
|
|
210
|
+
};
|
|
211
|
+
const applyWorkflowFieldOverrides = (workflow, overrides = {}) => {
|
|
212
|
+
if (overrides.podGC === void 0) return workflow;
|
|
213
|
+
return parseStrictWithSchema(runnerArgoWorkflowManifestSchema, {
|
|
214
|
+
...workflow,
|
|
215
|
+
spec: {
|
|
216
|
+
...workflow.spec,
|
|
217
|
+
podGC: overrides.podGC
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
const workflowSubmitResult = (response, workflow, base) => {
|
|
222
|
+
const created = parseWithSchema(createdWorkflowSchema, response, { onExcessProperty: "preserve" });
|
|
223
|
+
return parseStrictWithSchema(workflowSubmitResultSchema, {
|
|
224
|
+
...base,
|
|
225
|
+
workflowName: created.metadata.name ?? workflow.metadata.name,
|
|
226
|
+
workflowUid: created.metadata.uid
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
const submitWorkflowManifest = (input) => Effect.gen(function* effectBody() {
|
|
230
|
+
const service = yield* KubernetesArgoService;
|
|
231
|
+
const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
|
|
232
|
+
return workflowSubmitResult(yield* service.createWorkflow({
|
|
233
|
+
body: parseStrictWithSchema(runnerArgoWorkflowManifestSchema, workflow),
|
|
234
|
+
dependencies: input.dependencies,
|
|
235
|
+
namespace: input.namespace,
|
|
236
|
+
options: input.options
|
|
237
|
+
}), workflow, {
|
|
238
|
+
namespace: input.namespace,
|
|
239
|
+
...input.resultExtras
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
const submitWorkflowWithRunConfigMaps = (input) => submitWorkflowManifest({
|
|
243
|
+
dependencies: input.dependencies,
|
|
244
|
+
namespace: input.namespace,
|
|
245
|
+
options: input.options,
|
|
246
|
+
resultExtras: input.resultExtras,
|
|
247
|
+
workflow: input.workflow,
|
|
248
|
+
workflowFieldOverrides: input.workflowFieldOverrides
|
|
249
|
+
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
250
|
+
configMapNames: input.configMapNames,
|
|
251
|
+
dependencies: input.dependencies,
|
|
252
|
+
error,
|
|
253
|
+
namespace: input.namespace,
|
|
254
|
+
options: input.options
|
|
255
|
+
})), Effect.flatMap((result) => ownRunConfigMapsBestEffort({
|
|
256
|
+
configMapNames: input.configMapNames,
|
|
257
|
+
dependencies: input.dependencies,
|
|
258
|
+
namespace: input.namespace,
|
|
259
|
+
options: input.options,
|
|
260
|
+
result
|
|
261
|
+
})));
|
|
262
|
+
const buildCommandScheduleYaml = (rawOptions) => {
|
|
263
|
+
const options = parseStrictWithSchema(commandScheduleOptionsSchema, rawOptions);
|
|
264
|
+
const scheduleId = options.scheduleId ?? `custom-${randomHex(8)}`;
|
|
265
|
+
const generatedAt = options.generatedAt ?? DateTime.nowUnsafe();
|
|
266
|
+
const artifact = {
|
|
267
|
+
generated_at: DateTime.formatIso(generatedAt),
|
|
268
|
+
kind: "pipeline-schedule",
|
|
269
|
+
root_workflow: "root",
|
|
270
|
+
schedule_id: scheduleId,
|
|
271
|
+
source_entrypoint: "custom",
|
|
272
|
+
task: options.task,
|
|
273
|
+
version: 1,
|
|
274
|
+
workflows: { root: { nodes: [{
|
|
275
|
+
command: options.command,
|
|
276
|
+
id: "command",
|
|
277
|
+
kind: "command"
|
|
278
|
+
}] } }
|
|
279
|
+
};
|
|
280
|
+
return stringify(appendPullRequestDelivery(options.deliverPullRequest, artifact));
|
|
281
|
+
};
|
|
282
|
+
const normalizeRunnerPayloadForSubmit = (input) => {
|
|
283
|
+
const repository = normalizeRunnerRepositoryForSubmit(input.payload.repository);
|
|
284
|
+
if (repository === input.payload.repository) return input;
|
|
285
|
+
const payload = parseStrictWithSchema(runnerCommandPayloadSchema, {
|
|
286
|
+
...input.payload,
|
|
287
|
+
repository
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
payload,
|
|
291
|
+
payloadJson: encodeRunnerCommandPayloadJson(payload)
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
295
|
+
const { config: _config, ...schemaOptions } = rawOptions;
|
|
296
|
+
const options = parseStrictWithSchema(submitDynamicRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
93
297
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
94
|
-
payload: runnerCommandPayloadSchema
|
|
298
|
+
payload: parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson)),
|
|
95
299
|
payloadJson: options.payloadJson
|
|
96
300
|
});
|
|
97
|
-
|
|
98
|
-
const payloadConfigMapName = `pipeline-payload-${
|
|
99
|
-
const scheduleArtifactConfigMapName = `pipeline-schedule-${randomBytes(6).toString("hex")}`;
|
|
100
|
-
const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomBytes(6).toString("hex")}`;
|
|
101
|
-
if (payload.workflow.id !== compiled.workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'`);
|
|
102
|
-
const graphEffect = compileSubmitArgoGraph(compiled).pipe(Effect.mapError((error) => /* @__PURE__ */ new Error(`Schedule '${compiled.workflowId}' cannot be submitted: ${error.message}`)));
|
|
301
|
+
if (payload.workflow.id !== options.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${options.workflowId}'` }));
|
|
302
|
+
const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
|
|
103
303
|
const labels = {
|
|
104
304
|
"pipeline.oisin.dev/project": payload.run.project,
|
|
105
305
|
"pipeline.oisin.dev/run-id": payload.run.id,
|
|
106
306
|
"pipeline.oisin.dev/source": "argo-workflow",
|
|
107
|
-
"pipeline.oisin.dev/workflow":
|
|
307
|
+
"pipeline.oisin.dev/workflow": options.workflowId
|
|
108
308
|
};
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
309
|
+
const annotations = payload.task.kind === "ticket" ? {
|
|
310
|
+
"pipeline.oisin.dev/ticket-id": payload.task.id,
|
|
311
|
+
"pipeline.oisin.dev/ticket-project": payload.run.project,
|
|
312
|
+
"pipeline.oisin.dev/ticket-title": payload.task.title
|
|
313
|
+
} : {};
|
|
314
|
+
const workflow = buildDynamicRunnerArgoWorkflowManifest({
|
|
115
315
|
activeDeadlineSeconds: options.activeDeadlineSeconds,
|
|
316
|
+
annotations,
|
|
116
317
|
brokerAuth: options.brokerAuth,
|
|
117
318
|
dbAuth: options.dbAuth,
|
|
118
|
-
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
119
319
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
120
320
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
121
321
|
generateName: options.generateName,
|
|
@@ -125,29 +325,23 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
125
325
|
imagePullPolicy: options.imagePullPolicy,
|
|
126
326
|
imagePullSecretName: options.imagePullSecretName,
|
|
127
327
|
labels,
|
|
328
|
+
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
128
329
|
name: options.name,
|
|
129
330
|
namespace: options.namespace,
|
|
130
331
|
npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
|
|
131
332
|
payloadConfigMapName,
|
|
132
|
-
plan: compiled.plan,
|
|
133
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
134
333
|
serviceAccountName: options.serviceAccountName,
|
|
135
|
-
|
|
136
|
-
|
|
334
|
+
ttlStrategy: options.ttlStrategy,
|
|
335
|
+
workflowId: options.workflowId
|
|
137
336
|
});
|
|
138
|
-
return Effect.gen(function* () {
|
|
139
|
-
const graph = yield* graphEffect;
|
|
337
|
+
return Effect.gen(function* effectBody() {
|
|
140
338
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
141
339
|
configMapNames: yield* createRunConfigMaps({
|
|
142
|
-
configMaps:
|
|
340
|
+
configMaps: dynamicRunConfigMaps({
|
|
143
341
|
labels,
|
|
144
342
|
namespace: options.namespace,
|
|
145
343
|
payloadConfigMapName,
|
|
146
|
-
payloadJson
|
|
147
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
148
|
-
scheduleYaml: options.scheduleYaml,
|
|
149
|
-
taskDescriptorConfigMapName,
|
|
150
|
-
tasks: graph.tasks
|
|
344
|
+
payloadJson
|
|
151
345
|
}),
|
|
152
346
|
dependencies,
|
|
153
347
|
namespace: options.namespace,
|
|
@@ -156,37 +350,45 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
156
350
|
dependencies,
|
|
157
351
|
namespace: options.namespace,
|
|
158
352
|
options,
|
|
159
|
-
resultExtras: {
|
|
160
|
-
payloadConfigMapName,
|
|
161
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
162
|
-
taskDescriptorConfigMapName
|
|
163
|
-
},
|
|
353
|
+
resultExtras: { payloadConfigMapName },
|
|
164
354
|
workflow,
|
|
165
355
|
workflowFieldOverrides: { podGC: options.podGC }
|
|
166
356
|
});
|
|
167
357
|
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
358
|
+
};
|
|
359
|
+
const submitDynamicRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies)));
|
|
360
|
+
const compileSubmitArgoGraph = (compiled) => Effect.try({
|
|
361
|
+
catch: (error) => {
|
|
362
|
+
if (error instanceof ArgoGraphCompilerError) return error;
|
|
363
|
+
throw error;
|
|
364
|
+
},
|
|
365
|
+
try: () => compileArgoExecutionGraph(compiled.plan)
|
|
366
|
+
});
|
|
367
|
+
const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
368
|
+
const { config, ...schemaOptions } = rawOptions;
|
|
369
|
+
const options = parseStrictWithSchema(submitRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
172
370
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
173
|
-
payload: runnerCommandPayloadSchema
|
|
371
|
+
payload: parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson)),
|
|
174
372
|
payloadJson: options.payloadJson
|
|
175
373
|
});
|
|
176
|
-
|
|
177
|
-
const payloadConfigMapName = `pipeline-payload-${
|
|
374
|
+
const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
|
|
375
|
+
const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
|
|
376
|
+
const scheduleArtifactConfigMapName = `pipeline-schedule-${randomHex(6)}`;
|
|
377
|
+
const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomHex(6)}`;
|
|
378
|
+
if (payload.workflow.id !== compiled.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'` }));
|
|
379
|
+
const graphEffect = compileSubmitArgoGraph(compiled).pipe(Effect.mapError((error) => new ArgoSubmitError({ message: `Schedule '${compiled.workflowId}' cannot be submitted: ${error.message}` })));
|
|
178
380
|
const labels = {
|
|
179
381
|
"pipeline.oisin.dev/project": payload.run.project,
|
|
180
382
|
"pipeline.oisin.dev/run-id": payload.run.id,
|
|
181
383
|
"pipeline.oisin.dev/source": "argo-workflow",
|
|
182
|
-
"pipeline.oisin.dev/workflow":
|
|
384
|
+
"pipeline.oisin.dev/workflow": compiled.workflowId
|
|
183
385
|
};
|
|
184
386
|
const annotations = payload.task.kind === "ticket" ? {
|
|
185
387
|
"pipeline.oisin.dev/ticket-id": payload.task.id,
|
|
186
388
|
"pipeline.oisin.dev/ticket-project": payload.run.project,
|
|
187
389
|
"pipeline.oisin.dev/ticket-title": payload.task.title
|
|
188
390
|
} : {};
|
|
189
|
-
const workflow =
|
|
391
|
+
const workflow = buildRunnerArgoWorkflowManifest({
|
|
190
392
|
activeDeadlineSeconds: options.activeDeadlineSeconds,
|
|
191
393
|
annotations,
|
|
192
394
|
brokerAuth: options.brokerAuth,
|
|
@@ -199,24 +401,31 @@ function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
199
401
|
image: options.image,
|
|
200
402
|
imagePullPolicy: options.imagePullPolicy,
|
|
201
403
|
imagePullSecretName: options.imagePullSecretName,
|
|
202
|
-
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
203
404
|
labels,
|
|
405
|
+
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
204
406
|
name: options.name,
|
|
205
407
|
namespace: options.namespace,
|
|
206
408
|
npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
|
|
207
409
|
payloadConfigMapName,
|
|
410
|
+
plan: compiled.plan,
|
|
411
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
208
412
|
serviceAccountName: options.serviceAccountName,
|
|
209
|
-
|
|
210
|
-
|
|
413
|
+
taskDescriptorConfigMapName,
|
|
414
|
+
ttlStrategy: options.ttlStrategy
|
|
211
415
|
});
|
|
212
|
-
return Effect.gen(function* () {
|
|
416
|
+
return Effect.gen(function* effectBody() {
|
|
417
|
+
const graph = yield* graphEffect;
|
|
213
418
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
214
419
|
configMapNames: yield* createRunConfigMaps({
|
|
215
|
-
configMaps:
|
|
420
|
+
configMaps: staticRunConfigMaps({
|
|
216
421
|
labels,
|
|
217
422
|
namespace: options.namespace,
|
|
218
423
|
payloadConfigMapName,
|
|
219
|
-
payloadJson
|
|
424
|
+
payloadJson,
|
|
425
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
426
|
+
scheduleYaml: options.scheduleYaml,
|
|
427
|
+
taskDescriptorConfigMapName,
|
|
428
|
+
tasks: graph.tasks
|
|
220
429
|
}),
|
|
221
430
|
dependencies,
|
|
222
431
|
namespace: options.namespace,
|
|
@@ -225,248 +434,16 @@ function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
225
434
|
dependencies,
|
|
226
435
|
namespace: options.namespace,
|
|
227
436
|
options,
|
|
228
|
-
resultExtras: {
|
|
437
|
+
resultExtras: {
|
|
438
|
+
payloadConfigMapName,
|
|
439
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
440
|
+
taskDescriptorConfigMapName
|
|
441
|
+
},
|
|
229
442
|
workflow,
|
|
230
443
|
workflowFieldOverrides: { podGC: options.podGC }
|
|
231
444
|
});
|
|
232
445
|
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return [
|
|
236
|
-
runConfigMap({
|
|
237
|
-
data: { "payload.json": input.payloadJson },
|
|
238
|
-
labels: input.labels,
|
|
239
|
-
name: input.payloadConfigMapName,
|
|
240
|
-
namespace: input.namespace
|
|
241
|
-
}),
|
|
242
|
-
runConfigMap({
|
|
243
|
-
data: Object.fromEntries(input.tasks.map((task) => [`${task.taskName}.json`, `${JSON.stringify(buildRunnerTaskDescriptor(task.nodeId))}\n`])),
|
|
244
|
-
labels: input.labels,
|
|
245
|
-
name: input.taskDescriptorConfigMapName,
|
|
246
|
-
namespace: input.namespace
|
|
247
|
-
}),
|
|
248
|
-
runConfigMap({
|
|
249
|
-
data: { "schedule.yaml": input.scheduleYaml },
|
|
250
|
-
labels: input.labels,
|
|
251
|
-
name: input.scheduleConfigMapName,
|
|
252
|
-
namespace: input.namespace
|
|
253
|
-
})
|
|
254
|
-
];
|
|
255
|
-
}
|
|
256
|
-
function dynamicRunConfigMaps(input) {
|
|
257
|
-
return [runConfigMap({
|
|
258
|
-
data: { "payload.json": input.payloadJson },
|
|
259
|
-
labels: input.labels,
|
|
260
|
-
name: input.payloadConfigMapName,
|
|
261
|
-
namespace: input.namespace
|
|
262
|
-
})];
|
|
263
|
-
}
|
|
264
|
-
function runConfigMap(input) {
|
|
265
|
-
return {
|
|
266
|
-
body: configMapSchema.parse({
|
|
267
|
-
apiVersion: "v1",
|
|
268
|
-
data: input.data,
|
|
269
|
-
kind: "ConfigMap",
|
|
270
|
-
metadata: {
|
|
271
|
-
labels: input.labels,
|
|
272
|
-
name: input.name,
|
|
273
|
-
namespace: input.namespace
|
|
274
|
-
}
|
|
275
|
-
}),
|
|
276
|
-
name: input.name
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function createRunConfigMaps(input) {
|
|
280
|
-
const createdConfigMapNames = [];
|
|
281
|
-
return Effect.gen(function* () {
|
|
282
|
-
const service = yield* KubernetesArgoService;
|
|
283
|
-
yield* Effect.forEach(input.configMaps, (configMap) => service.createConfigMap({
|
|
284
|
-
body: configMap.body,
|
|
285
|
-
dependencies: input.dependencies,
|
|
286
|
-
namespace: input.namespace,
|
|
287
|
-
options: input.options
|
|
288
|
-
}).pipe(Effect.tap(() => Effect.sync(() => {
|
|
289
|
-
createdConfigMapNames.push(configMap.name);
|
|
290
|
-
}))));
|
|
291
|
-
return createdConfigMapNames;
|
|
292
|
-
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
293
|
-
configMapNames: createdConfigMapNames,
|
|
294
|
-
dependencies: input.dependencies,
|
|
295
|
-
error,
|
|
296
|
-
namespace: input.namespace,
|
|
297
|
-
options: input.options
|
|
298
|
-
})));
|
|
299
|
-
}
|
|
300
|
-
function ownRunConfigMaps(input) {
|
|
301
|
-
const ownerReference = workflowOwnerReference(input.result);
|
|
302
|
-
if (ownerReference === void 0) return warnRunConfigMapOwnershipSkipped({
|
|
303
|
-
error: new Error(missingWorkflowUidMessage(input.result)),
|
|
304
|
-
result: input.result
|
|
305
|
-
}).pipe(Effect.as(input.result));
|
|
306
|
-
return patchRunConfigMapOwnerReferences({
|
|
307
|
-
body: configMapOwnerReferencesPatch(ownerReference),
|
|
308
|
-
configMapNames: input.configMapNames,
|
|
309
|
-
dependencies: input.dependencies,
|
|
310
|
-
namespace: input.namespace,
|
|
311
|
-
options: input.options
|
|
312
|
-
}).pipe(Effect.as(input.result));
|
|
313
|
-
}
|
|
314
|
-
function ownRunConfigMapsBestEffort(input) {
|
|
315
|
-
return ownRunConfigMaps(input).pipe(Effect.catch((error) => warnRunConfigMapOwnershipSkipped({
|
|
316
|
-
error,
|
|
317
|
-
result: input.result
|
|
318
|
-
}).pipe(Effect.as(input.result))));
|
|
319
|
-
}
|
|
320
|
-
function warnRunConfigMapOwnershipSkipped(input) {
|
|
321
|
-
return Effect.sync(() => {
|
|
322
|
-
const reason = errorMessage(input.error);
|
|
323
|
-
const message = `moka submit: failed to set Workflow ownerReference for ConfigMaps after Argo Workflow '${input.result.workflowName}' was created; leaving ConfigMaps for TTL/sweeper cleanup: ${reason}\n`;
|
|
324
|
-
process.stderr.write(message);
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
function cleanupRunConfigMapsOnFailure(input) {
|
|
328
|
-
return deleteRunConfigMaps({
|
|
329
|
-
configMapNames: input.configMapNames,
|
|
330
|
-
dependencies: input.dependencies,
|
|
331
|
-
namespace: input.namespace,
|
|
332
|
-
options: input.options
|
|
333
|
-
}).pipe(Effect.catch((cleanupError) => Effect.fail(/* @__PURE__ */ new Error(`Failed to clean up ConfigMaps after submit failure: ${errorMessage(input.error)}; cleanup failed: ${errorMessage(cleanupError)}`))), Effect.flatMap(() => Effect.fail(input.error)));
|
|
334
|
-
}
|
|
335
|
-
function patchRunConfigMapOwnerReferences(input) {
|
|
336
|
-
return Effect.gen(function* () {
|
|
337
|
-
const service = yield* KubernetesArgoService;
|
|
338
|
-
return yield* Effect.all(input.configMapNames.map((name) => service.patchConfigMapOwnerReferences({
|
|
339
|
-
body: input.body,
|
|
340
|
-
dependencies: input.dependencies,
|
|
341
|
-
name,
|
|
342
|
-
namespace: input.namespace,
|
|
343
|
-
options: input.options
|
|
344
|
-
})), { concurrency: "unbounded" });
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
function deleteRunConfigMaps(input) {
|
|
348
|
-
return Effect.gen(function* () {
|
|
349
|
-
const service = yield* KubernetesArgoService;
|
|
350
|
-
return yield* Effect.all(input.configMapNames.map((name) => service.deleteConfigMap({
|
|
351
|
-
dependencies: input.dependencies,
|
|
352
|
-
name,
|
|
353
|
-
namespace: input.namespace,
|
|
354
|
-
options: input.options
|
|
355
|
-
})), { concurrency: "unbounded" });
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
function workflowOwnerReference(result) {
|
|
359
|
-
if (result.workflowUid === void 0) return;
|
|
360
|
-
return workflowOwnerReferenceSchema.parse({
|
|
361
|
-
apiVersion: "argoproj.io/v1alpha1",
|
|
362
|
-
kind: "Workflow",
|
|
363
|
-
name: result.workflowName,
|
|
364
|
-
uid: result.workflowUid
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
function missingWorkflowUidMessage(result) {
|
|
368
|
-
return `Created Argo Workflow '${result.workflowName}' did not include metadata.uid; cannot own ConfigMaps`;
|
|
369
|
-
}
|
|
370
|
-
function configMapOwnerReferencesPatch(ownerReference) {
|
|
371
|
-
return configMapOwnerReferencesPatchSchema.parse({ metadata: { ownerReferences: [ownerReference] } });
|
|
372
|
-
}
|
|
373
|
-
function errorMessage(error) {
|
|
374
|
-
return error instanceof Error ? error.message : String(error);
|
|
375
|
-
}
|
|
376
|
-
function submitWorkflowManifest(input) {
|
|
377
|
-
return Effect.gen(function* () {
|
|
378
|
-
const service = yield* KubernetesArgoService;
|
|
379
|
-
const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
|
|
380
|
-
return workflowSubmitResult(yield* service.createWorkflow({
|
|
381
|
-
body: runnerArgoWorkflowManifestSchema.parse(workflow),
|
|
382
|
-
dependencies: input.dependencies,
|
|
383
|
-
namespace: input.namespace,
|
|
384
|
-
options: input.options
|
|
385
|
-
}), workflow, {
|
|
386
|
-
namespace: input.namespace,
|
|
387
|
-
...input.resultExtras
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
function submitWorkflowWithRunConfigMaps(input) {
|
|
392
|
-
return submitWorkflowManifest({
|
|
393
|
-
dependencies: input.dependencies,
|
|
394
|
-
namespace: input.namespace,
|
|
395
|
-
options: input.options,
|
|
396
|
-
resultExtras: input.resultExtras,
|
|
397
|
-
workflow: input.workflow,
|
|
398
|
-
workflowFieldOverrides: input.workflowFieldOverrides
|
|
399
|
-
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
400
|
-
configMapNames: input.configMapNames,
|
|
401
|
-
dependencies: input.dependencies,
|
|
402
|
-
error,
|
|
403
|
-
namespace: input.namespace,
|
|
404
|
-
options: input.options
|
|
405
|
-
})), Effect.flatMap((result) => ownRunConfigMapsBestEffort({
|
|
406
|
-
configMapNames: input.configMapNames,
|
|
407
|
-
dependencies: input.dependencies,
|
|
408
|
-
namespace: input.namespace,
|
|
409
|
-
options: input.options,
|
|
410
|
-
result
|
|
411
|
-
})));
|
|
412
|
-
}
|
|
413
|
-
function applyWorkflowFieldOverrides(workflow, overrides) {
|
|
414
|
-
if (overrides?.podGC === void 0) return workflow;
|
|
415
|
-
return runnerArgoWorkflowManifestSchema.parse({
|
|
416
|
-
...workflow,
|
|
417
|
-
spec: {
|
|
418
|
-
...workflow.spec,
|
|
419
|
-
podGC: overrides.podGC
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
function workflowSubmitResult(response, workflow, base) {
|
|
424
|
-
const created = createdWorkflowSchema.parse(response);
|
|
425
|
-
return workflowSubmitResultSchema.parse({
|
|
426
|
-
...base,
|
|
427
|
-
workflowName: created.metadata.name ?? workflow.metadata.name,
|
|
428
|
-
workflowUid: created.metadata.uid
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
function buildCommandScheduleYaml(rawOptions) {
|
|
432
|
-
const options = commandScheduleOptionsSchema.parse(rawOptions);
|
|
433
|
-
const scheduleId = options.scheduleId ?? `custom-${randomBytes(8).toString("hex")}`;
|
|
434
|
-
const artifact = {
|
|
435
|
-
generated_at: options.generatedAt.toISOString(),
|
|
436
|
-
kind: "pipeline-schedule",
|
|
437
|
-
root_workflow: "root",
|
|
438
|
-
schedule_id: scheduleId,
|
|
439
|
-
source_entrypoint: "custom",
|
|
440
|
-
task: options.task,
|
|
441
|
-
version: 1,
|
|
442
|
-
workflows: { root: { nodes: [{
|
|
443
|
-
command: options.command,
|
|
444
|
-
id: "command",
|
|
445
|
-
kind: "command"
|
|
446
|
-
}] } }
|
|
447
|
-
};
|
|
448
|
-
return stringify(appendPullRequestDelivery(options.deliverPullRequest, artifact));
|
|
449
|
-
}
|
|
450
|
-
function normalizeRunnerPayloadForSubmit(input) {
|
|
451
|
-
const repository = normalizeRunnerRepositoryForSubmit(input.payload.repository);
|
|
452
|
-
if (repository === input.payload.repository) return input;
|
|
453
|
-
const payload = runnerCommandPayloadSchema.parse({
|
|
454
|
-
...input.payload,
|
|
455
|
-
repository
|
|
456
|
-
});
|
|
457
|
-
return {
|
|
458
|
-
payload,
|
|
459
|
-
payloadJson: JSON.stringify(payload)
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
function compileSubmitArgoGraph(compiled) {
|
|
463
|
-
return Effect.try({
|
|
464
|
-
try: () => compileArgoExecutionGraph(compiled.plan),
|
|
465
|
-
catch: (error) => {
|
|
466
|
-
if (error instanceof ArgoGraphCompilerError) return error;
|
|
467
|
-
throw error;
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
}
|
|
446
|
+
};
|
|
447
|
+
const submitRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitRunnerArgoWorkflowEffect(rawOptions, dependencies)));
|
|
471
448
|
//#endregion
|
|
472
449
|
export { buildCommandScheduleYaml, submitDynamicRunnerArgoWorkflow, submitRunnerArgoWorkflow };
|