Release: move npm dist-tag ops private (#66660)

This commit is contained in:
Onur Solmaz
2026-04-14 18:18:27 +02:00
committed by GitHub
parent 41d649c31a
commit 27b14124d0
2 changed files with 19 additions and 241 deletions

View File

@@ -24,19 +24,9 @@ on:
options:
- beta
- latest
promote_beta_to_latest:
description: Skip publish and promote the stable version already on npm beta to latest
required: true
default: false
type: boolean
sync_stable_dist_tags:
description: Skip publish and point both latest and beta at an already-published stable version
required: true
default: false
type: boolean
concurrency:
group: openclaw-npm-release-${{ github.event_name == 'workflow_dispatch' && format('{0}-{1}-{2}-{3}', inputs.tag, inputs.npm_dist_tag, inputs.promote_beta_to_latest, inputs.sync_stable_dist_tags) || github.ref }}
group: openclaw-npm-release-${{ github.event_name == 'workflow_dispatch' && format('{0}-{1}', inputs.tag, inputs.npm_dist_tag) || github.ref }}
cancel-in-progress: false
env:
@@ -48,8 +38,11 @@ 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.
# SECURITY NOTE: TOKEN-BASED npm dist-tag mutation moved to
# openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml
# so this public workflow can stay focused on OIDC publish only.
preflight_openclaw_npm:
if: ${{ inputs.preflight_only && !inputs.promote_beta_to_latest && !inputs.sync_stable_dist_tags }}
if: ${{ inputs.preflight_only }}
runs-on: blacksmith-32vcpu-ubuntu-2404
permissions:
contents: read
@@ -246,7 +239,7 @@ jobs:
if-no-files-found: error
validate_publish_request:
if: ${{ !inputs.preflight_only && !inputs.promote_beta_to_latest && !inputs.sync_stable_dist_tags }}
if: ${{ !inputs.preflight_only }}
runs-on: blacksmith-32vcpu-ubuntu-2404
permissions:
contents: read
@@ -275,7 +268,7 @@ jobs:
# 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 && !inputs.sync_stable_dist_tags }}
if: ${{ !inputs.preflight_only }}
runs-on: ubuntu-latest
environment: npm-release
permissions:
@@ -411,205 +404,3 @@ jobs:
publish_target="./${publish_target}"
fi
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 && !inputs.sync_stable_dist_tags }}
runs-on: ubuntu-latest
environment: npm-release
permissions:
contents: read
steps:
- name: Require main workflow ref for promotion
env:
WORKFLOW_REF: ${{ github.ref }}
run: |
set -euo pipefail
if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then
echo "Promotion runs must be dispatched from main."
exit 1
fi
- name: Validate promotion inputs
env:
PREFLIGHT_ONLY: ${{ inputs.preflight_only }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
run: |
set -euo pipefail
if [[ "${PREFLIGHT_ONLY}" == "true" ]]; then
echo "Promotion mode cannot run with preflight_only=true."
exit 1
fi
if [[ -n "${PREFLIGHT_RUN_ID}" ]]; then
echo "Promotion mode does not use preflight_run_id."
exit 1
fi
if [[ "${RELEASE_NPM_DIST_TAG}" != "beta" ]]; then
echo "Promotion mode expects npm_dist_tag=beta because it moves beta to latest without publishing."
exit 1
fi
- name: Validate stable tag input format
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*)?$ ]]; then
echo "Invalid stable release tag format: ${RELEASE_TAG}" >&2
exit 1
fi
echo "RELEASE_VERSION=${RELEASE_TAG#v}" >> "$GITHUB_ENV"
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "false"
use-sticky-disk: "false"
install-deps: "false"
- name: Validate npm dist-tags
env:
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
run: |
set -euo pipefail
beta_version="$(npm view openclaw dist-tags.beta)"
latest_version="$(npm view openclaw dist-tags.latest)"
echo "Current beta dist-tag: ${beta_version}"
echo "Current latest dist-tag: ${latest_version}"
if [[ "${beta_version}" != "${RELEASE_VERSION}" ]]; then
echo "npm beta points at ${beta_version}, expected ${RELEASE_VERSION}." >&2
exit 1
fi
if ! npm view "openclaw@${RELEASE_VERSION}" version >/dev/null 2>&1; then
echo "openclaw@${RELEASE_VERSION} is not published on npm." >&2
exit 1
fi
- name: Promote beta to latest
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
run: |
set -euo pipefail
npm_userconfig="$(mktemp)"
trap 'rm -f "${npm_userconfig}"' EXIT
chmod 0600 "${npm_userconfig}"
printf '//registry.npmjs.org/:_authToken=%s\n' "${NODE_AUTH_TOKEN}" > "${npm_userconfig}"
NPM_CONFIG_USERCONFIG="${npm_userconfig}" npm dist-tag add "openclaw@${RELEASE_VERSION}" latest
promoted_latest="$(npm view openclaw dist-tags.latest)"
if [[ "${promoted_latest}" != "${RELEASE_VERSION}" ]]; then
echo "npm latest points at ${promoted_latest}, expected ${RELEASE_VERSION} after promotion." >&2
exit 1
fi
echo "Promoted openclaw@${RELEASE_VERSION} from beta to latest."
sync_stable_dist_tags:
# This mode is for direct stable publishes where latest is correct but beta
# should also point at the same stable build after release.
if: ${{ inputs.sync_stable_dist_tags && !inputs.promote_beta_to_latest }}
runs-on: ubuntu-latest
environment: npm-release
permissions:
contents: read
steps:
- name: Require main workflow ref for dist-tag sync
env:
WORKFLOW_REF: ${{ github.ref }}
run: |
set -euo pipefail
if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then
echo "Dist-tag sync runs must be dispatched from main."
exit 1
fi
- name: Validate sync inputs
env:
PREFLIGHT_ONLY: ${{ inputs.preflight_only }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
run: |
set -euo pipefail
if [[ "${PREFLIGHT_ONLY}" == "true" ]]; then
echo "Dist-tag sync mode cannot run with preflight_only=true."
exit 1
fi
if [[ -n "${PREFLIGHT_RUN_ID}" ]]; then
echo "Dist-tag sync mode does not use preflight_run_id."
exit 1
fi
if [[ "${RELEASE_NPM_DIST_TAG}" != "latest" ]]; then
echo "Dist-tag sync mode expects npm_dist_tag=latest because it points latest and beta at the stable version."
exit 1
fi
- name: Validate stable tag input format
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*)?$ ]]; then
echo "Invalid stable release tag format: ${RELEASE_TAG}" >&2
exit 1
fi
echo "RELEASE_VERSION=${RELEASE_TAG#v}" >> "$GITHUB_ENV"
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "false"
use-sticky-disk: "false"
install-deps: "false"
- name: Validate published stable version
env:
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
run: |
set -euo pipefail
if ! npm view "openclaw@${RELEASE_VERSION}" version >/dev/null 2>&1; then
echo "openclaw@${RELEASE_VERSION} is not published on npm." >&2
exit 1
fi
echo "Current latest dist-tag: $(npm view openclaw dist-tags.latest)"
echo "Current beta dist-tag: $(npm view openclaw dist-tags.beta)"
- name: Sync stable dist-tags
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
run: |
set -euo pipefail
npm_userconfig="$(mktemp)"
trap 'rm -f "${npm_userconfig}"' EXIT
chmod 0600 "${npm_userconfig}"
printf '//registry.npmjs.org/:_authToken=%s\n' "${NODE_AUTH_TOKEN}" > "${npm_userconfig}"
NPM_CONFIG_USERCONFIG="${npm_userconfig}" npm dist-tag add "openclaw@${RELEASE_VERSION}" latest
NPM_CONFIG_USERCONFIG="${npm_userconfig}" npm dist-tag add "openclaw@${RELEASE_VERSION}" beta
synced_latest="$(npm view openclaw dist-tags.latest)"
synced_beta="$(npm view openclaw dist-tags.beta)"
if [[ "${synced_latest}" != "${RELEASE_VERSION}" ]]; then
echo "npm latest points at ${synced_latest}, expected ${RELEASE_VERSION} after sync." >&2
exit 1
fi
if [[ "${synced_beta}" != "${RELEASE_VERSION}" ]]; then
echo "npm beta points at ${synced_beta}, expected ${RELEASE_VERSION} after sync." >&2
exit 1
fi
echo "Synced openclaw@${RELEASE_VERSION} to npm latest and beta."

View File

@@ -74,10 +74,10 @@ OpenClaw has three public release lanes:
- real npm publish must pass a successful npm `preflight_run_id`
- stable npm releases default to `beta`
- stable npm publish can target `latest` explicitly via workflow input
- stable npm promotion from `beta` to `latest` is still available as an explicit manual mode on the trusted `OpenClaw NPM Release` workflow
- direct stable publishes can also run an explicit dist-tag sync mode that
points both `latest` and `beta` at the already-published stable version
- those dist-tag modes still need a valid `NPM_TOKEN` in the `npm-release` environment because npm `dist-tag` management is separate from trusted publishing
- token-based npm dist-tag mutation now lives in
`openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml`
for security, because `npm dist-tag add` still needs `NPM_TOKEN` while the
public repo keeps OIDC-only publish
- public `macOS Release` is validation-only
- real private mac publish must pass successful private mac
`preflight_run_id` and `validate_run_id`
@@ -116,10 +116,6 @@ OpenClaw has three public release lanes:
- `preflight_run_id`: required on the real publish path so the workflow reuses
the prepared tarball from the successful preflight run
- `npm_dist_tag`: npm target tag for the publish path; defaults to `beta`
- `promote_beta_to_latest`: `true` to skip publish and move an already-published
stable `beta` build onto `latest`
- `sync_stable_dist_tags`: `true` to skip publish and point both `latest` and
`beta` at an already-published stable version
`OpenClaw Release Checks` accepts these operator-controlled inputs:
@@ -134,14 +130,6 @@ Rules:
- 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`,
an empty `preflight_run_id`, and `npm_dist_tag=beta`
- Dist-tag sync mode must use a stable or correction tag,
`preflight_only=false`, an empty `preflight_run_id`, `npm_dist_tag=latest`,
and `promote_beta_to_latest=false`
- Promotion and dist-tag sync modes also require a valid `NPM_TOKEN` because
`npm dist-tag add` still needs regular npm auth; trusted publishing covers
the package publish path only
## Stable npm release sequence
@@ -159,17 +147,16 @@ When cutting a stable npm release:
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`
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`
6. If the release landed on `beta`, use the private
`openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml`
workflow to promote that stable version from `beta` to `latest`
7. If the release intentionally published directly to `latest` and `beta`
should follow the same stable build, run `OpenClaw NPM Release` with the same
stable `tag`, `sync_stable_dist_tags=true`, `promote_beta_to_latest=false`,
`preflight_only=false`, `preflight_run_id` empty, and `npm_dist_tag=latest`
should follow the same stable build immediately, use that same private
workflow to point both dist-tags at the stable version, or let its scheduled
self-healing sync move `beta` later
The promotion and dist-tag sync modes still require the `npm-release`
environment approval and a valid `NPM_TOKEN` accessible to that workflow run.
The dist-tag mutation lives in the private repo for security because it still
requires `NPM_TOKEN`, while the public repo keeps OIDC-only publish.
That keeps the direct publish path and the beta-first promotion path both
documented and operator-visible.