chore(pr): reuse green hosted CI from patch-identical pre-rebase heads (#111335)

* chore(pr): reuse green hosted CI from patch-identical pre-rebase heads (24h window)

* fix(pr): type-safe fixtures and lint cleanup for hosted-CI reuse tests
This commit is contained in:
Peter Steinberger
2026-07-19 05:34:48 -07:00
committed by GitHub
parent 46b9a3e12d
commit 6a8f16a69e
2 changed files with 607 additions and 50 deletions
+242 -13
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env node
import { execFileSync, spawnSync } from "node:child_process";
import { mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import { isDirectRunUrl } from "./lib/direct-run.mjs";
import { execGhApiRead } from "./lib/plain-gh.mjs";
import { execGhApiRead, plainGhEnv } from "./lib/plain-gh.mjs";
export const SCHEDULED_HOSTED_WORKFLOWS = [
"Blacksmith Testbox",
@@ -26,6 +27,9 @@ const COMPARE_COMMITS_PAGE_SIZE = 100;
export const HOSTED_GATE_MAX_AGE_HOURS = 24;
const HOSTED_GATE_MAX_AGE_MS = HOSTED_GATE_MAX_AGE_HOURS * 60 * 60 * 1_000;
const HOSTED_GATE_CLOCK_SKEW_MS = 5 * 60 * 1_000;
const MAX_CI_REUSE_CANDIDATES = 5;
const CI_REUSE_RUN_LIST_LIMIT = 50;
const GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
function readOptionValue(argv, index, optionName) {
const value = argv[index + 1];
@@ -151,6 +155,128 @@ function isSuccessfulRecentRun(run, nowMs) {
return run?.status === "completed" && run.conclusion === "success" && isRecentRun(run, nowMs);
}
function runGit(args, { input } = {}) {
const result = spawnSync("git", args, {
encoding: "utf8",
input,
maxBuffer: GIT_MAX_BUFFER_BYTES,
stdio: [input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
});
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
throw new Error(`git ${args[0]} failed with exit code ${result.status ?? "unknown"}`);
}
return result.stdout;
}
function parseSingleObjectId(raw, label) {
const values = String(raw).trim().split(/\s+/u).filter(Boolean);
if (values.length !== 1 || !/^[0-9a-f]{40,64}$/u.test(values[0])) {
throw new Error(`Expected one ${label} object id.`);
}
return values[0];
}
function computePatchId(sha, mainRef, execGit) {
const mergeBase = parseSingleObjectId(
execGit(["merge-base", mainRef, sha]),
`merge base for ${sha}`,
);
const diff = execGit(["diff", mergeBase, sha]);
const patchIdLines = String(execGit(["patch-id", "--stable"], { input: diff }))
.trim()
.split(/\r?\n/u)
.filter(Boolean);
if (patchIdLines.length !== 1) {
throw new Error(`Expected one patch id for ${sha}.`);
}
const [patchId, commitId, ...rest] = patchIdLines[0].trim().split(/\s+/u);
if (
rest.length > 0 ||
!/^[0-9a-f]{40,64}$/u.test(patchId ?? "") ||
!/^[0-9a-f]{40,64}$/u.test(commitId ?? "")
) {
throw new Error(`Invalid patch-id output for ${sha}.`);
}
return patchId;
}
function ensureCommitAvailable(sha, execGit) {
try {
execGit(["cat-file", "-e", `${sha}^{commit}`]);
return true;
} catch {
try {
execGit(["fetch", "origin", sha]);
execGit(["cat-file", "-e", `${sha}^{commit}`]);
return true;
} catch {
return false;
}
}
}
function isQualifyingCiReuseRun(run) {
if (run?.event === "pull_request") {
return run?.name === "CI";
}
return isReleaseGateCiRun(run, run?.head_sha);
}
function findPatchIdenticalCiReuse({
sha,
candidateRuns,
nowMs,
mainRef = "origin/main",
execGit = runGit,
}) {
if (!Array.isArray(candidateRuns)) {
return undefined;
}
const candidates = candidateRuns
.filter(
(run) =>
run?.head_sha !== sha &&
/^[0-9a-f]{40,64}$/u.test(String(run?.head_sha ?? "")) &&
isQualifyingCiReuseRun(run) &&
isSuccessfulRecentRun(run, nowMs),
)
.toSorted((left, right) =>
String(right.updated_at ?? "").localeCompare(String(left.updated_at ?? "")),
)
.slice(0, MAX_CI_REUSE_CANDIDATES);
if (candidates.length === 0) {
return undefined;
}
let currentPatchId;
try {
currentPatchId = computePatchId(sha, mainRef, execGit);
} catch {
return undefined;
}
for (const run of candidates) {
if (!ensureCommitAvailable(run.head_sha, execGit)) {
continue;
}
try {
if (computePatchId(run.head_sha, mainRef, execGit) === currentPatchId) {
return {
run,
reusedFromSha: run.head_sha,
reusedRunId: run.id,
patchIdMatched: true,
};
}
} catch {
// A candidate is reusable only when every local git proof step succeeds.
}
}
return undefined;
}
const CI_GATE_CHECK_NAME = "openclaw/ci-gate";
/**
@@ -349,6 +475,8 @@ export function collectHostedGateEvidence({
pullRequestHeadRepository = "",
workflowRuns,
ciGateJobs = [],
loadCiReuseCandidates = () => [],
execGit = runGit,
changelogOnly = false,
nowMs = Date.now(),
}) {
@@ -357,17 +485,21 @@ export function collectHostedGateEvidence({
}
const pullRequestCommitShaSet = new Set(pullRequestCommitShas);
const collectForSha = (evidenceSha, { allowManual, requiredScheduledWorkflows = new Set() }) => {
const collectForSha = (
evidenceSha,
{ allowManual, requiredScheduledWorkflows = new Set(), ciRun },
) => {
const workflows = [];
const fallbackCoveredWorkflows = [];
if (!changelogOnly) {
workflows.push(
successfulRunOrThrow(workflowRuns, "CI", evidenceSha, {
allowManual,
nowMs,
// Gate proof only vouches for the exact head under verification.
ciGateJobs: evidenceSha === sha ? ciGateJobs : [],
}),
ciRun ??
successfulRunOrThrow(workflowRuns, "CI", evidenceSha, {
allowManual,
nowMs,
// Gate proof only vouches for the exact head under verification.
ciGateJobs: evidenceSha === sha ? ciGateJobs : [],
}),
);
}
for (const workflowName of SCHEDULED_HOSTED_WORKFLOWS) {
@@ -402,14 +534,43 @@ export function collectHostedGateEvidence({
return { workflows, fallbackCoveredWorkflows };
};
let ciRun;
let ciReuse;
if (!changelogOnly) {
try {
ciRun = successfulRunOrThrow(workflowRuns, "CI", sha, {
allowManual: true,
nowMs,
ciGateJobs,
});
} catch (exactCiError) {
let candidateRuns;
try {
candidateRuns = loadCiReuseCandidates();
} catch {
candidateRuns = [];
}
ciReuse = findPatchIdenticalCiReuse({
sha,
candidateRuns,
nowMs,
execGit,
});
if (!ciReuse) {
throw exactCiError;
}
ciRun = ciReuse.run;
}
}
let evidenceSha = sha;
let selected;
try {
selected = collectForSha(sha, { allowManual: true });
selected = collectForSha(sha, { allowManual: true, ciRun });
} catch (exactError) {
// Hosted CI proves the PR cohort, not ancestry freshness. A newer head's
// failure must not discard a complete same-PR green cohort from the last
// 24 hours; review and focused gates own the newer delta.
// Scheduled hosted workflows retain their existing recent-cohort fallback.
// CI itself is either exact-head proof or the patch-identical run selected
// above; never silently replace it with an unverified prior-head run.
const targetScheduledWorkflows = new Set(
SCHEDULED_HOSTED_WORKFLOWS.filter(
(workflowName) =>
@@ -417,6 +578,7 @@ export function collectHostedGateEvidence({
),
);
const fallbackShas = [
ciReuse?.reusedFromSha,
recentSha,
...workflowRuns
.filter(
@@ -443,6 +605,7 @@ export function collectHostedGateEvidence({
selected = collectForSha(fallbackSha, {
allowManual: false,
requiredScheduledWorkflows: targetScheduledWorkflows,
ciRun,
});
evidenceSha = fallbackSha;
break;
@@ -473,6 +636,11 @@ export function collectHostedGateEvidence({
if (evidenceSha !== sha) {
evidence.evidenceHeadSha = evidenceSha;
}
if (ciReuse) {
evidence.reusedFromSha = ciReuse.reusedFromSha;
evidence.reusedRunId = ciReuse.reusedRunId;
evidence.patchIdMatched = true;
}
if (selected.fallbackCoveredWorkflows.length > 0) {
evidence.fallbackCoveredWorkflows = selected.fallbackCoveredWorkflows;
}
@@ -520,6 +688,51 @@ function loadWorkflowRuns(repo, sha, recentSha, headBranch) {
return [...new Map(workflowRuns.map((run) => [run.id, run])).values()];
}
function loadCiReuseCandidateRuns(repo, headBranch) {
const raw = execFileSync(
"gh",
[
"run",
"list",
"--repo",
repo,
"--workflow",
"ci.yml",
"--branch",
headBranch,
"--limit",
String(CI_REUSE_RUN_LIST_LIMIT),
"--json",
"databaseId,workflowName,headSha,headBranch,event,status,conclusion,createdAt,updatedAt,url,displayTitle",
],
{
encoding: "utf8",
env: plainGhEnv(),
stdio: ["ignore", "pipe", "pipe"],
},
);
const runs = JSON.parse(stripAnsi(raw));
if (!Array.isArray(runs)) {
throw new Error("Expected gh run list to return an array.");
}
// The workflow selector above supplies the path identity that the REST
// release-gate matcher normally reads from each full workflow-run object.
return runs.map((run) => ({
id: run.databaseId,
name: run.workflowName,
event: run.event,
status: run.status,
conclusion: run.conclusion,
head_sha: run.headSha,
head_branch: run.headBranch,
path: CI_WORKFLOW_PATH,
created_at: run.createdAt,
updated_at: run.updatedAt,
html_url: run.url,
display_title: run.displayTitle,
}));
}
export function compareCommitPageCount(totalCommits) {
if (!Number.isSafeInteger(totalCommits) || totalCommits < 0) {
throw new Error("Expected comparison total_commits to be a non-negative integer.");
@@ -643,6 +856,7 @@ function main(argv = process.argv.slice(2)) {
pullRequestHeadRepository: headRepository,
workflowRuns,
ciGateJobs: loadCiGateJobs(args.repo, workflowRuns, args.sha),
loadCiReuseCandidates: () => loadCiReuseCandidateRuns(args.repo, headBranch),
changelogOnly: args.changelogOnly,
});
const evidenceHeadSha = evidence.evidenceHeadSha ?? args.sha;
@@ -652,13 +866,28 @@ function main(argv = process.argv.slice(2)) {
repo: args.repo,
pullRequestNumber: args.pr,
selection: {
mode: evidenceHeadSha === args.sha ? "exact-head" : "recent-pr-head",
mode: evidence.patchIdMatched
? "patch-identical-pre-rebase"
: evidenceHeadSha === args.sha
? "exact-head"
: "recent-pr-head",
maxAgeHours: HOSTED_GATE_MAX_AGE_HOURS,
},
...evidence,
};
mkdirSync(path.dirname(args.output), { recursive: true });
writeFileSync(args.output, `${JSON.stringify(manifest, null, 2)}\n`);
if (evidence.patchIdMatched) {
const reusedRun = manifest.workflows.find((workflow) => workflow.id === evidence.reusedRunId);
const updatedAtMs = runUpdatedAtMs({ updated_at: reusedRun?.updatedAt });
const ageHours =
updatedAtMs === null
? "unknown"
: `${(Math.max(0, Date.now() - updatedAtMs) / (60 * 60 * 1_000)).toFixed(1)}h`;
console.log(
`hosted CI reused from patch-identical pre-rebase head ${evidence.reusedFromSha} (run ${evidence.reusedRunId}, age ${ageHours})`,
);
}
console.log(
`Hosted gates passed for PR #${args.pr} at ${args.sha} using ${evidenceHeadSha}: ${manifest.workflows
.map((workflow) => `${workflow.name}#${workflow.id}`)
+365 -37
View File
@@ -13,6 +13,7 @@ import {
const sha = "773ffd87a1e1e34451ad6e38fda37380c2569a50";
const previousSha = "8d86c44c6144f8f726a460914cddb8c9c201f119";
const scheduledFallbackSha = "ad620a11e5d9ed3888b6afb3c35c4c30e8054f4e";
const pr = 100606;
const nowMs = Date.parse("2026-06-17T10:55:00Z");
const BUILD_ARTIFACTS_WORKFLOW = "Blacksmith Build Artifacts Testbox";
@@ -27,7 +28,24 @@ const requiredCliArgs = [
".local/gates-hosted-checks.json",
];
function successfulRun(name: string, id: number, updatedAt: string) {
type WorkflowRunFixture = {
id: number;
name: string;
event: string;
status: string;
conclusion: string | null;
head_sha: string;
head_branch: string;
head_repository: { full_name: string };
pull_requests: Array<{ number: number }>;
path: string;
created_at: string;
updated_at: string;
html_url: string;
display_title?: string;
};
function successfulRun(name: string, id: number, updatedAt: string): WorkflowRunFixture {
return {
id,
name,
@@ -68,13 +86,234 @@ function queuedBuildArtifactFallbackRuns() {
];
}
function collectHostedGateEvidence(
options: Omit<Parameters<typeof collectHostedGateEvidenceRaw>[0], "nowMs" | "pr">,
function collectHostedGateEvidence(options: Omit<CollectHostedGateOptions, "nowMs" | "pr">) {
return collectHostedGateEvidenceWithReuse({ nowMs, pr, ...options });
}
type GitExec = (args: string[], options?: { input?: string }) => string;
type CollectHostedGateOptions = Parameters<typeof collectHostedGateEvidenceRaw>[0] & {
loadCiReuseCandidates?: () => Array<Record<string, unknown>>;
execGit?: GitExec;
};
type HostedGateEvidence = ReturnType<typeof collectHostedGateEvidenceRaw> & {
reusedFromSha?: string;
reusedRunId?: unknown;
patchIdMatched?: boolean;
};
const collectHostedGateEvidenceWithReuse = collectHostedGateEvidenceRaw as unknown as (
options: CollectHostedGateOptions,
) => HostedGateEvidence;
function priorSuccessfulCiRun(overrides: Partial<WorkflowRunFixture> = {}): WorkflowRunFixture {
return {
...successfulRun("CI", 101, "2026-06-17T09:55:00Z"),
head_sha: previousSha,
...overrides,
};
}
type PatchIdExecOptions = {
currentPatchId?: string;
priorPatchId?: string;
unfetchableShas?: Set<string>;
failCommand?: string;
};
function createPatchIdExec({
currentPatchId: suppliedCurrentPatchId = "a".repeat(40),
priorPatchId,
unfetchableShas = new Set<string>(),
failCommand = "",
}: PatchIdExecOptions = {}) {
const currentPatchId: string = suppliedCurrentPatchId;
const resolvedPriorPatchId = priorPatchId ?? currentPatchId;
const calls: string[] = [];
const execGit: GitExec = (args, options = {}) => {
const command = args.join(" ");
calls.push(command);
if (command === failCommand) {
throw new Error(`mock failure: ${command}`);
}
switch (args[0]) {
case "cat-file": {
const candidateSha = args[2]?.replace(/\^\{commit\}$/u, "") ?? "";
if (unfetchableShas.has(candidateSha)) {
throw new Error("missing object");
}
return "";
}
case "fetch":
if (unfetchableShas.has(args[2] ?? "")) {
throw new Error("unfetchable object");
}
return "";
case "merge-base":
return `${(args[2] === sha ? "b" : "c").repeat(40)}\n`;
case "diff":
return `diff:${args[2]}`;
case "patch-id": {
const patchId = options.input === `diff:${sha}` ? currentPatchId : resolvedPriorPatchId;
return `${patchId} ${"0".repeat(40)}\n`;
}
default:
throw new Error(`unexpected git command: ${command}`);
}
};
return { calls, execGit };
}
function patchReuseOptions(
candidate: WorkflowRunFixture = priorSuccessfulCiRun(),
execGit = createPatchIdExec().execGit,
) {
return collectHostedGateEvidenceRaw({ nowMs, pr, ...options });
return {
loadCiReuseCandidates: () => [candidate],
execGit,
};
}
describe("verify-pr-hosted-gates", () => {
it("reuses successful recent CI from a patch-identical pre-rebase head", () => {
const candidate = priorSuccessfulCiRun();
const evidence = collectHostedGateEvidence({
sha,
workflowRuns: [],
...patchReuseOptions(candidate),
});
expect(evidence).toEqual({
headSha: sha,
workflows: [expect.objectContaining({ id: 101, name: "CI", headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 101,
patchIdMatched: true,
});
});
it("rejects a successful prior-head CI run whose patch differs", () => {
const { execGit } = createPatchIdExec({ priorPatchId: "d".repeat(40) });
expect(() =>
collectHostedGateEvidence({
sha,
workflowRuns: [],
...patchReuseOptions(priorSuccessfulCiRun(), execGit),
}),
).toThrow(`Missing successful recent CI workflow for ${sha}`);
});
it("rejects patch-identical CI reuse after the 24-hour window", () => {
let gitCalled = false;
expect(() =>
collectHostedGateEvidence({
sha,
workflowRuns: [],
loadCiReuseCandidates: () => [priorSuccessfulCiRun({ updated_at: "2026-06-16T10:54:59Z" })],
execGit: () => {
gitCalled = true;
throw new Error("stale candidates must be filtered before git");
},
}),
).toThrow(`Missing successful recent CI workflow for ${sha}`);
expect(gitCalled).toBe(false);
});
it("skips an unfetchable prior head", () => {
const { calls, execGit } = createPatchIdExec({
unfetchableShas: new Set([previousSha]),
});
expect(() =>
collectHostedGateEvidence({
sha,
workflowRuns: [],
...patchReuseOptions(priorSuccessfulCiRun(), execGit),
}),
).toThrow(`Missing successful recent CI workflow for ${sha}`);
expect(calls).toContain(`fetch origin ${previousSha}`);
});
it("fails closed when patch-id computation errors", () => {
const { execGit } = createPatchIdExec({
failCommand: `merge-base origin/main ${previousSha}`,
});
expect(() =>
collectHostedGateEvidence({
sha,
workflowRuns: [],
...patchReuseOptions(priorSuccessfulCiRun(), execGit),
}),
).toThrow(`Missing successful recent CI workflow for ${sha}`);
});
it("filters prior runs by successful qualifying CI shape", () => {
const invalidCandidates = [
priorSuccessfulCiRun({ id: 1, conclusion: "failure" }),
priorSuccessfulCiRun({ id: 2, name: "Docs" }),
priorSuccessfulCiRun({
id: 3,
event: "workflow_dispatch",
display_title: `CI release gate ${previousSha}`,
path: ".github/workflows/not-ci.yml",
}),
];
let gitCalled = false;
expect(() =>
collectHostedGateEvidence({
sha,
workflowRuns: [],
loadCiReuseCandidates: () => invalidCandidates,
execGit: () => {
gitCalled = true;
throw new Error("invalid candidates must be filtered before git");
},
}),
).toThrow(`Missing successful recent CI workflow for ${sha}`);
expect(gitCalled).toBe(false);
});
it("accepts a patch-identical prior release-gate run with the exact dispatch title", () => {
const candidate = priorSuccessfulCiRun({
id: 102,
event: "workflow_dispatch",
path: ".github/workflows/ci.yml",
display_title: `CI release gate ${previousSha}`,
});
expect(
collectHostedGateEvidence({
sha,
workflowRuns: [],
...patchReuseOptions(candidate),
}),
).toEqual({
headSha: sha,
workflows: [expect.objectContaining({ id: 102, event: "workflow_dispatch" })],
reusedFromSha: previousSha,
reusedRunId: 102,
patchIdMatched: true,
});
});
it("short-circuits reuse discovery when exact-head CI already succeeds", () => {
let reuseCalled = false;
const evidence = collectHostedGateEvidence({
sha,
workflowRuns: [successfulRun("CI", 1, "2026-06-17T10:47:00Z")],
loadCiReuseCandidates: () => {
reuseCalled = true;
throw new Error("exact-head success must not inspect reuse candidates");
},
execGit: () => {
reuseCalled = true;
throw new Error("exact-head success must not execute git reuse proof");
},
});
expect(evidence).toEqual({
headSha: sha,
workflows: [expect.objectContaining({ id: 1, headSha: sha })],
});
expect(reuseCalled).toBe(false);
});
it("accepts an in-progress CI run whose own attempt's ci-gate job succeeded", () => {
const inProgressRun = {
...successfulRun("CI", 42, "2026-06-17T10:52:00Z"),
@@ -235,68 +474,77 @@ describe("verify-pr-hosted-gates", () => {
});
it("accepts 13-hour green evidence from the recorded pre-rebase head", () => {
const priorRun = {
...successfulRun("CI", 1, "2026-06-16T21:55:00Z"),
head_sha: previousSha,
};
const evidence = collectHostedGateEvidence({
sha,
recentSha: previousSha,
workflowRuns: [
{
...successfulRun("CI", 1, "2026-06-16T21:55:00Z"),
head_sha: previousSha,
},
priorRun,
{
...successfulRun("CI", 2, "2026-06-17T10:54:00Z"),
status: "in_progress",
conclusion: null,
},
],
...patchReuseOptions(priorRun),
});
expect(evidence).toEqual({
headSha: sha,
evidenceHeadSha: previousSha,
workflows: [expect.objectContaining({ name: "CI", id: 1, headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
});
it("accepts recent green evidence from an earlier head of the same PR", () => {
const priorRun = {
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
};
const evidence = collectHostedGateEvidence({
sha,
workflowRuns: [
{
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
},
priorRun,
{
...successfulRun("CI", 2, "2026-06-17T10:54:00Z"),
status: "in_progress",
conclusion: null,
},
],
...patchReuseOptions(priorRun),
});
expect(evidence).toEqual({
headSha: sha,
evidenceHeadSha: previousSha,
workflows: [expect.objectContaining({ name: "CI", id: 1, headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
});
it("accepts a recent green fork head when GitHub omits pull request links", () => {
const headBranch = "fix/token-listener";
const headRepository = "contributor/openclaw";
const priorRun = {
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
head_branch: headBranch,
head_repository: { full_name: headRepository },
pull_requests: [],
};
const evidence = collectHostedGateEvidence({
sha,
pullRequestCommitShas: [previousSha, sha],
pullRequestHeadBranch: headBranch,
pullRequestHeadRepository: headRepository,
workflowRuns: [
{
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
head_branch: headBranch,
head_repository: { full_name: headRepository },
pull_requests: [],
},
priorRun,
{
...successfulRun("CI", 2, "2026-06-17T10:54:00Z"),
head_branch: headBranch,
@@ -305,12 +553,15 @@ describe("verify-pr-hosted-gates", () => {
conclusion: "failure",
},
],
...patchReuseOptions(priorRun),
});
expect(evidence).toEqual({
headSha: sha,
evidenceHeadSha: previousSha,
workflows: [expect.objectContaining({ name: "CI", id: 1, headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
});
@@ -386,9 +637,14 @@ describe("verify-pr-hosted-gates", () => {
targetArmRun,
];
expect(() => collectHostedGateEvidence({ sha, recentSha: previousSha, workflowRuns })).toThrow(
`Missing successful recent Blacksmith ARM Testbox workflow for ${previousSha}`,
);
expect(() =>
collectHostedGateEvidence({
sha,
recentSha: previousSha,
workflowRuns,
...patchReuseOptions(workflowRuns[0]),
}),
).toThrow(`Missing successful recent Blacksmith ARM Testbox workflow for ${previousSha}`);
const evidence = collectHostedGateEvidence({
sha,
@@ -400,35 +656,85 @@ describe("verify-pr-hosted-gates", () => {
head_sha: previousSha,
},
],
...patchReuseOptions(workflowRuns[0]),
});
expect(evidence.workflows).toEqual([
expect.objectContaining({ name: "CI", headSha: previousSha }),
expect.objectContaining({ name: "Blacksmith ARM Testbox", headSha: previousSha }),
]);
expect(evidence).toMatchObject({
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
});
it("keeps the existing scheduled-workflow fallback after CI reuses another head", () => {
const priorCiRun = priorSuccessfulCiRun({ id: 1 });
const evidence = collectHostedGateEvidence({
sha,
workflowRuns: [
priorCiRun,
{
...successfulRun("CI", 2, "2026-06-17T10:54:00Z"),
status: "in_progress",
conclusion: null,
},
{
...successfulRun("Blacksmith ARM Testbox", 3, "2026-06-17T10:54:00Z"),
status: "queued",
conclusion: null,
},
{
...successfulRun("Blacksmith ARM Testbox", 4, "2026-06-17T10:53:00Z"),
head_sha: scheduledFallbackSha,
},
],
...patchReuseOptions(priorCiRun),
});
expect(evidence).toMatchObject({
headSha: sha,
evidenceHeadSha: scheduledFallbackSha,
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
workflows: [
expect.objectContaining({ name: "CI", headSha: previousSha }),
expect.objectContaining({
name: "Blacksmith ARM Testbox",
headSha: scheduledFallbackSha,
}),
],
});
});
it.each(["failure", "cancelled", "skipped"])(
"reuses recent same-PR green evidence after a current-head %s run",
(conclusion) => {
const priorRun = {
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
};
const evidence = collectHostedGateEvidence({
sha,
recentSha: previousSha,
workflowRuns: [
{
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
},
priorRun,
{
...successfulRun("CI", 2, "2026-06-17T10:54:00Z"),
conclusion,
},
],
...patchReuseOptions(priorRun),
});
expect(evidence).toEqual({
headSha: sha,
evidenceHeadSha: previousSha,
workflows: [expect.objectContaining({ name: "CI", id: 1, headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
},
);
@@ -473,7 +779,16 @@ describe("verify-pr-hosted-gates", () => {
status: "in_progress",
conclusion: null,
},
{
...successfulRun("Blacksmith ARM Testbox", 4, "2026-06-17T10:54:00Z"),
status: "queued",
conclusion: null,
},
],
...patchReuseOptions({
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
}),
}),
).toThrow(`Missing successful recent Blacksmith ARM Testbox workflow for ${previousSha}`);
});
@@ -497,20 +812,30 @@ describe("verify-pr-hosted-gates", () => {
status: "in_progress",
conclusion: null,
},
{
...successfulRun("Blacksmith ARM Testbox", 4, "2026-06-17T10:54:00Z"),
status: "queued",
conclusion: null,
},
],
...patchReuseOptions({
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
}),
}),
).toThrow(`Missing successful recent Blacksmith ARM Testbox workflow for ${previousSha}`);
});
it("reuses pre-rebase green evidence after a failed current-head manual gate", () => {
const priorRun = {
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
};
const evidence = collectHostedGateEvidence({
sha,
recentSha: previousSha,
workflowRuns: [
{
...successfulRun("CI", 1, "2026-06-17T10:50:00Z"),
head_sha: previousSha,
},
priorRun,
{
...successfulRun("CI", 2, "2026-06-17T10:53:00Z"),
status: "in_progress",
@@ -523,12 +848,15 @@ describe("verify-pr-hosted-gates", () => {
conclusion: "failure",
},
],
...patchReuseOptions(priorRun),
});
expect(evidence).toEqual({
headSha: sha,
evidenceHeadSha: previousSha,
workflows: [expect.objectContaining({ name: "CI", id: 1, headSha: previousSha })],
reusedFromSha: previousSha,
reusedRunId: 1,
patchIdMatched: true,
});
});
@@ -549,7 +877,7 @@ describe("verify-pr-hosted-gates", () => {
recentSha: previousSha,
workflowRuns: [staleRun, currentPending],
}),
).toThrow(`Missing successful recent CI workflow for ${previousSha}`);
).toThrow(`Missing successful recent CI workflow for ${sha}`);
const recentUnrelatedRun = {
...successfulRun("CI", 4, "2026-06-17T10:50:00Z"),
@@ -562,7 +890,7 @@ describe("verify-pr-hosted-gates", () => {
recentSha: previousSha,
workflowRuns: [recentUnrelatedRun, currentPending],
}),
).toThrow(`Missing successful recent CI workflow for ${previousSha}`);
).toThrow(`Missing successful recent CI workflow for ${sha}`);
expect(() =>
collectHostedGateEvidence({
sha,