ci(release): preserve direct repair publishes

This commit is contained in:
Peter Steinberger
2026-05-21 07:44:39 +01:00
parent 1e8d9666b0
commit 0604d25101
6 changed files with 101 additions and 32 deletions

View File

@@ -210,10 +210,36 @@ jobs:
fi
} >> "$GITHUB_STEP_SUMMARY"
docker_runtime_assets_preflight:
name: Verify Docker runtime-assets prune path
needs: [resolve_target]
if: inputs.rerun_group == 'all'
runs-on: ubuntu-24.04
timeout-minutes: 45
permissions:
contents: read
steps:
- name: Checkout target SHA
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_target.outputs.sha }}
fetch-depth: 1
persist-credentials: false
- name: Verify Docker runtime-assets prune path
env:
DOCKER_BUILDKIT: "1"
run: |
set -euo pipefail
timeout --foreground --kill-after=30s 35m docker build \
--target runtime-assets \
--build-arg OPENCLAW_EXTENSIONS="matrix" \
.
normal_ci:
name: Run normal full CI
needs: [resolve_target]
if: contains(fromJSON('["all","ci"]'), inputs.rerun_group)
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && contains(fromJSON('["all","ci"]'), inputs.rerun_group) && (inputs.rerun_group != 'all' || needs.docker_runtime_assets_preflight.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: ${{ inputs.release_profile != 'minimum' && 240 || 60 }}
outputs:
@@ -312,8 +338,8 @@ jobs:
plugin_prerelease:
name: Run plugin prerelease validation
needs: [resolve_target]
if: contains(fromJSON('["all","plugin-prerelease"]'), inputs.rerun_group)
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && contains(fromJSON('["all","plugin-prerelease"]'), inputs.rerun_group) && (inputs.rerun_group != 'all' || needs.docker_runtime_assets_preflight.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: ${{ inputs.release_profile == 'full' && 300 || inputs.release_profile == 'stable' && 240 || 60 }}
outputs:
@@ -412,8 +438,8 @@ jobs:
release_checks:
name: Run release/live/Docker/QA validation
needs: [resolve_target]
if: contains(fromJSON('["all","release-checks","install-smoke","cross-os","live-e2e","package","qa","qa-parity","qa-live"]'), inputs.rerun_group)
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && contains(fromJSON('["all","release-checks","install-smoke","cross-os","live-e2e","package","qa","qa-parity","qa-live"]'), inputs.rerun_group) && (inputs.rerun_group != 'all' || needs.docker_runtime_assets_preflight.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: ${{ inputs.release_profile != 'minimum' && 240 || 60 }}
outputs:
@@ -565,8 +591,8 @@ jobs:
prepare_release_package:
name: Prepare release package artifact
needs: [resolve_target]
if: ${{ inputs.npm_telegram_package_spec == '' && inputs.release_package_spec == '' && inputs.rerun_group == 'all' && inputs.release_profile == 'full' }}
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && inputs.npm_telegram_package_spec == '' && inputs.release_package_spec == '' && inputs.rerun_group == 'all' && inputs.release_profile == 'full' && needs.docker_runtime_assets_preflight.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
@@ -735,7 +761,7 @@ jobs:
summary:
name: Verify full validation
needs: [resolve_target, normal_ci, plugin_prerelease, release_checks, npm_telegram]
needs: [resolve_target, docker_runtime_assets_preflight, normal_ci, plugin_prerelease, release_checks, npm_telegram]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
@@ -751,6 +777,8 @@ jobs:
PLUGIN_PRERELEASE_RESULT: ${{ needs.plugin_prerelease.result }}
RELEASE_CHECKS_RESULT: ${{ needs.release_checks.result }}
NPM_TELEGRAM_RESULT: ${{ needs.npm_telegram.result }}
DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT: ${{ needs.docker_runtime_assets_preflight.result }}
RERUN_GROUP: ${{ inputs.rerun_group }}
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
run: |
@@ -798,6 +826,7 @@ jobs:
echo
echo "| Child | Result | Minutes | Head SHA | Run |"
echo "| --- | --- | ---: | --- | --- |"
echo "| \`docker_runtime_assets_preflight\` | \`${DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT}\` | | current workflow | |"
} >> "$GITHUB_STEP_SUMMARY"
append_child_row() {
@@ -933,23 +962,29 @@ jobs:
}
failed=0
required_after_preflight=1
if [[ "$RERUN_GROUP" == "all" && "$DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT" != "success" ]]; then
echo "::error::Docker runtime-assets preflight ended with ${DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT}."
failed=1
required_after_preflight=0
fi
append_child_overview
if [[ "$NORMAL_CI_RESULT" == "skipped" && -z "${NORMAL_CI_RUN_ID// }" ]]; then
check_child "normal_ci" "" 0 || failed=1
check_child "normal_ci" "" "$required_after_preflight" || failed=1
else
check_child "normal_ci" "$NORMAL_CI_RUN_ID" 1 || failed=1
fi
if [[ "$PLUGIN_PRERELEASE_RESULT" == "skipped" && -z "${PLUGIN_PRERELEASE_RUN_ID// }" ]]; then
check_child "plugin_prerelease" "" 0 || failed=1
check_child "plugin_prerelease" "" "$required_after_preflight" || failed=1
else
check_child "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID" 1 || failed=1
fi
if [[ "$RELEASE_CHECKS_RESULT" == "skipped" && -z "${RELEASE_CHECKS_RUN_ID// }" ]]; then
check_child "release_checks" "" 0 || failed=1
check_child "release_checks" "" "$required_after_preflight" || failed=1
else
check_child "release_checks" "$RELEASE_CHECKS_RUN_ID" 1 || failed=1
fi

View File

@@ -337,16 +337,6 @@ jobs:
PACKAGE_VERSION="$(node -p "require('./package.json').version")"
node --import tsx scripts/openclaw-npm-prepublish-verify.ts "$TARBALL_PATH" "$PACKAGE_VERSION"
- name: Verify Docker runtime-assets prune path
env:
DOCKER_BUILDKIT: "1"
run: |
set -euo pipefail
timeout --foreground --kill-after=30s 35m docker build \
--target runtime-assets \
--build-arg OPENCLAW_EXTENSIONS="matrix" \
.
- name: Upload dependency release evidence
uses: actions/upload-artifact@v7
with:
@@ -415,8 +405,8 @@ jobs:
echo "Real publish requires full_release_validation_run_id from a successful Full Release Validation run." >&2
exit 1
fi
if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" ]]; then
echo "Real publish requires release_publish_run_id from the approved OpenClaw Release Publish workflow." >&2
if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" && "${GITHUB_ACTOR}" == "github-actions[bot]" ]]; then
echo "Workflow-dispatched real publish requires release_publish_run_id from the approved OpenClaw Release Publish workflow." >&2
exit 1
fi
@@ -427,6 +417,14 @@ jobs:
EXPECTED_WORKFLOW_BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" ]]; then
if [[ "${GITHUB_ACTOR}" == "github-actions[bot]" ]]; then
echo "OpenClaw npm publish dispatched by another workflow must include release_publish_run_id." >&2
exit 1
fi
echo "Direct OpenClaw npm publish; relying on this workflow's npm-release environment approval."
exit 0
fi
if [[ "${GITHUB_ACTOR}" != "github-actions[bot]" ]]; then
echo "OpenClaw npm publish must be dispatched by the OpenClaw Release Publish workflow, not directly by ${GITHUB_ACTOR}." >&2
exit 1

View File

@@ -217,8 +217,12 @@ jobs:
run: |
set -euo pipefail
if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" ]]; then
echo "Plugin ClawHub publish must be dispatched by OpenClaw Release Publish after its npm-release gate." >&2
exit 1
if [[ "${GITHUB_ACTOR}" == "github-actions[bot]" ]]; then
echo "Plugin ClawHub publish dispatched by another workflow must include release_publish_run_id." >&2
exit 1
fi
echo "Direct Plugin ClawHub Release dispatch; relying on this workflow's clawhub-plugin-release environment approval."
exit 0
fi
if [[ "${GITHUB_ACTOR}" != "github-actions[bot]" ]]; then
echo "Plugin ClawHub publish must be dispatched by the OpenClaw Release Publish workflow, not directly by ${GITHUB_ACTOR}." >&2

View File

@@ -194,8 +194,12 @@ jobs:
run: |
set -euo pipefail
if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" ]]; then
echo "Plugin npm publish must be dispatched by OpenClaw Release Publish after its npm-release gate." >&2
exit 1
if [[ "${GITHUB_ACTOR}" == "github-actions[bot]" ]]; then
echo "Plugin npm publish dispatched by another workflow must include release_publish_run_id." >&2
exit 1
fi
echo "Direct Plugin NPM Release dispatch; relying on this workflow's npm-release environment approval."
exit 0
fi
if [[ "${GITHUB_ACTOR}" != "github-actions[bot]" ]]; then
echo "Plugin npm publish must be dispatched by the OpenClaw Release Publish workflow, not directly by ${GITHUB_ACTOR}." >&2

View File

@@ -721,9 +721,12 @@ describe("package artifact reuse", () => {
expect(workflow).toContain("CHILD_WORKFLOW_REF: ${{ github.ref_name }}");
expect(workflow).toContain('gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@"');
expect(preparePackageJob.name).toBe("Prepare release package artifact");
expect(preparePackageJob.needs).toEqual(["resolve_target"]);
expect(preparePackageJob.needs).toEqual(["resolve_target", "docker_runtime_assets_preflight"]);
expect(preparePackageJob.if).toContain("inputs.rerun_group == 'all'");
expect(preparePackageJob.if).toContain("inputs.release_profile == 'full'");
expect(preparePackageJob.if).toContain(
"needs.docker_runtime_assets_preflight.result == 'success'",
);
expectTextToIncludeAll(
workflowStep(preparePackageJob, "Resolve release package artifact").run,
[
@@ -883,6 +886,7 @@ describe("package artifact reuse", () => {
it("keeps release publish creation compatible with gh api and prerelease notes", () => {
const workflow = readFileSync(RELEASE_PUBLISH_WORKFLOW, "utf8");
const npmWorkflow = readFileSync(".github/workflows/openclaw-npm-release.yml", "utf8");
const fullReleaseWorkflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8");
expect(workflow).toContain("timeout-minutes: 60");
expect(workflow).toContain("environment: npm-release");
@@ -898,12 +902,20 @@ describe("package artifact reuse", () => {
expect(npmWorkflow).toContain("preflight-manifest.json");
expect(npmWorkflow).toContain("Verify full release validation run metadata");
expect(npmWorkflow).toContain("Verify full release validation target");
expect(npmWorkflow).toContain("Verify Docker runtime-assets prune path");
expect(npmWorkflow).toContain("--target runtime-assets");
expect(npmWorkflow).not.toContain("Verify Docker runtime-assets prune path");
expect(fullReleaseWorkflow).toContain("docker_runtime_assets_preflight");
expect(fullReleaseWorkflow).toContain("Verify Docker runtime-assets prune path");
expect(fullReleaseWorkflow).toContain("--target runtime-assets");
expect(fullReleaseWorkflow).toContain("inputs.rerun_group == 'all'");
expect(fullReleaseWorkflow).toContain(
"needs.docker_runtime_assets_preflight.result == 'success'",
);
expect(npmWorkflow).toContain("full_release_validation_run_id");
expect(npmWorkflow).toContain("release_publish_run_id");
expect(npmWorkflow).toContain("Real publish requires full_release_validation_run_id");
expect(npmWorkflow).toContain("Real publish requires release_publish_run_id");
expect(npmWorkflow).toContain(
"Workflow-dispatched real publish requires release_publish_run_id",
);
expect(npmWorkflow).toContain("tarballSha256");
expect(workflow).toContain("Checkout release SHA");
expect(workflow).toContain('git show "${TARGET_SHA}:CHANGELOG.md" > "${changelog_file}"');
@@ -962,6 +974,9 @@ describe("package artifact reuse", () => {
expect(pluginNpmWorkflow).toContain("Validate release publish approval run");
expect(clawHubWorkflow).toContain("Validate release publish approval run");
expect(openclawNpmWorkflow).toContain("Validate release publish approval run");
expect(pluginNpmWorkflow).toContain("Direct Plugin NPM Release dispatch");
expect(clawHubWorkflow).toContain("Direct Plugin ClawHub Release dispatch");
expect(openclawNpmWorkflow).toContain("Direct OpenClaw npm publish");
expect(pluginNpmWorkflow).toContain('GITHUB_ACTOR}" != "github-actions[bot]"');
expect(clawHubWorkflow).toContain('GITHUB_ACTOR}" != "github-actions[bot]"');
expect(openclawNpmWorkflow).toContain('GITHUB_ACTOR}" != "github-actions[bot]"');

View File

@@ -481,6 +481,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
expect(releaseChecksWorkflow.jobs.summary["runs-on"]).toBe("ubuntu-24.04");
for (const jobName of [
"resolve_target",
"docker_runtime_assets_preflight",
"normal_ci",
"plugin_prerelease",
"release_checks",
@@ -493,6 +494,18 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
expect(fullReleaseWorkflow.jobs.normal_ci["timeout-minutes"]).toBe(
"${{ inputs.release_profile != 'minimum' && 240 || 60 }}",
);
expect(fullReleaseWorkflow.jobs.normal_ci.needs).toEqual([
"resolve_target",
"docker_runtime_assets_preflight",
]);
expect(fullReleaseWorkflow.jobs.docker_runtime_assets_preflight.if).toBe(
"inputs.rerun_group == 'all'",
);
expect(
fullReleaseWorkflow.jobs.docker_runtime_assets_preflight.steps.find(
(step) => step.name === "Verify Docker runtime-assets prune path",
).run,
).toContain("--target runtime-assets");
expect(fullReleaseWorkflow.jobs.plugin_prerelease["timeout-minutes"]).toBe(
"${{ inputs.release_profile == 'full' && 300 || inputs.release_profile == 'stable' && 240 || 60 }}",
);