mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 19:08:22 -06:00
fix(release): preserve validation plan across reruns (#127343)
* fix(release): preserve validation plan across reruns * test(release): align rerun plan assertions * refactor(release): use canonical plan cache action * style(test): format release plan cache assertion
This commit is contained in:
@@ -350,9 +350,10 @@ the failure to that exact active run.
|
||||
The child-dispatch jobs record run ID, run attempt, and URL, then finish. The
|
||||
parent seals those tuples, original dispatch titles, gate coverage, reuse
|
||||
policy, and original parent attempt in one immutable
|
||||
`full-release-execution-plan-<run-id>` artifact. Collector retries restore that
|
||||
artifact and adopt its children; they never reconstruct the plan or redispatch
|
||||
tests.
|
||||
`full-release-execution-plan-<run-id>` artifact and exact run-ID cache entry.
|
||||
Collector retries restore the cached bytes, validate them, and re-upload the
|
||||
artifact for their attempt before adopting its children; they never reconstruct
|
||||
the plan or redispatch tests.
|
||||
`Release Decision` polls those exact identities and can report
|
||||
`blocked_diagnostics_running` before unrelated children finish.
|
||||
For reused evidence, it also repeats the canonical target, policy, changed-path,
|
||||
|
||||
@@ -57,10 +57,12 @@ Use this with `$release-openclaw-maintainer` and `$openclaw-testing` when a rele
|
||||
child that owns a blocking failure.
|
||||
- After dispatch, one immutable execution-plan artifact records the original
|
||||
parent attempt, exact child tuples and titles, selected coverage, gates, and
|
||||
reuse identity. Decision, Drain, manifest writing, evidence validation, and
|
||||
final verification consume that plan. A collector retry restores it and
|
||||
adopts the same children; missing plan state is an orchestration failure, not
|
||||
permission to redispatch.
|
||||
reuse identity. The same bytes are saved under an exact run-ID cache key.
|
||||
Decision, Drain, manifest writing, evidence validation, and final verification
|
||||
consume the artifact for their current attempt. A collector retry restores
|
||||
the cached plan, validates it, re-uploads its artifact, and adopts the same
|
||||
children; missing plan state is an orchestration failure, not permission to
|
||||
reconstruct the plan or redispatch.
|
||||
- Reused evidence is not trusted merely because plan sealing found it. Release
|
||||
Decision repeats the sealed target SHA, evidence SHA, policy, changed paths,
|
||||
selected run, root run, source manifest, trusted tooling identity, and
|
||||
@@ -321,7 +323,8 @@ The `full-release-diagnostics-<run-id>-<attempt>` artifact is the terminal
|
||||
failure and timing manifest. Use it after an early blocker instead of
|
||||
restarting `all` merely to discover what the still-running children found.
|
||||
The stable `full-release-execution-plan-<run-id>` artifact is the identity
|
||||
source for every collector attempt.
|
||||
source within each collector attempt; retry attempts restore its immutable
|
||||
run-ID-cached bytes first.
|
||||
|
||||
## Failure Triage
|
||||
|
||||
|
||||
@@ -1096,12 +1096,14 @@ jobs:
|
||||
sparse-checkout-cone-mode: false
|
||||
persist-credentials: false
|
||||
|
||||
- name: Restore immutable release execution plan
|
||||
if: ${{ github.run_attempt != 1 }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
# Parent reruns hide prior-attempt artifacts. This exact-key cache is immutable;
|
||||
# a miss fails closed instead of reconstructing or redispatching child identities.
|
||||
- name: Cache immutable release execution plan
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
name: full-release-execution-plan-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/full-release-execution-plan
|
||||
key: full-release-execution-plan-v1-${{ github.run_id }}
|
||||
fail-on-cache-miss: ${{ github.run_attempt != 1 }}
|
||||
|
||||
- name: Seal immutable release execution plan
|
||||
id: plan
|
||||
@@ -1226,7 +1228,7 @@ jobs:
|
||||
node scripts/full-release-validation-state.mjs plan
|
||||
|
||||
- name: Upload immutable release execution plan
|
||||
if: ${{ always() && github.run_attempt == 1 }}
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: full-release-execution-plan-${{ github.run_id }}
|
||||
|
||||
@@ -45,12 +45,16 @@ first-failure path is preferable; Release Decision then cancels only the exact
|
||||
still-active child that owns the blocking failure.
|
||||
|
||||
After dispatch, the parent writes one immutable
|
||||
`full-release-execution-plan-<run-id>` artifact. It records selected and
|
||||
`full-release-execution-plan-<run-id>` artifact and preserves the same bytes in
|
||||
an exact run-ID Actions cache. It records selected and
|
||||
required coverage, gate results, reuse identity, the original parent attempt,
|
||||
and every exact child run ID, attempt, title, workflow ref, and Tooling SHA.
|
||||
Decision, Drain, manifest generation, evidence verification, and the final
|
||||
verifier consume this artifact. Collector retries restore it and adopt the
|
||||
same children; they never rebuild the plan or redispatch tests.
|
||||
verifier consume the artifact for their current attempt. Collector retries
|
||||
restore the immutable cached copy, validate it, and upload the artifact again
|
||||
for the retry; they never rebuild the plan or redispatch tests. A missing or
|
||||
evicted cache fails closed, so start a new validation instead of retrying that
|
||||
stale parent.
|
||||
Release Decision also repeats canonical reuse-chain validation before a reused
|
||||
run can pass. The sealed target SHA, evidence SHA, policy, changed-path set,
|
||||
selected run, root run, source manifest, trusted tooling identity, and child
|
||||
|
||||
@@ -54,6 +54,7 @@ const ANDROID_RELEASE_WORKFLOW = ".github/workflows/android-release.yml";
|
||||
const STABLE_MAIN_CLOSEOUT_WORKFLOW = ".github/workflows/openclaw-stable-main-closeout.yml";
|
||||
const WINDOWS_NODE_RELEASE_WORKFLOW = ".github/workflows/windows-node-release.yml";
|
||||
const FULL_RELEASE_VALIDATION_WORKFLOW = ".github/workflows/full-release-validation.yml";
|
||||
const ACTIONS_CACHE_V5 = "actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae";
|
||||
const CI_WORKFLOW = ".github/workflows/ci.yml";
|
||||
const PERFORMANCE_WORKFLOW = ".github/workflows/openclaw-performance.yml";
|
||||
const FULL_RELEASE_CHILD_DISPATCHES = [
|
||||
@@ -2967,6 +2968,7 @@ describe("package acceptance workflow", () => {
|
||||
const decisionUpload = workflowStep(decision, "Upload release decision");
|
||||
const drainUpload = workflowStep(drain, "Upload diagnostic drain manifest");
|
||||
const planStep = workflowStep(executionPlan, "Seal immutable release execution plan");
|
||||
const planCache = workflowStep(executionPlan, "Cache immutable release execution plan");
|
||||
const planUpload = workflowStep(executionPlan, "Upload immutable release execution plan");
|
||||
const manifestStep = workflowStep(summary, "Write release validation manifest");
|
||||
const selectState = workflowStep(summary, "Select newest compatible release state artifacts");
|
||||
@@ -2999,7 +3001,12 @@ describe("package acceptance workflow", () => {
|
||||
expect(planStep.run).not.toContain("EVIDENCE_MANIFEST");
|
||||
expect(planStep.run).toContain('--arg evidenceRunId "$EVIDENCE_RUN_ID"');
|
||||
expect(planStep.run).toContain('--argjson trustedWorkflow "$TRUSTED_WORKFLOW_JSON"');
|
||||
expect(planUpload.if).toBe("${{ always() && github.run_attempt == 1 }}");
|
||||
expect(planCache.uses).toBe(ACTIONS_CACHE_V5);
|
||||
expect(planCache.with).toMatchObject({
|
||||
"fail-on-cache-miss": "${{ github.run_attempt != 1 }}",
|
||||
key: "full-release-execution-plan-v1-${{ github.run_id }}",
|
||||
});
|
||||
expect(planUpload.if).toBe("always()");
|
||||
expect(planUpload.with?.name).toBe("full-release-execution-plan-${{ github.run_id }}");
|
||||
expect(manifestStep.env).not.toHaveProperty("EVIDENCE_MANIFEST");
|
||||
expect(manifestStep.run).toContain(
|
||||
@@ -3065,7 +3072,7 @@ describe("package acceptance workflow", () => {
|
||||
(job.steps ?? []).filter((step) => step.uses?.startsWith("actions/download-artifact@")),
|
||||
);
|
||||
|
||||
expect(downloadSteps).toHaveLength(6);
|
||||
expect(downloadSteps).toHaveLength(5);
|
||||
for (const step of downloadSteps) {
|
||||
expect(step.uses).toBe(DOWNLOAD_ARTIFACT_V8);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user