fix(release): preserve focused evidence lineage (#129060)

* fix(release): preserve focused evidence lineage

* fix(release): bind focused evidence to authorized tooling

* fix(release): revalidate focused evidence after Docker approval
This commit is contained in:
Peter Steinberger
2026-08-25 01:58:18 -07:00
committed by GitHub
parent ccd8ede95e
commit afa89f03ee
6 changed files with 455 additions and 24 deletions
@@ -134,5 +134,7 @@ jobs:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
permissions:
actions: read
attestations: read
contents: read
packages: write
+111
View File
@@ -16,6 +16,26 @@ on:
required: false
default: ""
type: string
focused_release_evidence_run_id:
description: Optional immutable authorized beta focused evidence producer run
required: false
default: ""
type: string
focused_release_evidence_run_attempt:
description: Exact previously verified focused evidence producer attempt
required: false
default: ""
type: string
focused_release_evidence_workflow_full_ref:
description: Exact previously verified focused evidence producer tag
required: false
default: ""
type: string
focused_release_evidence_workflow_sha:
description: Exact previously verified focused evidence producer SHA
required: false
default: ""
type: string
outputs:
version:
description: Resolved Docker release version without the v prefix
@@ -178,6 +198,8 @@ jobs:
if: ${{ always() && needs.approve_docker_publish.result == 'success' && needs.resolve_release_policy.result == 'success' && needs.validate_publish_config.result == 'success' }}
runs-on: ubuntu-24.04
permissions:
actions: read
attestations: read
contents: read
outputs:
built_at: ${{ steps.build_provenance.outputs.built_at }}
@@ -188,6 +210,95 @@ jobs:
with:
ref: ${{ inputs.release_sha }}
fetch-depth: 0
persist-credentials: false
- name: Checkout trusted focused evidence verifier
if: ${{ inputs.focused_release_evidence_run_id != '' }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
path: .release-harness
fetch-depth: 1
persist-credentials: false
- name: Revalidate focused evidence producer after Docker approval
if: ${{ inputs.focused_release_evidence_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
FOCUSED_RELEASE_EVIDENCE_RUN_ID: ${{ inputs.focused_release_evidence_run_id }}
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: ${{ inputs.focused_release_evidence_run_attempt }}
PRODUCER_WORKFLOW_FULL_REF: ${{ inputs.focused_release_evidence_workflow_full_ref }}
PRODUCER_WORKFLOW_SHA: ${{ inputs.focused_release_evidence_workflow_sha }}
run: |
set -euo pipefail
if [[ ! "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" =~ ^[1-9][0-9]*$ ||
! "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" =~ ^[1-9][0-9]*$ ||
! "${PRODUCER_WORKFLOW_SHA}" =~ ^[a-f0-9]{40}$ ||
! "${PRODUCER_WORKFLOW_FULL_REF}" =~ ^refs/tags/release-publish/([a-f0-9]{12})-[1-9][0-9]*$ ||
"${PRODUCER_WORKFLOW_SHA:0:12}" != "${BASH_REMATCH[1]}" ]]; then
echo "Focused evidence producer identity is missing or invalid after Docker approval." >&2
exit 1
fi
producer_ref="${PRODUCER_WORKFLOW_FULL_REF#refs/tags/}"
run_json="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${FOCUSED_RELEASE_EVIDENCE_RUN_ID}")"
if ! jq -e \
--arg run_id "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--arg attempt "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" \
--arg producer_ref "${producer_ref}" \
--arg producer_sha "${PRODUCER_WORKFLOW_SHA}" \
'.id == ($run_id | tonumber) and .run_attempt == ($attempt | tonumber) and
.name == "Authorized Beta Focused Validation" and
.path == ".github/workflows/authorized-beta-focused-validation.yml" and
.event == "workflow_dispatch" and .status == "completed" and .conclusion == "success" and
.head_branch == $producer_ref and .head_sha == $producer_sha' \
<<<"${run_json}" >/dev/null; then
echo "Focused evidence producer changed after Docker approval." >&2
exit 1
fi
remote_producer_sha="$(
gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${producer_ref}" \
--jq '.object | select(.type == "commit") | .sha'
)"
if [[ "${remote_producer_sha}" != "${PRODUCER_WORKFLOW_SHA}" ]]; then
echo "Focused evidence producer tag is missing, moved, or annotated after Docker approval." >&2
exit 1
fi
- name: Download focused release evidence after Docker approval
if: ${{ inputs.focused_release_evidence_run_id != '' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: authorized-beta-focused-v1-${{ inputs.focused_release_evidence_run_id }}-${{ inputs.focused_release_evidence_run_attempt }}
path: ${{ runner.temp }}/authorized-beta-focused-evidence
repository: ${{ github.repository }}
run-id: ${{ inputs.focused_release_evidence_run_id }}
github-token: ${{ github.token }}
- name: Verify focused release evidence after Docker approval
if: ${{ inputs.focused_release_evidence_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
FOCUSED_RELEASE_EVIDENCE_RUN_ID: ${{ inputs.focused_release_evidence_run_id }}
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: ${{ inputs.focused_release_evidence_run_attempt }}
PRODUCER_WORKFLOW_FULL_REF: ${{ inputs.focused_release_evidence_workflow_full_ref }}
PRODUCER_WORKFLOW_SHA: ${{ inputs.focused_release_evidence_workflow_sha }}
run: |
set -euo pipefail
evidence="${RUNNER_TEMP}/authorized-beta-focused-evidence/evidence.json"
gh attestation verify "${evidence}" \
--repo "${GITHUB_REPOSITORY}" \
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/authorized-beta-focused-validation.yml" \
--signer-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--deny-self-hosted-runners
node .release-harness/scripts/validate-authorized-beta-focused-evidence.mts verify \
--candidate-root . \
--artifact "${evidence}" \
--producer-run-id "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--producer-run-attempt "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" \
--producer-workflow-full-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--producer-workflow-sha "${PRODUCER_WORKFLOW_SHA}"
- name: Resolve shared build provenance
id: build_provenance
+75 -21
View File
@@ -128,6 +128,8 @@ jobs:
preflight_tarball_sha256: ${{ steps.manifest.outputs.tarball_sha256 }}
full_release_validation_run_attempt: ${{ steps.full_run.outputs.attempt }}
focused_release_evidence_run_attempt: ${{ steps.focused_run.outputs.attempt }}
focused_release_evidence_workflow_full_ref: ${{ steps.focused_run.outputs.workflow_full_ref }}
focused_release_evidence_workflow_sha: ${{ steps.focused_run.outputs.workflow_sha }}
windows_node_installer_digests: ${{ steps.windows_source.outputs.installer_digests }}
steps:
- name: Validate inputs
@@ -726,17 +728,63 @@ jobs:
GH_TOKEN: ${{ github.token }}
FOCUSED_RELEASE_EVIDENCE_RUN_ID: ${{ inputs.focused_release_evidence_run_id }}
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: ${{ inputs.focused_release_evidence_run_attempt }}
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
set -euo pipefail
run_attempt="$(
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--jq '.run_attempt'
)"
if [[ "${run_attempt}" != "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" ]]; then
echo "Focused evidence run attempt mismatch: expected ${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}, got ${run_attempt}." >&2
run_json="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${FOCUSED_RELEASE_EVIDENCE_RUN_ID}")"
if ! jq -e \
--arg run_id "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--arg attempt "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" \
'.id == ($run_id | tonumber) and .run_attempt == ($attempt | tonumber) and
.name == "Authorized Beta Focused Validation" and
.path == ".github/workflows/authorized-beta-focused-validation.yml" and
.event == "workflow_dispatch" and .status == "completed" and .conclusion == "success"' \
<<<"${run_json}" >/dev/null; then
echo "Focused evidence producer must be the exact successful authorized workflow run and attempt." >&2
exit 1
fi
echo "attempt=${run_attempt}" >> "$GITHUB_OUTPUT"
producer_ref="$(jq -r '.head_branch // empty' <<<"${run_json}")"
producer_sha="$(jq -r '.head_sha // empty' <<<"${run_json}")"
if [[ ! "${producer_ref}" =~ ^release-publish/([a-f0-9]{12})-[1-9][0-9]*$ ]]; then
echo "Focused evidence producer must use a protected release-publish tag." >&2
exit 1
fi
producer_prefix="${BASH_REMATCH[1]}"
if [[ ! "${producer_sha}" =~ ^[a-f0-9]{40}$ || "${producer_sha:0:12}" != "${producer_prefix}" ]]; then
echo "Focused evidence producer tag does not match its workflow SHA." >&2
exit 1
fi
remote_producer_sha="$(
gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${producer_ref}" \
--jq '.object | select(.type == "commit") | .sha'
)"
if [[ "${remote_producer_sha}" != "${producer_sha}" ]]; then
echo "Focused evidence producer tag is missing, moved, or annotated." >&2
exit 1
fi
producer_workflow_path=".github/workflows/authorized-beta-focused-validation.yml"
producer_policy_path="scripts/authorized-beta-focused-policy.json"
for trusted_path in "${producer_workflow_path}" "${producer_policy_path}"; do
producer_blob="$(git rev-parse "${producer_sha}:${trusted_path}")"
consumer_blob="$(git rev-parse "${WORKFLOW_SHA}:${trusted_path}")"
if [[ "${producer_blob}" != "${consumer_blob}" ]]; then
echo "Focused evidence producer does not match the trusted ${trusted_path}." >&2
exit 1
fi
done
historical_tooling_sha="$(
git show "${WORKFLOW_SHA}:${producer_policy_path}" |
jq -er '.historicalToolingSha | select(type == "string" and test("^[a-f0-9]{40}$"))'
)"
if ! git merge-base --is-ancestor "${historical_tooling_sha}" "${producer_sha}"; then
echo "Focused evidence producer is not descended from the fixed authorized tooling." >&2
exit 1
fi
{
echo "attempt=${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}"
echo "workflow_full_ref=refs/tags/${producer_ref}"
echo "workflow_sha=${producer_sha}"
} >> "$GITHUB_OUTPUT"
- name: Download focused release evidence
if: ${{ inputs.release_evidence_mode == 'authorized-beta-focused-v1' }}
@@ -754,25 +802,25 @@ jobs:
GH_TOKEN: ${{ github.token }}
FOCUSED_RELEASE_EVIDENCE_RUN_ID: ${{ inputs.focused_release_evidence_run_id }}
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: ${{ steps.focused_run.outputs.attempt }}
WORKFLOW_FULL_REF: ${{ github.ref }}
WORKFLOW_SHA: ${{ github.workflow_sha }}
PRODUCER_WORKFLOW_FULL_REF: ${{ steps.focused_run.outputs.workflow_full_ref }}
PRODUCER_WORKFLOW_SHA: ${{ steps.focused_run.outputs.workflow_sha }}
run: |
set -euo pipefail
evidence="${RUNNER_TEMP}/authorized-beta-focused-evidence/evidence.json"
gh attestation verify "${evidence}" \
--repo "${GITHUB_REPOSITORY}" \
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/authorized-beta-focused-validation.yml" \
--signer-digest "${WORKFLOW_SHA}" \
--source-digest "${WORKFLOW_SHA}" \
--source-ref "${WORKFLOW_FULL_REF}" \
--signer-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--deny-self-hosted-runners
node "${RUNNER_TEMP}/release-validation-tooling/validate-authorized-beta-focused-evidence.mts" verify \
--candidate-root . \
--artifact "${evidence}" \
--producer-run-id "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--producer-run-attempt "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" \
--producer-workflow-full-ref "${WORKFLOW_FULL_REF}" \
--producer-workflow-sha "${WORKFLOW_SHA}"
--producer-workflow-full-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--producer-workflow-sha "${PRODUCER_WORKFLOW_SHA}"
- name: Validate release tag is reachable from a trusted release branch
env:
@@ -890,25 +938,25 @@ jobs:
GH_TOKEN: ${{ github.token }}
FOCUSED_RELEASE_EVIDENCE_RUN_ID: ${{ inputs.focused_release_evidence_run_id }}
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: ${{ needs.resolve_release_target.outputs.focused_release_evidence_run_attempt }}
WORKFLOW_FULL_REF: ${{ github.ref }}
WORKFLOW_SHA: ${{ github.workflow_sha }}
PRODUCER_WORKFLOW_FULL_REF: ${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_full_ref }}
PRODUCER_WORKFLOW_SHA: ${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_sha }}
run: |
set -euo pipefail
evidence="${RUNNER_TEMP}/authorized-beta-focused-evidence/evidence.json"
gh attestation verify "${evidence}" \
--repo "${GITHUB_REPOSITORY}" \
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/authorized-beta-focused-validation.yml" \
--signer-digest "${WORKFLOW_SHA}" \
--source-digest "${WORKFLOW_SHA}" \
--source-ref "${WORKFLOW_FULL_REF}" \
--signer-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-digest "${PRODUCER_WORKFLOW_SHA}" \
--source-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--deny-self-hosted-runners
node .release-harness/scripts/validate-authorized-beta-focused-evidence.mts verify \
--candidate-root . \
--artifact "${evidence}" \
--producer-run-id "${FOCUSED_RELEASE_EVIDENCE_RUN_ID}" \
--producer-run-attempt "${FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT}" \
--producer-workflow-full-ref "${WORKFLOW_FULL_REF}" \
--producer-workflow-sha "${WORKFLOW_SHA}"
--producer-workflow-full-ref "${PRODUCER_WORKFLOW_FULL_REF}" \
--producer-workflow-sha "${PRODUCER_WORKFLOW_SHA}"
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
@@ -2743,10 +2791,16 @@ jobs:
with:
tag: ${{ inputs.tag }}
release_sha: ${{ needs.resolve_release_target.outputs.sha }}
focused_release_evidence_run_id: ${{ inputs.release_evidence_mode == 'authorized-beta-focused-v1' && inputs.focused_release_evidence_run_id || '' }}
focused_release_evidence_run_attempt: ${{ needs.resolve_release_target.outputs.focused_release_evidence_run_attempt }}
focused_release_evidence_workflow_full_ref: ${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_full_ref }}
focused_release_evidence_workflow_sha: ${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_sha }}
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
permissions:
actions: read
attestations: read
contents: read
packages: write
@@ -24,7 +24,11 @@ type ParsedWorkflow = {
jobs?: Record<
string,
{
needs?: string | string[];
outputs?: Record<string, string>;
permissions?: Record<string, string>;
steps?: Array<{
env?: Record<string, string>;
if?: string;
name?: string;
run?: string;
@@ -83,6 +87,17 @@ function fixturePolicy(): { policy: AuthorizedBetaFocusedPolicy; root: string }
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: root });
execFileSync("git", ["config", "user.name", "Test"], { cwd: root });
writeFileSync(join(root, "published.txt"), "published\n");
const historicalToolingSha = commit(root, "historical tooling");
mkdirSync(join(root, ".github", "workflows"), { recursive: true });
mkdirSync(join(root, "scripts"));
writeFileSync(
join(root, ".github", "workflows", "authorized-beta-focused-validation.yml"),
"name: Authorized Beta Focused Validation\n",
);
writeFileSync(
join(root, "scripts", "authorized-beta-focused-policy.json"),
JSON.stringify({ historicalToolingSha }),
);
mkdirSync(join(root, "tests"));
writeFileSync(join(root, "tests", "proof.test.ts"), "one\n");
const baseCandidateSha = commit(root, "base");
@@ -101,6 +116,7 @@ function fixturePolicy(): { policy: AuthorizedBetaFocusedPolicy; root: string }
...readAuthorizedBetaFocusedPolicy(),
baseCandidateSha,
candidateSha,
historicalToolingSha,
reviewedHeadSha: candidateSha,
candidateTreeSha,
baseTreeSha,
@@ -117,7 +133,215 @@ function fixturePolicy(): { policy: AuthorizedBetaFocusedPolicy; root: string }
};
}
function resolveFocusedProducer(
options: {
annotatedTag?: boolean;
boundary?: "docker" | "resolve";
consumer?: "ancestor" | "current" | "diverged";
missingTag?: boolean;
producer?: "policy-drift" | "unanchored" | "workflow-drift";
run?: Record<string, unknown>;
tag?: Record<string, unknown>;
} = {},
) {
const { policy, root } = fixturePolicy();
let producerSha = options.consumer === "current" ? policy.candidateSha : policy.baseCandidateSha;
const consumerSha = policy.candidateSha;
if (options.consumer === "diverged" || options.producer) {
if (options.producer === "unanchored") {
git(root, ["checkout", "--quiet", "--orphan", "unanchored-producer"]);
} else {
git(root, ["checkout", "--quiet", "--detach", policy.baseCandidateSha]);
}
if (options.producer === "policy-drift") {
writeFileSync(
join(root, "scripts", "authorized-beta-focused-policy.json"),
JSON.stringify({ historicalToolingSha: "f".repeat(40) }),
);
} else if (options.producer === "workflow-drift") {
writeFileSync(
join(root, ".github", "workflows", "authorized-beta-focused-validation.yml"),
"name: Untrusted Validation\n",
);
} else {
writeFileSync(join(root, "published.txt"), "protected producer branch\n");
}
producerSha = commit(root, "protected producer");
git(root, ["checkout", "--quiet", "--detach", consumerSha]);
}
const producerRef = `release-publish/${producerSha.slice(0, 12)}-123`;
const outputPath = join(root, "github-output");
const isDockerBoundary = options.boundary === "docker";
const workflow = parse(
readFileSync(
isDockerBoundary
? ".github/workflows/docker-release.yml"
: ".github/workflows/openclaw-release-publish.yml",
"utf8",
),
) as ParsedWorkflow;
const run = {
id: 123,
run_attempt: 2,
name: "Authorized Beta Focused Validation",
path: ".github/workflows/authorized-beta-focused-validation.yml",
event: "workflow_dispatch",
status: "completed",
conclusion: "success",
head_branch: producerRef,
head_sha: producerSha,
...options.run,
};
const tag = {
ref: `refs/tags/${producerRef}`,
object: { sha: producerSha, type: options.annotatedTag ? "tag" : "commit" },
...options.tag,
};
const result = spawnSync(
"bash",
[
"-c",
[
"gh() {",
' if [[ "$2" == */actions/runs/* ]]; then',
' if [[ "${3:-}" == --jq ]]; then',
' printf "%s\\n" "$MOCK_RUN_JSON" | jq -r "$4"',
" else",
' printf "%s\\n" "$MOCK_RUN_JSON"',
" fi",
' elif [[ "$2" == */git/ref/tags/* && "$MOCK_TAG_MISSING" != true ]]; then',
' printf "%s\\n" "$MOCK_TAG_JSON" | jq -r "$4"',
" else",
" return 1",
" fi",
"}",
namedStep(
workflow,
isDockerBoundary ? "resolve_build_provenance" : "resolve_release_target",
isDockerBoundary
? "Revalidate focused evidence producer after Docker approval"
: "Resolve focused release evidence run",
).run,
].join("\n"),
],
{
cwd: root,
encoding: "utf8",
env: {
...process.env,
FOCUSED_RELEASE_EVIDENCE_RUN_ATTEMPT: "2",
FOCUSED_RELEASE_EVIDENCE_RUN_ID: "123",
GITHUB_OUTPUT: outputPath,
GITHUB_REPOSITORY: "openclaw/openclaw",
MOCK_RUN_JSON: JSON.stringify(run),
MOCK_TAG_JSON: JSON.stringify(tag),
MOCK_TAG_MISSING: String(options.missingTag ?? false),
PRODUCER_WORKFLOW_FULL_REF: `refs/tags/${producerRef}`,
PRODUCER_WORKFLOW_SHA: producerSha,
WORKFLOW_SHA: consumerSha,
},
},
);
return { consumerSha, outputPath, producerRef, producerSha, result };
}
describe("authorized beta focused evidence", () => {
it.each(["ancestor", "current", "diverged"] as const)(
"accepts an exact protected focused producer from %s trusted tooling",
(consumer) => {
const { outputPath, producerRef, producerSha, result } = resolveFocusedProducer({ consumer });
expect(result.stderr).toBe("");
expect(result.status).toBe(0);
expect(readFileSync(outputPath, "utf8")).toBe(
`attempt=2\nworkflow_full_ref=refs/tags/${producerRef}\nworkflow_sha=${producerSha}\n`,
);
},
);
it.each([
{ name: "unanchored producer", options: { producer: "unanchored" as const } },
{ name: "producer policy drift", options: { producer: "policy-drift" as const } },
{ name: "producer workflow drift", options: { producer: "workflow-drift" as const } },
{
name: "moved producer tag",
options: { tag: { object: { sha: "f".repeat(40), type: "commit" } } },
},
{ name: "missing producer tag", options: { missingTag: true } },
{ name: "annotated producer tag", options: { annotatedTag: true } },
{
name: "producer SHA prefix mismatch",
options: { run: { head_branch: "release-publish/ffffffffffff-123" } },
},
{
name: "malformed producer tag",
options: { run: { head_branch: "release-publish/ffffffffffff-0" } },
},
{
name: "wrong producer workflow path",
options: { run: { path: ".github/workflows/openclaw-release-publish.yml" } },
},
{ name: "wrong producer workflow name", options: { run: { name: "Other Validation" } } },
{ name: "wrong producer event", options: { run: { event: "push" } } },
{ name: "unfinished producer", options: { run: { status: "in_progress" } } },
{ name: "failed producer", options: { run: { conclusion: "failure" } } },
{ name: "wrong producer attempt", options: { run: { run_attempt: 3 } } },
])("rejects $name before focused artifact download", ({ options }) => {
expect(resolveFocusedProducer(options).result.status).not.toBe(0);
});
it("accepts the exact focused producer again after Docker approval", () => {
const { result } = resolveFocusedProducer({ boundary: "docker", consumer: "diverged" });
expect(result.stderr).toBe("");
expect(result.status).toBe(0);
});
it.each([
{
name: "moved producer tag",
options: { tag: { object: { sha: "f".repeat(40), type: "commit" } } },
},
{ name: "missing producer tag", options: { missingTag: true } },
{ name: "rerun producer", options: { run: { run_attempt: 3 } } },
{ name: "substituted producer", options: { run: { head_sha: "f".repeat(40) } } },
{ name: "failed producer", options: { run: { conclusion: "failure" } } },
])("rejects $name after Docker approval before registry access", ({ options }) => {
expect(resolveFocusedProducer({ ...options, boundary: "docker" }).result.status).not.toBe(0);
});
it("gates every Docker build on post-approval focused evidence revalidation", () => {
const docker = parse(
readFileSync(".github/workflows/docker-release.yml", "utf8"),
) as ParsedWorkflow;
const gate = docker.jobs?.resolve_build_provenance;
if (!gate) {
throw new Error("Docker build provenance gate is missing");
}
expect(gate.needs).toContain("approve_docker_publish");
expect(gate.permissions).toMatchObject({
actions: "read",
attestations: "read",
contents: "read",
});
const names = (gate.steps ?? []).map((step) => step.name);
const revalidation = names.indexOf(
"Revalidate focused evidence producer after Docker approval",
);
const download = names.indexOf("Download focused release evidence after Docker approval");
const verification = names.indexOf("Verify focused release evidence after Docker approval");
const provenance = names.indexOf("Resolve shared build provenance");
expect(revalidation).toBeGreaterThan(-1);
expect(revalidation).toBeLessThan(download);
expect(download).toBeLessThan(verification);
expect(verification).toBeLessThan(provenance);
for (const jobName of ["build-amd64", "build-arm64"]) {
expect(docker.jobs?.[jobName]?.needs).toContain("resolve_build_provenance");
}
});
it("pins the exact beta.3 candidate, inventories, trust split, and repaired leaves", () => {
const policy = readAuthorizedBetaFocusedPolicy();
expect(policy.releaseTag).toBe("v2026.8.1-beta.3");
@@ -320,8 +544,11 @@ describe("authorized beta focused evidence", () => {
const source = readFileSync(path, "utf8");
expect(source).toContain("Verify focused release evidence");
expect(source).toContain("gh attestation verify");
expect(source).toContain('--signer-digest "${WORKFLOW_SHA}"');
expect(source).toContain('--source-digest "${WORKFLOW_SHA}"');
const signerSha = path.endsWith("openclaw-release-publish.yml")
? "PRODUCER_WORKFLOW_SHA"
: "WORKFLOW_SHA";
expect(source).toContain(`--signer-digest "\${${signerSha}}"`);
expect(source).toContain(`--source-digest "\${${signerSha}}"`);
expect(source).toContain("validate-authorized-beta-focused-evidence.mts");
expect(source).toContain("inputs.release_evidence_mode == 'full-release-validation'");
expect(source).toContain("validate-full-release-validation-evidence.mjs");
@@ -348,6 +575,30 @@ describe("authorized beta focused evidence", () => {
const resolveSteps = parentWorkflow.jobs?.resolve_release_target?.steps ?? [];
const resolveStepNames = resolveSteps.map((step) => step.name);
expect(resolveStepNames).not.toContain("Install focused release verifier dependency");
expect(parentWorkflow.jobs?.resolve_release_target?.outputs).toMatchObject({
focused_release_evidence_workflow_full_ref:
"${{ steps.focused_run.outputs.workflow_full_ref }}",
focused_release_evidence_workflow_sha: "${{ steps.focused_run.outputs.workflow_sha }}",
});
for (const [jobName, stepName] of [
["resolve_release_target", "Verify focused release evidence"],
["publish", "Verify focused release evidence after approval"],
] as const) {
const verifyStep = namedStep(parentWorkflow, jobName, stepName);
const outputPrefix =
jobName === "publish"
? "needs.resolve_release_target.outputs.focused_release_evidence_"
: "steps.focused_run.outputs.";
expect(verifyStep.env).toMatchObject({
PRODUCER_WORKFLOW_FULL_REF: `\${{ ${outputPrefix}workflow_full_ref }}`,
PRODUCER_WORKFLOW_SHA: `\${{ ${outputPrefix}workflow_sha }}`,
});
expect(verifyStep.run).toContain('--source-ref "${PRODUCER_WORKFLOW_FULL_REF}"');
expect(verifyStep.run).toContain(
'--producer-workflow-full-ref "${PRODUCER_WORKFLOW_FULL_REF}"',
);
expect(verifyStep.run).toContain('--producer-workflow-sha "${PRODUCER_WORKFLOW_SHA}"');
}
const publishSteps = parentWorkflow.jobs?.publish?.steps ?? [];
const publishStepNames = publishSteps.map((step) => step.name);
expect(publishStepNames.indexOf("Verify focused release evidence after approval")).toBeLessThan(
+6 -1
View File
@@ -2043,7 +2043,12 @@ NODE
DOCKERHUB_USERNAME: "${{ secrets.DOCKERHUB_USERNAME }}",
DOCKERHUB_TOKEN: "${{ secrets.DOCKERHUB_TOKEN }}",
});
expect(publish.permissions).toEqual({ contents: "read", packages: "write" });
expect(publish.permissions).toEqual({
actions: "read",
attestations: "read",
contents: "read",
packages: "write",
});
expect(releaseWorkflow.jobs.approve_docker_publish.environment).toBe("docker-release");
});
@@ -1483,6 +1483,14 @@ describe("release validation no-push transport", () => {
expect(dockerCall.with).toEqual({
tag: "${{ inputs.tag }}",
release_sha: "${{ needs.resolve_release_target.outputs.sha }}",
focused_release_evidence_run_id:
"${{ inputs.release_evidence_mode == 'authorized-beta-focused-v1' && inputs.focused_release_evidence_run_id || '' }}",
focused_release_evidence_run_attempt:
"${{ needs.resolve_release_target.outputs.focused_release_evidence_run_attempt }}",
focused_release_evidence_workflow_full_ref:
"${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_full_ref }}",
focused_release_evidence_workflow_sha:
"${{ needs.resolve_release_target.outputs.focused_release_evidence_workflow_sha }}",
});
expect(dockerCall.secrets).toEqual({
DOCKERHUB_USERNAME: "${{ secrets.DOCKERHUB_USERNAME }}",