@oisincoveney/pipeline 1.27.16 → 1.27.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/argo-submit.d.ts +2 -1
- package/dist/argo-submit.js +3 -1
- package/dist/argo-workflow.d.ts +2 -0
- package/dist/argo-workflow.js +34 -18
- package/dist/config.d.ts +9 -9
- package/dist/hooks.d.ts +1 -1
- package/dist/index.js +22 -12
- package/dist/install-commands.js +3 -2
- package/dist/moka-global-config.d.ts +29 -0
- package/dist/moka-global-config.js +56 -0
- package/dist/moka-submit.d.ts +23 -20
- package/dist/moka-submit.js +30 -19
- package/dist/pipeline-init.js +1 -1
- package/dist/run-state/git-refs.js +97 -22
- package/dist/runner-command-contract.d.ts +2 -2
- package/docs/config-architecture.md +1 -1
- package/docs/operator-guide.md +41 -14
- package/docs/pipeline-console-runner-contract.md +16 -5
- package/package.json +5 -1
package/dist/argo-submit.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
8
8
|
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
9
9
|
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
10
10
|
generateName: z.ZodOptional<z.ZodString>;
|
|
11
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
11
12
|
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
12
13
|
image: z.ZodOptional<z.ZodString>;
|
|
13
14
|
imagePullPolicy: z.ZodOptional<z.ZodEnum<{
|
|
@@ -18,7 +19,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
18
19
|
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
19
20
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
20
21
|
name: z.ZodOptional<z.ZodString>;
|
|
21
|
-
namespace: z.
|
|
22
|
+
namespace: z.ZodString;
|
|
22
23
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
23
24
|
payloadJson: z.ZodString;
|
|
24
25
|
queueName: z.ZodOptional<z.ZodString>;
|
package/dist/argo-submit.js
CHANGED
|
@@ -24,6 +24,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
24
24
|
eventAuthSecretKey: z.string().min(1).optional(),
|
|
25
25
|
eventAuthSecretName: z.string().min(1).optional(),
|
|
26
26
|
generateName: z.string().min(1).optional(),
|
|
27
|
+
gitCredentialsSecretName: z.string().min(1).optional(),
|
|
27
28
|
githubAuthSecretName: z.string().min(1).optional(),
|
|
28
29
|
image: z.string().min(1).optional(),
|
|
29
30
|
imagePullPolicy: z.enum([
|
|
@@ -34,7 +35,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
34
35
|
imagePullSecretName: z.string().min(1).optional(),
|
|
35
36
|
kubeconfigPath: z.string().min(1).optional(),
|
|
36
37
|
name: z.string().min(1).optional(),
|
|
37
|
-
namespace: z.string().min(1)
|
|
38
|
+
namespace: z.string().min(1),
|
|
38
39
|
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
39
40
|
payloadJson: z.string().min(1),
|
|
40
41
|
queueName: z.string().min(1).optional(),
|
|
@@ -72,6 +73,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
|
|
|
72
73
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
73
74
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
74
75
|
generateName: options.generateName,
|
|
76
|
+
gitCredentialsSecretName: options.gitCredentialsSecretName,
|
|
75
77
|
githubAuthSecretName: options.githubAuthSecretName,
|
|
76
78
|
image: options.image,
|
|
77
79
|
imagePullPolicy: options.imagePullPolicy,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
|
|
|
102
102
|
key: z.ZodString;
|
|
103
103
|
path: z.ZodString;
|
|
104
104
|
}, z.core.$strict>>>;
|
|
105
|
+
optional: z.ZodOptional<z.ZodBoolean>;
|
|
105
106
|
secretName: z.ZodString;
|
|
106
107
|
}, z.core.$strict>>;
|
|
107
108
|
}, z.core.$strict>>;
|
|
@@ -112,6 +113,7 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
112
113
|
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
113
114
|
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
114
115
|
generateName: z.ZodOptional<z.ZodString>;
|
|
116
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
115
117
|
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
116
118
|
annotations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
|
|
117
119
|
image: z.ZodDefault<z.ZodString>;
|
package/dist/argo-workflow.js
CHANGED
|
@@ -10,6 +10,7 @@ const RUNNER_WORKFLOW_SERVICE_ACCOUNT = "pipeline-runner";
|
|
|
10
10
|
const RUNNER_WORKFLOW_ENTRYPOINT = "pipeline";
|
|
11
11
|
const RUNNER_WORKFLOW_PAYLOAD_PATH = "/etc/pipeline/payload.json";
|
|
12
12
|
const RUNNER_WORKFLOW_SCHEDULE_PATH = "/etc/pipeline/schedule.yaml";
|
|
13
|
+
const RUNNER_GIT_CREDENTIALS_PATH = "/etc/pipeline/git-credentials";
|
|
13
14
|
const kubernetesNameSchema = z.string().min(1);
|
|
14
15
|
const labelValueSchema = z.string().min(1);
|
|
15
16
|
const stringMapSchema = z.record(z.string().min(1), z.string().min(1));
|
|
@@ -26,6 +27,7 @@ const secretVolumeSchema = z.object({
|
|
|
26
27
|
key: z.string().min(1),
|
|
27
28
|
path: z.string().min(1)
|
|
28
29
|
}).strict()).optional(),
|
|
30
|
+
optional: z.boolean().optional(),
|
|
29
31
|
secretName: kubernetesNameSchema
|
|
30
32
|
}).strict();
|
|
31
33
|
const argoWorkflowVolumeSchema = z.object({
|
|
@@ -110,6 +112,7 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
110
112
|
eventAuthSecretKey: z.string().min(1).optional(),
|
|
111
113
|
eventAuthSecretName: kubernetesNameSchema.optional(),
|
|
112
114
|
generateName: z.string().min(1).optional(),
|
|
115
|
+
gitCredentialsSecretName: kubernetesNameSchema.optional(),
|
|
113
116
|
githubAuthSecretName: kubernetesNameSchema.optional(),
|
|
114
117
|
annotations: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
|
|
115
118
|
image: z.string().min(1).default(RUNNER_WORKFLOW_IMAGE),
|
|
@@ -270,38 +273,51 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
270
273
|
subPath: "auth.json"
|
|
271
274
|
});
|
|
272
275
|
}
|
|
273
|
-
if (options.
|
|
276
|
+
if (options.gitCredentialsSecretName) {
|
|
274
277
|
volumes.push({
|
|
275
|
-
name: "
|
|
278
|
+
name: "runner-git-credentials",
|
|
276
279
|
secret: {
|
|
280
|
+
defaultMode: 256,
|
|
277
281
|
items: [
|
|
278
282
|
{
|
|
279
|
-
key: "
|
|
280
|
-
path: "
|
|
283
|
+
key: "username",
|
|
284
|
+
path: "username"
|
|
281
285
|
},
|
|
282
286
|
{
|
|
283
|
-
key: "
|
|
284
|
-
path: "
|
|
287
|
+
key: "password",
|
|
288
|
+
path: "password"
|
|
285
289
|
},
|
|
286
290
|
{
|
|
287
|
-
key: "
|
|
288
|
-
path: "
|
|
291
|
+
key: "identity",
|
|
292
|
+
path: "identity"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
key: "known_hosts",
|
|
296
|
+
path: "known_hosts"
|
|
289
297
|
}
|
|
290
298
|
],
|
|
291
|
-
|
|
299
|
+
optional: true,
|
|
300
|
+
secretName: options.gitCredentialsSecretName
|
|
292
301
|
}
|
|
293
302
|
});
|
|
294
303
|
volumeMounts.push({
|
|
295
|
-
mountPath:
|
|
296
|
-
name: "
|
|
297
|
-
readOnly: true
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
304
|
+
mountPath: RUNNER_GIT_CREDENTIALS_PATH,
|
|
305
|
+
name: "runner-git-credentials",
|
|
306
|
+
readOnly: true
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
if (options.githubAuthSecretName) {
|
|
310
|
+
volumes.push({
|
|
301
311
|
name: "github-auth",
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
312
|
+
secret: {
|
|
313
|
+
items: [{
|
|
314
|
+
key: "hosts.yml",
|
|
315
|
+
path: "hosts.yml"
|
|
316
|
+
}],
|
|
317
|
+
secretName: options.githubAuthSecretName
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
volumeMounts.push({
|
|
305
321
|
mountPath: "/root/.config/gh/hosts.yml",
|
|
306
322
|
name: "github-auth",
|
|
307
323
|
readOnly: true,
|
package/dist/config.d.ts
CHANGED
|
@@ -362,8 +362,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
362
362
|
policy: z.ZodOptional<z.ZodObject<{
|
|
363
363
|
commands: z.ZodOptional<z.ZodEnum<{
|
|
364
364
|
allow: "allow";
|
|
365
|
-
"trusted-only": "trusted-only";
|
|
366
365
|
deny: "deny";
|
|
366
|
+
"trusted-only": "trusted-only";
|
|
367
367
|
}>>;
|
|
368
368
|
modules: z.ZodOptional<z.ZodEnum<{
|
|
369
369
|
allow: "allow";
|
|
@@ -387,8 +387,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
387
387
|
}, z.core.$strict>>>;
|
|
388
388
|
default_profile: z.ZodOptional<z.ZodString>;
|
|
389
389
|
mode: z.ZodEnum<{
|
|
390
|
-
hosted: "hosted";
|
|
391
390
|
local: "local";
|
|
391
|
+
hosted: "hosted";
|
|
392
392
|
}>;
|
|
393
393
|
provider: z.ZodLiteral<"toolhive">;
|
|
394
394
|
authorization_env: z.ZodDefault<z.ZodString>;
|
|
@@ -431,10 +431,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
431
431
|
}, z.core.$strict>>;
|
|
432
432
|
output: z.ZodOptional<z.ZodObject<{
|
|
433
433
|
format: z.ZodEnum<{
|
|
434
|
+
json_schema: "json_schema";
|
|
434
435
|
text: "text";
|
|
435
436
|
json: "json";
|
|
436
437
|
jsonl: "jsonl";
|
|
437
|
-
json_schema: "json_schema";
|
|
438
438
|
}>;
|
|
439
439
|
repair: z.ZodOptional<z.ZodObject<{
|
|
440
440
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -452,6 +452,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
452
452
|
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
453
453
|
timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
454
454
|
tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
455
|
+
task: "task";
|
|
455
456
|
read: "read";
|
|
456
457
|
list: "list";
|
|
457
458
|
grep: "grep";
|
|
@@ -459,7 +460,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
459
460
|
bash: "bash";
|
|
460
461
|
edit: "edit";
|
|
461
462
|
write: "write";
|
|
462
|
-
task: "task";
|
|
463
463
|
}>>>;
|
|
464
464
|
}, z.core.$strict>>>;
|
|
465
465
|
runner_command: z.ZodDefault<z.ZodObject<{
|
|
@@ -485,8 +485,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
485
485
|
rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
486
486
|
path: z.ZodString;
|
|
487
487
|
source_root: z.ZodDefault<z.ZodEnum<{
|
|
488
|
-
package: "package";
|
|
489
488
|
project: "project";
|
|
489
|
+
package: "package";
|
|
490
490
|
}>>;
|
|
491
491
|
}, z.core.$strict>>>;
|
|
492
492
|
runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
@@ -503,14 +503,15 @@ declare const configSchema: z.ZodObject<{
|
|
|
503
503
|
disabled: "disabled";
|
|
504
504
|
}>>>;
|
|
505
505
|
output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
506
|
+
json_schema: "json_schema";
|
|
506
507
|
text: "text";
|
|
507
508
|
json: "json";
|
|
508
509
|
jsonl: "jsonl";
|
|
509
|
-
json_schema: "json_schema";
|
|
510
510
|
}>>>;
|
|
511
511
|
rules: z.ZodOptional<z.ZodBoolean>;
|
|
512
512
|
skills: z.ZodOptional<z.ZodBoolean>;
|
|
513
513
|
tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
514
|
+
task: "task";
|
|
514
515
|
read: "read";
|
|
515
516
|
list: "list";
|
|
516
517
|
grep: "grep";
|
|
@@ -518,7 +519,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
518
519
|
bash: "bash";
|
|
519
520
|
edit: "edit";
|
|
520
521
|
write: "write";
|
|
521
|
-
task: "task";
|
|
522
522
|
}>>>;
|
|
523
523
|
}, z.core.$strict>;
|
|
524
524
|
command: z.ZodOptional<z.ZodString>;
|
|
@@ -613,8 +613,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
613
613
|
schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
614
614
|
description: z.ZodOptional<z.ZodString>;
|
|
615
615
|
baseline: z.ZodEnum<{
|
|
616
|
-
execute: "execute";
|
|
617
616
|
quick: "quick";
|
|
617
|
+
execute: "execute";
|
|
618
618
|
}>;
|
|
619
619
|
max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
|
|
620
620
|
node_catalog: z.ZodOptional<z.ZodString>;
|
|
@@ -626,8 +626,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
626
626
|
skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
627
627
|
path: z.ZodString;
|
|
628
628
|
source_root: z.ZodDefault<z.ZodEnum<{
|
|
629
|
-
package: "package";
|
|
630
629
|
project: "project";
|
|
630
|
+
package: "package";
|
|
631
631
|
}>>;
|
|
632
632
|
}, z.core.$strict>>>;
|
|
633
633
|
task_context: z.ZodOptional<z.ZodObject<{
|
package/dist/hooks.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ declare const hookResultSchema: z.ZodObject<{
|
|
|
13
13
|
taskContext: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
14
14
|
}, z.core.$strict>>;
|
|
15
15
|
status: z.ZodEnum<{
|
|
16
|
-
fail: "fail";
|
|
17
16
|
pass: "pass";
|
|
17
|
+
fail: "fail";
|
|
18
18
|
skip: "skip";
|
|
19
19
|
}>;
|
|
20
20
|
summary: z.ZodOptional<z.ZodString>;
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { resolvePackageAssetPath } from "./package-assets.js";
|
|
|
10
10
|
import { formatConfigError, runPipelineFromConfig } from "./pipeline-runtime.js";
|
|
11
11
|
import { registerRunnerCommandCommand } from "./commands/runner-command-command.js";
|
|
12
12
|
import { formatInstallCommandsResult, installCommands, parseCommandHost } from "./install-commands.js";
|
|
13
|
+
import { loadMokaGlobalConfig } from "./moka-global-config.js";
|
|
13
14
|
import { submitMoka } from "./moka-submit.js";
|
|
14
15
|
import { formatPipelineInitResult, initPipelineProject } from "./pipeline-init.js";
|
|
15
16
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
@@ -313,31 +314,40 @@ function addMokaSubmitOptions(command) {
|
|
|
313
314
|
}
|
|
314
315
|
function runMokaSubmitFromCli(input, flags) {
|
|
315
316
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
317
|
+
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
318
|
+
const globalConfig = loadMokaGlobalConfig();
|
|
316
319
|
const commonOptions = mokaCommonSubmitOptions({
|
|
317
|
-
config
|
|
320
|
+
config,
|
|
318
321
|
cwd,
|
|
319
|
-
eventUrl: resolveMokaEventUrl(flags),
|
|
320
|
-
flags
|
|
322
|
+
eventUrl: resolveMokaEventUrl(flags, globalConfig),
|
|
323
|
+
flags,
|
|
324
|
+
globalConfig
|
|
321
325
|
});
|
|
322
326
|
if (flags.command) return submitMokaCommandFromCli(input, flags, commonOptions);
|
|
323
327
|
return submitMokaGraphFromCli(input, flags, commonOptions);
|
|
324
328
|
}
|
|
325
|
-
function resolveMokaEventUrl(flags) {
|
|
326
|
-
return flags.eventUrl ??
|
|
329
|
+
function resolveMokaEventUrl(flags, globalConfig) {
|
|
330
|
+
return flags.eventUrl ?? globalConfig?.momokaya.submit.eventUrl;
|
|
327
331
|
}
|
|
328
332
|
function mokaCommonSubmitOptions(input) {
|
|
333
|
+
const momokaya = input.globalConfig?.momokaya;
|
|
329
334
|
return {
|
|
330
335
|
config: input.config,
|
|
331
336
|
eventUrl: input.eventUrl,
|
|
337
|
+
eventAuthSecretKey: momokaya?.submit.eventAuthSecretKey,
|
|
338
|
+
eventAuthSecretName: momokaya?.submit.eventAuthSecretName,
|
|
332
339
|
generateName: input.flags.generateName,
|
|
340
|
+
gitCredentialsSecretName: momokaya?.submit.gitCredentialsSecretName,
|
|
341
|
+
githubAuthSecretName: momokaya?.submit.githubAuthSecretName,
|
|
333
342
|
image: input.flags.image,
|
|
334
343
|
imagePullPolicy: parseImagePullPolicy(input.flags.imagePullPolicy),
|
|
335
|
-
imagePullSecretName: input.flags.imagePullSecret,
|
|
336
|
-
kubeconfigPath: input.flags.kubeconfig,
|
|
344
|
+
imagePullSecretName: input.flags.imagePullSecret ?? momokaya?.submit.imagePullSecretName,
|
|
345
|
+
kubeconfigPath: input.flags.kubeconfig ?? momokaya?.kubernetes.kubeconfig,
|
|
337
346
|
name: input.flags.name,
|
|
338
|
-
namespace: input.flags.namespace,
|
|
339
|
-
|
|
340
|
-
|
|
347
|
+
namespace: input.flags.namespace ?? momokaya?.kubernetes.namespace,
|
|
348
|
+
opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
|
|
349
|
+
queueName: input.flags.queueName ?? momokaya?.submit.queueName,
|
|
350
|
+
serviceAccountName: input.flags.serviceAccount ?? momokaya?.submit.serviceAccountName,
|
|
341
351
|
worktreePath: input.cwd
|
|
342
352
|
};
|
|
343
353
|
}
|
|
@@ -377,7 +387,7 @@ function parseGatewayHostScope(value) {
|
|
|
377
387
|
throw new Error("scope must be project or global");
|
|
378
388
|
}
|
|
379
389
|
function addRunnerArgoOptions(command, options = {}) {
|
|
380
|
-
command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace"
|
|
390
|
+
command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace");
|
|
381
391
|
if (options.kubeconfig) command.option("--kubeconfig <path>", "kubeconfig path");
|
|
382
392
|
return command.option("--queue-name <name>", "Kueue LocalQueue label for Workflow pods").option("--service-account <name>", "Workflow service account").option("--image <image>", "runner image").addOption(new Option("--image-pull-policy <policy>", "runner image pull policy").choices([
|
|
383
393
|
"Always",
|
|
@@ -429,7 +439,7 @@ function lintMissingFileReferences(config, projectRoot) {
|
|
|
429
439
|
}
|
|
430
440
|
function missingFileReferenceMessage(path, value) {
|
|
431
441
|
const base = `${path} references missing file '${value}'`;
|
|
432
|
-
if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`moka init\` to install project-local skills with \`npx --yes skills add
|
|
442
|
+
if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`moka init\` to install project-local skills with \`npx --yes skills add oisin-ee/skills\`.`;
|
|
433
443
|
return base;
|
|
434
444
|
}
|
|
435
445
|
function resolveLintPathReference(projectRoot, ref) {
|
package/dist/install-commands.js
CHANGED
|
@@ -172,7 +172,7 @@ function entrypointDispatchBlock(host, config, id, entrypoint) {
|
|
|
172
172
|
`The schedule policy is \`${entrypoint.schedule}\`.`,
|
|
173
173
|
id === "quick" ? "Run `moka submit --quick <task description>` to submit the graph as an Argo Workflow." : `Run \`moka submit <task description>\` to submit the \`${id}\` graph as an Argo Workflow.`,
|
|
174
174
|
"The pipeline runtime executes as Argo DAG tasks using the package-owned runner image.",
|
|
175
|
-
"
|
|
175
|
+
"Configure the target in `~/.config/moka/config.yaml`; use `--kubeconfig <path>` and `--namespace <namespace>` only for explicit command overrides.",
|
|
176
176
|
"Use `moka submit --schedule <schedule.yaml> <task description>` only when rerunning an existing schedule artifact."
|
|
177
177
|
].join("\n");
|
|
178
178
|
}
|
|
@@ -446,7 +446,8 @@ function selectedHosts(host) {
|
|
|
446
446
|
const GENERATED_RESOURCE_ROOTS = { opencode: [
|
|
447
447
|
".opencode/commands",
|
|
448
448
|
".opencode/agents",
|
|
449
|
-
".opencode/plugins"
|
|
449
|
+
".opencode/plugins",
|
|
450
|
+
".opencode/skills"
|
|
450
451
|
] };
|
|
451
452
|
async function listFiles(root) {
|
|
452
453
|
if (!existsSync(root)) return [];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/moka-global-config.d.ts
|
|
4
|
+
declare const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
|
|
5
|
+
declare const mokaGlobalConfigSchema: z.ZodObject<{
|
|
6
|
+
momokaya: z.ZodObject<{
|
|
7
|
+
kubernetes: z.ZodObject<{
|
|
8
|
+
kubeconfig: z.ZodOptional<z.ZodString>;
|
|
9
|
+
namespace: z.ZodString;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
submit: z.ZodObject<{
|
|
12
|
+
eventAuthSecretKey: z.ZodString;
|
|
13
|
+
eventAuthSecretName: z.ZodString;
|
|
14
|
+
eventUrl: z.ZodString;
|
|
15
|
+
gitCredentialsSecretName: z.ZodString;
|
|
16
|
+
githubAuthSecretName: z.ZodString;
|
|
17
|
+
imagePullSecretName: z.ZodString;
|
|
18
|
+
opencodeAuthSecretName: z.ZodString;
|
|
19
|
+
queueName: z.ZodString;
|
|
20
|
+
serviceAccountName: z.ZodString;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
type MokaGlobalConfig = z.infer<typeof mokaGlobalConfigSchema>;
|
|
25
|
+
declare function mokaGlobalConfigPath(homeDir?: string): string;
|
|
26
|
+
declare function loadMokaGlobalConfig(): MokaGlobalConfig | null;
|
|
27
|
+
declare function parseMokaGlobalConfig(source: string, sourcePath: string): MokaGlobalConfig;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MOKA_GLOBAL_CONFIG_PATH, MokaGlobalConfig, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PipelineConfigError } from "./config.js";
|
|
2
|
+
import { parseDocument } from "yaml";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
//#region src/moka-global-config.ts
|
|
8
|
+
const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
|
|
9
|
+
const mokaSubmitGlobalConfigSchema = z.object({
|
|
10
|
+
eventAuthSecretKey: z.string().min(1),
|
|
11
|
+
eventAuthSecretName: z.string().min(1),
|
|
12
|
+
eventUrl: z.string().url(),
|
|
13
|
+
gitCredentialsSecretName: z.string().min(1),
|
|
14
|
+
githubAuthSecretName: z.string().min(1),
|
|
15
|
+
imagePullSecretName: z.string().min(1),
|
|
16
|
+
opencodeAuthSecretName: z.string().min(1),
|
|
17
|
+
queueName: z.string().min(1),
|
|
18
|
+
serviceAccountName: z.string().min(1)
|
|
19
|
+
}).strict();
|
|
20
|
+
const mokaKubernetesGlobalConfigSchema = z.object({
|
|
21
|
+
kubeconfig: z.string().min(1).optional(),
|
|
22
|
+
namespace: z.string().min(1)
|
|
23
|
+
}).strict();
|
|
24
|
+
const mokaGlobalConfigSchema = z.object({ momokaya: z.object({
|
|
25
|
+
kubernetes: mokaKubernetesGlobalConfigSchema,
|
|
26
|
+
submit: mokaSubmitGlobalConfigSchema
|
|
27
|
+
}).strict() }).strict();
|
|
28
|
+
function mokaGlobalConfigPath(homeDir = homedir()) {
|
|
29
|
+
return join(homeDir, MOKA_GLOBAL_CONFIG_PATH);
|
|
30
|
+
}
|
|
31
|
+
function loadMokaGlobalConfig() {
|
|
32
|
+
const configPath = mokaGlobalConfigPath();
|
|
33
|
+
if (!existsSync(configPath)) return null;
|
|
34
|
+
return parseMokaGlobalConfig(readFileSync(configPath, "utf8"), configPath);
|
|
35
|
+
}
|
|
36
|
+
function parseMokaGlobalConfig(source, sourcePath) {
|
|
37
|
+
const document = parseDocument(source, {
|
|
38
|
+
prettyErrors: false,
|
|
39
|
+
uniqueKeys: true
|
|
40
|
+
});
|
|
41
|
+
if (document.errors.length > 0) throw new PipelineConfigError("PIPELINE_CONFIG_PARSE_ERROR", `Failed to parse ${sourcePath}`, document.errors.map((err) => ({
|
|
42
|
+
message: err.message,
|
|
43
|
+
path: sourcePath
|
|
44
|
+
})));
|
|
45
|
+
const parsed = mokaGlobalConfigSchema.safeParse(document.toJS());
|
|
46
|
+
if (!parsed.success) {
|
|
47
|
+
const issues = parsed.error.issues.map((issue) => ({
|
|
48
|
+
path: issue.path.join("."),
|
|
49
|
+
message: issue.message
|
|
50
|
+
}));
|
|
51
|
+
throw new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", [`Invalid ${sourcePath}:`, ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
|
|
52
|
+
}
|
|
53
|
+
return parsed.data;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { MOKA_GLOBAL_CONFIG_PATH, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
|
package/dist/moka-submit.d.ts
CHANGED
|
@@ -73,16 +73,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
73
73
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
74
74
|
url: z.ZodString;
|
|
75
75
|
}, z.core.$strict>>;
|
|
76
|
-
eventAuthSecretKey: z.
|
|
77
|
-
eventAuthSecretName: z.
|
|
78
|
-
eventUrl: z.
|
|
76
|
+
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
77
|
+
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
78
|
+
eventUrl: z.ZodOptional<z.ZodString>;
|
|
79
79
|
events: z.ZodOptional<z.ZodObject<{
|
|
80
80
|
authHeader: z.ZodDefault<z.ZodString>;
|
|
81
81
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
82
82
|
url: z.ZodString;
|
|
83
83
|
}, z.core.$strict>>;
|
|
84
84
|
generateName: z.ZodOptional<z.ZodString>;
|
|
85
|
-
|
|
85
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
86
|
+
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
86
87
|
hookPolicy: z.ZodOptional<z.ZodObject<{
|
|
87
88
|
allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
88
89
|
allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -142,12 +143,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
142
143
|
IfNotPresent: "IfNotPresent";
|
|
143
144
|
Never: "Never";
|
|
144
145
|
}>>;
|
|
145
|
-
imagePullSecretName: z.
|
|
146
|
+
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
146
147
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
147
148
|
name: z.ZodOptional<z.ZodString>;
|
|
148
|
-
namespace: z.
|
|
149
|
-
opencodeAuthSecretName: z.
|
|
150
|
-
queueName: z.
|
|
149
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
150
|
+
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
151
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
151
152
|
repository: z.ZodOptional<z.ZodObject<{
|
|
152
153
|
baseBranch: z.ZodString;
|
|
153
154
|
sha: z.ZodOptional<z.ZodString>;
|
|
@@ -158,10 +159,10 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
158
159
|
project: z.ZodString;
|
|
159
160
|
requestedBy: z.ZodOptional<z.ZodString>;
|
|
160
161
|
}, z.core.$strict>>;
|
|
161
|
-
serviceAccountName: z.
|
|
162
|
+
serviceAccountName: z.ZodOptional<z.ZodString>;
|
|
162
163
|
mode: z.ZodEnum<{
|
|
163
|
-
quick: "quick";
|
|
164
164
|
full: "full";
|
|
165
|
+
quick: "quick";
|
|
165
166
|
}>;
|
|
166
167
|
schedulePath: z.ZodOptional<z.ZodString>;
|
|
167
168
|
scheduleYaml: z.ZodOptional<z.ZodString>;
|
|
@@ -185,16 +186,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
185
186
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
186
187
|
url: z.ZodString;
|
|
187
188
|
}, z.core.$strict>>;
|
|
188
|
-
eventAuthSecretKey: z.
|
|
189
|
-
eventAuthSecretName: z.
|
|
190
|
-
eventUrl: z.
|
|
189
|
+
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
190
|
+
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
191
|
+
eventUrl: z.ZodOptional<z.ZodString>;
|
|
191
192
|
events: z.ZodOptional<z.ZodObject<{
|
|
192
193
|
authHeader: z.ZodDefault<z.ZodString>;
|
|
193
194
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
194
195
|
url: z.ZodString;
|
|
195
196
|
}, z.core.$strict>>;
|
|
196
197
|
generateName: z.ZodOptional<z.ZodString>;
|
|
197
|
-
|
|
198
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
199
|
+
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
198
200
|
hookPolicy: z.ZodOptional<z.ZodObject<{
|
|
199
201
|
allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
200
202
|
allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -254,12 +256,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
254
256
|
IfNotPresent: "IfNotPresent";
|
|
255
257
|
Never: "Never";
|
|
256
258
|
}>>;
|
|
257
|
-
imagePullSecretName: z.
|
|
259
|
+
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
258
260
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
259
261
|
name: z.ZodOptional<z.ZodString>;
|
|
260
|
-
namespace: z.
|
|
261
|
-
opencodeAuthSecretName: z.
|
|
262
|
-
queueName: z.
|
|
262
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
263
|
+
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
264
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
263
265
|
repository: z.ZodOptional<z.ZodObject<{
|
|
264
266
|
baseBranch: z.ZodString;
|
|
265
267
|
sha: z.ZodOptional<z.ZodString>;
|
|
@@ -270,7 +272,7 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
270
272
|
project: z.ZodString;
|
|
271
273
|
requestedBy: z.ZodOptional<z.ZodString>;
|
|
272
274
|
}, z.core.$strict>>;
|
|
273
|
-
serviceAccountName: z.
|
|
275
|
+
serviceAccountName: z.ZodOptional<z.ZodString>;
|
|
274
276
|
commandArgv: z.ZodArray<z.ZodString>;
|
|
275
277
|
task: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
276
278
|
kind: z.ZodLiteral<"prompt">;
|
|
@@ -315,13 +317,14 @@ interface MokaWorkflowSubmitOptions {
|
|
|
315
317
|
eventAuthSecretKey?: string;
|
|
316
318
|
eventAuthSecretName?: string;
|
|
317
319
|
generateName?: string;
|
|
320
|
+
gitCredentialsSecretName?: string;
|
|
318
321
|
githubAuthSecretName?: string;
|
|
319
322
|
image?: string;
|
|
320
323
|
imagePullPolicy?: "Always" | "IfNotPresent" | "Never";
|
|
321
324
|
imagePullSecretName?: string;
|
|
322
325
|
kubeconfigPath?: string;
|
|
323
326
|
name?: string;
|
|
324
|
-
namespace
|
|
327
|
+
namespace: string;
|
|
325
328
|
opencodeAuthSecretName?: string;
|
|
326
329
|
payloadJson: string;
|
|
327
330
|
queueName?: string;
|
package/dist/moka-submit.js
CHANGED
|
@@ -9,14 +9,6 @@ import parseGitUrl from "git-url-parse";
|
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { simpleGit } from "simple-git";
|
|
11
11
|
//#region src/moka-submit.ts
|
|
12
|
-
const MOMOKAYA_EVENT_AUTH_SECRET_KEY = "OISIN_PIPELINE_EVENT_AUTH_TOKEN";
|
|
13
|
-
const MOMOKAYA_EVENT_AUTH_SECRET_NAME = "pipeline-runner-event-auth";
|
|
14
|
-
const MOMOKAYA_EVENT_URL = "https://pipeline-console.momokaya.ee/api/pipeline/runner-events";
|
|
15
|
-
const MOMOKAYA_GITHUB_AUTH_SECRET_NAME = "oisin-bot-github-auth";
|
|
16
|
-
const MOMOKAYA_IMAGE_PULL_SECRET_NAME = "ghcr-pull-secret";
|
|
17
|
-
const MOMOKAYA_OPENCODE_AUTH_SECRET_NAME = "opencode-auth-1";
|
|
18
|
-
const MOMOKAYA_QUEUE_NAME = "momokaya-pipeline";
|
|
19
|
-
const MOMOKAYA_RUNNER_SERVICE_ACCOUNT_NAME = "pipeline-runner";
|
|
20
12
|
const imagePullPolicySchema = z.enum([
|
|
21
13
|
"Always",
|
|
22
14
|
"IfNotPresent",
|
|
@@ -69,25 +61,26 @@ const mokaSubmitResultSchema = workflowSubmitResultSchema;
|
|
|
69
61
|
const mokaSubmitBaseOptionsSchema = z.object({
|
|
70
62
|
delivery: runnerDeliverySchema.default({ pullRequest: false }),
|
|
71
63
|
eventSink: mokaSubmitEventsSchema.optional(),
|
|
72
|
-
eventAuthSecretKey: z.string().min(1).
|
|
73
|
-
eventAuthSecretName: z.string().min(1).
|
|
74
|
-
eventUrl: z.string().url().
|
|
64
|
+
eventAuthSecretKey: z.string().min(1).optional(),
|
|
65
|
+
eventAuthSecretName: z.string().min(1).optional(),
|
|
66
|
+
eventUrl: z.string().url().optional(),
|
|
75
67
|
events: mokaSubmitEventsSchema.optional(),
|
|
76
68
|
generateName: z.string().min(1).optional(),
|
|
77
|
-
|
|
69
|
+
gitCredentialsSecretName: z.string().min(1).optional(),
|
|
70
|
+
githubAuthSecretName: z.string().min(1).optional(),
|
|
78
71
|
hookPolicy: mokaSubmitHookPolicySchema.optional(),
|
|
79
72
|
hooks: mokaSubmitDirectHooksSchema.optional(),
|
|
80
73
|
image: z.string().min(1).optional(),
|
|
81
74
|
imagePullPolicy: imagePullPolicySchema,
|
|
82
|
-
imagePullSecretName: z.string().min(1).
|
|
75
|
+
imagePullSecretName: z.string().min(1).optional(),
|
|
83
76
|
kubeconfigPath: z.string().min(1).optional(),
|
|
84
77
|
name: z.string().min(1).optional(),
|
|
85
|
-
namespace: z.string().min(1).
|
|
86
|
-
opencodeAuthSecretName: z.string().min(1).
|
|
87
|
-
queueName: z.string().min(1).
|
|
78
|
+
namespace: z.string().min(1).optional(),
|
|
79
|
+
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
80
|
+
queueName: z.string().min(1).optional(),
|
|
88
81
|
repository: runnerRepositoryContextSchema.optional(),
|
|
89
82
|
run: runnerRunIdentitySchema.optional(),
|
|
90
|
-
serviceAccountName: z.string().min(1).
|
|
83
|
+
serviceAccountName: z.string().min(1).optional()
|
|
91
84
|
}).strict();
|
|
92
85
|
const mokaGraphSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
|
|
93
86
|
mode: z.enum(["full", "quick"]),
|
|
@@ -107,6 +100,11 @@ const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOpt
|
|
|
107
100
|
message: "Choose either eventSink or events, not both",
|
|
108
101
|
path: ["eventSink"]
|
|
109
102
|
});
|
|
103
|
+
if (data.eventSink === void 0 && data.events === void 0 && data.eventUrl === void 0) ctx.addIssue({
|
|
104
|
+
code: "custom",
|
|
105
|
+
message: "eventUrl is required unless eventSink or events is provided",
|
|
106
|
+
path: ["eventUrl"]
|
|
107
|
+
});
|
|
110
108
|
});
|
|
111
109
|
const submitHookId = (event) => `moka-submit-${event.replaceAll(".", "-")}`;
|
|
112
110
|
function objectWithoutUndefined(value) {
|
|
@@ -284,18 +282,23 @@ function workflowSubmitOptions(options) {
|
|
|
284
282
|
return {
|
|
285
283
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
286
284
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
285
|
+
gitCredentialsSecretName: options.gitCredentialsSecretName,
|
|
287
286
|
githubAuthSecretName: options.githubAuthSecretName,
|
|
288
287
|
image: options.image,
|
|
289
288
|
imagePullPolicy: options.imagePullPolicy,
|
|
290
289
|
imagePullSecretName: options.imagePullSecretName,
|
|
291
290
|
kubeconfigPath: options.kubeconfigPath,
|
|
292
291
|
name: options.name,
|
|
293
|
-
namespace: options.namespace,
|
|
292
|
+
namespace: requireSubmitOption(options.namespace, "namespace"),
|
|
294
293
|
opencodeAuthSecretName: options.opencodeAuthSecretName,
|
|
295
294
|
queueName: options.queueName,
|
|
296
295
|
serviceAccountName: options.serviceAccountName
|
|
297
296
|
};
|
|
298
297
|
}
|
|
298
|
+
function requireSubmitOption(value, name) {
|
|
299
|
+
if (!value) throw new Error(`${name} is required for moka submit`);
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
299
302
|
function runnerPayloadJson(input) {
|
|
300
303
|
return JSON.stringify(buildRunnerCommandPayload({
|
|
301
304
|
delivery: input.options.delivery,
|
|
@@ -323,6 +326,7 @@ function runnerEvents(options) {
|
|
|
323
326
|
authTokenFile: eventSink.authTokenFile ?? eventAuthTokenFile(options),
|
|
324
327
|
url: eventSink.url
|
|
325
328
|
};
|
|
329
|
+
if (!options.eventUrl) throw new Error("eventUrl is required unless eventSink or events is provided");
|
|
326
330
|
return {
|
|
327
331
|
authHeader: "Authorization",
|
|
328
332
|
authTokenFile: eventAuthTokenFile(options),
|
|
@@ -330,19 +334,23 @@ function runnerEvents(options) {
|
|
|
330
334
|
};
|
|
331
335
|
}
|
|
332
336
|
function eventAuthTokenFile(options) {
|
|
337
|
+
if (!options.eventAuthSecretKey) throw new Error("eventAuthSecretKey is required unless eventSink.authTokenFile is provided");
|
|
333
338
|
return `/etc/pipeline/event-auth/${options.eventAuthSecretKey}`;
|
|
334
339
|
}
|
|
335
340
|
async function resolveSubmissionContext(options, dependencies, runId) {
|
|
336
341
|
const explicitContext = explicitSubmissionContext(options);
|
|
337
342
|
if (explicitContext) return explicitContext;
|
|
338
343
|
const git = await resolveRequiredGit(options, dependencies);
|
|
344
|
+
const repository = repositoryContext(options, git);
|
|
345
|
+
assertRepositoryCredentialConfiguration(options);
|
|
339
346
|
return {
|
|
340
|
-
repository
|
|
347
|
+
repository,
|
|
341
348
|
run: runContext(options, git, runId)
|
|
342
349
|
};
|
|
343
350
|
}
|
|
344
351
|
function explicitSubmissionContext(options) {
|
|
345
352
|
if (!(options.repository && options.run)) return null;
|
|
353
|
+
assertRepositoryCredentialConfiguration(options);
|
|
346
354
|
return {
|
|
347
355
|
repository: options.repository,
|
|
348
356
|
run: options.run
|
|
@@ -359,6 +367,9 @@ function repositoryContext(options, git) {
|
|
|
359
367
|
url: git.url
|
|
360
368
|
};
|
|
361
369
|
}
|
|
370
|
+
function assertRepositoryCredentialConfiguration(options) {
|
|
371
|
+
if (!options.gitCredentialsSecretName) throw new Error("gitCredentialsSecretName is required for runner git clone, fetch, and push operations");
|
|
372
|
+
}
|
|
362
373
|
function runContext(options, git, runId) {
|
|
363
374
|
return options.run ?? {
|
|
364
375
|
id: runId,
|
package/dist/pipeline-init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { installCommands } from "./install-commands.js";
|
|
2
2
|
import { execa } from "execa";
|
|
3
3
|
//#region src/pipeline-init.ts
|
|
4
|
-
const DEFAULT_SKILL_INSTALL_SOURCE = "
|
|
4
|
+
const DEFAULT_SKILL_INSTALL_SOURCE = "oisin-ee/skills";
|
|
5
5
|
const DEFAULT_SKILL_INSTALL_ARGS = [
|
|
6
6
|
"--agent",
|
|
7
7
|
"opencode",
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { chmodSync,
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
//#region src/run-state/git-refs.ts
|
|
7
7
|
const DEFAULT_WORKSPACE_PATH = "/workspace";
|
|
8
|
-
const
|
|
8
|
+
const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
|
|
9
9
|
const WRITABLE_GIT_CREDENTIAL_STORE = resolve(tmpdir(), "pipeline-git-credentials");
|
|
10
|
+
const SCP_LIKE_SSH_REMOTE_RE = /^[^@\s]+@[^:\s]+:.+/u;
|
|
10
11
|
const execGit = promisify(execFile);
|
|
11
|
-
let
|
|
12
|
+
let preparedBasicAuthCredentialStore;
|
|
12
13
|
function runnerGitRefs(payload, nodeId) {
|
|
13
14
|
const prefix = `refs/heads/pipeline/runs/${payload.run.id}/${payload.workflow.id}`;
|
|
14
15
|
return {
|
|
@@ -102,21 +103,19 @@ async function configureGitCommitter(worktreePath, committer) {
|
|
|
102
103
|
]);
|
|
103
104
|
}
|
|
104
105
|
function runnerGitCommandArgs(args) {
|
|
105
|
-
return [...gitCredentialConfigArgs(), ...args];
|
|
106
|
+
return [...gitCredentialConfigArgs(remoteUrlFromGitArgs(args)), ...args];
|
|
106
107
|
}
|
|
107
108
|
async function runGit(cwd, args) {
|
|
109
|
+
const remoteUrl = remoteUrlFromGitArgs(args);
|
|
108
110
|
const { stdout } = await execGit("git", runnerGitCommandArgs(args), {
|
|
109
111
|
cwd,
|
|
110
112
|
encoding: "utf8",
|
|
111
|
-
env:
|
|
112
|
-
...process.env,
|
|
113
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
114
|
-
}
|
|
113
|
+
env: runnerGitEnv(remoteUrl)
|
|
115
114
|
});
|
|
116
115
|
return stdout;
|
|
117
116
|
}
|
|
118
|
-
function gitCredentialConfigArgs() {
|
|
119
|
-
const writablePath = prepareWritableGitCredentialStore();
|
|
117
|
+
function gitCredentialConfigArgs(remoteUrl) {
|
|
118
|
+
const writablePath = prepareWritableGitCredentialStore(remoteUrl);
|
|
120
119
|
if (!writablePath) return [];
|
|
121
120
|
return [
|
|
122
121
|
"-c",
|
|
@@ -125,26 +124,102 @@ function gitCredentialConfigArgs() {
|
|
|
125
124
|
`credential.helper=store --file=${writablePath}`
|
|
126
125
|
];
|
|
127
126
|
}
|
|
128
|
-
function prepareWritableGitCredentialStore() {
|
|
129
|
-
const sourcePath = availableGitCredentialStore();
|
|
130
|
-
if (!sourcePath) return;
|
|
127
|
+
function prepareWritableGitCredentialStore(remoteUrl) {
|
|
131
128
|
const writablePath = writableGitCredentialStore();
|
|
132
|
-
|
|
133
|
-
return writablePath;
|
|
129
|
+
const basicAuth = availableBasicAuthCredentials();
|
|
130
|
+
if (basicAuth) return prepareBasicAuthCredentialStore(basicAuth, writablePath, remoteUrl);
|
|
131
|
+
}
|
|
132
|
+
function availableBasicAuthCredentials() {
|
|
133
|
+
const credentialsDir = gitCredentialsDir();
|
|
134
|
+
const usernamePath = resolve(credentialsDir, "username");
|
|
135
|
+
const passwordPath = resolve(credentialsDir, "password");
|
|
136
|
+
if (!(existsSync(usernamePath) && existsSync(passwordPath))) return;
|
|
137
|
+
return {
|
|
138
|
+
password: readCredentialFile(passwordPath),
|
|
139
|
+
username: readCredentialFile(usernamePath)
|
|
140
|
+
};
|
|
134
141
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
return existsSync(sourcePath) ? sourcePath : void 0;
|
|
142
|
+
function gitCredentialsDir() {
|
|
143
|
+
return process.env.PIPELINE_GIT_CREDENTIALS_DIR ?? DEFAULT_GIT_CREDENTIALS_DIR;
|
|
138
144
|
}
|
|
139
145
|
function writableGitCredentialStore() {
|
|
140
146
|
return process.env.PIPELINE_WRITABLE_GIT_CREDENTIAL_STORE ?? WRITABLE_GIT_CREDENTIAL_STORE;
|
|
141
147
|
}
|
|
142
|
-
function
|
|
143
|
-
if (preparedCredentialStore === writablePath) return;
|
|
148
|
+
function writeGitCredentialStore(credentials, writablePath, host) {
|
|
144
149
|
mkdirSync(dirname(writablePath), { recursive: true });
|
|
145
|
-
|
|
150
|
+
writeFileSync(writablePath, `https://${encodeURIComponent(credentials.username)}:${encodeURIComponent(credentials.password)}@${host}\n`, { mode: 384 });
|
|
146
151
|
chmodSync(writablePath, 384);
|
|
147
|
-
|
|
152
|
+
}
|
|
153
|
+
function prepareBasicAuthCredentialStore(credentials, writablePath, remoteUrl) {
|
|
154
|
+
const host = remoteUrl ? credentialHost(remoteUrl) : void 0;
|
|
155
|
+
if (!host) return existingPreparedBasicAuthCredentialStore(writablePath);
|
|
156
|
+
if (isPreparedBasicAuthCredentialStore(writablePath, host)) return writablePath;
|
|
157
|
+
writeGitCredentialStore(credentials, writablePath, host);
|
|
158
|
+
preparedBasicAuthCredentialStore = {
|
|
159
|
+
host,
|
|
160
|
+
path: writablePath
|
|
161
|
+
};
|
|
162
|
+
return writablePath;
|
|
163
|
+
}
|
|
164
|
+
function existingPreparedBasicAuthCredentialStore(writablePath) {
|
|
165
|
+
return preparedBasicAuthCredentialStore?.path === writablePath ? writablePath : void 0;
|
|
166
|
+
}
|
|
167
|
+
function isPreparedBasicAuthCredentialStore(writablePath, host) {
|
|
168
|
+
return preparedBasicAuthCredentialStore?.path === writablePath && preparedBasicAuthCredentialStore.host === host;
|
|
169
|
+
}
|
|
170
|
+
function runnerGitEnv(remoteUrl) {
|
|
171
|
+
const env = {
|
|
172
|
+
...process.env,
|
|
173
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
174
|
+
};
|
|
175
|
+
if (remoteUrl && isSshRemote(remoteUrl)) {
|
|
176
|
+
const sshCommand = gitSshCommand();
|
|
177
|
+
if (sshCommand) env.GIT_SSH_COMMAND = sshCommand;
|
|
178
|
+
}
|
|
179
|
+
return env;
|
|
180
|
+
}
|
|
181
|
+
function gitSshCommand() {
|
|
182
|
+
const credentialsDir = gitCredentialsDir();
|
|
183
|
+
const identityPath = resolve(credentialsDir, "identity");
|
|
184
|
+
const knownHostsPath = resolve(credentialsDir, "known_hosts");
|
|
185
|
+
if (!(existsSync(identityPath) && existsSync(knownHostsPath))) return;
|
|
186
|
+
chmodSync(identityPath, 256);
|
|
187
|
+
return [
|
|
188
|
+
"ssh",
|
|
189
|
+
"-i",
|
|
190
|
+
shellQuote(identityPath),
|
|
191
|
+
"-o",
|
|
192
|
+
"IdentitiesOnly=yes",
|
|
193
|
+
"-o",
|
|
194
|
+
`UserKnownHostsFile=${shellQuote(knownHostsPath)}`,
|
|
195
|
+
"-o",
|
|
196
|
+
"StrictHostKeyChecking=yes"
|
|
197
|
+
].join(" ");
|
|
198
|
+
}
|
|
199
|
+
function readCredentialFile(path) {
|
|
200
|
+
return readFileSync(path, "utf8").trim();
|
|
201
|
+
}
|
|
202
|
+
function remoteUrlFromGitArgs(args) {
|
|
203
|
+
if (args[0] === "clone") return args.find((arg) => isRemoteUrl(arg));
|
|
204
|
+
}
|
|
205
|
+
function isRemoteUrl(value) {
|
|
206
|
+
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("ssh://") || isScpLikeSshRemote(value);
|
|
207
|
+
}
|
|
208
|
+
function isSshRemote(value) {
|
|
209
|
+
return value.startsWith("ssh://") || isScpLikeSshRemote(value);
|
|
210
|
+
}
|
|
211
|
+
function isScpLikeSshRemote(value) {
|
|
212
|
+
return SCP_LIKE_SSH_REMOTE_RE.test(value);
|
|
213
|
+
}
|
|
214
|
+
function credentialHost(remoteUrl) {
|
|
215
|
+
try {
|
|
216
|
+
return new URL(remoteUrl).host || void 0;
|
|
217
|
+
} catch {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function shellQuote(value) {
|
|
222
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
148
223
|
}
|
|
149
224
|
//#endregion
|
|
150
225
|
export { commitAndPushNodeRef, mergeDependencyRefs, prepareRunnerGitWorkspace, promoteFinalRef };
|
|
@@ -43,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
|
|
|
43
43
|
declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
44
44
|
kind: z.ZodLiteral<"graph">;
|
|
45
45
|
mode: z.ZodEnum<{
|
|
46
|
-
quick: "quick";
|
|
47
46
|
full: "full";
|
|
47
|
+
quick: "quick";
|
|
48
48
|
}>;
|
|
49
49
|
}, z.core.$strict>, z.ZodObject<{
|
|
50
50
|
argv: z.ZodArray<z.ZodString>;
|
|
@@ -104,8 +104,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
|
|
|
104
104
|
submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
105
105
|
kind: z.ZodLiteral<"graph">;
|
|
106
106
|
mode: z.ZodEnum<{
|
|
107
|
-
quick: "quick";
|
|
108
107
|
full: "full";
|
|
108
|
+
quick: "quick";
|
|
109
109
|
}>;
|
|
110
110
|
}, z.core.$strict>, z.ZodObject<{
|
|
111
111
|
argv: z.ZodArray<z.ZodString>;
|
|
@@ -152,7 +152,7 @@ receive explicit grants:
|
|
|
152
152
|
- `output`: text, JSON, JSONL, or JSON Schema output.
|
|
153
153
|
|
|
154
154
|
Default skills resolve from project-installed skill files created by
|
|
155
|
-
`moka init` via `npx --yes skills add
|
|
155
|
+
`moka init` via `npx --yes skills add oisin-ee/skills`:
|
|
156
156
|
|
|
157
157
|
```yaml
|
|
158
158
|
skills:
|
package/docs/operator-guide.md
CHANGED
|
@@ -129,11 +129,27 @@ description and current git context, creates payload/schedule ConfigMaps, and
|
|
|
129
129
|
submits an Argo Workflow that runs the graph as DAG tasks using the package-owned
|
|
130
130
|
runner image.
|
|
131
131
|
|
|
132
|
-
`moka submit`
|
|
133
|
-
|
|
132
|
+
`moka submit` reads the private Momokaya target from
|
|
133
|
+
`~/.config/moka/config.yaml`.
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
momokaya:
|
|
137
|
+
kubernetes:
|
|
138
|
+
kubeconfig: /path/to/cluster.kubeconfig
|
|
139
|
+
namespace: <workflow-namespace>
|
|
140
|
+
submit:
|
|
141
|
+
eventAuthSecretKey: <event-auth-secret-key>
|
|
142
|
+
eventAuthSecretName: <event-auth-secret-name>
|
|
143
|
+
eventUrl: <runner-event-sink-url>
|
|
144
|
+
gitCredentialsSecretName: <git-credentials-secret-name>
|
|
145
|
+
githubAuthSecretName: <github-auth-secret-name>
|
|
146
|
+
imagePullSecretName: <image-pull-secret-name>
|
|
147
|
+
opencodeAuthSecretName: <opencode-auth-secret-name>
|
|
148
|
+
queueName: <local-queue-name>
|
|
149
|
+
serviceAccountName: <runner-service-account-name>
|
|
150
|
+
```
|
|
134
151
|
|
|
135
152
|
```shell
|
|
136
|
-
export PIPELINE_EVENT_URL="https://console.example.com/api/pipeline/runner-events"
|
|
137
153
|
moka submit "fix the login bug" --quick
|
|
138
154
|
moka submit "Implement PIPE-54"
|
|
139
155
|
```
|
|
@@ -142,7 +158,7 @@ For a local cluster, point the same commands at that cluster with
|
|
|
142
158
|
`--kubeconfig <path>` and `--namespace <namespace>`:
|
|
143
159
|
|
|
144
160
|
```shell
|
|
145
|
-
moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace
|
|
161
|
+
moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace <workflow-namespace>
|
|
146
162
|
```
|
|
147
163
|
|
|
148
164
|
There is no separate workstation-local `submit` path; local submission means
|
|
@@ -169,7 +185,7 @@ apiVersion: argoproj.io/v1alpha1
|
|
|
169
185
|
kind: Workflow
|
|
170
186
|
metadata:
|
|
171
187
|
generateName: pipeline-run-alpha-
|
|
172
|
-
namespace:
|
|
188
|
+
namespace: <workflow-namespace>
|
|
173
189
|
spec:
|
|
174
190
|
entrypoint: pipeline
|
|
175
191
|
onExit: pipeline-finalizer
|
|
@@ -211,17 +227,28 @@ Secret volume at the path configured in `events.authTokenFile`.
|
|
|
211
227
|
|
|
212
228
|
Expected namespace resources:
|
|
213
229
|
|
|
214
|
-
- ServiceAccount `
|
|
215
|
-
- Secret `
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
- The ServiceAccount named by `submit.serviceAccountName` with the required RBAC
|
|
231
|
+
- The OpenCode auth Secret named by `submit.opencodeAuthSecretName` with key
|
|
232
|
+
`auth.json`
|
|
233
|
+
- The event auth Secret named by `submit.eventAuthSecretName` with the key named
|
|
234
|
+
by `submit.eventAuthSecretKey`
|
|
235
|
+
- The git credentials Secret named by `submit.gitCredentialsSecretName` using
|
|
236
|
+
the same key conventions as Flux `GitRepository` credentials: `username` and
|
|
237
|
+
`password` for HTTPS remotes, or `identity` and `known_hosts` for SSH remotes
|
|
238
|
+
- The GitHub CLI auth Secret named by `submit.githubAuthSecretName` with key
|
|
239
|
+
`hosts.yml`; this Secret is for `gh` and pull request delivery, not git
|
|
240
|
+
clone/fetch/push authentication
|
|
220
241
|
- A pipeline-console event sink reachable from the pod
|
|
221
242
|
|
|
222
|
-
Credential rotation
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
Credential issuance and rotation are owned by the cluster/infra layer, not by
|
|
244
|
+
runner payloads; existing infra repository scripts can continue to own the
|
|
245
|
+
operator-facing lifecycle. Recommended production setups use External Secrets
|
|
246
|
+
Operator or Secrets Store CSI Driver. For GitHub HTTPS, prefer a GitHub App
|
|
247
|
+
installation token materialized by External Secrets Operator's
|
|
248
|
+
`GithubAccessToken` generator with a refresh interval below the token lifetime,
|
|
249
|
+
then template it as `username`/`password`. For SSH, materialize `identity` and
|
|
250
|
+
`known_hosts` from the external secret manager. `moka submit` references
|
|
251
|
+
configured Secret names; it does not accept per-run secret values.
|
|
225
252
|
|
|
226
253
|
## Payload Contract
|
|
227
254
|
|
|
@@ -43,7 +43,7 @@ validation, tests, and docs.
|
|
|
43
43
|
"events": {
|
|
44
44
|
"url": "https://console.example/api/pipeline/runner-events",
|
|
45
45
|
"authHeader": "Authorization",
|
|
46
|
-
"authTokenFile": "/etc/pipeline/event-auth
|
|
46
|
+
"authTokenFile": "/etc/pipeline/event-auth/<event-auth-secret-key>"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
```
|
|
@@ -160,11 +160,16 @@ environment and mounted secrets.
|
|
|
160
160
|
|
|
161
161
|
Expected runner Job secrets and mounts include:
|
|
162
162
|
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
- The configured OpenCode auth Secret mounted at
|
|
164
|
+
`/root/.local/share/opencode/auth.json`
|
|
165
|
+
- The configured event auth Secret mounted at
|
|
166
|
+
`/etc/pipeline/event-auth/<event-auth-secret-key>`
|
|
166
167
|
- MCP gateway auth Secret mounted at a path referenced by the runner
|
|
167
|
-
-
|
|
168
|
+
- The configured git credentials Secret mounted for `git`; HTTPS remotes use
|
|
169
|
+
`username` and `password`, and SSH remotes use `identity` and `known_hosts`
|
|
170
|
+
following Flux `GitRepository` credential conventions
|
|
171
|
+
- The configured GitHub CLI auth Secret mounted for `gh` when pull request
|
|
172
|
+
delivery is enabled
|
|
168
173
|
|
|
169
174
|
The runner image sets `HOME=/root`; Kubernetes must project each numbered auth
|
|
170
175
|
Secret with the key `auth.json` into the target directory. The runner does not
|
|
@@ -172,6 +177,12 @@ read event auth tokens or payloads from environment variables. No
|
|
|
172
177
|
`OISIN_PIPELINE_RUNNER_PAYLOAD_JSON`, `PIPELINE_EVENT_API_TOKEN`, or
|
|
173
178
|
`OPENCODE_AUTH_JSON` env vars are used.
|
|
174
179
|
|
|
180
|
+
Git credential issuance and rotation are cluster concerns. For GitHub HTTPS,
|
|
181
|
+
operators should prefer GitHub App installation tokens generated by External
|
|
182
|
+
Secrets Operator or equivalent control-plane automation. For SSH, operators
|
|
183
|
+
must provide both the private identity and pinned `known_hosts` data. Runner
|
|
184
|
+
payloads never carry credential values or Secret names.
|
|
185
|
+
|
|
175
186
|
## Boundary
|
|
176
187
|
|
|
177
188
|
The `moka` command is its own user-facing command and runtime. Argo
|
package/package.json
CHANGED
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"types": "./dist/moka-submit.d.ts",
|
|
71
71
|
"import": "./dist/moka-submit.js"
|
|
72
72
|
},
|
|
73
|
+
"./moka-global-config": {
|
|
74
|
+
"types": "./dist/moka-global-config.d.ts",
|
|
75
|
+
"import": "./dist/moka-global-config.js"
|
|
76
|
+
},
|
|
73
77
|
"./planner": {
|
|
74
78
|
"types": "./dist/workflow-planner.d.ts",
|
|
75
79
|
"import": "./dist/workflow-planner.js"
|
|
@@ -118,7 +122,7 @@
|
|
|
118
122
|
"prepack": "bun run build:cli"
|
|
119
123
|
},
|
|
120
124
|
"type": "module",
|
|
121
|
-
"version": "1.27.
|
|
125
|
+
"version": "1.27.18",
|
|
122
126
|
"description": "Config-driven multi-agent pipeline runner for repository work",
|
|
123
127
|
"main": "./dist/index.js",
|
|
124
128
|
"types": "./dist/index.d.ts",
|