@h-rig/bundle-default-lifecycle 0.0.6-alpha.135 → 0.0.6-alpha.137
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/src/cli.d.ts +27 -0
- package/dist/src/cli.js +279 -0
- package/dist/src/defaultPipeline.d.ts +16 -5
- package/dist/src/defaultPipeline.js +52 -15
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +387 -125
- package/dist/src/pipelineCloseout.d.ts +14 -0
- package/dist/src/pipelineCloseout.js +633 -0
- package/dist/src/plugin.d.ts +7 -6
- package/dist/src/plugin.js +191 -11
- package/dist/src/stages/auto-merge.js +0 -1
- package/dist/src/stages/commit.js +0 -1
- package/dist/src/stages/isolation.js +1 -3
- package/dist/src/stages/journal-append.js +1 -3
- package/dist/src/stages/merge-gate.d.ts +23 -0
- package/dist/src/stages/merge-gate.js +22 -3
- package/dist/src/stages/open-pr.js +0 -1
- package/dist/src/stages/push.js +0 -1
- package/dist/src/stages/source-closeout.js +0 -1
- package/dist/src/stages/types.d.ts +0 -2
- package/dist/src/stages/types.js +0 -1
- package/dist/src/stages/validate.js +0 -1
- package/dist/src/stages/verify.js +0 -1
- package/package.json +8 -8
- package/dist/src/stagedCloseout.d.ts +0 -7
- package/dist/src/stagedCloseout.js +0 -325
package/dist/src/index.js
CHANGED
|
@@ -96,6 +96,7 @@ function assertCloseoutShadowEquivalent(input) {
|
|
|
96
96
|
}
|
|
97
97
|
// packages/bundle-default-lifecycle/src/defaultPipeline.ts
|
|
98
98
|
import { resolveKernelStages } from "@rig/kernel/resolver";
|
|
99
|
+
import { createDefaultKernelPlugin } from "@rig/kernel/default-kernel";
|
|
99
100
|
|
|
100
101
|
// packages/bundle-default-lifecycle/src/stages/auto-merge.ts
|
|
101
102
|
import { runRepoDefaultMerge } from "@rig/runtime/control-plane/native/pr-automation";
|
|
@@ -104,7 +105,6 @@ import { runRepoDefaultMerge } from "@rig/runtime/control-plane/native/pr-automa
|
|
|
104
105
|
function defineDefaultLifecycleStage(input) {
|
|
105
106
|
return {
|
|
106
107
|
...input,
|
|
107
|
-
protected: input.protected ?? false,
|
|
108
108
|
priority: input.priority ?? 0
|
|
109
109
|
};
|
|
110
110
|
}
|
|
@@ -138,8 +138,7 @@ var isolationStage = defineDefaultLifecycleStage({
|
|
|
138
138
|
id: "isolation",
|
|
139
139
|
kind: "transform",
|
|
140
140
|
description: "Provision the isolated runtime worktree through the runtime isolation subsystem.",
|
|
141
|
-
calls: ["ensureAgentRuntime"]
|
|
142
|
-
protected: true
|
|
141
|
+
calls: ["ensureAgentRuntime"]
|
|
143
142
|
});
|
|
144
143
|
async function runIsolationStage(input) {
|
|
145
144
|
return await ensureAgentRuntime(input);
|
|
@@ -150,21 +149,37 @@ var journalAppendStage = defineDefaultLifecycleStage({
|
|
|
150
149
|
id: "journal-append",
|
|
151
150
|
kind: "observe",
|
|
152
151
|
description: "Record resolved pipeline and per-stage outcome entries through the kernel journal capability.",
|
|
153
|
-
calls: ["journalCapability.append"]
|
|
154
|
-
protected: true
|
|
152
|
+
calls: ["journalCapability.append"]
|
|
155
153
|
});
|
|
156
154
|
async function runJournalAppendStage(input) {
|
|
157
155
|
await input.journal.append(input.event);
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
// packages/bundle-default-lifecycle/src/stages/merge-gate.ts
|
|
159
|
+
import {
|
|
160
|
+
runStrictPrMergeGate
|
|
161
|
+
} from "@rig/runtime/control-plane/native/pr-review-gate";
|
|
161
162
|
var mergeGateStage = defineDefaultLifecycleStage({
|
|
162
163
|
id: "merge-gate",
|
|
163
164
|
kind: "gate",
|
|
164
165
|
description: "Enforce GitHub review state, required checks, and configured review gates through runtime PR automation.",
|
|
165
|
-
calls: ["runStrictPrMergeGate"]
|
|
166
|
-
protected: true
|
|
166
|
+
calls: ["runStrictPrMergeGate"]
|
|
167
167
|
});
|
|
168
|
+
async function runMergeGateStage(input) {
|
|
169
|
+
return await runStrictPrMergeGate({
|
|
170
|
+
projectRoot: input.projectRoot,
|
|
171
|
+
prUrl: input.prUrl,
|
|
172
|
+
taskId: input.taskId,
|
|
173
|
+
runId: input.runId,
|
|
174
|
+
cycle: input.cycle,
|
|
175
|
+
command: input.command,
|
|
176
|
+
...input.artifactRoot !== undefined ? { artifactRoot: input.artifactRoot } : {},
|
|
177
|
+
...input.allowedFailures !== undefined ? { allowedFailures: input.allowedFailures } : {},
|
|
178
|
+
...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
|
|
179
|
+
...input.final !== undefined ? { final: input.final } : {},
|
|
180
|
+
...input.requireGreptile !== undefined ? { requireGreptile: input.requireGreptile } : {}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
168
183
|
|
|
169
184
|
// packages/bundle-default-lifecycle/src/stages/open-pr.ts
|
|
170
185
|
import { runPrAutomation } from "@rig/runtime/control-plane/native/pr-automation";
|
|
@@ -260,7 +275,21 @@ var DEFAULT_LIFECYCLE_STAGE_IDS = [
|
|
|
260
275
|
"isolation",
|
|
261
276
|
"journal-append"
|
|
262
277
|
];
|
|
263
|
-
var
|
|
278
|
+
var DEFAULT_STAGE_AFTER = {
|
|
279
|
+
verify: ["validate"],
|
|
280
|
+
commit: ["verify"],
|
|
281
|
+
push: ["commit"],
|
|
282
|
+
"open-pr": ["push"],
|
|
283
|
+
"merge-gate": ["open-pr"],
|
|
284
|
+
"auto-merge": ["merge-gate"],
|
|
285
|
+
"source-closeout": ["auto-merge"],
|
|
286
|
+
isolation: ["source-closeout"],
|
|
287
|
+
"journal-append": ["isolation"]
|
|
288
|
+
};
|
|
289
|
+
function withDefaultAnchors(stage) {
|
|
290
|
+
const after = DEFAULT_STAGE_AFTER[stage.id];
|
|
291
|
+
return after ? { ...stage, after } : stage;
|
|
292
|
+
}
|
|
264
293
|
var defaultLifecycleStages = [
|
|
265
294
|
validateStage,
|
|
266
295
|
verifyStage,
|
|
@@ -272,7 +301,7 @@ var defaultLifecycleStages = [
|
|
|
272
301
|
sourceCloseoutStage,
|
|
273
302
|
isolationStage,
|
|
274
303
|
journalAppendStage
|
|
275
|
-
];
|
|
304
|
+
].map(withDefaultAnchors);
|
|
276
305
|
function defaultLifecycleStageById(id) {
|
|
277
306
|
const stage = defaultLifecycleStages.find((candidate) => candidate.id === id);
|
|
278
307
|
if (!stage)
|
|
@@ -280,8 +309,7 @@ function defaultLifecycleStageById(id) {
|
|
|
280
309
|
return stage;
|
|
281
310
|
}
|
|
282
311
|
function resolveDefaultLifecycle(input = {}) {
|
|
283
|
-
|
|
284
|
-
return resolveKernelStages(defaultLifecycleStages, input.mutations ?? [], grants);
|
|
312
|
+
return resolveKernelStages(defaultLifecycleStages, input.mutations ?? []);
|
|
285
313
|
}
|
|
286
314
|
function defaultPipelineShowData(input = {}) {
|
|
287
315
|
const resolved = resolveDefaultLifecycle(input);
|
|
@@ -290,7 +318,6 @@ function defaultPipelineShowData(input = {}) {
|
|
|
290
318
|
index: orderIndex.get(entry.stageId) ?? null,
|
|
291
319
|
id: entry.stageId,
|
|
292
320
|
contributedBy: entry.contributedBy,
|
|
293
|
-
protected: entry.isProtected,
|
|
294
321
|
...entry.removedBy !== undefined ? { removedBy: entry.removedBy } : {},
|
|
295
322
|
...entry.replacedBy !== undefined ? { replacedBy: entry.replacedBy } : {},
|
|
296
323
|
wrappedBy: entry.wrappedBy ?? [],
|
|
@@ -305,17 +332,41 @@ function defaultPipelineShowData(input = {}) {
|
|
|
305
332
|
...resolved.resolvedAt !== undefined ? { resolvedAt: resolved.resolvedAt } : {}
|
|
306
333
|
};
|
|
307
334
|
}
|
|
335
|
+
function defaultKernelStatusData() {
|
|
336
|
+
const plugin = createDefaultKernelPlugin();
|
|
337
|
+
const resolved = resolveDefaultLifecycle();
|
|
338
|
+
const capabilities = {};
|
|
339
|
+
for (const capability of plugin.provides)
|
|
340
|
+
capabilities[capability] = plugin.meta.id;
|
|
341
|
+
return {
|
|
342
|
+
kernelProviderId: plugin.meta.id,
|
|
343
|
+
kernelVersion: plugin.meta.version,
|
|
344
|
+
capabilities,
|
|
345
|
+
pipelineStageCount: resolved.order.length,
|
|
346
|
+
stageOrder: [...resolved.order]
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function formatKernelStatus(data = defaultKernelStatusData()) {
|
|
350
|
+
const lines = [
|
|
351
|
+
`kernel: ${data.kernelProviderId} v${data.kernelVersion}`,
|
|
352
|
+
"capabilities (resolved provider):",
|
|
353
|
+
...Object.entries(data.capabilities).map(([cap, provider]) => ` ${cap.padEnd(14)} ${provider}`),
|
|
354
|
+
`lifecycle stages: mutable`,
|
|
355
|
+
`resolved pipeline (${data.pipelineStageCount} stages): ${data.stageOrder.join(" -> ")}`
|
|
356
|
+
];
|
|
357
|
+
return lines.join(`
|
|
358
|
+
`);
|
|
359
|
+
}
|
|
308
360
|
function formatDefaultPipelineShow(input = {}) {
|
|
309
361
|
const data = defaultPipelineShowData(input);
|
|
310
362
|
const lines = [`${data.title}:`];
|
|
311
363
|
for (const stage of data.stages) {
|
|
312
364
|
if (stage.removedBy) {
|
|
313
|
-
lines.push(` - ${stage.id.padEnd(20)} [${stage.contributedBy}] removed by [${stage.removedBy}]
|
|
365
|
+
lines.push(` - ${stage.id.padEnd(20)} [${stage.contributedBy}] removed by [${stage.removedBy}]`);
|
|
314
366
|
continue;
|
|
315
367
|
}
|
|
316
368
|
const ordinal = stage.index === null ? " -." : `${String(stage.index).padStart(2)}.`;
|
|
317
369
|
const annotations = [
|
|
318
|
-
stage.protected ? "PROTECTED" : "",
|
|
319
370
|
stage.replacedBy ? `replaced by [${stage.replacedBy}]` : "",
|
|
320
371
|
stage.wrappedBy.length > 0 ? `wrapped by [${stage.wrappedBy.join(", ")}]` : ""
|
|
321
372
|
].filter(Boolean);
|
|
@@ -325,12 +376,125 @@ function formatDefaultPipelineShow(input = {}) {
|
|
|
325
376
|
return lines.join(`
|
|
326
377
|
`);
|
|
327
378
|
}
|
|
328
|
-
// packages/bundle-default-lifecycle/src/
|
|
379
|
+
// packages/bundle-default-lifecycle/src/pipelineCloseout.ts
|
|
329
380
|
import { resolve as resolve2 } from "path";
|
|
330
381
|
import { loadConfig } from "@rig/core/load-config";
|
|
382
|
+
import { createPluginHost } from "@rig/core";
|
|
383
|
+
import { createDefaultKernel } from "@rig/kernel/default-kernel";
|
|
331
384
|
import { buildPluginHostContext } from "@rig/runtime/control-plane/plugin-host-context";
|
|
332
|
-
import {
|
|
385
|
+
import {
|
|
386
|
+
CloseoutValidationError
|
|
387
|
+
} from "@rig/runtime/control-plane/native/in-process-closeout";
|
|
333
388
|
import { taskValidate } from "@rig/runtime/control-plane/native/task-ops";
|
|
389
|
+
|
|
390
|
+
// packages/bundle-default-lifecycle/src/plugin.ts
|
|
391
|
+
import { definePlugin } from "@rig/core";
|
|
392
|
+
|
|
393
|
+
// packages/bundle-default-lifecycle/src/cli.ts
|
|
394
|
+
var DEFAULT_PIPELINE_CLI_ID = "default-lifecycle.pipeline";
|
|
395
|
+
var DEFAULT_KERNEL_CLI_ID = "default-lifecycle.kernel";
|
|
396
|
+
function printJson(value) {
|
|
397
|
+
console.log(JSON.stringify(value, null, 2));
|
|
398
|
+
}
|
|
399
|
+
function takeFlag(args, flag) {
|
|
400
|
+
const rest = [...args];
|
|
401
|
+
const index = rest.indexOf(flag);
|
|
402
|
+
if (index < 0)
|
|
403
|
+
return { value: false, rest };
|
|
404
|
+
rest.splice(index, 1);
|
|
405
|
+
return { value: true, rest };
|
|
406
|
+
}
|
|
407
|
+
function requireNoExtraArgs(args, usage) {
|
|
408
|
+
if (args.length > 0)
|
|
409
|
+
throw new Error(`Unexpected argument: ${args[0]}
|
|
410
|
+
Usage: ${usage}`);
|
|
411
|
+
}
|
|
412
|
+
async function executePipeline(context, args) {
|
|
413
|
+
const [first = "show", ...rest] = args;
|
|
414
|
+
const command = first.startsWith("-") ? "show" : first;
|
|
415
|
+
const commandArgs = first.startsWith("-") ? args : rest;
|
|
416
|
+
if (command !== "show")
|
|
417
|
+
throw new Error(`Unknown pipeline command: ${command}`);
|
|
418
|
+
const json = takeFlag(commandArgs, "--json");
|
|
419
|
+
requireNoExtraArgs(json.rest, "rig pipeline show [--json]");
|
|
420
|
+
const details = defaultPipelineShowData();
|
|
421
|
+
if (context.outputMode === "text") {
|
|
422
|
+
if (json.value)
|
|
423
|
+
printJson(details);
|
|
424
|
+
else
|
|
425
|
+
console.log(formatDefaultPipelineShow());
|
|
426
|
+
}
|
|
427
|
+
return { ok: true, group: "pipeline", command: "show", details };
|
|
428
|
+
}
|
|
429
|
+
async function executeKernel(context, args) {
|
|
430
|
+
const [first = "status", ...rest] = args;
|
|
431
|
+
const command = first.startsWith("-") ? "status" : first;
|
|
432
|
+
const commandArgs = first.startsWith("-") ? args : rest;
|
|
433
|
+
if (command !== "status")
|
|
434
|
+
throw new Error(`Unknown kernel command: ${command}`);
|
|
435
|
+
const json = takeFlag(commandArgs, "--json");
|
|
436
|
+
requireNoExtraArgs(json.rest, "rig kernel status [--json]");
|
|
437
|
+
const details = defaultKernelStatusData();
|
|
438
|
+
if (context.outputMode === "text") {
|
|
439
|
+
if (json.value)
|
|
440
|
+
printJson(details);
|
|
441
|
+
else
|
|
442
|
+
console.log(formatKernelStatus(details));
|
|
443
|
+
}
|
|
444
|
+
return { ok: true, group: "kernel", command: "status", details };
|
|
445
|
+
}
|
|
446
|
+
var defaultLifecycleCliCommands = [
|
|
447
|
+
{
|
|
448
|
+
id: DEFAULT_PIPELINE_CLI_ID,
|
|
449
|
+
family: "pipeline",
|
|
450
|
+
command: "rig pipeline show [--json]",
|
|
451
|
+
description: "Show the resolved default lifecycle pipeline.",
|
|
452
|
+
usage: "rig pipeline show [--json]",
|
|
453
|
+
run: executePipeline
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
id: DEFAULT_KERNEL_CLI_ID,
|
|
457
|
+
family: "kernel",
|
|
458
|
+
command: "rig kernel status [--json]",
|
|
459
|
+
description: "Show default kernel provider and lifecycle status.",
|
|
460
|
+
usage: "rig kernel status [--json]",
|
|
461
|
+
run: executeKernel
|
|
462
|
+
}
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
// packages/bundle-default-lifecycle/src/plugin.ts
|
|
466
|
+
var DEFAULT_LIFECYCLE_PLUGIN_ID = "@rig/bundle-default-lifecycle";
|
|
467
|
+
function createDefaultLifecyclePlugin(stages = {}) {
|
|
468
|
+
const plugin = definePlugin({
|
|
469
|
+
name: DEFAULT_LIFECYCLE_PLUGIN_ID,
|
|
470
|
+
version: "0.0.0-alpha.1",
|
|
471
|
+
provides: [],
|
|
472
|
+
contributes: {
|
|
473
|
+
stages: defaultLifecycleStages,
|
|
474
|
+
capabilities: [
|
|
475
|
+
{ id: "default-lifecycle.pipeline", title: "Default lifecycle pipeline", commandId: DEFAULT_PIPELINE_CLI_ID },
|
|
476
|
+
{ id: "default-lifecycle.kernel-status", title: "Default kernel status", commandId: DEFAULT_KERNEL_CLI_ID }
|
|
477
|
+
],
|
|
478
|
+
cliCommands: defaultLifecycleCliCommands.map(({ run: _run, ...metadata }) => metadata)
|
|
479
|
+
}
|
|
480
|
+
}, {
|
|
481
|
+
stages,
|
|
482
|
+
featureCapabilities: [
|
|
483
|
+
{ id: "default-lifecycle.pipeline", title: "Default lifecycle pipeline", commandId: DEFAULT_PIPELINE_CLI_ID },
|
|
484
|
+
{ id: "default-lifecycle.kernel-status", title: "Default kernel status", commandId: DEFAULT_KERNEL_CLI_ID }
|
|
485
|
+
],
|
|
486
|
+
cliCommands: defaultLifecycleCliCommands
|
|
487
|
+
});
|
|
488
|
+
return {
|
|
489
|
+
...plugin,
|
|
490
|
+
meta: { id: DEFAULT_LIFECYCLE_PLUGIN_ID, name: "Default Rig Lifecycle", version: "0.0.0-alpha.1" },
|
|
491
|
+
contributes: { ...plugin.contributes ?? {}, stages: defaultLifecycleStages },
|
|
492
|
+
runtime: { stages }
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
var defaultLifecyclePlugin = createDefaultLifecyclePlugin();
|
|
496
|
+
|
|
497
|
+
// packages/bundle-default-lifecycle/src/pipelineCloseout.ts
|
|
334
498
|
function cleanString(value) {
|
|
335
499
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
336
500
|
}
|
|
@@ -346,9 +510,6 @@ function closeoutOutcome(status) {
|
|
|
346
510
|
return "started";
|
|
347
511
|
}
|
|
348
512
|
}
|
|
349
|
-
function normalizePrStatus(status) {
|
|
350
|
-
return status === "needs_attention" ? "needs-attention" : status;
|
|
351
|
-
}
|
|
352
513
|
async function loadRigAutomationConfig(projectRoot) {
|
|
353
514
|
try {
|
|
354
515
|
return await loadConfig(projectRoot);
|
|
@@ -360,140 +521,233 @@ async function runRigProjectValidation({ projectRoot, taskId }) {
|
|
|
360
521
|
const pluginHostCtx = await buildPluginHostContext(projectRoot);
|
|
361
522
|
return taskValidate(projectRoot, taskId, pluginHostCtx?.validatorRegistry ?? undefined);
|
|
362
523
|
}
|
|
363
|
-
|
|
524
|
+
function shouldAttemptRigMerge(config) {
|
|
525
|
+
const mode = config.merge?.mode;
|
|
526
|
+
return mode !== "off" && mode !== "pr-ready";
|
|
527
|
+
}
|
|
528
|
+
async function loadPluginStageContributions(projectRoot) {
|
|
529
|
+
try {
|
|
530
|
+
const config = await loadConfig(projectRoot);
|
|
531
|
+
const host = createPluginHost(config.plugins ?? []);
|
|
532
|
+
return { executors: host.listStageExecutors(), mutations: host.listStageMutations() };
|
|
533
|
+
} catch {
|
|
534
|
+
return { executors: {}, mutations: [] };
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function runPipelineCloseout(input) {
|
|
364
538
|
const taskId = cleanString(input.taskId);
|
|
365
539
|
if (!taskId) {
|
|
366
|
-
throw new Error("
|
|
540
|
+
throw new Error("Pipeline closeout requires a task id.");
|
|
367
541
|
}
|
|
368
542
|
const loadedConfig = input.config ?? await loadRigAutomationConfig(input.projectRoot);
|
|
369
543
|
const prMode = loadedConfig?.pr?.mode ?? "off";
|
|
370
544
|
const reviewProvider = loadedConfig?.review?.provider ?? "github";
|
|
371
545
|
const effectiveConfig = {
|
|
372
546
|
...loadedConfig ?? {},
|
|
373
|
-
pr: {
|
|
374
|
-
|
|
375
|
-
mode: prMode
|
|
376
|
-
},
|
|
377
|
-
review: {
|
|
378
|
-
...loadedConfig?.review ?? {},
|
|
379
|
-
provider: reviewProvider
|
|
380
|
-
}
|
|
547
|
+
pr: { ...loadedConfig?.pr ?? {}, mode: prMode },
|
|
548
|
+
review: { ...loadedConfig?.review ?? {}, provider: reviewProvider }
|
|
381
549
|
};
|
|
550
|
+
const openOnlyConfig = {
|
|
551
|
+
...effectiveConfig,
|
|
552
|
+
merge: { ...effectiveConfig.merge ?? {}, mode: "pr-ready" }
|
|
553
|
+
};
|
|
554
|
+
const shouldMerge = shouldAttemptRigMerge(effectiveConfig);
|
|
382
555
|
const workspace = input.workspace;
|
|
383
556
|
const artifactRoot = input.artifactRoot ?? resolve2(input.projectRoot, "artifacts", taskId);
|
|
384
|
-
let branch = input.branch;
|
|
385
|
-
let currentPhase = "queued";
|
|
386
557
|
const journal = async (phase, status, detail) => {
|
|
387
|
-
currentPhase = phase;
|
|
388
558
|
await input.journalPhase(phase, closeoutOutcome(status), detail ?? null);
|
|
389
559
|
};
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
560
|
+
if (prMode === "off" || prMode === "ask") {
|
|
561
|
+
const reason = prMode === "ask" ? "PR creation awaits operator approval." : "PR automation disabled.";
|
|
562
|
+
await input.reflect("under_review", reason);
|
|
563
|
+
await journal("completed", "completed", reason);
|
|
564
|
+
return {
|
|
565
|
+
mode: "pipeline",
|
|
566
|
+
pipelineStageIds: [...resolveDefaultLifecycle().order],
|
|
567
|
+
result: { status: "skipped", iterations: 0, feedback: [] }
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
const state = {
|
|
571
|
+
branch: input.branch,
|
|
572
|
+
validationPassed: false,
|
|
573
|
+
committed: false,
|
|
574
|
+
pushed: false,
|
|
575
|
+
prUrl: null,
|
|
576
|
+
prReady: false,
|
|
577
|
+
pr: null,
|
|
578
|
+
gate: null,
|
|
579
|
+
mergeGate: null,
|
|
580
|
+
merged: false,
|
|
581
|
+
iterations: 0,
|
|
582
|
+
feedback: [],
|
|
583
|
+
blockedDetail: null
|
|
393
584
|
};
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
585
|
+
const cont = (ctx2) => ({ kind: "continue", ctx: ctx2 });
|
|
586
|
+
const executors = {
|
|
587
|
+
isolation: (ctx2) => cont(ctx2),
|
|
588
|
+
validate: async (ctx2) => {
|
|
589
|
+
await input.onValidationStart?.();
|
|
590
|
+
await journal("queued", "running", `Validating task ${taskId} before closeout.`);
|
|
591
|
+
let passed = false;
|
|
592
|
+
try {
|
|
593
|
+
passed = await runValidateStage({ projectRoot: input.projectRoot, taskId }, input.runValidation ?? runRigProjectValidation);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
const detail = `Rig validation failed before closeout: ${error instanceof Error ? error.message : String(error)}`;
|
|
596
|
+
await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
|
|
597
|
+
throw new CloseoutValidationError(detail);
|
|
598
|
+
}
|
|
599
|
+
if (!passed) {
|
|
600
|
+
const detail = `Rig validation failed for task ${taskId}; closeout blocked before commit.`;
|
|
601
|
+
await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
|
|
602
|
+
throw new CloseoutValidationError(detail);
|
|
603
|
+
}
|
|
604
|
+
state.validationPassed = true;
|
|
605
|
+
await journal("queued", "completed", `Validation passed for task ${taskId}.`);
|
|
606
|
+
const workspaceBranch = await input.gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: workspace });
|
|
607
|
+
const currentWorkspaceBranch = workspaceBranch.exitCode === 0 ? cleanString(workspaceBranch.stdout) : null;
|
|
608
|
+
if (currentWorkspaceBranch && currentWorkspaceBranch !== "HEAD" && currentWorkspaceBranch !== state.branch) {
|
|
609
|
+
state.branch = currentWorkspaceBranch;
|
|
610
|
+
}
|
|
611
|
+
return cont(ctx2);
|
|
612
|
+
},
|
|
613
|
+
verify: () => state.validationPassed ? { kind: "allow" } : { kind: "block", reason: "validation did not pass" },
|
|
614
|
+
commit: async (ctx2) => {
|
|
615
|
+
await journal("commit", "running", `Committing changes in ${workspace}.`);
|
|
616
|
+
const committed = await runCommitStage({ cwd: workspace, message: `rig: complete task ${taskId}`, command: input.gitCommand });
|
|
617
|
+
state.committed = committed.committed;
|
|
618
|
+
return cont(ctx2);
|
|
619
|
+
},
|
|
620
|
+
push: async (ctx2) => {
|
|
621
|
+
await journal("push", "running", `Pushing branch ${state.branch}.`);
|
|
622
|
+
await runPushStage({ projectRoot: workspace, branch: state.branch, gitCommand: input.gitCommand });
|
|
623
|
+
state.pushed = true;
|
|
624
|
+
return cont(ctx2);
|
|
625
|
+
},
|
|
626
|
+
"open-pr": async (ctx2) => {
|
|
627
|
+
await journal("pr-review-merge", "running", `Opening a pull request for ${state.branch}.`);
|
|
628
|
+
const pr = await runOpenPrStage({
|
|
629
|
+
...input,
|
|
630
|
+
taskId,
|
|
631
|
+
branch: state.branch,
|
|
632
|
+
artifactRoot,
|
|
633
|
+
config: openOnlyConfig,
|
|
634
|
+
sourceTask: { title: cleanString(input.sourceTask?.title) },
|
|
635
|
+
lifecycle: {
|
|
636
|
+
onPrOpened: async ({ prUrl }) => {
|
|
637
|
+
await journal("pr-opened", "running", prUrl);
|
|
638
|
+
await input.reflect("under_review", "Rig opened a pull request for this task.");
|
|
639
|
+
},
|
|
640
|
+
onFeedback: async ({ feedback }) => {
|
|
641
|
+
await input.reflect("ci_fixing", "Rig is fixing CI/review feedback for this task.", { errorText: feedback.join(`
|
|
440
642
|
`) || null });
|
|
441
|
-
|
|
442
|
-
onMergeStarted: async ({ prUrl }) => {
|
|
443
|
-
await journal("merge", "running", prUrl);
|
|
444
|
-
await input.reflect("merging", "Rig is merging the pull request for this task.");
|
|
445
|
-
},
|
|
446
|
-
onMerged: async ({ prUrl }) => {
|
|
447
|
-
await journal("close-source", "running", prUrl);
|
|
643
|
+
}
|
|
448
644
|
}
|
|
645
|
+
});
|
|
646
|
+
state.pr = pr;
|
|
647
|
+
state.prUrl = pr.prUrl ?? null;
|
|
648
|
+
state.prReady = pr.status === "opened" || pr.status === "merged";
|
|
649
|
+
state.iterations = pr.iterations;
|
|
650
|
+
state.feedback = [...pr.actionableFeedback];
|
|
651
|
+
if (pr.status === "needs_attention") {
|
|
652
|
+
state.blockedDetail = pr.actionableFeedback.join(`
|
|
653
|
+
`) || "PR automation did not produce a mergeable PR.";
|
|
654
|
+
}
|
|
655
|
+
return cont(ctx2);
|
|
656
|
+
},
|
|
657
|
+
"merge-gate": async (ctx2) => {
|
|
658
|
+
if (!shouldMerge || !state.prReady || !state.prUrl) {
|
|
659
|
+
state.mergeGate = state.prReady ? "skipped" : null;
|
|
660
|
+
return state.prReady ? cont(ctx2) : { kind: "block", reason: state.blockedDetail ?? "no mergeable PR to gate" };
|
|
661
|
+
}
|
|
662
|
+
const gate = await runMergeGateStage({
|
|
663
|
+
projectRoot: workspace,
|
|
664
|
+
prUrl: state.prUrl,
|
|
665
|
+
taskId,
|
|
666
|
+
runId: input.runId,
|
|
667
|
+
cycle: 1,
|
|
668
|
+
command: input.command,
|
|
669
|
+
artifactRoot,
|
|
670
|
+
final: true,
|
|
671
|
+
...effectiveConfig.merge?.allowedFailures ? { allowedFailures: effectiveConfig.merge.allowedFailures } : {},
|
|
672
|
+
...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
|
|
673
|
+
requireGreptile: reviewProvider === "greptile"
|
|
674
|
+
});
|
|
675
|
+
state.gate = gate;
|
|
676
|
+
if (gate.approved) {
|
|
677
|
+
state.mergeGate = "passed";
|
|
678
|
+
return cont(ctx2);
|
|
679
|
+
}
|
|
680
|
+
state.mergeGate = "blocked";
|
|
681
|
+
const detail = gate.actionableFeedback.join(`
|
|
682
|
+
`) || gate.reasons.join("; ") || "merge gate blocked the PR.";
|
|
683
|
+
state.blockedDetail = detail;
|
|
684
|
+
await input.reflect("needs_attention", "Rig needs operator attention before this task can merge.", { errorText: detail });
|
|
685
|
+
await journal("pr-review-merge", "needs-attention", detail);
|
|
686
|
+
return { kind: "block", reason: detail };
|
|
687
|
+
},
|
|
688
|
+
"auto-merge": async (ctx2) => {
|
|
689
|
+
if (!shouldMerge || state.mergeGate !== "passed" || !state.prUrl || !state.gate) {
|
|
690
|
+
return cont(ctx2);
|
|
449
691
|
}
|
|
450
|
-
|
|
451
|
-
|
|
692
|
+
await journal("merge", "running", state.prUrl);
|
|
693
|
+
await input.reflect("merging", "Rig is merging the pull request for this task.");
|
|
694
|
+
await runAutoMergeStage({ prUrl: state.prUrl, config: effectiveConfig, command: input.command, cwd: workspace, strictGate: state.gate });
|
|
695
|
+
state.merged = true;
|
|
696
|
+
return cont(ctx2);
|
|
697
|
+
},
|
|
698
|
+
"source-closeout": async (ctx2) => {
|
|
699
|
+
if (!state.merged || !state.prUrl)
|
|
700
|
+
return cont(ctx2);
|
|
701
|
+
await journal("close-source", "running", state.prUrl);
|
|
702
|
+
const mergedPr = { ...state.pr ?? { iterations: state.iterations, actionableFeedback: state.feedback }, status: "merged", prUrl: state.prUrl, merged: true };
|
|
452
703
|
await runSourceCloseoutStage({
|
|
453
704
|
projectRoot: input.projectRoot,
|
|
454
705
|
taskId,
|
|
455
706
|
runId: input.runId,
|
|
456
|
-
pr,
|
|
707
|
+
pr: mergedPr,
|
|
457
708
|
...input.sourceTask !== undefined ? { sourceTask: input.sourceTask } : {},
|
|
458
709
|
updateTaskSource: async () => {
|
|
459
710
|
await input.reflect("closed", "Rig merged the pull request and closed this task source.");
|
|
460
711
|
return { updated: true, taskId, status: "closed", source: "runtime", sourceKind: "runtime" };
|
|
461
712
|
}
|
|
462
713
|
});
|
|
463
|
-
return
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
return await complete({ status: "opened", prUrl: pr.prUrl, iterations: pr.iterations, feedback: pr.actionableFeedback }, `PR ready without merge: ${pr.prUrl}`);
|
|
467
|
-
}
|
|
468
|
-
const detail = pr.actionableFeedback.join(`
|
|
469
|
-
`) || "PR automation did not merge the PR.";
|
|
470
|
-
await input.reflect("needs_attention", "Rig needs operator attention before this task can proceed.", { errorText: detail });
|
|
471
|
-
await journal("pr-review-merge", "needs-attention", detail);
|
|
472
|
-
return { status: normalizePrStatus(pr.status), ...pr.prUrl ? { prUrl: pr.prUrl } : {}, iterations: pr.iterations, feedback: pr.actionableFeedback };
|
|
473
|
-
} catch (error) {
|
|
474
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
475
|
-
await journal(currentPhase, "failed", detail);
|
|
476
|
-
throw error;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
async function runStagedCloseout(input) {
|
|
480
|
-
const resolved = resolveDefaultLifecycle();
|
|
481
|
-
const result = await executeStagedCloseout(input);
|
|
482
|
-
return {
|
|
483
|
-
mode: "staged",
|
|
484
|
-
pipelineStageIds: [...resolved.order],
|
|
485
|
-
result
|
|
714
|
+
return cont(ctx2);
|
|
715
|
+
},
|
|
716
|
+
"journal-append": (ctx2) => cont(ctx2)
|
|
486
717
|
};
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
718
|
+
const defaultLifecyclePlugin2 = createDefaultLifecyclePlugin(executors);
|
|
719
|
+
const pluginStages = await loadPluginStageContributions(input.projectRoot);
|
|
720
|
+
const kernel = createDefaultKernel({ ...input.kernelJournal ? { journal: input.kernelJournal } : {}, stageExecutors: { ...defaultLifecyclePlugin2.runtime.stages, ...pluginStages.executors } });
|
|
721
|
+
const resolved = kernel.stageRunner.resolve(defaultLifecyclePlugin2.contributes.stages, pluginStages.mutations);
|
|
722
|
+
const ctx = {
|
|
723
|
+
runId: input.runId,
|
|
724
|
+
taskId,
|
|
725
|
+
state,
|
|
726
|
+
metadata: { projectRoot: input.projectRoot, workspace, closeoutState: state }
|
|
496
727
|
};
|
|
728
|
+
await kernel.stageRunner.runPipeline(input.runId, resolved, ctx);
|
|
729
|
+
const result = mapStateToResult(state);
|
|
730
|
+
if (result.status === "merged" || result.status === "opened") {
|
|
731
|
+
await journal("completed", "completed", result.prUrl ? `${result.status === "merged" ? "PR merged and issue closed" : "PR ready without merge"}: ${result.prUrl}` : result.status);
|
|
732
|
+
}
|
|
733
|
+
return { mode: "pipeline", pipelineStageIds: [...resolved.order], result };
|
|
734
|
+
}
|
|
735
|
+
function mapStateToResult(state) {
|
|
736
|
+
if (state.merged && state.prUrl) {
|
|
737
|
+
return { status: "merged", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
|
|
738
|
+
}
|
|
739
|
+
if (state.mergeGate === "blocked" || state.blockedDetail) {
|
|
740
|
+
return {
|
|
741
|
+
status: "needs-attention",
|
|
742
|
+
...state.prUrl ? { prUrl: state.prUrl } : {},
|
|
743
|
+
iterations: state.iterations,
|
|
744
|
+
feedback: state.feedback
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
if (state.prReady && state.prUrl) {
|
|
748
|
+
return { status: "opened", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
|
|
749
|
+
}
|
|
750
|
+
return { status: "needs-attention", ...state.prUrl ? { prUrl: state.prUrl } : {}, iterations: state.iterations, feedback: state.feedback };
|
|
497
751
|
}
|
|
498
752
|
export {
|
|
499
753
|
verifyStage,
|
|
@@ -502,10 +756,11 @@ export {
|
|
|
502
756
|
snapshotCloseoutArtifacts,
|
|
503
757
|
runVerifyStage,
|
|
504
758
|
runValidateStage,
|
|
505
|
-
runStagedCloseout,
|
|
506
759
|
runSourceCloseoutStage,
|
|
507
760
|
runPushStage,
|
|
761
|
+
runPipelineCloseout,
|
|
508
762
|
runOpenPrStage,
|
|
763
|
+
runMergeGateStage,
|
|
509
764
|
runJournalAppendStage,
|
|
510
765
|
runIsolationStage,
|
|
511
766
|
runCommitStage,
|
|
@@ -516,20 +771,27 @@ export {
|
|
|
516
771
|
mergeGateStage,
|
|
517
772
|
journalAppendStage,
|
|
518
773
|
isolationStage,
|
|
774
|
+
formatKernelStatus,
|
|
519
775
|
formatDefaultPipelineShow,
|
|
776
|
+
executePipeline,
|
|
777
|
+
executeKernel,
|
|
520
778
|
defineDefaultLifecycleStage,
|
|
521
779
|
defaultPipelineShowData,
|
|
522
780
|
defaultLifecycleStages,
|
|
523
781
|
defaultLifecycleStageById,
|
|
782
|
+
defaultLifecyclePlugin,
|
|
783
|
+
defaultLifecycleCliCommands,
|
|
784
|
+
defaultKernelStatusData,
|
|
524
785
|
createDefaultLifecyclePlugin,
|
|
525
786
|
compareCloseoutShadowRuns,
|
|
526
787
|
compareCloseoutEquivalence,
|
|
527
788
|
commitStage,
|
|
528
789
|
autoMergeStage,
|
|
529
790
|
assertCloseoutShadowEquivalent,
|
|
530
|
-
|
|
791
|
+
DEFAULT_PIPELINE_CLI_ID,
|
|
531
792
|
DEFAULT_LIFECYCLE_STAGE_IDS,
|
|
532
793
|
DEFAULT_LIFECYCLE_PLUGIN_ID,
|
|
794
|
+
DEFAULT_KERNEL_CLI_ID,
|
|
533
795
|
CLOSEOUT_EQUIVALENCE_STATE_FILES,
|
|
534
796
|
CLOSEOUT_EQUIVALENCE_ARTIFACTS
|
|
535
797
|
};
|