From cdcdb4bb93c0a5e2d04eaf0fe2e12885cef287e3 Mon Sep 17 00:00:00 2001 From: Onur Date: Sun, 12 Apr 2026 23:58:56 +0200 Subject: [PATCH] Release: separate release checks workflow (#65552) * Release: separate live cache validation * Docs: restore live validation secret details * Release: rename live validation to release checks * Release: document release check split rationale * Release: tone down workflow warning * Release: require full sha for release checks * CI: use larger runners for release checks * CI: keep release promotion on github runner * CI: document github-hosted release jobs * Release: allow sha validation-only preflight --- .github/workflows/openclaw-npm-release.yml | 80 ++++++------ .github/workflows/openclaw-release-checks.yml | 120 ++++++++++++++++++ docs/reference/RELEASING.md | 48 ++++++- 3 files changed, 203 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/openclaw-release-checks.yml diff --git a/.github/workflows/openclaw-npm-release.yml b/.github/workflows/openclaw-npm-release.yml index 42d2f7f6bfc..d59e6251f28 100644 --- a/.github/workflows/openclaw-npm-release.yml +++ b/.github/workflows/openclaw-npm-release.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: tag: - description: Release tag to publish (for example v2026.3.22, v2026.3.22-beta.1, or fallback v2026.3.22-1) + description: Release tag to publish, or a full 40-character main commit SHA for validation-only preflight (for example v2026.3.22 or 0123456789abcdef0123456789abcdef01234567) required: true type: string preflight_only: @@ -40,23 +40,31 @@ env: PNPM_VERSION: "10.32.1" jobs: + # PLEASE DON'T ADD LONG-RUNNING OR FLAKY CHECKS TO THE npm RELEASE PATH. + # KEEP THIS WORKFLOW SHORT AND DETERMINISTIC OR IT CAN GET STUCK AND JEOPARDIZE THE RELEASE. + # RELEASE-TIME LIVE OR END-TO-END VALIDATION BELONGS IN openclaw-release-checks.yml. preflight_openclaw_npm: if: ${{ inputs.preflight_only && !inputs.promote_beta_to_latest }} - runs-on: ubuntu-latest + runs-on: blacksmith-16vcpu-ubuntu-2404 permissions: contents: read steps: - - name: Validate tag input format + - name: Validate release ref input format env: - RELEASE_TAG: ${{ inputs.tag }} + RELEASE_REF: ${{ inputs.tag }} + PREFLIGHT_ONLY: ${{ inputs.preflight_only }} RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }} run: | set -euo pipefail - if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]]; then - echo "Invalid release tag format: ${RELEASE_TAG}" + if [[ ! "${RELEASE_REF}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]] && [[ ! "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "Invalid release ref format: ${RELEASE_REF}" exit 1 fi - if [[ "${RELEASE_TAG}" == *"-beta."* && "${RELEASE_NPM_DIST_TAG}" != "beta" ]]; then + if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]] && [[ "${PREFLIGHT_ONLY}" != "true" ]]; then + echo "Full commit SHA input is only supported for validation-only preflight runs." + exit 1 + fi + if [[ "${RELEASE_REF}" == *"-beta."* && "${RELEASE_NPM_DIST_TAG}" != "beta" ]]; then echo "Beta prerelease tags must publish to npm dist-tag beta." exit 1 fi @@ -70,7 +78,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 with: - ref: refs/tags/${{ inputs.tag }} + ref: ${{ inputs.tag }} fetch-depth: 0 - name: Setup Node environment @@ -110,50 +118,42 @@ jobs: - name: Build Control UI run: pnpm ui:build - - name: Validate release tag and package metadata + - name: Validate release metadata if: ${{ inputs.preflight_run_id == '' }} env: OPENCLAW_NPM_RELEASE_SKIP_PACK_CHECK: "1" - RELEASE_TAG: ${{ inputs.tag }} + RELEASE_REF: ${{ inputs.tag }} + PREFLIGHT_ONLY: ${{ inputs.preflight_only }} RELEASE_MAIN_REF: origin/main OPENCLAW_NPM_PUBLISH_TAG: ${{ inputs.npm_dist_tag }} run: | set -euo pipefail RELEASE_SHA=$(git rev-parse HEAD) - export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF + export RELEASE_SHA RELEASE_MAIN_REF # Fetch the full main ref so merge-base ancestry checks keep working # for older tagged commits that are still contained in main. git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then + MAIN_SHA="$(git rev-parse origin/main)" + if [[ "${RELEASE_SHA}" != "${MAIN_SHA}" ]]; then + echo "Validation-only SHA mode only supports the current origin/main HEAD." >&2 + exit 1 + fi + RELEASE_TAG="v$(node -p "require('./package.json').version")" + export RELEASE_TAG + echo "Validation-only SHA mode: using synthetic release tag ${RELEASE_TAG} for package metadata checks." + else + RELEASE_TAG="${RELEASE_REF}" + export RELEASE_TAG + fi pnpm release:openclaw:npm:check + # KEEP THIS LANE LIMITED TO FAST, REPEATABLE RELEASE READINESS CHECKS. + # IF A CHECK CAN TAKE A LONG TIME, NEEDS LIVE CREDENTIALS, OR IS KNOWN TO BE FLAKY, + # IT BELONGS IN openclaw-release-checks.yml INSTEAD OF BLOCKING npm PUBLISH. - name: Verify release contents run: pnpm release:check - - name: Validate live cache credentials - if: ${{ github.ref == 'refs/heads/main' }} - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - set -euo pipefail - if [[ -z "${OPENAI_API_KEY}" ]]; then - echo "Missing OPENAI_API_KEY secret for release live cache validation." >&2 - exit 1 - fi - if [[ -z "${ANTHROPIC_API_KEY}" ]]; then - echo "Missing ANTHROPIC_API_KEY secret for release live cache validation." >&2 - exit 1 - fi - - - name: Verify live prompt cache floors - if: ${{ github.ref == 'refs/heads/main' }} - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENCLAW_LIVE_CACHE_TEST: "1" - OPENCLAW_LIVE_TEST: "1" - run: pnpm test:live:cache - - name: Pack prepared npm tarball id: packed_tarball env: @@ -242,7 +242,7 @@ jobs: validate_publish_request: if: ${{ !inputs.preflight_only && !inputs.promote_beta_to_latest }} - runs-on: ubuntu-latest + runs-on: blacksmith-16vcpu-ubuntu-2404 permissions: contents: read steps: @@ -267,7 +267,8 @@ jobs: fi publish_openclaw_npm: - # npm trusted publishing + provenance requires a GitHub-hosted runner. + # KEEP THE REAL RELEASE/PUBLISH PATH ON A GITHUB-HOSTED RUNNER. + # npm trusted publishing + provenance requires this to stay on ubuntu-latest. needs: [validate_publish_request] if: ${{ !inputs.preflight_only && !inputs.promote_beta_to_latest }} runs-on: ubuntu-latest @@ -407,6 +408,9 @@ jobs: bash scripts/openclaw-npm-publish.sh --publish "${publish_target}" promote_beta_to_latest: + # KEEP THE MUTATING RELEASE PATH ON A GITHUB-HOSTED RUNNER TOO. + # This job changes the public npm dist-tags, so we keep it aligned with the + # real release path instead of moving it onto the larger Blacksmith runners. if: ${{ inputs.promote_beta_to_latest }} runs-on: ubuntu-latest environment: npm-release diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml new file mode 100644 index 00000000000..e70f6a149fc --- /dev/null +++ b/.github/workflows/openclaw-release-checks.yml @@ -0,0 +1,120 @@ +name: OpenClaw Release Checks + +on: + workflow_dispatch: + inputs: + ref: + description: Existing release tag or current full 40-character main commit SHA to validate (for example v2026.4.12 or 0123456789abcdef0123456789abcdef01234567) + required: true + type: string + +concurrency: + group: openclaw-release-checks-${{ inputs.ref }} + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + NODE_VERSION: "24.x" + PNPM_VERSION: "10.32.1" + +jobs: + # THIS WORKFLOW EXISTS SO RELEASE-TIME LIVE CHECKS CAN RUN WITHOUT BLOCKING npm PUBLISH. + # PUT THE SLOWER, EXTERNAL, OR SOMETIMES-FLAKY RELEASE CHECKS HERE INSTEAD OF + # RECOUPLING THEM TO openclaw-npm-release.yml. + validate_release_live_cache: + runs-on: blacksmith-16vcpu-ubuntu-2404 + timeout-minutes: 60 + permissions: + contents: read + steps: + - name: Require main workflow ref for release checks + env: + WORKFLOW_REF: ${{ github.ref }} + run: | + set -euo pipefail + if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then + echo "Release checks must be dispatched from main so the workflow logic and secrets stay canonical." >&2 + exit 1 + fi + + - name: Validate ref input + env: + RELEASE_REF: ${{ inputs.ref }} + run: | + set -euo pipefail + if [[ ! "${RELEASE_REF}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]] && [[ ! "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "Expected an existing release tag or current full 40-character main commit SHA, got: ${RELEASE_REF}" >&2 + exit 1 + fi + + - name: Checkout selected ref + uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} + fetch-depth: 0 + + - name: Resolve checked-out SHA + id: ref + run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Validate selected ref is on main + env: + RELEASE_REF: ${{ inputs.ref }} + run: | + set -euo pipefail + git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then + MAIN_SHA="$(git rev-parse origin/main)" + if [[ "$(git rev-parse HEAD)" != "${MAIN_SHA}" ]]; then + echo "Commit SHA mode only supports the current origin/main HEAD. Use a release tag for older commits." >&2 + exit 1 + fi + else + git merge-base --is-ancestor HEAD origin/main + fi + + - name: Setup Node environment + uses: ./.github/actions/setup-node-env + with: + node-version: ${{ env.NODE_VERSION }} + pnpm-version: ${{ env.PNPM_VERSION }} + install-bun: "true" + use-sticky-disk: "false" + + - name: Validate live cache credentials + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + set -euo pipefail + if [[ -z "${OPENAI_API_KEY}" ]]; then + echo "Missing OPENAI_API_KEY secret for release checks." >&2 + exit 1 + fi + if [[ -z "${ANTHROPIC_API_KEY}" ]]; then + echo "Missing ANTHROPIC_API_KEY secret for release checks." >&2 + exit 1 + fi + + # KEEP RELEASE-TIME LIVE COVERAGE HERE SO OPERATORS CAN RUN IT ON DEMAND + # WITHOUT MAKING THE PUBLISH PATH WAIT FOR A SLOW OR FLAKY EXTERNAL CHECK. + - name: Verify live prompt cache floors + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENCLAW_LIVE_CACHE_TEST: "1" + OPENCLAW_LIVE_TEST: "1" + run: pnpm test:live:cache + + - name: Summarize validated ref + env: + RELEASE_REF: ${{ inputs.ref }} + RELEASE_SHA: ${{ steps.ref.outputs.sha }} + run: | + { + echo "## Release checks" + echo + echo "- Requested ref: \`${RELEASE_REF}\`" + echo "- Validated SHA: \`${RELEASE_SHA}\`" + echo "- Check: \`pnpm test:live:cache\`" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index 2a003e3ae12..1ab6fb5dfe1 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -41,10 +41,29 @@ OpenClaw has three public release lanes: `dist/*` release artifacts and Control UI bundle exist for the pack validation step - Run `pnpm release:check` before every tagged release -- Main-branch npm preflight also runs +- Release checks now run in a separate manual workflow: + `OpenClaw Release Checks` +- This split is intentional: keep the real npm release path short, + deterministic, and artifact-focused, while slower live checks stay in their + own lane so they do not stall or block publish +- Release checks must be dispatched from the `main` workflow ref so the + workflow logic and secrets stay canonical +- That workflow accepts either an existing release tag or the current full + 40-character `main` commit SHA +- In commit-SHA mode it only accepts the current `origin/main` HEAD; use a + release tag for older release commits +- `OpenClaw NPM Release` validation-only preflight also accepts the current + full 40-character `main` commit SHA without requiring a pushed tag +- That SHA path is validation-only and cannot be promoted into a real publish +- In SHA mode the workflow synthesizes `v` only for the + package metadata check; real publish still requires a real release tag +- Both workflows keep the real publish and promotion path on GitHub-hosted + runners, while the non-mutating validation path can use the larger + Blacksmith Linux runners +- That workflow runs `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 pnpm test:live:cache` - before packaging the tarball, using both `OPENAI_API_KEY` and - `ANTHROPIC_API_KEY` workflow secrets + using both `OPENAI_API_KEY` and `ANTHROPIC_API_KEY` workflow secrets +- npm release preflight no longer waits on the separate release checks lane - Run `RELEASE_TAG=vYYYY.M.D node --import tsx scripts/openclaw-npm-release-check.ts` (or the matching beta/correction tag) before approval - After npm publish, run @@ -85,7 +104,8 @@ OpenClaw has three public release lanes: `OpenClaw NPM Release` accepts these operator-controlled inputs: - `tag`: required release tag such as `v2026.4.2`, `v2026.4.2-1`, or - `v2026.4.2-beta.1` + `v2026.4.2-beta.1`; when `preflight_only=true`, it may also be the current + full 40-character `main` commit SHA for validation-only preflight - `preflight_only`: `true` for validation/build/package only, `false` for the real publish path - `preflight_run_id`: required on the real publish path so the workflow reuses @@ -94,10 +114,17 @@ OpenClaw has three public release lanes: - `promote_beta_to_latest`: `true` to skip publish and move an already-published stable `beta` build onto `latest` +`OpenClaw Release Checks` accepts these operator-controlled inputs: + +- `ref`: existing release tag or the current full 40-character `main` commit + SHA to validate + Rules: - Stable and correction tags may publish to either `beta` or `latest` - Beta prerelease tags may publish only to `beta` +- Full commit SHA input is allowed only when `preflight_only=true` +- Release checks commit-SHA mode also requires the current `origin/main` HEAD - The real publish path must use the same `npm_dist_tag` used during preflight; the workflow verifies that metadata before publish continues - Promotion mode must use a stable or correction tag, `preflight_only=false`, @@ -110,12 +137,18 @@ Rules: When cutting a stable npm release: 1. Run `OpenClaw NPM Release` with `preflight_only=true` + - Before a tag exists, you may use the current full `main` commit SHA for a + validation-only dry run of the preflight workflow 2. Choose `npm_dist_tag=beta` for the normal beta-first flow, or `latest` only when you intentionally want a direct stable publish -3. Save the successful `preflight_run_id` -4. Run `OpenClaw NPM Release` again with `preflight_only=false`, the same +3. Run `OpenClaw Release Checks` separately with the same tag or the + full current `main` commit SHA when you want live prompt cache coverage + - This is separate on purpose so live coverage stays available without + recoupling long-running or flaky checks to the publish workflow +4. Save the successful `preflight_run_id` +5. Run `OpenClaw NPM Release` again with `preflight_only=false`, the same `tag`, the same `npm_dist_tag`, and the saved `preflight_run_id` -5. If the release landed on `beta`, run `OpenClaw NPM Release` later with the +6. If the release landed on `beta`, run `OpenClaw NPM Release` later with the same stable `tag`, `promote_beta_to_latest=true`, `preflight_only=false`, `preflight_run_id` empty, and `npm_dist_tag=beta` when you want to move that published build to `latest` @@ -129,6 +162,7 @@ documented and operator-visible. ## Public references - [`.github/workflows/openclaw-npm-release.yml`](https://github.com/openclaw/openclaw/blob/main/.github/workflows/openclaw-npm-release.yml) +- [`.github/workflows/openclaw-release-checks.yml`](https://github.com/openclaw/openclaw/blob/main/.github/workflows/openclaw-release-checks.yml) - [`scripts/openclaw-npm-release-check.ts`](https://github.com/openclaw/openclaw/blob/main/scripts/openclaw-npm-release-check.ts) - [`scripts/package-mac-dist.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/package-mac-dist.sh) - [`scripts/make_appcast.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/make_appcast.sh)