fix(ci): harden full release validation

This commit is contained in:
Peter Steinberger
2026-07-16 10:26:29 +01:00
parent e322edccfa
commit 57ae43a89c
3 changed files with 19 additions and 89 deletions
+2 -1
View File
@@ -71,7 +71,8 @@ env:
OCM_LINUX_X64_SHA256: 57530199d21eb5bfa29695749928b40fd2869484c7edff69b7c65bfc84f2f1aa
KOVA_REPOSITORY: openclaw/Kova
PERFORMANCE_MODEL_ID: gpt-5.6-luna
KOVA_SCENARIO_TIMEOUT_MS: "300000"
# Release matrices cold-build the candidate runtime before measurement.
KOVA_SCENARIO_TIMEOUT_MS: ${{ inputs.profile == 'release' && '900000' || '300000' }}
jobs:
resolve_target:
+13 -68
View File
@@ -16,28 +16,6 @@ const DEFAULT_INPUTS = {
rerun_group: "all",
reuse_evidence: "true",
};
const WORKFLOW_RUN_CHECK_SUITE_QUERY = `
query($owner: String!, $repo: String!, $oid: GitObjectID!, $after: String) {
repository(owner: $owner, name: $repo) {
object(oid: $oid) {
... on Commit {
checkSuites(first: 100, after: $after) {
nodes {
status
conclusion
workflowRun {
url
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
}`;
function usage() {
console.error(`Usage: node scripts/full-release-validation-at-sha.mjs [--sha <target-sha>] [--target-ref <canonical-release-branch-or-tag>] [--workflow-sha <trusted-main-ref>] [--keep-branch] [--dry-run] [-- -f key=value ...]
@@ -282,50 +260,19 @@ function findLatestRunId(branch, sha) {
return match?.databaseId ? String(match.databaseId) : "";
}
export function selectWorkflowRunCheckSuite(nodes, parentRunId) {
function readWorkflowRun(parentRunId, workflowSha) {
if (!/^[1-9][0-9]*$/u.test(String(parentRunId))) {
throw new Error("parent run ID must be a positive decimal");
}
const expectedUrl = `https://github.com/openclaw/openclaw/actions/runs/${parentRunId}`;
return nodes.find((node) => node?.workflowRun?.url === expectedUrl);
}
function readWorkflowRunCheckSuite(parentRunId, workflowSha) {
let after;
for (let page = 0; page < 20; page += 1) {
const queryArgs = [
"api",
"graphql",
"-F",
"owner=openclaw",
"-F",
"repo=openclaw",
"-F",
`oid=${workflowSha}`,
"-f",
`query=${WORKFLOW_RUN_CHECK_SUITE_QUERY}`,
];
if (after) {
queryArgs.push("-F", `after=${after}`);
}
const response = JSON.parse(run("gh", queryArgs));
const checkSuites = response?.data?.repository?.object?.checkSuites;
if (!checkSuites || !Array.isArray(checkSuites.nodes)) {
throw new Error("GraphQL response did not include commit check suites");
}
const match = selectWorkflowRunCheckSuite(checkSuites.nodes, parentRunId);
if (match) {
return match;
}
if (!checkSuites.pageInfo?.hasNextPage) {
return undefined;
}
after = checkSuites.pageInfo.endCursor;
if (!after) {
throw new Error("GraphQL check-suite pagination omitted its end cursor");
}
const workflowRun = JSON.parse(
run("gh", ["api", `repos/openclaw/openclaw/actions/runs/${parentRunId}`]),
);
if (workflowRun.head_sha !== workflowSha) {
throw new Error(
`Full Release Validation run ${parentRunId} head ${String(workflowRun.head_sha)} does not match trusted workflow SHA ${workflowSha}`,
);
}
throw new Error("Full Release Validation check suite was not found within 20 pages");
return workflowRun;
}
function waitForWorkflowRun(parentRunId, workflowSha) {
@@ -334,7 +281,7 @@ function waitForWorkflowRun(parentRunId, workflowSha) {
for (let attempt = 0; attempt < 480; attempt += 1) {
let suite;
try {
suite = readWorkflowRunCheckSuite(parentRunId, workflowSha);
suite = readWorkflowRun(parentRunId, workflowSha);
consecutiveErrors = 0;
} catch (error) {
consecutiveErrors += 1;
@@ -345,15 +292,13 @@ function waitForWorkflowRun(parentRunId, workflowSha) {
console.warn(`Parent run status query failed; retrying: ${message}`);
}
const summary = suite
? `${String(suite.status).toLowerCase()}/${String(suite.conclusion ?? "pending").toLowerCase()}`
: "awaiting-check-suite";
const summary = `${String(suite?.status ?? "pending").toLowerCase()}/${String(suite?.conclusion ?? "pending").toLowerCase()}`;
if (summary !== lastSummary) {
console.log(`Parent run status: ${summary}`);
lastSummary = summary;
}
if (suite?.status === "COMPLETED") {
if (suite.conclusion === "SUCCESS") {
if (suite?.status === "completed") {
if (suite.conclusion === "success") {
return;
}
throw new Error(
@@ -8,7 +8,6 @@ import {
releaseEvidenceVerificationArgs,
releaseEvidenceVerifierPath,
resolveRemoteTargetRefSha,
selectWorkflowRunCheckSuite,
} from "../../scripts/full-release-validation-at-sha.mjs";
describe("full-release-validation-at-sha", () => {
@@ -148,26 +147,11 @@ describe("full-release-validation-at-sha", () => {
expect(() => releaseEvidenceVerificationArgs("")).toThrow("positive decimal");
});
it("selects the exact workflow run through GraphQL check-suite metadata", () => {
const nodes = [
{
status: "COMPLETED",
conclusion: "SUCCESS",
workflowRun: { url: "https://github.com/openclaw/openclaw/actions/runs/122" },
},
{
status: "IN_PROGRESS",
conclusion: null,
workflowRun: { url: "https://github.com/openclaw/openclaw/actions/runs/123" },
},
];
expect(selectWorkflowRunCheckSuite(nodes, "123")).toEqual(nodes[1]);
expect(selectWorkflowRunCheckSuite(nodes, "999")).toBeUndefined();
expect(() => selectWorkflowRunCheckSuite(nodes, "")).toThrow("positive decimal");
it("polls the exact workflow run without GraphQL quota use", () => {
const source = readFileSync("scripts/full-release-validation-at-sha.mjs", "utf8");
expect(source).toContain("checkSuites(first: 100, after: $after)");
expect(source).toContain("actions/runs/${parentRunId}");
expect(source).toContain("workflowRun.head_sha !== workflowSha");
expect(source).not.toContain('"graphql"');
expect(source).not.toContain('["run", "watch"');
});