diff --git a/.github/workflows/full-release-validation.yml b/.github/workflows/full-release-validation.yml index 27ea93d66d3c..4214d9633067 100644 --- a/.github/workflows/full-release-validation.yml +++ b/.github/workflows/full-release-validation.yml @@ -496,10 +496,13 @@ jobs: fail_fast_failed_jobs() { local failed_jobs_json - failed_jobs_json="$( + if ! failed_jobs_json="$( fetch_child_jobs | jq -s '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")]' - )" + )"; then + echo "::warning::Could not list ${workflow} child jobs; continuing with authoritative workflow conclusion." + return 0 + fi if jq -e 'length > 0' <<< "$failed_jobs_json" >/dev/null; then echo "::error::${workflow} has failed child jobs before the workflow completed; cancelling the remaining matrix." jq '.[] | {name, conclusion, url: .html_url}' <<< "$failed_jobs_json" @@ -686,10 +689,13 @@ jobs: fail_fast_failed_jobs() { local failed_jobs_json - failed_jobs_json="$( + if ! failed_jobs_json="$( fetch_child_jobs | jq -s '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")]' - )" + )"; then + echo "::warning::Could not list ${workflow} child jobs; continuing with authoritative workflow conclusion." + return 0 + fi if jq -e 'length > 0' <<< "$failed_jobs_json" >/dev/null; then echo "::error::${workflow} has failed child jobs before the workflow completed; cancelling the remaining matrix." jq '.[] | {name, conclusion, url: .html_url}' <<< "$failed_jobs_json" @@ -923,10 +929,13 @@ jobs: if [[ "$workflow" == "openclaw-release-checks.yml" && "$CHILD_WORKFLOW_REF" =~ ^tideclaw/alpha/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{4}Z$ ]]; then return 0 fi - failed_jobs_json="$( + if ! failed_jobs_json="$( fetch_child_jobs | jq -s '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")]' - )" + )"; then + echo "::warning::Could not list ${workflow} child jobs; continuing with authoritative workflow conclusion." + return 0 + fi if [[ "$workflow" == "openclaw-release-checks.yml" ]]; then # These jobs are continue-on-error in the child workflow. Let its # status-artifact verifier decide whether their evidence is usable. @@ -1279,6 +1288,7 @@ jobs: id: dispatch env: GH_TOKEN: ${{ github.token }} + RELEASE_PROFILE: ${{ inputs.release_profile }} TARGET_SHA: ${{ needs.resolve_target.outputs.sha }} CHILD_WORKFLOW_REF: ${{ github.ref_name }} PARENT_WORKFLOW_SHA: ${{ github.sha }} @@ -1326,7 +1336,11 @@ jobs: echo "- Deep profile: \`false\`" echo "- Live OpenAI candidate: \`false\`" echo "- Report publication: disabled (artifacts only)" - echo "- Release impact: blocking" + if [[ "$RELEASE_PROFILE" == "beta" ]]; then + echo "- Release impact: advisory" + else + echo "- Release impact: blocking" + fi } >> "$GITHUB_STEP_SUMMARY" dispatch_id="full-release-validation-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" @@ -1423,6 +1437,10 @@ jobs: echo "url=${url}" >> "$GITHUB_OUTPUT" echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT" if [[ "$conclusion" != "success" ]]; then + if [[ "$RELEASE_PROFILE" == "beta" ]]; then + echo "::warning::OpenClaw Performance ended with ${conclusion}; advisory for beta: ${url}" + exit 0 + fi echo "::error::OpenClaw Performance ended with ${conclusion}: ${url}" gh_with_retry run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true exit 1 @@ -1458,6 +1476,7 @@ jobs: RELEASE_CHECKS_RESULT: ${{ needs.release_checks.result }} NPM_TELEGRAM_RESULT: ${{ needs.npm_telegram.result }} PERFORMANCE_RESULT: ${{ needs.performance.result }} + RELEASE_PROFILE: ${{ inputs.release_profile }} DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT: ${{ needs.docker_runtime_assets_preflight.result }} EVIDENCE_REUSE: ${{ needs.evidence_reuse.outputs.reuse }} EVIDENCE_ROOT_RUN_ID: ${{ needs.evidence_reuse.outputs.evidence_root_run_id }} @@ -1565,6 +1584,10 @@ jobs: fi if [[ "$status" != "completed" || "$conclusion" != "success" ]]; then + if [[ "$advisory_ok" == "1" && "$label" == "product_performance" && "$status" == "completed" ]]; then + echo "::warning::${label} ended with ${conclusion}; advisory for beta: ${url}" + return 0 + fi if [[ "$advisory_ok" == "1" && "$label" == "release_checks" ]]; then if release_checks_advisory_only "$run_json"; then echo "::warning::${label} child run ended with ${status}/${conclusion}, but Verify release checks accepted Tideclaw alpha advisory lanes: ${url}" @@ -1822,7 +1845,9 @@ jobs: if [[ "$PERFORMANCE_RESULT" == "skipped" && -z "${PERFORMANCE_RUN_ID// }" ]]; then check_child "product_performance" "" "$performance_required" || failed=1 else - check_child "product_performance" "$PERFORMANCE_RUN_ID" "$performance_required" || failed=1 + performance_advisory=0 + [[ "$RELEASE_PROFILE" == "beta" ]] && performance_advisory=1 + check_child "product_performance" "$PERFORMANCE_RUN_ID" "$performance_required" "$performance_advisory" || failed=1 fi summarize_child_timing "normal_ci" "$NORMAL_CI_RUN_ID" @@ -2052,7 +2077,7 @@ jobs: }, controls: { stableSoakRequired: ($releaseProfile == "stable" or $releaseProfile == "full"), - performanceBlocking: true, + performanceBlocking: ($releaseProfile != "beta"), performanceReportPublication: "artifact-only" }, childRuns: { @@ -2063,7 +2088,7 @@ jobs: productPerformance: { runId: $performanceRunId, conclusion: $performanceConclusion, - blocking: true + blocking: ($releaseProfile != "beta") } } }' > "${manifest_dir}/full-release-validation-manifest.json" diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index beb44333b2ff..cf86ac699652 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -1245,6 +1245,37 @@ describe("package acceptance workflow", () => { ); }); + it("keeps performance evidence advisory for beta releases", () => { + const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8"); + const performanceStep = workflowStep( + workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "performance"), + "Dispatch and monitor OpenClaw Performance", + ); + const summaryStep = workflowStep( + workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "summary"), + "Verify child workflow results", + ); + + expect(performanceStep.env?.RELEASE_PROFILE).toBe("${{ inputs.release_profile }}"); + expectTextToIncludeAll(performanceStep.run, [ + 'if [[ "$RELEASE_PROFILE" == "beta" ]]', + "Release impact: advisory", + "advisory for beta", + ]); + expect(summaryStep.env?.RELEASE_PROFILE).toBe("${{ inputs.release_profile }}"); + expectTextToIncludeAll(summaryStep.run, [ + '[[ "$RELEASE_PROFILE" == "beta" ]] && performance_advisory=1', + 'check_child "product_performance" "$PERFORMANCE_RUN_ID" "$performance_required" "$performance_advisory"', + ]); + expect(workflow).toContain('performanceBlocking: ($releaseProfile != "beta")'); + expect(workflow).toContain('blocking: ($releaseProfile != "beta")'); + }); + + it("keeps child-job fail-fast polling best-effort", () => { + const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8"); + expect(workflow.match(/continuing with authoritative workflow conclusion\./gu)).toHaveLength(3); + }); + it("adopts exact full-release child runs without retrying ambiguous dispatch posts", () => { const childDispatches = [ ["normal_ci", "Dispatch and monitor CI"],