mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-22 13:31:16 +00:00
ci: stabilize release validation tests (#108420)
* test(codex): remove turn-watch timing race * ci: pin workflow sanity shellcheck * ci: run workflow sanity on blacksmith * ci: serialize workflow sanity lint * ci: bound workflow lint stalls * ci: cap actionlint process fanout * ci: report stalled workflow shellcheck * ci: stabilize release validation checks * ci: restore release workflow shellcheck * test: preserve codex turn watch reset proof
This commit is contained in:
committed by
GitHub
parent
ff8a015981
commit
28d30e9dae
112
test/scripts/openclaw-npm-resume-run.test.ts
Normal file
112
test/scripts/openclaw-npm-resume-run.test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
resolveOpenClawNpmResumeRun,
|
||||
validateOpenClawNpmResumeRun,
|
||||
} from "../../scripts/openclaw-npm-resume-run.mjs";
|
||||
import type { OpenClawNpmResumeValidationInput } from "../../scripts/openclaw-npm-resume-run.mjs";
|
||||
|
||||
const SHA = "a".repeat(40);
|
||||
const TAG_OBJECT_SHA = "b".repeat(40);
|
||||
const BRANCH = `release-publish/${SHA.slice(0, 12)}-123`;
|
||||
const URL = "https://github.com/openclaw/openclaw/actions/runs/456";
|
||||
|
||||
function fixture(
|
||||
overrides: Partial<OpenClawNpmResumeValidationInput> = {},
|
||||
): OpenClawNpmResumeValidationInput {
|
||||
return {
|
||||
canonicalWorkflowId: 101,
|
||||
compareStatus: "identical",
|
||||
jobs: [{ conclusion: "success", name: "validate_publish_request" }],
|
||||
run: {
|
||||
conclusion: "success",
|
||||
event: "workflow_dispatch",
|
||||
head_branch: BRANCH,
|
||||
head_sha: SHA,
|
||||
html_url: URL,
|
||||
path: `.github/workflows/openclaw-npm-release.yml@refs/tags/${BRANCH}`,
|
||||
workflow_id: 101,
|
||||
},
|
||||
tag: {
|
||||
object: { sha: SHA, type: "commit" },
|
||||
verification: { verified: true },
|
||||
},
|
||||
tagRef: { object: { sha: TAG_OBJECT_SHA, type: "tag" } },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("openclaw npm resume run identity", () => {
|
||||
it("accepts a successful run bound to a signed main-reachable tooling tag", () => {
|
||||
expect(validateOpenClawNpmResumeRun(fixture())).toEqual({
|
||||
tagObjectSha: TAG_OBJECT_SHA,
|
||||
url: URL,
|
||||
workflowRef: `refs/tags/${BRANCH}`,
|
||||
workflowSha: SHA,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["branch", { run: { ...fixture().run, head_branch: "main" } }, "untrusted workflow ref"],
|
||||
["workflow", { run: { ...fixture().run, workflow_id: 999 } }, "untrusted workflow identity"],
|
||||
["event", { run: { ...fixture().run, event: "push" } }, "untrusted workflow identity"],
|
||||
[
|
||||
"conclusion",
|
||||
{ run: { ...fixture().run, conclusion: "failure" } },
|
||||
"untrusted workflow identity",
|
||||
],
|
||||
[
|
||||
"path",
|
||||
{ run: { ...fixture().run, path: ".github/workflows/ci.yml" } },
|
||||
"untrusted workflow identity",
|
||||
],
|
||||
[
|
||||
"tag kind",
|
||||
{ tagRef: { object: { sha: TAG_OBJECT_SHA, type: "commit" } } },
|
||||
"not a signed annotated tag",
|
||||
],
|
||||
[
|
||||
"tag target",
|
||||
{ tag: { ...fixture().tag, object: { sha: "c".repeat(40), type: "commit" } } },
|
||||
"not bound to a real",
|
||||
],
|
||||
[
|
||||
"signature",
|
||||
{ tag: { ...fixture().tag, verification: { verified: false } } },
|
||||
"not bound to a real",
|
||||
],
|
||||
["main ancestry", { compareStatus: "diverged" }, "not bound to a real"],
|
||||
[
|
||||
"approval",
|
||||
{ jobs: [{ conclusion: "failure", name: "validate_publish_request" }] },
|
||||
"lacks successful parent release approval",
|
||||
],
|
||||
])("rejects an untrusted %s", (_label, overrides, message) => {
|
||||
expect(() => validateOpenClawNpmResumeRun(fixture(overrides))).toThrow(message);
|
||||
});
|
||||
|
||||
it("loads the exact run, workflow, signed tag, ancestry, and approval job", () => {
|
||||
const responses = new Map<string, unknown>([
|
||||
[`api repos/openclaw/openclaw/actions/runs/456 --method GET`, fixture().run],
|
||||
[
|
||||
`api repos/openclaw/openclaw/actions/workflows/openclaw-npm-release.yml --method GET`,
|
||||
{ id: 101 },
|
||||
],
|
||||
[`api repos/openclaw/openclaw/git/ref/tags/${BRANCH} --method GET`, fixture().tagRef],
|
||||
[`api repos/openclaw/openclaw/git/tags/${TAG_OBJECT_SHA} --method GET`, fixture().tag],
|
||||
[`api repos/openclaw/openclaw/compare/${SHA}...main --method GET`, { status: "identical" }],
|
||||
[`run view 456 --repo openclaw/openclaw --json jobs --jq .jobs`, fixture().jobs],
|
||||
]);
|
||||
const runGh = vi.fn((args: string[]) => {
|
||||
const response = responses.get(args.join(" "));
|
||||
if (!response) {
|
||||
throw new Error(`Unexpected gh invocation: ${args.join(" ")}`);
|
||||
}
|
||||
return JSON.stringify(response);
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveOpenClawNpmResumeRun({ repo: "openclaw/openclaw", runGh, runId: "456" }),
|
||||
).toMatchObject({ workflowRef: `refs/tags/${BRANCH}`, workflowSha: SHA });
|
||||
expect(runGh).toHaveBeenCalledTimes(6);
|
||||
});
|
||||
});
|
||||
@@ -593,7 +593,6 @@ describe("package acceptance workflow", () => {
|
||||
if (!orchestration) {
|
||||
throw new Error("Expected release publish orchestration script");
|
||||
}
|
||||
const matcher = shellFunctionSource(orchestration, "release_evidence_zip_trees_match");
|
||||
const tempDir = tempDirs.make("release-evidence-zip-");
|
||||
const sourceDir = `${tempDir}/source`;
|
||||
const existingDir = `${tempDir}/existing`;
|
||||
@@ -621,20 +620,9 @@ describe("package acceptance workflow", () => {
|
||||
writeFileSync(corruptZip, sourceArchive.subarray(0, sourceArchive.length - 10));
|
||||
|
||||
const compare = (left: string, right: string) =>
|
||||
spawnSync(
|
||||
"bash",
|
||||
[
|
||||
"-c",
|
||||
`set -euo pipefail\n${matcher}\nrelease_evidence_zip_trees_match "$1" "$2"`,
|
||||
"bash",
|
||||
left,
|
||||
right,
|
||||
],
|
||||
{
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, RUNNER_TEMP: tempDir },
|
||||
},
|
||||
);
|
||||
spawnSync("python3", ["scripts/compare-release-evidence-zip.py", left, right], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
const result = compare(sourceZip, existingZip);
|
||||
const symlinkResult = compare(symlinkZip, symlinkZip);
|
||||
@@ -649,6 +637,9 @@ describe("package acceptance workflow", () => {
|
||||
expect(orchestration).toContain(
|
||||
'attach_or_verify_release_asset "${asset_path}" "${asset_name}" zip-tree',
|
||||
);
|
||||
expect(orchestration).toContain(
|
||||
'"${GITHUB_WORKSPACE}/.release-harness/scripts/compare-release-evidence-zip.py"',
|
||||
);
|
||||
});
|
||||
|
||||
it("verifies immutable postpublish evidence before stable closeout reads it", () => {
|
||||
@@ -4111,15 +4102,11 @@ describe("package artifact reuse", () => {
|
||||
expect(releaseWorkflow).toContain("Approve child release gate after parent release approval");
|
||||
expect(releaseWorkflow).toContain("openclaw_npm_resume_run_id");
|
||||
expect(releaseWorkflow).toContain(
|
||||
'.name == "validate_publish_request" and .conclusion == "success"',
|
||||
'"${GITHUB_WORKSPACE}/.release-harness/scripts/openclaw-npm-resume-run.mjs"',
|
||||
);
|
||||
expect(releaseWorkflow).toContain("actions/workflows/openclaw-npm-release.yml\" --jq '.id'");
|
||||
expect(releaseWorkflow).toContain(
|
||||
'".github/workflows/openclaw-npm-release.yml@refs/tags/${resume_branch}"',
|
||||
);
|
||||
expect(releaseWorkflow).toContain("git/ref/tags/${resume_branch}");
|
||||
expect(releaseWorkflow).toContain(".verification.verified");
|
||||
expect(releaseWorkflow).toContain("compare/${resume_sha}...main");
|
||||
expect(releaseWorkflow).toContain('--run-id "${OPENCLAW_NPM_RESUME_RUN_ID}"');
|
||||
expect(releaseWorkflow).toContain("openclaw_npm_expected_workflow_ref=\"$(printf '%s'");
|
||||
expect(releaseWorkflow).toContain("openclaw_npm_expected_workflow_sha=\"$(printf '%s'");
|
||||
expect(releaseWorkflow).toContain(
|
||||
'"${GITHUB_WORKSPACE}/.release-harness/scripts/openclaw-npm-postpublish-verify.ts"',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user