fix(release): accept trusted extended-stable validation evidence (#119264)

* fix(release): accept trusted extended-stable validation evidence

* fix(release): document trusted candidate promotion

* docs(release): clarify validation evidence recovery
This commit is contained in:
Dallin Romney
2026-08-05 04:14:30 +08:00
committed by GitHub
parent 11027a3712
commit 7c93cff84a
5 changed files with 173 additions and 84 deletions
@@ -252,22 +252,29 @@ on pinned current `main` as the exact command and validation contract.
and workflow validation. Run focused checks and freeze the untagged tip SHA.
2. From that branch, run npm preflight with the SHA as `tag`,
`preflight_only=true`, and `npm_dist_tag=extended-stable`; save the run ID.
3. Run complete Full Release Validation from and against the canonical branch
with `release_profile=stable`; save its run ID and successful `run_attempt`.
Any branch change invalidates both gates.
3. Run complete Full Release Validation against the canonical branch with
`release_profile=stable`; save its run ID and successful `run_attempt`.
Prefer the trusted main-pinned harness, which attests the immutable target
SHA in its v3 manifest. Any candidate branch change invalidates both gates.
4. Require the tip still equals the frozen SHA, then create signed `vYYYY.M.P`.
Never move or delete a final tag; later source changes need a new patch.
5. Require the saved validation run to be complete, successful, and bound to
the canonical branch, tag SHA, and attempt. Reject `release-ci/*` and narrow
reruns.
5. Require the saved validation run to be complete and successful, bind its
manifest target SHA and attempt to the tag, and accept a direct run from the
canonical branch, a direct current-`main` run whose workflow SHA is still
reachable from main, or a trusted main-pinned `release-ci/*` harness. Reject
narrow reruns.
6. Dispatch `plugin-npm-release.yml` from the same branch with
`publish_scope=all-publishable`, the full release SHA as `ref`, and
`npm_dist_tag=extended-stable`. Require complete exact-version and selector
readback, then save the successful plugin run ID.
7. Publish core from the same branch with the tag, `npm_dist_tag=extended-stable`,
all three run IDs, and
`full_release_validation_run_attempt=<saved-attempt>`. Require the prepared
tarball and every run to match the branch and release SHA.
7. Publish core with the tag, `npm_dist_tag=extended-stable`, all three run IDs,
and `full_release_validation_run_attempt=<saved-attempt>`. Normally dispatch
from the canonical branch. For a workflow-only recovery after the candidate
is immutable, dispatch trusted current `main` with
`release_candidate_branch=extended-stable/YYYY.M.33`; it still publishes the
tag checkout and accepts canonical-branch, current-main, or trusted-pinned
validation evidence; the prepared tarball and every evidence identity must
still match the candidate SHA.
8. From a clean current-`main` checkout, run
`node --import tsx scripts/openclaw-npm-postpublish-verify.ts YYYY.M.P`.
Verify signatures, provenance, inventories, exact versions, and selectors.
+60 -47
View File
@@ -32,6 +32,11 @@ on:
description: Successful Plugin NPM Release run id for the exact extended-stable branch and release SHA
required: false
type: string
release_candidate_branch:
description: Canonical extended-stable branch when a trusted main workflow promotes its immutable tag
required: false
default: ""
type: string
npm_dist_tag:
description: npm dist-tag to publish to
required: true
@@ -709,6 +714,7 @@ jobs:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.release_candidate_branch != '' && format('refs/tags/{0}', inputs.tag) || github.sha }}
fetch-depth: 0
filter: blob:none
persist-credentials: false
@@ -718,13 +724,14 @@ jobs:
BYPASS_EXTENDED_STABLE_GUARD: ${{ inputs.bypass_extended_stable_guard }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
RELEASE_TAG: ${{ inputs.tag }}
NPM_WORKFLOW_REF: ${{ github.ref }}
NPM_WORKFLOW_REF: ${{ inputs.release_candidate_branch != '' && format('refs/heads/{0}', inputs.release_candidate_branch) || github.ref }}
run: node scripts/openclaw-npm-extended-stable-release.mjs validate-request
- name: Require trusted workflow ref for publish
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
RELEASE_CANDIDATE_BRANCH: ${{ inputs.release_candidate_branch }}
WORKFLOW_REF: ${{ github.ref }}
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
@@ -732,12 +739,29 @@ jobs:
tideclaw_alpha_publish=false
extended_stable_publish=false
sha_pinned_release_publish=false
release_candidate_branch="${RELEASE_CANDIDATE_BRANCH:-}"
if [[ "${RELEASE_TAG}" == *"-alpha."* && "${RELEASE_NPM_DIST_TAG}" == "alpha" && "${WORKFLOW_REF}" =~ ^refs/heads/tideclaw/alpha/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{4}Z$ ]]; then
tideclaw_alpha_publish=true
fi
if [[ "${RELEASE_NPM_DIST_TAG}" == "extended-stable" && "${WORKFLOW_REF}" == refs/heads/extended-stable/* ]]; then
extended_stable_publish=true
fi
if [[ -n "${release_candidate_branch}" ]]; then
if [[ "${RELEASE_NPM_DIST_TAG}" != "extended-stable" || "${WORKFLOW_REF}" != "refs/heads/main" ]]; then
echo "release_candidate_branch is only valid for an extended-stable publish dispatched from main." >&2
exit 1
fi
if [[ ! "${RELEASE_TAG}" =~ ^v([0-9]{4})\.([1-9][0-9]*)\.[1-9][0-9]*$ ]]; then
echo "release_candidate_branch requires an exact final release tag." >&2
exit 1
fi
expected_candidate_branch="extended-stable/${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.33"
if [[ "${release_candidate_branch}" != "${expected_candidate_branch}" ]]; then
echo "release_candidate_branch must be ${expected_candidate_branch} for ${RELEASE_TAG}." >&2
exit 1
fi
extended_stable_publish=true
fi
if [[ "${WORKFLOW_REF}" =~ ^refs/tags/release-publish/([a-f0-9]{12})-[1-9][0-9]*$ ]]; then
workflow_sha_prefix="${BASH_REMATCH[1]}"
[[ "${WORKFLOW_SHA}" =~ ^[a-f0-9]{40}$ && "${WORKFLOW_SHA:0:12}" == "${workflow_sha_prefix}" ]] || {
@@ -857,6 +881,16 @@ jobs:
# tarball, so real publishes do not need the repository's full history.
fetch-depth: ${{ inputs.preflight_run_id != '' && 1 || 0 }}
- name: Checkout trusted validation verifier
if: ${{ inputs.full_release_validation_run_id != '' }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
path: trusted-workflow
fetch-depth: 0
filter: blob:none
persist-credentials: false
- name: Validate Tideclaw alpha publish target
if: startsWith(github.ref, 'refs/heads/tideclaw/alpha/')
env:
@@ -902,7 +936,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
EXPECTED_EXTENDED_STABLE_BRANCH: ${{ github.ref_name }}
EXPECTED_EXTENDED_STABLE_BRANCH: ${{ inputs.release_candidate_branch || github.ref_name }}
RUN_KIND: preflight
run: |
set -euo pipefail
@@ -911,37 +945,32 @@ jobs:
RUN_JSON="$(gh run view "$PREFLIGHT_RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,headBranch,headSha,event,conclusion,url)"
printf '%s' "$RUN_JSON" | node scripts/openclaw-npm-extended-stable-release.mjs verify-run
- name: Verify full release validation run metadata
id: full_run
- name: Download full release validation manifest
if: ${{ inputs.full_release_validation_run_id != '' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ inputs.full_release_validation_run_attempt }}
path: full-release-validation
repository: ${{ github.repository }}
run-id: ${{ inputs.full_release_validation_run_id }}
github-token: ${{ github.token }}
- name: Verify full release validation evidence
if: ${{ inputs.full_release_validation_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
FULL_RELEASE_VALIDATION_RUN_ID: ${{ inputs.full_release_validation_run_id }}
FULL_RELEASE_VALIDATION_RUN_ATTEMPT: ${{ inputs.full_release_validation_run_attempt }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
EXPECTED_EXTENDED_STABLE_BRANCH: ${{ github.ref_name }}
RUN_KIND: validation
EXPECTED_WORKFLOW_BRANCH: ${{ inputs.release_candidate_branch || github.ref_name }}
STRICT_VALIDATOR_FILE: ${{ github.workspace }}/trusted-workflow/scripts/release-ci-summary.mjs
run: |
set -euo pipefail
EXPECTED_RELEASE_SHA="$(git rev-parse HEAD)"
export EXPECTED_RELEASE_SHA
run_file="${RUNNER_TEMP}/full-release-validation-run.json"
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${FULL_RELEASE_VALIDATION_RUN_ID}/attempts/${FULL_RELEASE_VALIDATION_RUN_ATTEMPT}" > "$run_file"
jq '{
workflowName: .name,
headBranch: .head_branch,
headSha: .head_sha,
event,
status,
conclusion,
url: .html_url
}' "$run_file" | node scripts/openclaw-npm-extended-stable-release.mjs verify-run
run_attempt="$(jq -r '.run_attempt // ""' "$run_file")"
if [[ "$run_attempt" != "$FULL_RELEASE_VALIDATION_RUN_ATTEMPT" ]]; then
echo "Full Release Validation run ${FULL_RELEASE_VALIDATION_RUN_ID} attempt mismatch: expected ${FULL_RELEASE_VALIDATION_RUN_ATTEMPT}, got ${run_attempt:-<missing>}." >&2
exit 1
fi
echo "attempt=$run_attempt" >> "$GITHUB_OUTPUT"
EXPECTED_SHA="$(git rev-parse HEAD)"
MANIFEST_FILE="full-release-validation/full-release-validation-manifest.json"
export EXPECTED_SHA MANIFEST_FILE
timeout --signal=TERM --kill-after=10s 120s git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${FULL_RELEASE_VALIDATION_RUN_ID}/attempts/${FULL_RELEASE_VALIDATION_RUN_ATTEMPT}" | \
node trusted-workflow/scripts/validate-full-release-validation-evidence.mjs
- name: Verify plugin npm release run metadata
if: ${{ inputs.npm_dist_tag == 'extended-stable' }}
@@ -949,7 +978,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
PLUGIN_NPM_RUN_ID: ${{ inputs.plugin_npm_run_id }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
EXPECTED_EXTENDED_STABLE_BRANCH: ${{ github.ref_name }}
EXPECTED_EXTENDED_STABLE_BRANCH: ${{ inputs.release_candidate_branch || github.ref_name }}
RUN_KIND: plugin
run: |
set -euo pipefail
@@ -1011,16 +1040,6 @@ jobs:
download_preflight_artifact
- name: Download full release validation manifest
if: ${{ inputs.full_release_validation_run_id != '' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ steps.full_run.outputs.attempt }}
path: full-release-validation
repository: ${{ github.repository }}
run-id: ${{ inputs.full_release_validation_run_id }}
github-token: ${{ github.token }}
- name: Validate release tag and package metadata
if: ${{ inputs.preflight_run_id == '' }}
env:
@@ -1167,20 +1186,14 @@ jobs:
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
EXPECTED_WORKFLOW_REF: ${{ github.ref_name }}
FULL_RELEASE_VALIDATION_RUN_ID: ${{ inputs.full_release_validation_run_id }}
FULL_RELEASE_VALIDATION_RUN_ATTEMPT: ${{ steps.full_run.outputs.attempt }}
run: |
set -euo pipefail
EXPECTED_RELEASE_SHA="$(git rev-parse HEAD)"
MANIFEST_FILE="full-release-validation/full-release-validation-manifest.json"
if [[ ! -f "$MANIFEST_FILE" ]]; then
echo "Full release validation manifest is missing." >&2
ls -la full-release-validation >&2 || true
exit 1
fi
export EXPECTED_RELEASE_SHA MANIFEST_FILE
node scripts/openclaw-npm-extended-stable-release.mjs verify-manifest
RERUN_GROUP="$(jq -r '.rerunGroup // ""' "$MANIFEST_FILE")"
RUN_RELEASE_SOAK="$(jq -r '.runReleaseSoak // ""' "$MANIFEST_FILE")"
PERFORMANCE_BLOCKING="$(jq -r '.controls.performanceBlocking // false' "$MANIFEST_FILE")"
@@ -1202,7 +1215,7 @@ jobs:
BYPASS_EXTENDED_STABLE_GUARD: ${{ inputs.bypass_extended_stable_guard }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
RELEASE_TAG: ${{ inputs.tag }}
NPM_WORKFLOW_REF: ${{ github.ref }}
NPM_WORKFLOW_REF: ${{ inputs.release_candidate_branch != '' && format('refs/heads/{0}', inputs.release_candidate_branch) || github.ref }}
run: node scripts/openclaw-npm-extended-stable-release.mjs validate-request
- name: Capture previous extended-stable selector
@@ -1255,8 +1268,7 @@ jobs:
env:
BYPASS_EXTENDED_STABLE_GUARD: ${{ inputs.bypass_extended_stable_guard }}
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_SHA: ${{ github.sha }}
EXTENDED_STABLE_BRANCH: ${{ github.ref_name }}
EXTENDED_STABLE_BRANCH: ${{ inputs.release_candidate_branch || github.ref_name }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
FULL_RELEASE_VALIDATION_RUN_ID: ${{ inputs.full_release_validation_run_id }}
TARBALL_NAME: ${{ steps.preflight_provenance.outputs.tarball_name }}
@@ -1270,13 +1282,14 @@ jobs:
run: |
set -euo pipefail
release_version="${RELEASE_TAG#v}"
release_sha="$(git rev-parse HEAD)"
{
echo "## npm extended-stable publication"
echo "- Package: openclaw@${release_version}"
echo "- Extended-stable guard bypass: ${BYPASS_EXTENDED_STABLE_GUARD}"
echo "- Extended-stable branch: ${EXTENDED_STABLE_BRANCH}"
echo "- Release tag: ${RELEASE_TAG}"
echo "- Release SHA: ${RELEASE_SHA}"
echo "- Release SHA: ${release_sha}"
echo "- npm preflight run: ${PREFLIGHT_RUN_ID}"
echo "- Full Release Validation run: ${FULL_RELEASE_VALIDATION_RUN_ID}"
echo "- Tarball: ${TARBALL_NAME:-unavailable}"
+26
View File
@@ -137,6 +137,32 @@ gh workflow run openclaw-npm-release.yml \
-f plugin_npm_run_id=<plugin-npm-run-id>
```
If the immutable candidate has already passed its saved preflight and Full
Release Validation but core publication needs a workflow-only recovery, dispatch
the trusted current-`main` workflow instead. Keep the same tag and evidence
identities; do not move the tag or republish plugins:
```bash
gh workflow run openclaw-npm-release.yml \
--ref main \
-f tag=vYYYY.M.P \
-f preflight_only=false \
-f npm_dist_tag=extended-stable \
-f release_candidate_branch=extended-stable/YYYY.M.33 \
-f preflight_run_id=<npm-preflight-run-id> \
-f full_release_validation_run_id=<full-validation-run-id> \
-f full_release_validation_run_attempt=<full-validation-run-attempt> \
-f plugin_npm_run_id=<plugin-npm-run-id>
```
This recovery path checks out and publishes the immutable tag and requires the
canonical branch implied by that tag. It accepts Full Release Validation
evidence from the canonical candidate branch directly, from current `main`
directly when its workflow SHA is reachable from current `main`, or from the
trusted main-pinned harness. Every accepted form must attest the immutable
tag's SHA. Use it only when the candidate source and recorded evidence are
unchanged.
For non-production rehearsal only, add
`-f bypass_extended_stable_guard=true` to preflight and publish. It bypasses the
month guard only, never canonical-ref, SHA/tag/version equality, provenance,
@@ -21,6 +21,7 @@ type Workflow = {
bypass_extended_stable_guard?: { default?: boolean; type?: string };
npm_dist_tag?: { options?: string[] };
plugin_npm_run_id?: { required?: boolean; type?: string };
release_candidate_branch?: { default?: string; required?: boolean; type?: string };
};
};
};
@@ -43,7 +44,7 @@ describe("minimal npm extended-stable workflow", () => {
it("bounds every git fetch operation", () => {
const source = readFileSync(workflowPath, "utf8");
const gitFetchLines = source.split("\n").filter((line) => line.includes("git fetch"));
expect(gitFetchLines).toHaveLength(6);
expect(gitFetchLines).toHaveLength(7);
expect(
gitFetchLines.every((line) => line.includes("timeout --signal=TERM --kill-after=10s 120s")),
).toBe(true);
@@ -128,6 +129,41 @@ describe("minimal npm extended-stable workflow", () => {
expect(summary.run).toContain("Extended-stable guard bypass: ${BYPASS_EXTENDED_STABLE_GUARD}");
});
it("lets main promote only the canonical immutable extended-stable candidate", () => {
const parsed = workflow();
const releaseDocs = readFileSync("docs/reference/RELEASING.md", "utf8");
const input = parsed.on?.workflow_dispatch?.inputs?.release_candidate_branch;
expect(input).toMatchObject({ default: "", required: false, type: "string" });
const validate = step(parsed.jobs?.validate_publish_request, "Validate npm release request");
expect(validate.env?.NPM_WORKFLOW_REF).toBe(
"${{ inputs.release_candidate_branch != '' && format('refs/heads/{0}', inputs.release_candidate_branch) || github.ref }}",
);
const checkout = step(parsed.jobs?.validate_publish_request, "Checkout");
expect(checkout.with?.ref).toBe(
"${{ inputs.release_candidate_branch != '' && format('refs/tags/{0}', inputs.tag) || github.sha }}",
);
const trustedRef = step(
parsed.jobs?.validate_publish_request,
"Require trusted workflow ref for publish",
);
expect(trustedRef.env?.RELEASE_CANDIDATE_BRANCH).toBe("${{ inputs.release_candidate_branch }}");
expect(trustedRef.run).toContain('release_candidate_branch="${RELEASE_CANDIDATE_BRANCH:-}"');
expect(trustedRef.run).toContain('"${WORKFLOW_REF}" != "refs/heads/main"');
expect(trustedRef.run).toContain(
'expected_candidate_branch="extended-stable/${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.33"',
);
const recheck = step(parsed.jobs?.publish_openclaw_npm, "Recheck npm release request");
expect(recheck.env?.NPM_WORKFLOW_REF).toBe(validate.env?.NPM_WORKFLOW_REF);
expect(releaseDocs).toContain("--ref main");
expect(releaseDocs).toContain("-f release_candidate_branch=extended-stable/YYYY.M.33");
expect(releaseDocs).toContain("canonical candidate branch directly");
expect(releaseDocs).toContain("workflow SHA is reachable from current `main`");
expect(releaseDocs).toContain("trusted main-pinned harness");
});
it("accepts arbitrary SHA preflight targets and exercises every publishable plugin package", () => {
const parsed = workflow();
const preflight = parsed.jobs?.preflight_openclaw_npm;
@@ -192,26 +228,35 @@ describe("minimal npm extended-stable workflow", () => {
expect(save.with?.key).toBe("${{ steps.dist_build_cache.outputs.cache-primary-key }}");
});
it("authenticates exact extended-stable run and Full Validation identities", () => {
it("uses the trusted Full Validation evidence verifier", () => {
const parsed = workflow();
const raw = readFileSync(workflowPath, "utf8");
expect(raw).toContain("--json workflowName,headBranch,headSha,event,conclusion,url");
const fullValidationRun = step(
const verifier = step(
parsed.jobs?.publish_openclaw_npm,
"Verify full release validation run metadata",
"Checkout trusted validation verifier",
);
expect(fullValidationRun.env?.FULL_RELEASE_VALIDATION_RUN_ATTEMPT).toBe(
expect(verifier.with?.ref).toBe("${{ github.workflow_sha }}");
expect(verifier.with?.path).toBe("trusted-workflow");
const fullValidation = step(
parsed.jobs?.publish_openclaw_npm,
"Verify full release validation evidence",
);
expect(fullValidation.env?.EXPECTED_WORKFLOW_BRANCH).toBe(
"${{ inputs.release_candidate_branch || github.ref_name }}",
);
expect(fullValidation.env?.FULL_RELEASE_VALIDATION_RUN_ATTEMPT).toBe(
"${{ inputs.full_release_validation_run_attempt }}",
);
expect(fullValidationRun.run).toContain(
expect(fullValidation.run).toContain(
"actions/runs/${FULL_RELEASE_VALIDATION_RUN_ID}/attempts/${FULL_RELEASE_VALIDATION_RUN_ATTEMPT}",
);
expect(fullValidationRun.run).toContain(
'"$run_attempt" != "$FULL_RELEASE_VALIDATION_RUN_ATTEMPT"',
expect(fullValidation.run).toContain(
"trusted-workflow/scripts/validate-full-release-validation-evidence.mjs",
);
expect(fullValidationRun.run).toContain('echo "attempt=$run_attempt" >> "$GITHUB_OUTPUT"');
expect(raw.match(/openclaw-npm-extended-stable-release\.mjs verify-run/g)).toHaveLength(3);
expect(raw).toContain("openclaw-npm-extended-stable-release.mjs verify-manifest");
expect(raw.match(/openclaw-npm-extended-stable-release\.mjs verify-run/g)).toHaveLength(2);
expect(raw).not.toContain("openclaw-npm-extended-stable-release.mjs verify-manifest");
});
it("requires and authenticates the plugin npm run before an extended-stable core publish", () => {
@@ -250,6 +295,11 @@ describe("minimal npm extended-stable workflow", () => {
expect(summary.if).toContain("always()");
expect(summary.run).toContain("openclaw-npm-extended-stable-release.mjs repair-command");
expect(summary.run).toContain('EXPECTED_VERSION="$RELEASE_TAG"');
expect(summary.env?.EXTENDED_STABLE_BRANCH).toBe(
"${{ inputs.release_candidate_branch || github.ref_name }}",
);
expect(summary.env?.RELEASE_SHA).toBeUndefined();
expect(summary.run).toContain('release_sha="$(git rev-parse HEAD)"');
expect(publish?.environment).toBe("npm-release");
});
@@ -4215,7 +4215,7 @@ describe("package artifact reuse", () => {
"Download full release validation manifest",
);
const npmPublishJob = workflowJob(OPENCLAW_NPM_RELEASE_WORKFLOW, "publish_openclaw_npm");
const npmRun = workflowStep(npmPublishJob, "Verify full release validation run metadata");
const npmRun = workflowStep(npmPublishJob, "Verify full release validation evidence");
const npmManifest = workflowStep(npmPublishJob, "Download full release validation manifest");
expect(releaseRun).toMatchObject({
@@ -4236,18 +4236,15 @@ describe("package artifact reuse", () => {
"run-id": "${{ inputs.full_release_validation_run_id }}",
});
expect(npmRun).toMatchObject({
id: "full_run",
env: {
FULL_RELEASE_VALIDATION_RUN_ID: "${{ inputs.full_release_validation_run_id }}",
FULL_RELEASE_VALIDATION_RUN_ATTEMPT: "${{ inputs.full_release_validation_run_attempt }}",
},
expect(npmRun.env).toMatchObject({
FULL_RELEASE_VALIDATION_RUN_ID: "${{ inputs.full_release_validation_run_id }}",
FULL_RELEASE_VALIDATION_RUN_ATTEMPT: "${{ inputs.full_release_validation_run_attempt }}",
});
expect(npmRun.run).toContain(
"actions/runs/${FULL_RELEASE_VALIDATION_RUN_ID}/attempts/${FULL_RELEASE_VALIDATION_RUN_ATTEMPT}",
);
expect(npmManifest.with).toMatchObject({
name: "full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ steps.full_run.outputs.attempt }}",
name: "full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ inputs.full_release_validation_run_attempt }}",
"run-id": "${{ inputs.full_release_validation_run_id }}",
});
});
@@ -4263,7 +4260,7 @@ describe("package artifact reuse", () => {
const publishOrchestration = workflowStep(publishJob, "Dispatch publish workflows");
const npmPublishJob = workflowJob(OPENCLAW_NPM_RELEASE_WORKFLOW, "publish_openclaw_npm");
const npmCheckout = workflowStep(npmPublishJob, "Checkout");
const npmFullRun = workflowStep(npmPublishJob, "Verify full release validation run metadata");
const npmFullRun = workflowStep(npmPublishJob, "Verify full release validation evidence");
const npmDownload = workflowStep(npmPublishJob, "Download full release validation manifest");
const npmTarget = workflowStep(npmPublishJob, "Verify full release validation target");
@@ -4295,18 +4292,14 @@ describe("package artifact reuse", () => {
"${{ needs.resolve_release_target.outputs.full_release_validation_run_attempt }}",
);
expect(publishOrchestration.run).toContain('"${validation_target_sha}" != "${TARGET_SHA}"');
expect(npmFullRun.id).toBe("full_run");
expect(npmFullRun.env?.FULL_RELEASE_VALIDATION_RUN_ATTEMPT).toBe(
"${{ inputs.full_release_validation_run_attempt }}",
);
expect(npmDownload.with?.name).toBe(
"full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ steps.full_run.outputs.attempt }}",
"full-release-validation-${{ inputs.full_release_validation_run_id }}-${{ inputs.full_release_validation_run_attempt }}",
);
expect(npmTarget.env).toMatchObject({
FULL_RELEASE_VALIDATION_RUN_ID: "${{ inputs.full_release_validation_run_id }}",
FULL_RELEASE_VALIDATION_RUN_ATTEMPT: "${{ steps.full_run.outputs.attempt }}",
});
expect(npmTarget.run).toContain(
expect(npmTarget.env?.FULL_RELEASE_VALIDATION_RUN_ID).toBeUndefined();
expect(npmTarget.run).not.toContain(
"node scripts/openclaw-npm-extended-stable-release.mjs verify-manifest",
);
expect(npmCheckout.with?.["fetch-depth"]).toBe(