From 57ae43a89c925005ad8aadf03d1771f866b85b18 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 16 Jul 2026 10:26:29 +0100 Subject: [PATCH] fix(ci): harden full release validation --- .github/workflows/openclaw-performance.yml | 3 +- scripts/full-release-validation-at-sha.mjs | 81 +++---------------- .../full-release-validation-at-sha.test.ts | 24 +----- 3 files changed, 19 insertions(+), 89 deletions(-) diff --git a/.github/workflows/openclaw-performance.yml b/.github/workflows/openclaw-performance.yml index 1ce6a04e162..140ca4451ac 100644 --- a/.github/workflows/openclaw-performance.yml +++ b/.github/workflows/openclaw-performance.yml @@ -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: diff --git a/scripts/full-release-validation-at-sha.mjs b/scripts/full-release-validation-at-sha.mjs index 107237604bf..5c7a46a5508 100755 --- a/scripts/full-release-validation-at-sha.mjs +++ b/scripts/full-release-validation-at-sha.mjs @@ -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-ref ] [--workflow-sha ] [--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( diff --git a/test/scripts/full-release-validation-at-sha.test.ts b/test/scripts/full-release-validation-at-sha.test.ts index 6b25791a695..6b561f10f1f 100644 --- a/test/scripts/full-release-validation-at-sha.test.ts +++ b/test/scripts/full-release-validation-at-sha.test.ts @@ -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"'); });