

























@@ -348,6 +348,7 @@ jobs:
348348needs: [resolve_release_target]
349349runs-on: ubuntu-latest
350350timeout-minutes: 60
351+environment: npm-release
351352steps:
352353 - name: Checkout release SHA
353354uses: actions/checkout@v6
@@ -450,7 +451,7 @@ jobs:
450451451452 pending_json="$(gh api -X GET "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/pending_deployments" 2>/dev/null || true)"
452453 if [[ -z "${pending_json}" ]] || ! printf '%s' "${pending_json}" | jq -e 'length > 0' >/dev/null 2>&1; then
453- return 0
454+ return 1
454455 fi
455456456457 approved=0
@@ -462,13 +463,15 @@ jobs:
462463 gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/pending_deployments" \
463464 -F "environment_ids[]=${env_id}" \
464465 -f state=approved \
465- -f comment="Approve release gate from OpenClaw Release Publish wrapper" >/dev/null
466+ -f comment="Approve child release gate after parent release approval" >/dev/null
466467 approved=1
467468 done < <(printf '%s' "${pending_json}" | jq -r '.[] | select(.current_user_can_approve == true) | [.environment.id, .environment.name] | @tsv')
468469469470 if [[ "${approved}" == "1" ]]; then
470471 echo "${workflow}: approved available pending environment gates"
472+ return 0
471473 fi
474+ return 1
472475 }
473476474477 print_failed_run_summary() {
@@ -511,7 +514,7 @@ jobs:
511514 if [[ "$state" != "$last_state" ]]; then
512515 echo "${workflow} still ${status} (updated ${updated_at}): ${url}"
513516 print_pending_deployments "${workflow}" "${run_id}"
514- approve_pending_deployments "${workflow}" "${run_id}"
517+ approve_pending_deployments "${workflow}" "${run_id}" || true
515518 last_state="$state"
516519 fi
517520 sleep 30
@@ -558,6 +561,85 @@ jobs:
558561 wait_run_pid="$!"
559562 }
560563564+ wait_for_job_success() {
565+ local workflow="$1"
566+ local run_id="$2"
567+ local job_name="$3"
568+ local jobs_json job_json run_status run_conclusion status conclusion url deadline
569+570+ deadline=$((SECONDS + 900))
571+ while true; do
572+ jobs_json="$(gh run view --repo "$GITHUB_REPOSITORY" "$run_id" --json status,conclusion,jobs)"
573+ run_status="$(printf '%s' "$jobs_json" | jq -r '.status')"
574+ run_conclusion="$(printf '%s' "$jobs_json" | jq -r '.conclusion // ""')"
575+ job_json="$(printf '%s' "$jobs_json" | jq -c --arg name "$job_name" '.jobs[]? | select(.name == $name) | {status, conclusion, url}' | head -n 1)"
576+ if [[ -n "$job_json" ]]; then
577+ status="$(printf '%s' "$job_json" | jq -r '.status')"
578+ conclusion="$(printf '%s' "$job_json" | jq -r '.conclusion // ""')"
579+ url="$(printf '%s' "$job_json" | jq -r '.url // ""')"
580+ if [[ "$status" == "completed" ]]; then
581+ if [[ "$conclusion" == "success" || "$conclusion" == "skipped" ]]; then
582+ echo "${workflow} ${job_name} ${conclusion}: ${url}"
583+ echo "- ${workflow} ${job_name}: ${conclusion} (${url})" >> "$GITHUB_STEP_SUMMARY"
584+ return 0
585+ fi
586+ echo "${workflow} ${job_name} failed: ${conclusion} ${url}" >&2
587+ print_failed_run_summary "${run_id}"
588+ return 1
589+ fi
590+ echo "${workflow} ${job_name} still ${status}: ${url}"
591+ elif [[ "$run_status" == "completed" ]]; then
592+ if [[ "$run_conclusion" == "success" ]]; then
593+ echo "${workflow} completed before ${job_name} was needed."
594+ echo "- ${workflow} ${job_name}: not needed" >> "$GITHUB_STEP_SUMMARY"
595+ return 0
596+ fi
597+ echo "${workflow} completed before ${job_name} with ${run_conclusion}." >&2
598+ print_failed_run_summary "${run_id}"
599+ return 1
600+ else
601+ echo "${workflow} waiting for ${job_name} to start: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
602+ fi
603+ if (( SECONDS >= deadline )); then
604+ echo "${workflow} ${job_name} did not complete within 15 minutes." >&2
605+ return 1
606+ fi
607+ sleep 10
608+ done
609+ }
610+611+ approve_child_publish_environment() {
612+ local workflow="$1"
613+ local run_id="$2"
614+ local run_json status conclusion deadline
615+616+ deadline=$((SECONDS + 900))
617+ while true; do
618+ if approve_pending_deployments "${workflow}" "${run_id}"; then
619+ echo "- ${workflow}: child environment gate approved" >> "$GITHUB_STEP_SUMMARY"
620+ return 0
621+ fi
622+ run_json="$(gh run view --repo "$GITHUB_REPOSITORY" "$run_id" --json status,conclusion,url)"
623+ status="$(printf '%s' "$run_json" | jq -r '.status')"
624+ conclusion="$(printf '%s' "$run_json" | jq -r '.conclusion // ""')"
625+ if [[ "$status" == "completed" ]]; then
626+ if [[ "$conclusion" == "success" ]]; then
627+ echo "${workflow}: completed before child environment approval was needed"
628+ return 0
629+ fi
630+ echo "${workflow}: completed before child environment approval with ${conclusion}" >&2
631+ print_failed_run_summary "${run_id}"
632+ return 1
633+ fi
634+ if (( SECONDS >= deadline )); then
635+ echo "${workflow}: child environment approval was not available within 15 minutes." >&2
636+ print_pending_deployments "${workflow}" "${run_id}"
637+ return 1
638+ fi
639+ sleep 10
640+ done
641+ }
642+561643 create_or_update_github_release() {
562644 local release_version notes_version title notes_file changelog_file latest_arg prerelease_args
563645 release_version="${RELEASE_TAG#v}"
@@ -679,12 +761,81 @@ jobs:
679761 } >> "$GITHUB_STEP_SUMMARY"
680762 }
681763764+ append_release_proof_to_github_release() {
765+ local release_version body_file notes_file tarball integrity telegram_line clawhub_line
766+767+ release_version="${RELEASE_TAG#v}"
768+ body_file="${RUNNER_TEMP}/release-body.md"
769+ notes_file="${RUNNER_TEMP}/release-notes-with-proof.md"
770+ tarball="$(npm view "openclaw@${release_version}" dist.tarball --json | jq -r '.')"
771+ integrity="$(npm view "openclaw@${release_version}" dist.integrity --json | jq -r '.')"
772+ gh release view "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" --json body --jq .body > "${body_file}"
773+774+ if [[ -n "${NPM_TELEGRAM_RUN_ID// }" ]]; then
775+ telegram_line="- npm Telegram beta E2E: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${NPM_TELEGRAM_RUN_ID}"
776+ else
777+ telegram_line="- npm Telegram beta E2E: not supplied"
778+ fi
779+ if [[ "${WAIT_FOR_CLAWHUB}" == "true" ]]; then
780+ clawhub_line="- plugin ClawHub publish: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${plugin_clawhub_run_id}"
781+ else
782+ clawhub_line="- plugin ClawHub publish: dispatched separately, not awaited by this proof: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${plugin_clawhub_run_id}"
783+ fi
784+785+ RELEASE_BODY_FILE="${body_file}" \
786+ RELEASE_NOTES_FILE="${notes_file}" \
787+ RELEASE_VERSION="${release_version}" \
788+ RELEASE_TAG="${RELEASE_TAG}" \
789+ RELEASE_REPO="${GITHUB_REPOSITORY}" \
790+ RELEASE_TARBALL="${tarball}" \
791+ RELEASE_INTEGRITY="${integrity}" \
792+ RELEASE_PUBLISH_RUN_ID="${GITHUB_RUN_ID}" \
793+ PREFLIGHT_RUN_ID="${PREFLIGHT_RUN_ID}" \
794+ FULL_RELEASE_VALIDATION_RUN_ID="${FULL_RELEASE_VALIDATION_RUN_ID}" \
795+ PLUGIN_NPM_RUN_ID="${plugin_npm_run_id}" \
796+ OPENCLAW_NPM_RUN_ID="${openclaw_npm_run_id}" \
797+ CLAWHUB_LINE="${clawhub_line}" \
798+ TELEGRAM_LINE="${telegram_line}" \
799+ node --input-type=module <<'NODE'
800+ import { readFileSync, writeFileSync } from "node:fs";
801+802+ const bodyFile = process.env.RELEASE_BODY_FILE;
803+ const notesFile = process.env.RELEASE_NOTES_FILE;
804+ if (!bodyFile || !notesFile) {
805+ throw new Error("Missing release notes file paths.");
806+ }
807+808+ const body = readFileSync(bodyFile, "utf8").trimEnd();
809+ const section = [
810+ "### Release verification",
811+ "",
812+ `- npm package: https://www.npmjs.com/package/openclaw/v/${process.env.RELEASE_VERSION}`,
813+ `- registry tarball: ${process.env.RELEASE_TARBALL}`,
814+ `- integrity: \`${process.env.RELEASE_INTEGRITY}\``,
815+ `- release publish: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.RELEASE_PUBLISH_RUN_ID}`,
816+ `- npm preflight: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.PREFLIGHT_RUN_ID}`,
817+ `- full release validation: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.FULL_RELEASE_VALIDATION_RUN_ID}`,
818+ `- plugin npm publish: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.PLUGIN_NPM_RUN_ID}`,
819+ process.env.CLAWHUB_LINE,
820+ `- OpenClaw npm publish: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.OPENCLAW_NPM_RUN_ID}`,
821+ process.env.TELEGRAM_LINE,
822+ ].join("\n");
823+824+ const withoutOldProof = body.replace(/\n?### Release verification\n[\s\S]*?(?=\n### |\n## |$)/, "");
825+ writeFileSync(notesFile, `${withoutOldProof.trimEnd()}\n\n${section}\n`);
826+ NODE
827+828+ gh release edit "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" --notes-file "${notes_file}"
829+ echo "- Release proof: appended to GitHub release" >> "$GITHUB_STEP_SUMMARY"
830+ }
831+682832 {
683833 echo "### Publish sequence"
684834 echo
685835 echo "- Workflow ref: \`${CHILD_WORKFLOW_REF}\`"
686836 echo "- Release tag: \`${RELEASE_TAG}\`"
687837 echo "- Release SHA: \`${TARGET_SHA}\`"
838+ echo "- Release approval: this workflow job"
688839 echo "- Plugin npm and ClawHub publish: dispatched in parallel"
689840 if [[ "${PUBLISH_OPENCLAW_NPM}" == "true" ]]; then
690841 echo "- OpenClaw npm publish: starts after plugin npm succeeds"
@@ -698,8 +849,8 @@ jobs:
698849 fi
699850 } >> "$GITHUB_STEP_SUMMARY"
700851701- npm_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}")
702- clawhub_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}")
852+ npm_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}" -f release_publish_run_id="${GITHUB_RUN_ID}")
853+ clawhub_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}" -f release_publish_run_id="${GITHUB_RUN_ID}")
703854 if [[ -n "${PLUGINS}" ]]; then
704855 npm_args+=(-f plugins="${PLUGINS}")
705856 clawhub_args+=(-f plugins="${PLUGINS}")
@@ -725,6 +876,7 @@ jobs:
725876 -f preflight_only=false \
726877 -f preflight_run_id="${PREFLIGHT_RUN_ID}" \
727878 -f full_release_validation_run_id="${FULL_RELEASE_VALIDATION_RUN_ID}" \
879+ -f release_publish_run_id="${GITHUB_RUN_ID}" \
728880 -f npm_dist_tag="${RELEASE_NPM_DIST_TAG}")"
729881 echo "- OpenClaw npm run ID: \`${openclaw_npm_run_id}\`" >> "$GITHUB_STEP_SUMMARY"
730882 else
@@ -739,7 +891,9 @@ jobs:
739891 wait_for_run_background plugin-clawhub-release.yml "${plugin_clawhub_run_id}" "${clawhub_result}"
740892 clawhub_pid="${wait_run_pid}"
741893 else
742- echo "- plugin-clawhub-release.yml: not awaited (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
894+ wait_for_job_success plugin-clawhub-release.yml "${plugin_clawhub_run_id}" "Validate release publish approval"
895+ approve_child_publish_environment plugin-clawhub-release.yml "${plugin_clawhub_run_id}"
896+ echo "- plugin-clawhub-release.yml: publish not awaited (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
743897 fi
744898745899 openclaw_result=""
@@ -776,6 +930,7 @@ jobs:
776930777931 if [[ "${failed}" == "0" && -n "${openclaw_npm_run_id}" ]]; then
778932 verify_published_release
933+ append_release_proof_to_github_release
779934 fi
780935 if [[ "${failed}" != "0" ]]; then
781936 exit 1
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。