mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
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:
committed by
GitHub
parent
ccd8ede95e
commit
afa89f03ee
@@ -134,5 +134,7 @@ jobs:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
permissions:
|
||||
actions: read
|
||||
attestations: read
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 }}",
|
||||
|
||||
Reference in New Issue
Block a user