merge-steward 0.22.5 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.d.ts
CHANGED
|
@@ -30,6 +30,9 @@ export declare const stewardConfigSchema: z.ZodObject<{
|
|
|
30
30
|
admissionLabel: z.ZodDefault<z.ZodString>;
|
|
31
31
|
priorityQueueLabel: z.ZodDefault<z.ZodString>;
|
|
32
32
|
mergeQueueCheckName: z.ZodDefault<z.ZodString>;
|
|
33
|
+
evictionCheckName: z.ZodDefault<z.ZodString>;
|
|
34
|
+
specReadyCheckName: z.ZodDefault<z.ZodString>;
|
|
35
|
+
specBranchPrefix: z.ZodDefault<z.ZodString>;
|
|
33
36
|
excludeBranches: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
34
37
|
autoResolvePatterns: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
35
38
|
glob: z.ZodString;
|
package/dist/config.js
CHANGED
|
@@ -31,6 +31,21 @@ export const stewardConfigSchema = z.object({
|
|
|
31
31
|
admissionLabel: z.string().default("queue"),
|
|
32
32
|
priorityQueueLabel: z.string().default("queue:priority"),
|
|
33
33
|
mergeQueueCheckName: z.string().default(DEFAULT_MERGE_QUEUE_CHECK_NAME),
|
|
34
|
+
// Plan §2.4 — bus-contract artifact names exposed for cross-service
|
|
35
|
+
// alignment. Defaults preserve current behavior. The steward writes
|
|
36
|
+
// `evictionCheckName` (today: same as mergeQueueCheckName, kept for
|
|
37
|
+
// contract clarity) and `specReadyCheckName`; consumers read them.
|
|
38
|
+
/** Eviction check run name. Synonym for mergeQueueCheckName, surfaced
|
|
39
|
+
* under the bus-contract name so consumer code can be naming-aligned
|
|
40
|
+
* with patchrelay's resolver. */
|
|
41
|
+
evictionCheckName: z.string().default(DEFAULT_MERGE_QUEUE_CHECK_NAME),
|
|
42
|
+
/** Spec-ready check run name (default: "merge-steward/spec-ready").
|
|
43
|
+
* Read by review-quill in integration_tree mode (plan §3.5). */
|
|
44
|
+
specReadyCheckName: z.string().default("merge-steward/spec-ready"),
|
|
45
|
+
/** Prefix for spec branch names (default: "mq-spec-"); matches the
|
|
46
|
+
* existing SPEC_BRANCH_PREFIX in reconciler-core.ts. The pattern
|
|
47
|
+
* review-quill matches against is `${prefix}*`. */
|
|
48
|
+
specBranchPrefix: z.string().default("mq-spec-"),
|
|
34
49
|
/** Branch name patterns to exclude from admission (glob-style). */
|
|
35
50
|
excludeBranches: z.array(z.string()).default([]),
|
|
36
51
|
/**
|
|
@@ -12,6 +12,8 @@ export declare class GitHubCheckRunReporter implements EvictionReporter {
|
|
|
12
12
|
private readonly publicBaseUrl?;
|
|
13
13
|
private readonly admissionLabel;
|
|
14
14
|
private readonly mergeQueueCheckName;
|
|
15
|
-
|
|
15
|
+
private readonly specReadyCheckName;
|
|
16
|
+
constructor(repoFullName: string, serverHost: string, serverPort: number, publicBaseUrl?: string | undefined, admissionLabel?: string, mergeQueueCheckName?: string, specReadyCheckName?: string);
|
|
16
17
|
reportEviction(entry: QueueEntry, incident: IncidentRecord): Promise<void>;
|
|
18
|
+
reportSpecReady(entry: QueueEntry, specBranch: string, specSha: string): Promise<void>;
|
|
17
19
|
}
|
|
@@ -4,6 +4,9 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { exec } from "../exec.js";
|
|
6
6
|
import { DEFAULT_MERGE_QUEUE_CHECK_NAME } from "../config.js";
|
|
7
|
+
// Plan §5.2 default — review-quill subscribes by default; configurable
|
|
8
|
+
// per project via the bus contract added in PR 1.
|
|
9
|
+
const DEFAULT_SPEC_READY_CHECK_NAME = "merge-steward/spec-ready";
|
|
7
10
|
/**
|
|
8
11
|
* Reports evictions by creating a GitHub check run on the PR's head SHA.
|
|
9
12
|
* The check run output contains the incident context as structured JSON.
|
|
@@ -16,13 +19,15 @@ export class GitHubCheckRunReporter {
|
|
|
16
19
|
publicBaseUrl;
|
|
17
20
|
admissionLabel;
|
|
18
21
|
mergeQueueCheckName;
|
|
19
|
-
|
|
22
|
+
specReadyCheckName;
|
|
23
|
+
constructor(repoFullName, serverHost, serverPort, publicBaseUrl, admissionLabel = "queue", mergeQueueCheckName = DEFAULT_MERGE_QUEUE_CHECK_NAME, specReadyCheckName = DEFAULT_SPEC_READY_CHECK_NAME) {
|
|
20
24
|
this.repoFullName = repoFullName;
|
|
21
25
|
this.serverHost = serverHost;
|
|
22
26
|
this.serverPort = serverPort;
|
|
23
27
|
this.publicBaseUrl = publicBaseUrl;
|
|
24
28
|
this.admissionLabel = admissionLabel;
|
|
25
29
|
this.mergeQueueCheckName = mergeQueueCheckName;
|
|
30
|
+
this.specReadyCheckName = specReadyCheckName;
|
|
26
31
|
}
|
|
27
32
|
async reportEviction(entry, incident) {
|
|
28
33
|
const title = formatTitle(incident);
|
|
@@ -70,6 +75,52 @@ export class GitHubCheckRunReporter {
|
|
|
70
75
|
"--remove-label", this.admissionLabel,
|
|
71
76
|
], { timeoutMs: 15_000, githubRepoFullName: this.repoFullName }).catch(() => { });
|
|
72
77
|
}
|
|
78
|
+
// Plan §5.2: a "spec is ready" event on the PR head — not a verdict.
|
|
79
|
+
// Conclusion is `neutral` so branch-protection rules treat it as a
|
|
80
|
+
// signal rather than a gate. Best-effort: failures are logged in the
|
|
81
|
+
// exec layer and not propagated.
|
|
82
|
+
async reportSpecReady(entry, specBranch, specSha) {
|
|
83
|
+
const targetUrl = `https://github.com/${this.repoFullName}/commit/${specSha}`;
|
|
84
|
+
const summary = [
|
|
85
|
+
`Spec branch \`${specBranch}\` is at \`${specSha.slice(0, 12)}\`.`,
|
|
86
|
+
``,
|
|
87
|
+
`This is the integration tree the lander will run CI on. Reviewers`,
|
|
88
|
+
`subscribed to integration_tree mode use this commit as the head`,
|
|
89
|
+
`they review.`,
|
|
90
|
+
].join("\n");
|
|
91
|
+
const body = JSON.stringify({
|
|
92
|
+
name: this.specReadyCheckName,
|
|
93
|
+
head_sha: entry.headSha,
|
|
94
|
+
status: "completed",
|
|
95
|
+
conclusion: "neutral",
|
|
96
|
+
details_url: targetUrl,
|
|
97
|
+
output: {
|
|
98
|
+
title: `Spec ready (${specBranch})`,
|
|
99
|
+
summary,
|
|
100
|
+
text: JSON.stringify({ specBranch, specSha, prNumber: entry.prNumber, queueEntryId: entry.id }),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const tmpPath = join(tmpdir(), `steward-specready-${randomUUID()}.json`);
|
|
104
|
+
try {
|
|
105
|
+
writeFileSync(tmpPath, body);
|
|
106
|
+
await exec("gh", [
|
|
107
|
+
"api",
|
|
108
|
+
`repos/${this.repoFullName}/check-runs`,
|
|
109
|
+
"--method", "POST",
|
|
110
|
+
"--input", tmpPath,
|
|
111
|
+
], { timeoutMs: 30_000, githubRepoFullName: this.repoFullName });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Best-effort — review-quill can also fall back to polling the
|
|
115
|
+
// spec branch directly if it never sees this check_run.
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(tmpPath);
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
73
124
|
}
|
|
74
125
|
function buildIncidentProjection(incident, detailsUrl) {
|
|
75
126
|
return {
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -64,4 +64,13 @@ export interface GitHubPRApi {
|
|
|
64
64
|
*/
|
|
65
65
|
export interface EvictionReporter {
|
|
66
66
|
reportEviction(entry: QueueEntry, incident: IncidentRecord): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Plan §5.2: emit a "spec ready" check_run on the PR's head SHA when
|
|
69
|
+
* a fresh spec branch has been pushed. Lets review-quill (and other
|
|
70
|
+
* consumers) subscribe to integration-tree availability via the
|
|
71
|
+
* GitHub bus instead of polling. Optional — implementations that
|
|
72
|
+
* cannot or should not write this check (sim, in-memory, dry-run)
|
|
73
|
+
* may omit or no-op.
|
|
74
|
+
*/
|
|
75
|
+
reportSpecReady?(entry: QueueEntry, specBranch: string, specSha: string): Promise<void>;
|
|
67
76
|
}
|
|
@@ -161,6 +161,13 @@ export async function prepareEntry(ctx, entry, isHead, prevEntry) {
|
|
|
161
161
|
const specSha = result.sha ?? entry.headSha;
|
|
162
162
|
emit(ctx, entry, "spec_build_succeeded", { specBranch: specName, ...(prevEntry ? { dependsOn: prevEntry.id } : {}) });
|
|
163
163
|
await ctx.git.push(specName, true);
|
|
164
|
+
// Plan §5.2: emit `merge-steward/spec-ready` on the PR head so
|
|
165
|
+
// review-quill (and any other consumer) can react to spec
|
|
166
|
+
// availability via the GitHub bus instead of polling. Best-effort —
|
|
167
|
+
// failures are caught inside the reporter; CI still triggers.
|
|
168
|
+
if (ctx.eviction.reportSpecReady) {
|
|
169
|
+
await ctx.eviction.reportSpecReady(entry, specName, specSha).catch(() => undefined);
|
|
170
|
+
}
|
|
164
171
|
const runId = await ctx.ci.triggerRun(specName, specSha);
|
|
165
172
|
emit(ctx, entry, "ci_triggered", { ciRunId: runId, specBranch: specName });
|
|
166
173
|
ctx.store.transition(entry.id, "validating", {
|