mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 15:43:57 -06:00
ci: persist extended-stable Docker completion
This commit is contained in:
@@ -756,6 +756,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
statuses: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -763,6 +764,14 @@ jobs:
|
||||
ref: ${{ needs.resolve_build_provenance.outputs.source_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted release tooling
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: .release-harness
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- *buildkit_prepull_step
|
||||
- name: Set up Docker Builder
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||
@@ -912,3 +921,32 @@ jobs:
|
||||
--version "${VERSION}" \
|
||||
--image "${GHCR_IMAGE}" \
|
||||
--image "${DOCKERHUB_IMAGE}"
|
||||
|
||||
# This status is the durable resume boundary. Release visibility is not
|
||||
# Docker evidence and must never suppress a missing publication.
|
||||
- name: Record durable extended-stable Docker completion
|
||||
if: ${{ needs.resolve_release_policy.outputs.channel == 'extended-stable' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_SHA: ${{ inputs.release_sha }}
|
||||
VERSION: ${{ needs.resolve_release_policy.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
payload="$(node .release-harness/scripts/docker-channel-promote.mjs \
|
||||
--status-payload \
|
||||
--version "${VERSION}" \
|
||||
--repository "${GITHUB_REPOSITORY}" \
|
||||
--source-sha "${RELEASE_SHA}" \
|
||||
--run-id "${GITHUB_RUN_ID}")"
|
||||
for attempt in 1 2 3; do
|
||||
if printf '%s' "${payload}" | gh api --method POST \
|
||||
"repos/${GITHUB_REPOSITORY}/statuses/${RELEASE_SHA}" \
|
||||
--input - >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${attempt}" != "3" ]]; then
|
||||
sleep $((attempt * 5))
|
||||
fi
|
||||
done
|
||||
echo "Failed to persist durable Docker completion after verified publication." >&2
|
||||
exit 1
|
||||
|
||||
@@ -103,12 +103,14 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
sha: ${{ steps.manifest.outputs.sha || steps.ref.outputs.sha }}
|
||||
expected_validation_branch: ${{ steps.inputs.outputs.expected_validation_branch }}
|
||||
preflight_artifact_name: ${{ steps.preflight_artifact.outputs.name }}
|
||||
preflight_tarball_sha256: ${{ steps.manifest.outputs.tarball_sha256 }}
|
||||
full_release_validation_run_attempt: ${{ steps.full_run.outputs.attempt }}
|
||||
windows_node_installer_digests: ${{ steps.windows_source.outputs.installer_digests }}
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
id: inputs
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
@@ -247,6 +249,16 @@ jobs:
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
expected_validation_branch="${WORKFLOW_REF#refs/heads/}"
|
||||
if [[ "${PUBLISH_DOCKER_ONLY}" == "true" ]]; then
|
||||
if [[ ! "${RELEASE_TAG}" =~ ^v([0-9]{4})\.([1-9][0-9]*)\.([1-9][0-9]*)$ ]] || \
|
||||
(( 10#${BASH_REMATCH[3]:-0} < 33 )); then
|
||||
echo "Extended-stable Docker closeout requires a final .33+ release tag." >&2
|
||||
exit 1
|
||||
fi
|
||||
expected_validation_branch="extended-stable/${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.33"
|
||||
fi
|
||||
echo "expected_validation_branch=${expected_validation_branch}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate stable Windows source release
|
||||
id: windows_source
|
||||
@@ -486,7 +498,7 @@ jobs:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
EXPECTED_SHA: ${{ steps.ref.outputs.sha }}
|
||||
EXPECTED_RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
EXPECTED_WORKFLOW_BRANCH: ${{ github.ref_name }}
|
||||
EXPECTED_WORKFLOW_BRANCH: ${{ steps.inputs.outputs.expected_validation_branch }}
|
||||
RUN_JSON_FILE: ${{ runner.temp }}/full-release-validation-run.json
|
||||
TRUSTED_MAIN_REF: refs/remotes/origin/main
|
||||
VALIDATOR_FILE: ${{ runner.temp }}/release-validation-tooling/validate-full-release-validation-evidence.mjs
|
||||
@@ -537,6 +549,8 @@ jobs:
|
||||
|
||||
- name: Validate release tag is reachable from a trusted release branch
|
||||
env:
|
||||
EXPECTED_VALIDATION_BRANCH: ${{ steps.inputs.outputs.expected_validation_branch }}
|
||||
PUBLISH_DOCKER_ONLY: ${{ inputs.publish_docker_only && 'true' || 'false' }}
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
WORKFLOW_REF_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
@@ -545,6 +559,15 @@ jobs:
|
||||
+refs/heads/main:refs/remotes/origin/main \
|
||||
'+refs/heads/release/*:refs/remotes/origin/release/*' \
|
||||
'+refs/heads/extended-stable/*:refs/remotes/origin/extended-stable/*'
|
||||
if [[ "${PUBLISH_DOCKER_ONLY}" == "true" ]]; then
|
||||
expected_ref="refs/remotes/origin/${EXPECTED_VALIDATION_BRANCH}"
|
||||
if git show-ref --verify --quiet "${expected_ref}" && \
|
||||
git merge-base --is-ancestor HEAD "${expected_ref}"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "Extended-stable tag ${RELEASE_TAG} must be reachable from ${EXPECTED_VALIDATION_BRANCH}." >&2
|
||||
exit 1
|
||||
fi
|
||||
if git merge-base --is-ancestor HEAD origin/main; then
|
||||
exit 0
|
||||
fi
|
||||
@@ -2385,11 +2408,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-release
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
statuses: read
|
||||
outputs:
|
||||
docker_already_published: ${{ steps.release.outputs.docker_already_published }}
|
||||
docker_status_run_id: ${{ steps.release.outputs.docker_status_run_id }}
|
||||
release_id: ${{ steps.release.outputs.release_id }}
|
||||
release_body_sha256: ${{ steps.release.outputs.release_body_sha256 }}
|
||||
release_already_public: ${{ steps.release.outputs.release_already_public }}
|
||||
steps:
|
||||
- name: Verify exact npm and selector readback matches preflight bytes
|
||||
env:
|
||||
@@ -2550,7 +2576,7 @@ jobs:
|
||||
|
||||
- Scope: \`openclaw@${release_version}\`, official npm plugins, and Gateway container images only.
|
||||
- Channel: npm and container \`extended-stable\`; regular \`latest\` is unchanged.
|
||||
- Container publication: this draft is made public only after Docker publication succeeds.
|
||||
- Container publication: this closeout requires durable Docker completion on the immutable release SHA.
|
||||
- Excluded: ClawHub, macOS, Windows, mobile, website, and private dist-tags.
|
||||
EOF
|
||||
node .release-harness/scripts/render-github-release-notes.mjs \
|
||||
@@ -2560,7 +2586,7 @@ jobs:
|
||||
--verification-file "${verification_file}" \
|
||||
--output "${notes_file}"
|
||||
|
||||
release_already_public=false
|
||||
release_is_public=false
|
||||
release_id="$(resolve_release_id)"
|
||||
if [[ -n "${release_id}" ]]; then
|
||||
verify_release_resource "${release_id}"
|
||||
@@ -2591,8 +2617,8 @@ jobs:
|
||||
elif [[ "${release_draft}" == "false" ]]; then
|
||||
verify_release_resource "${release_id}" false true
|
||||
wait_until_not_latest
|
||||
release_already_public=true
|
||||
echo "Existing canonical public release is already complete; Docker will not be rebuilt."
|
||||
release_is_public=true
|
||||
echo "Existing canonical public release found; Docker completion will be checked independently."
|
||||
else
|
||||
echo "GitHub release ${release_id} has an invalid draft state." >&2
|
||||
exit 1
|
||||
@@ -2600,11 +2626,39 @@ jobs:
|
||||
release_body_sha256="$(
|
||||
sha256sum "${notes_file}" | awk '{print $1}'
|
||||
)"
|
||||
docker_status_file="${RUNNER_TEMP}/extended-stable-docker-status.json"
|
||||
gh api "repos/${GITHUB_REPOSITORY}/commits/${TARGET_SHA}/status" > "${docker_status_file}"
|
||||
docker_status_run_id="$(node .release-harness/scripts/docker-channel-promote.mjs \
|
||||
--find-status-file "${docker_status_file}" \
|
||||
--version "${release_version}" \
|
||||
--repository "${GITHUB_REPOSITORY}" \
|
||||
--source-sha "${TARGET_SHA}")"
|
||||
docker_already_published=false
|
||||
if [[ -n "${docker_status_run_id}" ]]; then
|
||||
docker_run_file="${RUNNER_TEMP}/extended-stable-docker-run.json"
|
||||
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${docker_status_run_id}" > "${docker_run_file}"
|
||||
jq -e --arg repository "${GITHUB_REPOSITORY}" \
|
||||
--arg run_id "${docker_status_run_id}" '
|
||||
(.id | tostring) == $run_id and
|
||||
.name == "OpenClaw Release Publish" and
|
||||
(.path | split("@")[0]) == ".github/workflows/openclaw-release-publish.yml" and
|
||||
.event == "workflow_dispatch" and
|
||||
.status == "completed" and
|
||||
.repository.full_name == $repository
|
||||
' "${docker_run_file}" >/dev/null || {
|
||||
echo "Docker completion status does not reference a completed OpenClaw Release Publish run." >&2
|
||||
exit 1
|
||||
}
|
||||
docker_already_published=true
|
||||
fi
|
||||
echo "release_id=${release_id}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_body_sha256=${release_body_sha256}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_already_public=${release_already_public}" >> "$GITHUB_OUTPUT"
|
||||
if [[ "${release_already_public}" == "true" ]]; then
|
||||
echo "- Extended-stable GitHub release: already public; Docker rebuild skipped" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "docker_already_published=${docker_already_published}" >> "$GITHUB_OUTPUT"
|
||||
echo "docker_status_run_id=${docker_status_run_id}" >> "$GITHUB_OUTPUT"
|
||||
if [[ "${docker_already_published}" == "true" ]]; then
|
||||
echo "- Docker completion: durable status from run ${docker_status_run_id}; rebuild skipped" >> "$GITHUB_STEP_SUMMARY"
|
||||
elif [[ "${release_is_public}" == "true" ]]; then
|
||||
echo "- Extended-stable GitHub release: public without Docker completion; Docker publication required" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "- Extended-stable GitHub release draft: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
@@ -2612,7 +2666,7 @@ jobs:
|
||||
publish_docker:
|
||||
name: Publish Docker images
|
||||
needs: [resolve_release_target, publish, prepare_extended_stable_release]
|
||||
if: ${{ always() && ((inputs.publish_openclaw_npm && needs.publish.result == 'success') || (inputs.publish_docker_only && needs.prepare_extended_stable_release.result == 'success' && needs.prepare_extended_stable_release.outputs.release_already_public != 'true')) }}
|
||||
if: ${{ always() && ((inputs.publish_openclaw_npm && needs.publish.result == 'success') || (inputs.publish_docker_only && needs.prepare_extended_stable_release.result == 'success' && needs.prepare_extended_stable_release.outputs.docker_already_published != 'true')) }}
|
||||
uses: ./.github/workflows/docker-release.yml
|
||||
with:
|
||||
tag: ${{ inputs.tag }}
|
||||
@@ -2623,6 +2677,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
statuses: write
|
||||
|
||||
finalize_github_release:
|
||||
name: Finalize GitHub release
|
||||
@@ -2651,10 +2706,76 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_extended_stable_docker_completion:
|
||||
name: Verify durable extended-stable Docker completion
|
||||
needs: [resolve_release_target, prepare_extended_stable_release, publish_docker]
|
||||
if: ${{ always() && inputs.publish_docker_only && needs.prepare_extended_stable_release.result == 'success' && (needs.publish_docker.result == 'success' || (needs.publish_docker.result == 'skipped' && needs.prepare_extended_stable_release.outputs.docker_already_published == 'true')) }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
statuses: read
|
||||
steps:
|
||||
- name: Checkout trusted release tooling
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Verify durable Docker completion status
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
TARGET_SHA: ${{ needs.resolve_release_target.outputs.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
release_version="${RELEASE_TAG#v}"
|
||||
status_file="${RUNNER_TEMP}/extended-stable-docker-status.json"
|
||||
docker_status_run_id=""
|
||||
for attempt in $(seq 1 12); do
|
||||
gh api "repos/${GITHUB_REPOSITORY}/commits/${TARGET_SHA}/status" > "${status_file}"
|
||||
docker_status_run_id="$(node scripts/docker-channel-promote.mjs \
|
||||
--find-status-file "${status_file}" \
|
||||
--version "${release_version}" \
|
||||
--repository "${GITHUB_REPOSITORY}" \
|
||||
--source-sha "${TARGET_SHA}")"
|
||||
if [[ -n "${docker_status_run_id}" ]]; then
|
||||
break
|
||||
fi
|
||||
if [[ "${attempt}" == "12" ]]; then
|
||||
echo "Docker publication completed without a durable completion status." >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
docker_run_file="${RUNNER_TEMP}/extended-stable-docker-run.json"
|
||||
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${docker_status_run_id}" > "${docker_run_file}"
|
||||
jq -e --arg current_run_id "${GITHUB_RUN_ID}" \
|
||||
--arg repository "${GITHUB_REPOSITORY}" \
|
||||
--arg run_id "${docker_status_run_id}" '
|
||||
(.id | tostring) == $run_id and
|
||||
.name == "OpenClaw Release Publish" and
|
||||
(.path | split("@")[0]) == ".github/workflows/openclaw-release-publish.yml" and
|
||||
.event == "workflow_dispatch" and
|
||||
(.status == "completed" or ($run_id == $current_run_id and .status == "in_progress")) and
|
||||
.repository.full_name == $repository
|
||||
' "${docker_run_file}" >/dev/null || {
|
||||
echo "Docker completion status does not reference the expected release workflow run." >&2
|
||||
exit 1
|
||||
}
|
||||
echo "- Durable Docker completion: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${docker_status_run_id}" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
finalize_extended_stable_github_release:
|
||||
name: Finalize extended-stable GitHub release
|
||||
needs: [resolve_release_target, prepare_extended_stable_release, publish_docker]
|
||||
if: ${{ always() && inputs.publish_docker_only && needs.prepare_extended_stable_release.result == 'success' && needs.publish_docker.result == 'success' }}
|
||||
needs:
|
||||
[
|
||||
resolve_release_target,
|
||||
prepare_extended_stable_release,
|
||||
verify_extended_stable_docker_completion,
|
||||
]
|
||||
if: ${{ always() && inputs.publish_docker_only && needs.prepare_extended_stable_release.result == 'success' && needs.verify_extended_stable_docker_completion.result == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-release
|
||||
permissions:
|
||||
|
||||
Reference in New Issue
Block a user