@oisincoveney/pipeline 3.24.2 → 4.0.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 +14 -2
- package/defaults/pipeline.yaml +23 -5
- package/defaults/profiles.yaml +104 -21
- package/dist/argo-graph.js +90 -88
- package/dist/argo-submit.d.ts +110 -138
- package/dist/argo-submit.js +363 -378
- package/dist/argo-workflow.d.ts +156 -177
- 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 +176 -171
- package/dist/cli/factory-commands.js +13 -13
- 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 +3 -3
- package/dist/cli/program.js +41 -43
- package/dist/cli/run-command.d.ts +1 -1
- package/dist/cli/run-command.js +25 -25
- package/dist/cli/run-commands.js +31 -29
- 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 -3
- package/dist/cli/run-service.js +229 -239
- package/dist/cli/submit-options.js +85 -83
- 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/coordinator-command.js +35 -0
- package/dist/commands/pipeline-command.js +13 -7
- package/dist/commands/runner-command-command.js +39 -24
- package/dist/commands/runner-node-command.js +31 -0
- package/dist/commands/ticket/complete.js +29 -33
- package/dist/commands/ticket/create.js +84 -82
- package/dist/commands/ticket/graph-check.js +11 -11
- package/dist/commands/ticket/next.js +36 -36
- package/dist/commands/ticket/sequence.js +11 -12
- package/dist/commands/ticket/shared.d.ts +5 -2
- 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 -140
- package/dist/config/defaults.js +81 -80
- package/dist/config/lint.js +52 -64
- package/dist/config/load.d.ts +4 -5
- package/dist/config/load.js +64 -74
- package/dist/config/schema/catalog.d.ts +2 -1
- package/dist/config/schema/catalog.js +3 -2
- package/dist/config/schema/mcp.js +46 -52
- package/dist/config/schemas.d.ts +382 -528
- package/dist/config/schemas.js +400 -398
- package/dist/config/validate.d.ts +1 -2
- package/dist/config/validate.js +236 -201
- package/dist/config.d.ts +2 -2
- package/dist/context/repo-map.js +105 -119
- package/dist/coordinator/composition.js +169 -0
- package/dist/coordinator/config.js +71 -0
- package/dist/coordinator/run-owner.js +64 -0
- package/dist/coordinator/wave-advance.js +148 -0
- package/dist/coordinator/wave-scheduler.js +50 -0
- 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 +285 -121
- 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 -26
- package/dist/hooks.js +20 -22
- package/dist/index.d.ts +4 -3
- package/dist/index.js +27 -36
- package/dist/install-commands/claude-code.js +100 -92
- package/dist/install-commands/host-selection.js +3 -6
- package/dist/install-commands/opencode.js +264 -259
- package/dist/install-commands/planner.js +163 -181
- 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.d.ts +0 -1
- package/dist/loop/argo-poll.js +15 -95
- package/dist/loop/backlog-records.js +1 -3
- package/dist/loop/controller-deps.js +150 -161
- package/dist/loop/controller.js +164 -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 +29 -30
- package/dist/loop/merge.js +36 -40
- package/dist/loop/run-terminal-poll.js +69 -0
- package/dist/mcp/gateway-config.js +25 -29
- package/dist/mcp/gateway-doctor.js +77 -119
- 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 +51 -54
- package/dist/mcp/toolhive-vmcp.js +39 -39
- package/dist/model-resolver.js +29 -33
- package/dist/moka-global-config.d.ts +78 -55
- package/dist/moka-global-config.js +93 -92
- package/dist/moka-submit.d.ts +2179 -866
- 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 +15 -14
- package/dist/pipeline-runtime.js +51 -50
- package/dist/planned-node.js +6 -3
- package/dist/planning/compile.d.ts +5 -5
- package/dist/planning/compile.js +140 -139
- package/dist/planning/dependency-refs.js +5 -5
- package/dist/planning/generate.d.ts +1296 -1296
- package/dist/planning/generate.js +307 -314
- package/dist/planning/graph.d.ts +0 -1
- package/dist/planning/graph.js +131 -149
- package/dist/remote/argo/model.d.ts +104 -122
- 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/jobs/execution-identity.js +70 -0
- package/dist/remote/jobs/job-outcome.js +108 -0
- package/dist/remote/jobs/manifest.js +96 -0
- package/dist/remote/jobs/model.d.ts +158 -0
- package/dist/remote/jobs/model.js +162 -0
- package/dist/remote/jobs/node-bootstrap.js +213 -0
- package/dist/remote/jobs/policy.js +123 -0
- package/dist/remote/jobs/reconcile.js +100 -0
- package/dist/remote/jobs/run-spec-client.js +64 -0
- package/dist/remote/jobs/run-spec-path.js +31 -0
- package/dist/remote/jobs/run-spec-server.d.ts +34 -0
- package/dist/remote/jobs/run-spec-server.js +116 -0
- package/dist/remote/jobs/submit.d.ts +23 -0
- package/dist/remote/jobs/submit.js +116 -0
- package/dist/remote/jobs/watch.js +59 -0
- package/dist/remote/submit/argo-submission.d.ts +3 -1
- package/dist/remote/submit/argo-submission.js +53 -58
- package/dist/remote/submit/compilation.d.ts +2 -0
- 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 +6 -0
- package/dist/remote/submit/io.d.ts +7 -1
- package/dist/remote/submit/io.js +40 -43
- package/dist/remote/submit/jobs-submission.d.ts +59 -0
- package/dist/remote/submit/jobs-submission.js +97 -0
- package/dist/remote/submit/jobs-submit-result.d.ts +25 -0
- package/dist/remote/submit/jobs-submit-result.js +34 -0
- package/dist/remote/submit/options.d.ts +49 -0
- package/dist/remote/submit/options.js +27 -0
- package/dist/remote/submit/service.d.ts +40 -1
- package/dist/remote/submit/service.js +67 -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 +52 -101
- package/dist/run-control/contracts.js +83 -69
- package/dist/run-control/detach.js +66 -72
- package/dist/run-control/execution-store.js +0 -0
- 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 +67 -78
- package/dist/run-control/postgres/postgres-run-control-store.js +196 -225
- package/dist/run-control/resume-command.js +34 -41
- package/dist/run-control/run-artifacts-command.js +84 -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 +5 -3
- 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 +86 -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 +78 -68
- package/dist/run-control/workspace.js +2 -2
- package/dist/run-state/git-refs.js +258 -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/environment-commands.js +91 -0
- package/dist/runner-command/finalize.js +174 -153
- package/dist/runner-command/lifecycle-context.js +115 -130
- package/dist/runner-command/lifecycle.js +78 -72
- package/dist/runner-command/pre-schedule.js +203 -223
- package/dist/runner-command/run.js +450 -448
- package/dist/runner-command/schedule-source-options.js +8 -10
- package/dist/runner-command/select-ready-wave.js +39 -30
- package/dist/runner-command/task-descriptor.js +11 -17
- package/dist/runner-command-contract.d.ts +147 -157
- package/dist/runner-command-contract.js +403 -411
- package/dist/runner-event-schema.d.ts +364 -505
- 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 +57 -37
- package/dist/runner.js +176 -127
- package/dist/runtime/actor-ids.d.ts +1 -0
- package/dist/runtime/actor-ids.js +2 -2
- package/dist/runtime/agent-node/agent-node.js +99 -110
- 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 -204
- package/dist/runtime/agent-node/prompt-rendering.js +208 -251
- package/dist/runtime/agent-node/session-execution.js +43 -39
- package/dist/runtime/builtins/builtins.js +218 -284
- package/dist/runtime/changed-files/changed-files.js +29 -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/contracts.d.ts +3 -0
- 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 +139 -164
- package/dist/runtime/durable-store/acquisition.js +5 -3
- package/dist/runtime/durable-store/durable-store.d.ts +3 -1
- package/dist/runtime/durable-store/postgres/client.js +29 -0
- package/dist/runtime/durable-store/postgres/migrate-substrate.js +34 -29
- package/dist/runtime/durable-store/postgres/postgres-store.js +18 -20
- package/dist/runtime/events/events.js +273 -331
- package/dist/runtime/events/index.js +2 -2
- package/dist/runtime/execution-envelope.js +44 -0
- 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 +68 -71
- 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 +2 -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 +36 -35
- package/dist/runtime/hooks/policy.js +32 -29
- package/dist/runtime/hooks/results.js +56 -66
- package/dist/runtime/journal-acquisition.d.ts +3 -2
- package/dist/runtime/journal-acquisition.js +14 -14
- package/dist/runtime/json-validation/json-validation.js +51 -72
- package/dist/runtime/launch-plan-args.js +39 -0
- package/dist/runtime/local-scheduler.js +46 -48
- package/dist/runtime/node-execution.js +376 -445
- package/dist/runtime/node-protocol/node-protocol.js +36 -79
- package/dist/runtime/node-state-store.d.ts +4 -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 +201 -187
- package/dist/runtime/opencode-adapter.js +23 -24
- package/dist/runtime/opencode-agent-name.js +6 -6
- package/dist/runtime/opencode-runtime.js +115 -93
- package/dist/runtime/opencode-server.js +39 -53
- package/dist/runtime/opencode-session-executor.js +312 -344
- package/dist/runtime/parallel-node/parallel-node.js +123 -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.d.ts +2 -0
- package/dist/runtime/run-journal.js +7 -7
- package/dist/runtime/runtime-results.js +54 -69
- package/dist/runtime/scheduled-dependencies.d.ts +2 -1
- package/dist/runtime/scheduled-dependencies.js +61 -75
- package/dist/runtime/scheduler.js +179 -202
- package/dist/runtime/services/agent-node-runtime-service.js +3 -3
- package/dist/runtime/services/backlog-service.d.ts +1 -2
- 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 -3
- package/dist/runtime/services/kubernetes-argo-service.js +90 -84
- package/dist/runtime/services/kubernetes-jobs-read-service.js +115 -0
- package/dist/runtime/services/kubernetes-jobs-service.d.ts +37 -0
- package/dist/runtime/services/kubernetes-jobs-service.js +45 -0
- package/dist/runtime/services/kubernetes-jobs-watch-service.js +61 -0
- package/dist/runtime/services/kubernetes-lease-service.js +21 -0
- package/dist/runtime/services/mcp-gateway-service.js +143 -94
- 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 +32 -39
- package/dist/runtime/step/step-node.js +15 -15
- package/dist/runtime/workflow-execution.js +56 -57
- package/dist/runtime/workflow-lifecycle.js +82 -101
- package/dist/runtime/yeet-event-record.js +104 -0
- package/dist/runtime/yeet-executor.js +184 -0
- package/dist/safe-json.js +16 -16
- package/dist/schedule/backlog-context.js +122 -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 +16 -20
- package/dist/schedule/passes/references.js +18 -22
- package/dist/schedule/prompts.js +135 -141
- package/dist/schedule/scheduling-roles.js +8 -14
- package/dist/schema-boundary.d.ts +4 -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 +52 -61
- package/dist/tickets/backlog-task-store.d.ts +15 -23
- package/dist/tickets/backlog-task-store.js +82 -94
- package/dist/tickets/completion/complete-ticket.js +53 -59
- package/dist/tickets/ticket-graph-dto.d.ts +24 -48
- package/dist/tickets/ticket-graph-dto.js +40 -40
- package/dist/tickets/ticket-graph.d.ts +7 -2
- 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 +32 -41
- package/dist/tickets/ticket-plan.js +97 -77
- package/dist/tickets/ticket-selection.d.ts +3 -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 -10
- package/dist/workflow-submit-contract.js +10 -9
- package/docs/adr-moka-postgres-schema-isolation.md +48 -0
- 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/pipeline-console-runner-contract.md +10 -6
- package/docs/runtime-actor-model.md +18 -18
- package/docs/runtime-test-subsystems.md +200 -0
- package/docs/slash-command-adapter-contract.md +3 -3
- package/package.json +49 -85
- package/dist/config/schema/reference-validation.js +0 -83
package/dist/argo-submit.js
CHANGED
|
@@ -1,121 +1,324 @@
|
|
|
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
|
+
const body = configMapOwnerReferencesPatch(ownerReference.value);
|
|
173
|
+
return patchRunConfigMapOwnerReferences({
|
|
174
|
+
body,
|
|
175
|
+
configMapNames: input.configMapNames,
|
|
176
|
+
dependencies: input.dependencies,
|
|
177
|
+
namespace: input.namespace,
|
|
178
|
+
options: input.options
|
|
179
|
+
}).pipe(Effect.as(input.result));
|
|
180
|
+
};
|
|
181
|
+
const ownRunConfigMapsBestEffort = (input) => ownRunConfigMaps(input).pipe(Effect.catch((error) => warnRunConfigMapOwnershipSkipped({
|
|
182
|
+
error,
|
|
183
|
+
result: input.result
|
|
184
|
+
}).pipe(Effect.as(input.result))));
|
|
185
|
+
const cleanupRunConfigMapsOnFailure = (input) => deleteRunConfigMaps({
|
|
186
|
+
configMapNames: input.configMapNames,
|
|
187
|
+
dependencies: input.dependencies,
|
|
188
|
+
namespace: input.namespace,
|
|
189
|
+
options: input.options
|
|
190
|
+
}).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)));
|
|
191
|
+
const createRunConfigMaps = (input) => {
|
|
192
|
+
const createdConfigMapNames = [];
|
|
193
|
+
return Effect.gen(function* effectBody() {
|
|
194
|
+
const service = yield* KubernetesArgoService;
|
|
195
|
+
yield* Effect.all(input.configMaps.map((configMap) => service.createConfigMap({
|
|
196
|
+
body: configMap.body,
|
|
197
|
+
dependencies: input.dependencies,
|
|
198
|
+
namespace: input.namespace,
|
|
199
|
+
options: input.options
|
|
200
|
+
}).pipe(Effect.tap(() => Effect.sync(() => {
|
|
201
|
+
createdConfigMapNames.push(configMap.name);
|
|
202
|
+
})))), { concurrency: "unbounded" });
|
|
203
|
+
return createdConfigMapNames;
|
|
204
|
+
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
205
|
+
configMapNames: createdConfigMapNames,
|
|
206
|
+
dependencies: input.dependencies,
|
|
207
|
+
error,
|
|
208
|
+
namespace: input.namespace,
|
|
209
|
+
options: input.options
|
|
210
|
+
})));
|
|
211
|
+
};
|
|
212
|
+
const applyWorkflowFieldOverrides = (workflow, overrides = {}) => {
|
|
213
|
+
if (overrides.podGC === void 0) return workflow;
|
|
214
|
+
return parseStrictWithSchema(runnerArgoWorkflowManifestSchema, {
|
|
215
|
+
...workflow,
|
|
216
|
+
spec: {
|
|
217
|
+
...workflow.spec,
|
|
218
|
+
podGC: overrides.podGC
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
const workflowSubmitResult = (response, workflow, base) => {
|
|
223
|
+
const created = parseWithSchema(createdWorkflowSchema, response, { onExcessProperty: "preserve" });
|
|
224
|
+
return parseStrictWithSchema(workflowSubmitResultSchema, {
|
|
225
|
+
...base,
|
|
226
|
+
workflowName: created.metadata.name ?? workflow.metadata.name,
|
|
227
|
+
workflowUid: created.metadata.uid
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
const submitWorkflowManifest = (input) => Effect.gen(function* effectBody() {
|
|
231
|
+
const service = yield* KubernetesArgoService;
|
|
232
|
+
const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
|
|
233
|
+
const response = yield* service.createWorkflow({
|
|
234
|
+
body: parseStrictWithSchema(runnerArgoWorkflowManifestSchema, workflow),
|
|
235
|
+
dependencies: input.dependencies,
|
|
236
|
+
namespace: input.namespace,
|
|
237
|
+
options: input.options
|
|
238
|
+
});
|
|
239
|
+
return workflowSubmitResult(response, workflow, {
|
|
240
|
+
namespace: input.namespace,
|
|
241
|
+
...input.resultExtras
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
const submitWorkflowWithRunConfigMaps = (input) => submitWorkflowManifest({
|
|
245
|
+
dependencies: input.dependencies,
|
|
246
|
+
namespace: input.namespace,
|
|
247
|
+
options: input.options,
|
|
248
|
+
resultExtras: input.resultExtras,
|
|
249
|
+
workflow: input.workflow,
|
|
250
|
+
workflowFieldOverrides: input.workflowFieldOverrides
|
|
251
|
+
}).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
|
|
252
|
+
configMapNames: input.configMapNames,
|
|
253
|
+
dependencies: input.dependencies,
|
|
254
|
+
error,
|
|
255
|
+
namespace: input.namespace,
|
|
256
|
+
options: input.options
|
|
257
|
+
})), Effect.flatMap((result) => ownRunConfigMapsBestEffort({
|
|
258
|
+
configMapNames: input.configMapNames,
|
|
259
|
+
dependencies: input.dependencies,
|
|
260
|
+
namespace: input.namespace,
|
|
261
|
+
options: input.options,
|
|
262
|
+
result
|
|
263
|
+
})));
|
|
264
|
+
const buildCommandScheduleYaml = (rawOptions) => {
|
|
265
|
+
const options = parseStrictWithSchema(commandScheduleOptionsSchema, rawOptions);
|
|
266
|
+
const scheduleId = options.scheduleId ?? `custom-${randomHex(8)}`;
|
|
267
|
+
const generatedAt = options.generatedAt ?? DateTime.nowUnsafe();
|
|
268
|
+
const artifact = {
|
|
269
|
+
generated_at: DateTime.formatIso(generatedAt),
|
|
270
|
+
kind: "pipeline-schedule",
|
|
271
|
+
root_workflow: "root",
|
|
272
|
+
schedule_id: scheduleId,
|
|
273
|
+
source_entrypoint: "custom",
|
|
274
|
+
task: options.task,
|
|
275
|
+
version: 1,
|
|
276
|
+
workflows: { root: { nodes: [{
|
|
277
|
+
command: options.command,
|
|
278
|
+
id: "command",
|
|
279
|
+
kind: "command"
|
|
280
|
+
}] } }
|
|
281
|
+
};
|
|
282
|
+
return stringify(appendPullRequestDelivery(options.deliverPullRequest, artifact));
|
|
283
|
+
};
|
|
284
|
+
const normalizeRunnerPayloadForSubmit = (input) => {
|
|
285
|
+
const repository = normalizeRunnerRepositoryForSubmit(input.payload.repository);
|
|
286
|
+
if (repository === input.payload.repository) return input;
|
|
287
|
+
const payload = parseStrictWithSchema(runnerCommandPayloadSchema, {
|
|
288
|
+
...input.payload,
|
|
289
|
+
repository
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
payload,
|
|
293
|
+
payloadJson: encodeRunnerCommandPayloadJson(payload)
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
297
|
+
const { config: _config, ...schemaOptions } = rawOptions;
|
|
298
|
+
const options = parseStrictWithSchema(submitDynamicRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
299
|
+
const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
|
|
93
300
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
94
|
-
payload:
|
|
301
|
+
payload: parsedPayload,
|
|
95
302
|
payloadJson: options.payloadJson
|
|
96
303
|
});
|
|
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}`)));
|
|
304
|
+
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}'` }));
|
|
305
|
+
const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
|
|
103
306
|
const labels = {
|
|
104
307
|
"pipeline.oisin.dev/project": payload.run.project,
|
|
105
308
|
"pipeline.oisin.dev/run-id": payload.run.id,
|
|
106
309
|
"pipeline.oisin.dev/source": "argo-workflow",
|
|
107
|
-
"pipeline.oisin.dev/workflow":
|
|
310
|
+
"pipeline.oisin.dev/workflow": options.workflowId
|
|
108
311
|
};
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
312
|
+
const annotations = payload.task.kind === "ticket" ? {
|
|
313
|
+
"pipeline.oisin.dev/ticket-id": payload.task.id,
|
|
314
|
+
"pipeline.oisin.dev/ticket-project": payload.run.project,
|
|
315
|
+
"pipeline.oisin.dev/ticket-title": payload.task.title
|
|
316
|
+
} : {};
|
|
317
|
+
const workflow = buildDynamicRunnerArgoWorkflowManifest({
|
|
115
318
|
activeDeadlineSeconds: options.activeDeadlineSeconds,
|
|
319
|
+
annotations,
|
|
116
320
|
brokerAuth: options.brokerAuth,
|
|
117
321
|
dbAuth: options.dbAuth,
|
|
118
|
-
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
119
322
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
120
323
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
121
324
|
generateName: options.generateName,
|
|
@@ -125,68 +328,73 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
125
328
|
imagePullPolicy: options.imagePullPolicy,
|
|
126
329
|
imagePullSecretName: options.imagePullSecretName,
|
|
127
330
|
labels,
|
|
331
|
+
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
128
332
|
name: options.name,
|
|
129
333
|
namespace: options.namespace,
|
|
130
334
|
npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
|
|
131
335
|
payloadConfigMapName,
|
|
132
|
-
plan: compiled.plan,
|
|
133
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
134
336
|
serviceAccountName: options.serviceAccountName,
|
|
135
|
-
|
|
136
|
-
|
|
337
|
+
ttlStrategy: options.ttlStrategy,
|
|
338
|
+
workflowId: options.workflowId
|
|
137
339
|
});
|
|
138
|
-
return Effect.gen(function* () {
|
|
139
|
-
const
|
|
340
|
+
return Effect.gen(function* effectBody() {
|
|
341
|
+
const configMaps = dynamicRunConfigMaps({
|
|
342
|
+
labels,
|
|
343
|
+
namespace: options.namespace,
|
|
344
|
+
payloadConfigMapName,
|
|
345
|
+
payloadJson
|
|
346
|
+
});
|
|
347
|
+
const createdConfigMapNames = yield* createRunConfigMaps({
|
|
348
|
+
configMaps,
|
|
349
|
+
dependencies,
|
|
350
|
+
namespace: options.namespace,
|
|
351
|
+
options
|
|
352
|
+
});
|
|
140
353
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
141
|
-
configMapNames:
|
|
142
|
-
configMaps: staticRunConfigMaps({
|
|
143
|
-
labels,
|
|
144
|
-
namespace: options.namespace,
|
|
145
|
-
payloadConfigMapName,
|
|
146
|
-
payloadJson,
|
|
147
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
148
|
-
scheduleYaml: options.scheduleYaml,
|
|
149
|
-
taskDescriptorConfigMapName,
|
|
150
|
-
tasks: graph.tasks
|
|
151
|
-
}),
|
|
152
|
-
dependencies,
|
|
153
|
-
namespace: options.namespace,
|
|
154
|
-
options
|
|
155
|
-
}),
|
|
354
|
+
configMapNames: createdConfigMapNames,
|
|
156
355
|
dependencies,
|
|
157
356
|
namespace: options.namespace,
|
|
158
357
|
options,
|
|
159
|
-
resultExtras: {
|
|
160
|
-
payloadConfigMapName,
|
|
161
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
162
|
-
taskDescriptorConfigMapName
|
|
163
|
-
},
|
|
358
|
+
resultExtras: { payloadConfigMapName },
|
|
164
359
|
workflow,
|
|
165
360
|
workflowFieldOverrides: { podGC: options.podGC }
|
|
166
361
|
});
|
|
167
362
|
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
363
|
+
};
|
|
364
|
+
const submitDynamicRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies)));
|
|
365
|
+
const compileSubmitArgoGraph = (compiled) => Effect.try({
|
|
366
|
+
catch: (error) => {
|
|
367
|
+
if (error instanceof ArgoGraphCompilerError) return error;
|
|
368
|
+
throw error;
|
|
369
|
+
},
|
|
370
|
+
try: () => compileArgoExecutionGraph(compiled.plan)
|
|
371
|
+
});
|
|
372
|
+
const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
373
|
+
const { config, ...schemaOptions } = rawOptions;
|
|
374
|
+
const options = parseStrictWithSchema(submitRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
375
|
+
const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
|
|
172
376
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
173
|
-
payload:
|
|
377
|
+
payload: parsedPayload,
|
|
174
378
|
payloadJson: options.payloadJson
|
|
175
379
|
});
|
|
176
|
-
|
|
177
|
-
const payloadConfigMapName = `pipeline-payload-${
|
|
380
|
+
const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
|
|
381
|
+
const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
|
|
382
|
+
const scheduleArtifactConfigMapName = `pipeline-schedule-${randomHex(6)}`;
|
|
383
|
+
const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomHex(6)}`;
|
|
384
|
+
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}'` }));
|
|
385
|
+
const graphEffect = compileSubmitArgoGraph(compiled).pipe(Effect.mapError((error) => new ArgoSubmitError({ message: `Schedule '${compiled.workflowId}' cannot be submitted: ${error.message}` })));
|
|
178
386
|
const labels = {
|
|
179
387
|
"pipeline.oisin.dev/project": payload.run.project,
|
|
180
388
|
"pipeline.oisin.dev/run-id": payload.run.id,
|
|
181
389
|
"pipeline.oisin.dev/source": "argo-workflow",
|
|
182
|
-
"pipeline.oisin.dev/workflow":
|
|
390
|
+
"pipeline.oisin.dev/workflow": compiled.workflowId
|
|
183
391
|
};
|
|
184
392
|
const annotations = payload.task.kind === "ticket" ? {
|
|
185
393
|
"pipeline.oisin.dev/ticket-id": payload.task.id,
|
|
186
394
|
"pipeline.oisin.dev/ticket-project": payload.run.project,
|
|
187
395
|
"pipeline.oisin.dev/ticket-title": payload.task.title
|
|
188
396
|
} : {};
|
|
189
|
-
const workflow =
|
|
397
|
+
const workflow = buildRunnerArgoWorkflowManifest({
|
|
190
398
|
activeDeadlineSeconds: options.activeDeadlineSeconds,
|
|
191
399
|
annotations,
|
|
192
400
|
brokerAuth: options.brokerAuth,
|
|
@@ -199,274 +407,51 @@ function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
199
407
|
image: options.image,
|
|
200
408
|
imagePullPolicy: options.imagePullPolicy,
|
|
201
409
|
imagePullSecretName: options.imagePullSecretName,
|
|
202
|
-
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
203
410
|
labels,
|
|
411
|
+
mcpGatewayAuth: options.mcpGatewayAuth,
|
|
204
412
|
name: options.name,
|
|
205
413
|
namespace: options.namespace,
|
|
206
414
|
npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
|
|
207
415
|
payloadConfigMapName,
|
|
416
|
+
plan: compiled.plan,
|
|
417
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
208
418
|
serviceAccountName: options.serviceAccountName,
|
|
209
|
-
|
|
210
|
-
|
|
419
|
+
taskDescriptorConfigMapName,
|
|
420
|
+
ttlStrategy: options.ttlStrategy
|
|
211
421
|
});
|
|
212
|
-
return Effect.gen(function* () {
|
|
422
|
+
return Effect.gen(function* effectBody() {
|
|
423
|
+
const graph = yield* graphEffect;
|
|
424
|
+
const configMaps = staticRunConfigMaps({
|
|
425
|
+
labels,
|
|
426
|
+
namespace: options.namespace,
|
|
427
|
+
payloadConfigMapName,
|
|
428
|
+
payloadJson,
|
|
429
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
430
|
+
scheduleYaml: options.scheduleYaml,
|
|
431
|
+
taskDescriptorConfigMapName,
|
|
432
|
+
tasks: graph.tasks
|
|
433
|
+
});
|
|
434
|
+
const createdConfigMapNames = yield* createRunConfigMaps({
|
|
435
|
+
configMaps,
|
|
436
|
+
dependencies,
|
|
437
|
+
namespace: options.namespace,
|
|
438
|
+
options
|
|
439
|
+
});
|
|
213
440
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
214
|
-
configMapNames:
|
|
215
|
-
configMaps: dynamicRunConfigMaps({
|
|
216
|
-
labels,
|
|
217
|
-
namespace: options.namespace,
|
|
218
|
-
payloadConfigMapName,
|
|
219
|
-
payloadJson
|
|
220
|
-
}),
|
|
221
|
-
dependencies,
|
|
222
|
-
namespace: options.namespace,
|
|
223
|
-
options
|
|
224
|
-
}),
|
|
441
|
+
configMapNames: createdConfigMapNames,
|
|
225
442
|
dependencies,
|
|
226
443
|
namespace: options.namespace,
|
|
227
444
|
options,
|
|
228
|
-
resultExtras: {
|
|
445
|
+
resultExtras: {
|
|
446
|
+
payloadConfigMapName,
|
|
447
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
448
|
+
taskDescriptorConfigMapName
|
|
449
|
+
},
|
|
229
450
|
workflow,
|
|
230
451
|
workflowFieldOverrides: { podGC: options.podGC }
|
|
231
452
|
});
|
|
232
453
|
});
|
|
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
|
-
}
|
|
454
|
+
};
|
|
455
|
+
const submitRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitRunnerArgoWorkflowEffect(rawOptions, dependencies)));
|
|
471
456
|
//#endregion
|
|
472
457
|
export { buildCommandScheduleYaml, submitDynamicRunnerArgoWorkflow, submitRunnerArgoWorkflow };
|