@h-rig/bundle-default-lifecycle 0.0.6-alpha.135 → 0.0.6-alpha.136
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/defaultPipeline.d.ts +17 -1
- package/dist/src/defaultPipeline.js +48 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +267 -101
- package/dist/src/pipelineCloseout.d.ts +17 -0
- package/dist/src/pipelineCloseout.js +465 -0
- package/dist/src/plugin.js +20 -1
- package/dist/src/stages/merge-gate.d.ts +23 -0
- package/dist/src/stages/merge-gate.js +21 -0
- package/package.json +8 -8
- package/dist/src/stagedCloseout.d.ts +0 -7
- package/dist/src/stagedCloseout.js +0 -325
|
@@ -3,7 +3,7 @@ import { type KernelStageResolverResult } from "@rig/kernel/resolver";
|
|
|
3
3
|
import type { DefaultLifecycleStageDescriptor, DefaultLifecycleStageId } from "./stages/types";
|
|
4
4
|
export declare const DEFAULT_LIFECYCLE_STAGE_IDS: readonly ["validate", "verify", "commit", "push", "open-pr", "merge-gate", "auto-merge", "source-closeout", "isolation", "journal-append"];
|
|
5
5
|
export declare const PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS: readonly ["merge-gate", "isolation", "journal-append"];
|
|
6
|
-
export declare const defaultLifecycleStages: readonly [
|
|
6
|
+
export declare const defaultLifecycleStages: readonly DefaultLifecycleStageDescriptor[];
|
|
7
7
|
export type ResolveDefaultLifecycleInput = {
|
|
8
8
|
readonly mutations?: readonly StageMutation[];
|
|
9
9
|
readonly protectedStageGrants?: readonly ProtectedStageGrant[];
|
|
@@ -29,4 +29,20 @@ export type PipelineShowData = {
|
|
|
29
29
|
readonly resolvedAt?: string;
|
|
30
30
|
};
|
|
31
31
|
export declare function defaultPipelineShowData(input?: ResolveDefaultLifecycleInput): PipelineShowData;
|
|
32
|
+
export type KernelStatusData = {
|
|
33
|
+
readonly kernelProviderId: string;
|
|
34
|
+
readonly kernelVersion: string;
|
|
35
|
+
readonly capabilities: Readonly<Record<string, string>>;
|
|
36
|
+
readonly protectedStages: readonly string[];
|
|
37
|
+
readonly pipelineStageCount: number;
|
|
38
|
+
readonly stageOrder: readonly string[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Data backing `rig kernel status`: the resolved provider per kernel capability,
|
|
42
|
+
* the kernel version, the protected-stage set, and the resolved default
|
|
43
|
+
* pipeline. Derived from the default kernel plugin so it reflects what actually
|
|
44
|
+
* boots, not a hardcoded list.
|
|
45
|
+
*/
|
|
46
|
+
export declare function defaultKernelStatusData(): KernelStatusData;
|
|
47
|
+
export declare function formatKernelStatus(data?: KernelStatusData): string;
|
|
32
48
|
export declare function formatDefaultPipelineShow(input?: ResolveDefaultLifecycleInput): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/bundle-default-lifecycle/src/defaultPipeline.ts
|
|
3
3
|
import { resolveKernelStages } from "@rig/kernel/resolver";
|
|
4
|
+
import { createDefaultKernelPlugin } from "@rig/kernel/default-kernel";
|
|
4
5
|
|
|
5
6
|
// packages/bundle-default-lifecycle/src/stages/auto-merge.ts
|
|
6
7
|
import { runRepoDefaultMerge } from "@rig/runtime/control-plane/native/pr-automation";
|
|
@@ -51,6 +52,9 @@ var journalAppendStage = defineDefaultLifecycleStage({
|
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
// packages/bundle-default-lifecycle/src/stages/merge-gate.ts
|
|
55
|
+
import {
|
|
56
|
+
runStrictPrMergeGate
|
|
57
|
+
} from "@rig/runtime/control-plane/native/pr-review-gate";
|
|
54
58
|
var mergeGateStage = defineDefaultLifecycleStage({
|
|
55
59
|
id: "merge-gate",
|
|
56
60
|
kind: "gate",
|
|
@@ -116,6 +120,21 @@ var DEFAULT_LIFECYCLE_STAGE_IDS = [
|
|
|
116
120
|
"journal-append"
|
|
117
121
|
];
|
|
118
122
|
var PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS = ["merge-gate", "isolation", "journal-append"];
|
|
123
|
+
var DEFAULT_STAGE_AFTER = {
|
|
124
|
+
verify: ["validate"],
|
|
125
|
+
commit: ["verify"],
|
|
126
|
+
push: ["commit"],
|
|
127
|
+
"open-pr": ["push"],
|
|
128
|
+
"merge-gate": ["open-pr"],
|
|
129
|
+
"auto-merge": ["merge-gate"],
|
|
130
|
+
"source-closeout": ["auto-merge"],
|
|
131
|
+
isolation: ["source-closeout"],
|
|
132
|
+
"journal-append": ["isolation"]
|
|
133
|
+
};
|
|
134
|
+
function withDefaultAnchors(stage) {
|
|
135
|
+
const after = DEFAULT_STAGE_AFTER[stage.id];
|
|
136
|
+
return after ? { ...stage, after } : stage;
|
|
137
|
+
}
|
|
119
138
|
var defaultLifecycleStages = [
|
|
120
139
|
validateStage,
|
|
121
140
|
verifyStage,
|
|
@@ -127,7 +146,7 @@ var defaultLifecycleStages = [
|
|
|
127
146
|
sourceCloseoutStage,
|
|
128
147
|
isolationStage,
|
|
129
148
|
journalAppendStage
|
|
130
|
-
];
|
|
149
|
+
].map(withDefaultAnchors);
|
|
131
150
|
function defaultLifecycleStageById(id) {
|
|
132
151
|
const stage = defaultLifecycleStages.find((candidate) => candidate.id === id);
|
|
133
152
|
if (!stage)
|
|
@@ -160,6 +179,32 @@ function defaultPipelineShowData(input = {}) {
|
|
|
160
179
|
...resolved.resolvedAt !== undefined ? { resolvedAt: resolved.resolvedAt } : {}
|
|
161
180
|
};
|
|
162
181
|
}
|
|
182
|
+
function defaultKernelStatusData() {
|
|
183
|
+
const plugin = createDefaultKernelPlugin();
|
|
184
|
+
const resolved = resolveDefaultLifecycle();
|
|
185
|
+
const capabilities = {};
|
|
186
|
+
for (const capability of plugin.provides)
|
|
187
|
+
capabilities[capability] = plugin.meta.id;
|
|
188
|
+
return {
|
|
189
|
+
kernelProviderId: plugin.meta.id,
|
|
190
|
+
kernelVersion: plugin.meta.version,
|
|
191
|
+
capabilities,
|
|
192
|
+
protectedStages: [...PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS],
|
|
193
|
+
pipelineStageCount: resolved.order.length,
|
|
194
|
+
stageOrder: [...resolved.order]
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function formatKernelStatus(data = defaultKernelStatusData()) {
|
|
198
|
+
const lines = [
|
|
199
|
+
`kernel: ${data.kernelProviderId} v${data.kernelVersion}`,
|
|
200
|
+
"capabilities (resolved provider):",
|
|
201
|
+
...Object.entries(data.capabilities).map(([cap, provider]) => ` ${cap.padEnd(14)} ${provider}`),
|
|
202
|
+
`protected stages: ${data.protectedStages.join(", ")}`,
|
|
203
|
+
`resolved pipeline (${data.pipelineStageCount} stages): ${data.stageOrder.join(" -> ")}`
|
|
204
|
+
];
|
|
205
|
+
return lines.join(`
|
|
206
|
+
`);
|
|
207
|
+
}
|
|
163
208
|
function formatDefaultPipelineShow(input = {}) {
|
|
164
209
|
const data = defaultPipelineShowData(input);
|
|
165
210
|
const lines = [`${data.title}:`];
|
|
@@ -182,10 +227,12 @@ function formatDefaultPipelineShow(input = {}) {
|
|
|
182
227
|
}
|
|
183
228
|
export {
|
|
184
229
|
resolveDefaultLifecycle,
|
|
230
|
+
formatKernelStatus,
|
|
185
231
|
formatDefaultPipelineShow,
|
|
186
232
|
defaultPipelineShowData,
|
|
187
233
|
defaultLifecycleStages,
|
|
188
234
|
defaultLifecycleStageById,
|
|
235
|
+
defaultKernelStatusData,
|
|
189
236
|
PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS,
|
|
190
237
|
DEFAULT_LIFECYCLE_STAGE_IDS
|
|
191
238
|
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./closeoutEquivalence";
|
|
2
2
|
export * from "./closeoutShadowHarness";
|
|
3
3
|
export * from "./defaultPipeline";
|
|
4
|
-
export * from "./
|
|
4
|
+
export * from "./pipelineCloseout";
|
|
5
5
|
export * from "./plugin";
|
|
6
6
|
export * from "./stages/auto-merge";
|
|
7
7
|
export * from "./stages/commit";
|
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";
|
|
@@ -158,6 +159,9 @@ async function runJournalAppendStage(input) {
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
// packages/bundle-default-lifecycle/src/stages/merge-gate.ts
|
|
162
|
+
import {
|
|
163
|
+
runStrictPrMergeGate
|
|
164
|
+
} from "@rig/runtime/control-plane/native/pr-review-gate";
|
|
161
165
|
var mergeGateStage = defineDefaultLifecycleStage({
|
|
162
166
|
id: "merge-gate",
|
|
163
167
|
kind: "gate",
|
|
@@ -165,6 +169,21 @@ var mergeGateStage = defineDefaultLifecycleStage({
|
|
|
165
169
|
calls: ["runStrictPrMergeGate"],
|
|
166
170
|
protected: true
|
|
167
171
|
});
|
|
172
|
+
async function runMergeGateStage(input) {
|
|
173
|
+
return await runStrictPrMergeGate({
|
|
174
|
+
projectRoot: input.projectRoot,
|
|
175
|
+
prUrl: input.prUrl,
|
|
176
|
+
taskId: input.taskId,
|
|
177
|
+
runId: input.runId,
|
|
178
|
+
cycle: input.cycle,
|
|
179
|
+
command: input.command,
|
|
180
|
+
...input.artifactRoot !== undefined ? { artifactRoot: input.artifactRoot } : {},
|
|
181
|
+
...input.allowedFailures !== undefined ? { allowedFailures: input.allowedFailures } : {},
|
|
182
|
+
...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
|
|
183
|
+
...input.final !== undefined ? { final: input.final } : {},
|
|
184
|
+
...input.requireGreptile !== undefined ? { requireGreptile: input.requireGreptile } : {}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
168
187
|
|
|
169
188
|
// packages/bundle-default-lifecycle/src/stages/open-pr.ts
|
|
170
189
|
import { runPrAutomation } from "@rig/runtime/control-plane/native/pr-automation";
|
|
@@ -261,6 +280,21 @@ var DEFAULT_LIFECYCLE_STAGE_IDS = [
|
|
|
261
280
|
"journal-append"
|
|
262
281
|
];
|
|
263
282
|
var PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS = ["merge-gate", "isolation", "journal-append"];
|
|
283
|
+
var DEFAULT_STAGE_AFTER = {
|
|
284
|
+
verify: ["validate"],
|
|
285
|
+
commit: ["verify"],
|
|
286
|
+
push: ["commit"],
|
|
287
|
+
"open-pr": ["push"],
|
|
288
|
+
"merge-gate": ["open-pr"],
|
|
289
|
+
"auto-merge": ["merge-gate"],
|
|
290
|
+
"source-closeout": ["auto-merge"],
|
|
291
|
+
isolation: ["source-closeout"],
|
|
292
|
+
"journal-append": ["isolation"]
|
|
293
|
+
};
|
|
294
|
+
function withDefaultAnchors(stage) {
|
|
295
|
+
const after = DEFAULT_STAGE_AFTER[stage.id];
|
|
296
|
+
return after ? { ...stage, after } : stage;
|
|
297
|
+
}
|
|
264
298
|
var defaultLifecycleStages = [
|
|
265
299
|
validateStage,
|
|
266
300
|
verifyStage,
|
|
@@ -272,7 +306,7 @@ var defaultLifecycleStages = [
|
|
|
272
306
|
sourceCloseoutStage,
|
|
273
307
|
isolationStage,
|
|
274
308
|
journalAppendStage
|
|
275
|
-
];
|
|
309
|
+
].map(withDefaultAnchors);
|
|
276
310
|
function defaultLifecycleStageById(id) {
|
|
277
311
|
const stage = defaultLifecycleStages.find((candidate) => candidate.id === id);
|
|
278
312
|
if (!stage)
|
|
@@ -305,6 +339,32 @@ function defaultPipelineShowData(input = {}) {
|
|
|
305
339
|
...resolved.resolvedAt !== undefined ? { resolvedAt: resolved.resolvedAt } : {}
|
|
306
340
|
};
|
|
307
341
|
}
|
|
342
|
+
function defaultKernelStatusData() {
|
|
343
|
+
const plugin = createDefaultKernelPlugin();
|
|
344
|
+
const resolved = resolveDefaultLifecycle();
|
|
345
|
+
const capabilities = {};
|
|
346
|
+
for (const capability of plugin.provides)
|
|
347
|
+
capabilities[capability] = plugin.meta.id;
|
|
348
|
+
return {
|
|
349
|
+
kernelProviderId: plugin.meta.id,
|
|
350
|
+
kernelVersion: plugin.meta.version,
|
|
351
|
+
capabilities,
|
|
352
|
+
protectedStages: [...PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS],
|
|
353
|
+
pipelineStageCount: resolved.order.length,
|
|
354
|
+
stageOrder: [...resolved.order]
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function formatKernelStatus(data = defaultKernelStatusData()) {
|
|
358
|
+
const lines = [
|
|
359
|
+
`kernel: ${data.kernelProviderId} v${data.kernelVersion}`,
|
|
360
|
+
"capabilities (resolved provider):",
|
|
361
|
+
...Object.entries(data.capabilities).map(([cap, provider]) => ` ${cap.padEnd(14)} ${provider}`),
|
|
362
|
+
`protected stages: ${data.protectedStages.join(", ")}`,
|
|
363
|
+
`resolved pipeline (${data.pipelineStageCount} stages): ${data.stageOrder.join(" -> ")}`
|
|
364
|
+
];
|
|
365
|
+
return lines.join(`
|
|
366
|
+
`);
|
|
367
|
+
}
|
|
308
368
|
function formatDefaultPipelineShow(input = {}) {
|
|
309
369
|
const data = defaultPipelineShowData(input);
|
|
310
370
|
const lines = [`${data.title}:`];
|
|
@@ -325,11 +385,15 @@ function formatDefaultPipelineShow(input = {}) {
|
|
|
325
385
|
return lines.join(`
|
|
326
386
|
`);
|
|
327
387
|
}
|
|
328
|
-
// packages/bundle-default-lifecycle/src/
|
|
388
|
+
// packages/bundle-default-lifecycle/src/pipelineCloseout.ts
|
|
329
389
|
import { resolve as resolve2 } from "path";
|
|
330
390
|
import { loadConfig } from "@rig/core/load-config";
|
|
391
|
+
import { createPluginHost } from "@rig/core";
|
|
392
|
+
import { createDefaultKernel } from "@rig/kernel/default-kernel";
|
|
331
393
|
import { buildPluginHostContext } from "@rig/runtime/control-plane/plugin-host-context";
|
|
332
|
-
import {
|
|
394
|
+
import {
|
|
395
|
+
CloseoutValidationError
|
|
396
|
+
} from "@rig/runtime/control-plane/native/in-process-closeout";
|
|
333
397
|
import { taskValidate } from "@rig/runtime/control-plane/native/task-ops";
|
|
334
398
|
function cleanString(value) {
|
|
335
399
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
@@ -346,9 +410,6 @@ function closeoutOutcome(status) {
|
|
|
346
410
|
return "started";
|
|
347
411
|
}
|
|
348
412
|
}
|
|
349
|
-
function normalizePrStatus(status) {
|
|
350
|
-
return status === "needs_attention" ? "needs-attention" : status;
|
|
351
|
-
}
|
|
352
413
|
async function loadRigAutomationConfig(projectRoot) {
|
|
353
414
|
try {
|
|
354
415
|
return await loadConfig(projectRoot);
|
|
@@ -360,130 +421,232 @@ async function runRigProjectValidation({ projectRoot, taskId }) {
|
|
|
360
421
|
const pluginHostCtx = await buildPluginHostContext(projectRoot);
|
|
361
422
|
return taskValidate(projectRoot, taskId, pluginHostCtx?.validatorRegistry ?? undefined);
|
|
362
423
|
}
|
|
363
|
-
|
|
424
|
+
function shouldAttemptRigMerge(config) {
|
|
425
|
+
const mode = config.merge?.mode;
|
|
426
|
+
return mode !== "off" && mode !== "pr-ready";
|
|
427
|
+
}
|
|
428
|
+
async function loadPluginStageContributions(projectRoot) {
|
|
429
|
+
try {
|
|
430
|
+
const config = await loadConfig(projectRoot);
|
|
431
|
+
const host = createPluginHost(config.plugins ?? []);
|
|
432
|
+
return { executors: host.listStageExecutors(), mutations: host.listStageMutations() };
|
|
433
|
+
} catch {
|
|
434
|
+
return { executors: {}, mutations: [] };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function runPipelineCloseout(input) {
|
|
364
438
|
const taskId = cleanString(input.taskId);
|
|
365
439
|
if (!taskId) {
|
|
366
|
-
throw new Error("
|
|
440
|
+
throw new Error("Pipeline closeout requires a task id.");
|
|
367
441
|
}
|
|
368
442
|
const loadedConfig = input.config ?? await loadRigAutomationConfig(input.projectRoot);
|
|
369
443
|
const prMode = loadedConfig?.pr?.mode ?? "off";
|
|
370
444
|
const reviewProvider = loadedConfig?.review?.provider ?? "github";
|
|
371
445
|
const effectiveConfig = {
|
|
372
446
|
...loadedConfig ?? {},
|
|
373
|
-
pr: {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
provider: reviewProvider
|
|
380
|
-
}
|
|
447
|
+
pr: { ...loadedConfig?.pr ?? {}, mode: prMode },
|
|
448
|
+
review: { ...loadedConfig?.review ?? {}, provider: reviewProvider }
|
|
449
|
+
};
|
|
450
|
+
const openOnlyConfig = {
|
|
451
|
+
...effectiveConfig,
|
|
452
|
+
merge: { ...effectiveConfig.merge ?? {}, mode: "pr-ready" }
|
|
381
453
|
};
|
|
454
|
+
const shouldMerge = shouldAttemptRigMerge(effectiveConfig);
|
|
382
455
|
const workspace = input.workspace;
|
|
383
456
|
const artifactRoot = input.artifactRoot ?? resolve2(input.projectRoot, "artifacts", taskId);
|
|
384
|
-
let branch = input.branch;
|
|
385
|
-
let currentPhase = "queued";
|
|
386
457
|
const journal = async (phase, status, detail) => {
|
|
387
|
-
currentPhase = phase;
|
|
388
458
|
await input.journalPhase(phase, closeoutOutcome(status), detail ?? null);
|
|
389
459
|
};
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
460
|
+
if (prMode === "off" || prMode === "ask") {
|
|
461
|
+
const reason = prMode === "ask" ? "PR creation awaits operator approval." : "PR automation disabled.";
|
|
462
|
+
await input.reflect("under_review", reason);
|
|
463
|
+
await journal("completed", "completed", reason);
|
|
464
|
+
return {
|
|
465
|
+
mode: "pipeline",
|
|
466
|
+
pipelineStageIds: [...resolveDefaultLifecycle().order],
|
|
467
|
+
result: { status: "skipped", iterations: 0, feedback: [] }
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const state = {
|
|
471
|
+
branch: input.branch,
|
|
472
|
+
validationPassed: false,
|
|
473
|
+
committed: false,
|
|
474
|
+
pushed: false,
|
|
475
|
+
prUrl: null,
|
|
476
|
+
prReady: false,
|
|
477
|
+
pr: null,
|
|
478
|
+
gate: null,
|
|
479
|
+
mergeGate: null,
|
|
480
|
+
merged: false,
|
|
481
|
+
iterations: 0,
|
|
482
|
+
feedback: [],
|
|
483
|
+
blockedDetail: null
|
|
393
484
|
};
|
|
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
|
-
|
|
485
|
+
const cont = (ctx2) => ({ kind: "continue", ctx: ctx2 });
|
|
486
|
+
const executors = {
|
|
487
|
+
isolation: (ctx2) => cont(ctx2),
|
|
488
|
+
validate: async (ctx2) => {
|
|
489
|
+
await input.onValidationStart?.();
|
|
490
|
+
await journal("queued", "running", `Validating task ${taskId} before closeout.`);
|
|
491
|
+
let passed = false;
|
|
492
|
+
try {
|
|
493
|
+
passed = await runValidateStage({ projectRoot: input.projectRoot, taskId }, input.runValidation ?? runRigProjectValidation);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
const detail = `Rig validation failed before closeout: ${error instanceof Error ? error.message : String(error)}`;
|
|
496
|
+
await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
|
|
497
|
+
throw new CloseoutValidationError(detail);
|
|
498
|
+
}
|
|
499
|
+
if (!passed) {
|
|
500
|
+
const detail = `Rig validation failed for task ${taskId}; closeout blocked before commit.`;
|
|
501
|
+
await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
|
|
502
|
+
throw new CloseoutValidationError(detail);
|
|
503
|
+
}
|
|
504
|
+
state.validationPassed = true;
|
|
505
|
+
await journal("queued", "completed", `Validation passed for task ${taskId}.`);
|
|
506
|
+
const workspaceBranch = await input.gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: workspace });
|
|
507
|
+
const currentWorkspaceBranch = workspaceBranch.exitCode === 0 ? cleanString(workspaceBranch.stdout) : null;
|
|
508
|
+
if (currentWorkspaceBranch && currentWorkspaceBranch !== "HEAD" && currentWorkspaceBranch !== state.branch) {
|
|
509
|
+
state.branch = currentWorkspaceBranch;
|
|
510
|
+
}
|
|
511
|
+
return cont(ctx2);
|
|
512
|
+
},
|
|
513
|
+
verify: () => state.validationPassed ? { kind: "allow" } : { kind: "block", reason: "validation did not pass" },
|
|
514
|
+
commit: async (ctx2) => {
|
|
515
|
+
await journal("commit", "running", `Committing changes in ${workspace}.`);
|
|
516
|
+
const committed = await runCommitStage({ cwd: workspace, message: `rig: complete task ${taskId}`, command: input.gitCommand });
|
|
517
|
+
state.committed = committed.committed;
|
|
518
|
+
return cont(ctx2);
|
|
519
|
+
},
|
|
520
|
+
push: async (ctx2) => {
|
|
521
|
+
await journal("push", "running", `Pushing branch ${state.branch}.`);
|
|
522
|
+
await runPushStage({ projectRoot: workspace, branch: state.branch, gitCommand: input.gitCommand });
|
|
523
|
+
state.pushed = true;
|
|
524
|
+
return cont(ctx2);
|
|
525
|
+
},
|
|
526
|
+
"open-pr": async (ctx2) => {
|
|
527
|
+
await journal("pr-review-merge", "running", `Opening a pull request for ${state.branch}.`);
|
|
528
|
+
const pr = await runOpenPrStage({
|
|
529
|
+
...input,
|
|
530
|
+
taskId,
|
|
531
|
+
branch: state.branch,
|
|
532
|
+
artifactRoot,
|
|
533
|
+
config: openOnlyConfig,
|
|
534
|
+
sourceTask: { title: cleanString(input.sourceTask?.title) },
|
|
535
|
+
lifecycle: {
|
|
536
|
+
onPrOpened: async ({ prUrl }) => {
|
|
537
|
+
await journal("pr-opened", "running", prUrl);
|
|
538
|
+
await input.reflect("under_review", "Rig opened a pull request for this task.");
|
|
539
|
+
},
|
|
540
|
+
onFeedback: async ({ feedback }) => {
|
|
541
|
+
await input.reflect("ci_fixing", "Rig is fixing CI/review feedback for this task.", { errorText: feedback.join(`
|
|
440
542
|
`) || 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);
|
|
543
|
+
}
|
|
448
544
|
}
|
|
545
|
+
});
|
|
546
|
+
state.pr = pr;
|
|
547
|
+
state.prUrl = pr.prUrl ?? null;
|
|
548
|
+
state.prReady = pr.status === "opened" || pr.status === "merged";
|
|
549
|
+
state.iterations = pr.iterations;
|
|
550
|
+
state.feedback = [...pr.actionableFeedback];
|
|
551
|
+
if (pr.status === "needs_attention") {
|
|
552
|
+
state.blockedDetail = pr.actionableFeedback.join(`
|
|
553
|
+
`) || "PR automation did not produce a mergeable PR.";
|
|
554
|
+
}
|
|
555
|
+
return cont(ctx2);
|
|
556
|
+
},
|
|
557
|
+
"merge-gate": async (ctx2) => {
|
|
558
|
+
if (!shouldMerge || !state.prReady || !state.prUrl) {
|
|
559
|
+
state.mergeGate = state.prReady ? "skipped" : null;
|
|
560
|
+
return state.prReady ? cont(ctx2) : { kind: "block", reason: state.blockedDetail ?? "no mergeable PR to gate" };
|
|
561
|
+
}
|
|
562
|
+
const gate = await runMergeGateStage({
|
|
563
|
+
projectRoot: workspace,
|
|
564
|
+
prUrl: state.prUrl,
|
|
565
|
+
taskId,
|
|
566
|
+
runId: input.runId,
|
|
567
|
+
cycle: 1,
|
|
568
|
+
command: input.command,
|
|
569
|
+
artifactRoot,
|
|
570
|
+
final: true,
|
|
571
|
+
...effectiveConfig.merge?.allowedFailures ? { allowedFailures: effectiveConfig.merge.allowedFailures } : {},
|
|
572
|
+
...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
|
|
573
|
+
requireGreptile: reviewProvider === "greptile"
|
|
574
|
+
});
|
|
575
|
+
state.gate = gate;
|
|
576
|
+
if (gate.approved) {
|
|
577
|
+
state.mergeGate = "passed";
|
|
578
|
+
return cont(ctx2);
|
|
579
|
+
}
|
|
580
|
+
state.mergeGate = "blocked";
|
|
581
|
+
const detail = gate.actionableFeedback.join(`
|
|
582
|
+
`) || gate.reasons.join("; ") || "merge gate blocked the PR.";
|
|
583
|
+
state.blockedDetail = detail;
|
|
584
|
+
await input.reflect("needs_attention", "Rig needs operator attention before this task can merge.", { errorText: detail });
|
|
585
|
+
await journal("pr-review-merge", "needs-attention", detail);
|
|
586
|
+
return { kind: "block", reason: detail };
|
|
587
|
+
},
|
|
588
|
+
"auto-merge": async (ctx2) => {
|
|
589
|
+
if (!shouldMerge || state.mergeGate !== "passed" || !state.prUrl || !state.gate) {
|
|
590
|
+
return cont(ctx2);
|
|
449
591
|
}
|
|
450
|
-
|
|
451
|
-
|
|
592
|
+
await journal("merge", "running", state.prUrl);
|
|
593
|
+
await input.reflect("merging", "Rig is merging the pull request for this task.");
|
|
594
|
+
await runAutoMergeStage({ prUrl: state.prUrl, config: effectiveConfig, command: input.command, cwd: workspace, strictGate: state.gate });
|
|
595
|
+
state.merged = true;
|
|
596
|
+
return cont(ctx2);
|
|
597
|
+
},
|
|
598
|
+
"source-closeout": async (ctx2) => {
|
|
599
|
+
if (!state.merged || !state.prUrl)
|
|
600
|
+
return cont(ctx2);
|
|
601
|
+
await journal("close-source", "running", state.prUrl);
|
|
602
|
+
const mergedPr = { ...state.pr ?? { iterations: state.iterations, actionableFeedback: state.feedback }, status: "merged", prUrl: state.prUrl, merged: true };
|
|
452
603
|
await runSourceCloseoutStage({
|
|
453
604
|
projectRoot: input.projectRoot,
|
|
454
605
|
taskId,
|
|
455
606
|
runId: input.runId,
|
|
456
|
-
pr,
|
|
607
|
+
pr: mergedPr,
|
|
457
608
|
...input.sourceTask !== undefined ? { sourceTask: input.sourceTask } : {},
|
|
458
609
|
updateTaskSource: async () => {
|
|
459
610
|
await input.reflect("closed", "Rig merged the pull request and closed this task source.");
|
|
460
611
|
return { updated: true, taskId, status: "closed", source: "runtime", sourceKind: "runtime" };
|
|
461
612
|
}
|
|
462
613
|
});
|
|
463
|
-
return
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
614
|
+
return cont(ctx2);
|
|
615
|
+
},
|
|
616
|
+
"journal-append": (ctx2) => cont(ctx2)
|
|
617
|
+
};
|
|
618
|
+
const pluginStages = await loadPluginStageContributions(input.projectRoot);
|
|
619
|
+
const kernel = createDefaultKernel({ stageExecutors: { ...executors, ...pluginStages.executors } });
|
|
620
|
+
const resolved = kernel.stageRunner.resolve(defaultLifecycleStages, pluginStages.mutations);
|
|
621
|
+
const ctx = {
|
|
622
|
+
runId: input.runId,
|
|
623
|
+
taskId,
|
|
624
|
+
state,
|
|
625
|
+
metadata: { projectRoot: input.projectRoot, workspace }
|
|
626
|
+
};
|
|
627
|
+
await kernel.stageRunner.runPipeline(input.runId, resolved, ctx);
|
|
628
|
+
const result = mapStateToResult(state);
|
|
629
|
+
if (result.status === "merged" || result.status === "opened") {
|
|
630
|
+
await journal("completed", "completed", result.prUrl ? `${result.status === "merged" ? "PR merged and issue closed" : "PR ready without merge"}: ${result.prUrl}` : result.status);
|
|
477
631
|
}
|
|
632
|
+
return { mode: "pipeline", pipelineStageIds: [...resolved.order], result };
|
|
478
633
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
634
|
+
function mapStateToResult(state) {
|
|
635
|
+
if (state.merged && state.prUrl) {
|
|
636
|
+
return { status: "merged", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
|
|
637
|
+
}
|
|
638
|
+
if (state.mergeGate === "blocked" || state.blockedDetail) {
|
|
639
|
+
return {
|
|
640
|
+
status: "needs-attention",
|
|
641
|
+
...state.prUrl ? { prUrl: state.prUrl } : {},
|
|
642
|
+
iterations: state.iterations,
|
|
643
|
+
feedback: state.feedback
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
if (state.prReady && state.prUrl) {
|
|
647
|
+
return { status: "opened", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
|
|
648
|
+
}
|
|
649
|
+
return { status: "needs-attention", ...state.prUrl ? { prUrl: state.prUrl } : {}, iterations: state.iterations, feedback: state.feedback };
|
|
487
650
|
}
|
|
488
651
|
// packages/bundle-default-lifecycle/src/plugin.ts
|
|
489
652
|
var DEFAULT_LIFECYCLE_PLUGIN_ID = "@rig/bundle-default-lifecycle";
|
|
@@ -502,10 +665,11 @@ export {
|
|
|
502
665
|
snapshotCloseoutArtifacts,
|
|
503
666
|
runVerifyStage,
|
|
504
667
|
runValidateStage,
|
|
505
|
-
runStagedCloseout,
|
|
506
668
|
runSourceCloseoutStage,
|
|
507
669
|
runPushStage,
|
|
670
|
+
runPipelineCloseout,
|
|
508
671
|
runOpenPrStage,
|
|
672
|
+
runMergeGateStage,
|
|
509
673
|
runJournalAppendStage,
|
|
510
674
|
runIsolationStage,
|
|
511
675
|
runCommitStage,
|
|
@@ -516,11 +680,13 @@ export {
|
|
|
516
680
|
mergeGateStage,
|
|
517
681
|
journalAppendStage,
|
|
518
682
|
isolationStage,
|
|
683
|
+
formatKernelStatus,
|
|
519
684
|
formatDefaultPipelineShow,
|
|
520
685
|
defineDefaultLifecycleStage,
|
|
521
686
|
defaultPipelineShowData,
|
|
522
687
|
defaultLifecycleStages,
|
|
523
688
|
defaultLifecycleStageById,
|
|
689
|
+
defaultKernelStatusData,
|
|
524
690
|
createDefaultLifecyclePlugin,
|
|
525
691
|
compareCloseoutShadowRuns,
|
|
526
692
|
compareCloseoutEquivalence,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type InProcessCloseoutInput, type InProcessCloseoutResult } from "@rig/runtime/control-plane/native/in-process-closeout";
|
|
2
|
+
export type PipelineCloseoutResult = {
|
|
3
|
+
readonly mode: "pipeline";
|
|
4
|
+
/** The resolved stage order the kernel executed (the journal-recorded pipeline). */
|
|
5
|
+
readonly pipelineStageIds: readonly string[];
|
|
6
|
+
readonly result: InProcessCloseoutResult;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Run the default-lifecycle closeout as a resolved stage pipeline driven by the
|
|
10
|
+
* kernel's stage runner. Each kernel stage executor invokes the corresponding
|
|
11
|
+
* `run<Stage>` wrapper against the shared closeout state, so `open-pr`,
|
|
12
|
+
* `merge-gate`, and `auto-merge` are discrete executed stages (the protected
|
|
13
|
+
* `merge-gate` runs `runStrictPrMergeGate` as a real gate, blocking the
|
|
14
|
+
* pipeline when the PR is not approved). This is the single closeout path; the
|
|
15
|
+
* earlier parallel imperative `runStagedCloseout` copy is retired.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runPipelineCloseout(input: InProcessCloseoutInput): Promise<PipelineCloseoutResult>;
|