mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 23:56:07 +00:00
build: make npm release tag configurable
This commit is contained in:
@@ -17,7 +17,7 @@ Use this skill for release and publish-time workflow. Keep ordinary development
|
||||
|
||||
## Keep release channel naming aligned
|
||||
|
||||
- `stable`: tagged releases only, published to npm `latest` and then mirrored onto npm `beta` unless `beta` already points at a newer prerelease
|
||||
- `stable`: tagged releases only, published to npm `beta` by default; operators may target npm `latest` explicitly or promote later
|
||||
- `beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
|
||||
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
|
||||
- `dev`: moving head on `main`
|
||||
@@ -234,21 +234,27 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
commit, and rerun all relevant preflights from scratch before continuing.
|
||||
Never reuse old preflight results after the commit changes.
|
||||
14. Start `.github/workflows/openclaw-npm-release.yml` with the same tag for
|
||||
the real publish and pass the successful npm `preflight_run_id`.
|
||||
the real publish, choose `npm_dist_tag` (`beta` default, `latest` only when
|
||||
you intentionally want direct stable publish), and pass the successful npm
|
||||
`preflight_run_id`.
|
||||
15. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`.
|
||||
16. Start
|
||||
16. If the stable release was published to `beta`, start
|
||||
`.github/workflows/openclaw-npm-promote-beta.yml` with the exact stable
|
||||
version after beta validation passes, then verify `latest` now points at
|
||||
that version.
|
||||
17. Start
|
||||
`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml`
|
||||
for the real publish with the successful private mac `preflight_run_id` and
|
||||
wait for success.
|
||||
17. Verify the successful real private mac run uploaded the `.zip`, `.dmg`,
|
||||
18. Verify the successful real private mac run uploaded the `.zip`, `.dmg`,
|
||||
and `.dSYM.zip` artifacts to the existing GitHub release in
|
||||
`openclaw/openclaw`.
|
||||
18. For stable releases, download `macos-appcast-<tag>` from the successful
|
||||
19. For stable releases, download `macos-appcast-<tag>` from the successful
|
||||
private mac run, update `appcast.xml` on `main`, and verify the feed.
|
||||
19. For beta releases, publish the mac assets but expect no shared production
|
||||
20. For beta releases, publish the mac assets but expect no shared production
|
||||
`appcast.xml` artifact and do not update the shared production feed unless a
|
||||
separate beta feed exists.
|
||||
20. After publish, verify npm and the attached release artifacts.
|
||||
21. After publish, verify npm and the attached release artifacts.
|
||||
|
||||
## GHSA advisory work
|
||||
|
||||
|
||||
89
.github/workflows/openclaw-npm-promote-beta.yml
vendored
Normal file
89
.github/workflows/openclaw-npm-promote-beta.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: OpenClaw NPM Promote Beta
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Stable version currently on npm beta to promote to latest (for example 2026.4.2 or 2026.4.2-1)
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: openclaw-npm-promote-beta-${{ inputs.version }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
NODE_VERSION: "24.x"
|
||||
PNPM_VERSION: "10.23.0"
|
||||
|
||||
jobs:
|
||||
promote_beta_to_latest:
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-release
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Validate version input format
|
||||
env:
|
||||
RELEASE_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ ! "${RELEASE_VERSION}" =~ ^[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*)?$ ]]; then
|
||||
echo "Invalid stable release version format: ${RELEASE_VERSION}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- 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: ${{ inputs.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: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
userconfig="$(mktemp)"
|
||||
trap 'rm -f "${userconfig}"' EXIT
|
||||
chmod 0600 "${userconfig}"
|
||||
printf '%s\n' "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > "${userconfig}"
|
||||
|
||||
NPM_CONFIG_USERCONFIG="${userconfig}" npm whoami >/dev/null
|
||||
NPM_CONFIG_USERCONFIG="${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."
|
||||
35
.github/workflows/openclaw-npm-release.yml
vendored
35
.github/workflows/openclaw-npm-release.yml
vendored
@@ -16,9 +16,17 @@ on:
|
||||
description: Existing successful preflight workflow run id to promote without rebuilding
|
||||
required: false
|
||||
type: string
|
||||
npm_dist_tag:
|
||||
description: npm dist-tag to publish to for stable releases
|
||||
required: true
|
||||
default: beta
|
||||
type: choice
|
||||
options:
|
||||
- beta
|
||||
- latest
|
||||
|
||||
concurrency:
|
||||
group: openclaw-npm-release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || 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:
|
||||
@@ -36,12 +44,17 @@ jobs:
|
||||
- name: Validate tag input format
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
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}"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_TAG}" == *"-beta."* && "${RELEASE_NPM_DIST_TAG}" != "beta" ]]; then
|
||||
echo "Beta prerelease tags must publish to npm dist-tag beta."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Forbid preflight artifact promotion on validation-only runs
|
||||
if: ${{ inputs.preflight_only && inputs.preflight_run_id != '' }}
|
||||
@@ -98,6 +111,7 @@ jobs:
|
||||
OPENCLAW_NPM_RELEASE_SKIP_PACK_CHECK: "1"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
RELEASE_MAIN_REF: origin/main
|
||||
OPENCLAW_NPM_PUBLISH_TAG: ${{ inputs.npm_dist_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
RELEASE_SHA=$(git rev-parse HEAD)
|
||||
@@ -115,6 +129,7 @@ jobs:
|
||||
env:
|
||||
OPENCLAW_PREPACK_PREPARED: "1"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
PACK_JSON="$(npm pack --json)"
|
||||
@@ -131,6 +146,7 @@ jobs:
|
||||
cp "$PACK_PATH" "$ARTIFACT_DIR/"
|
||||
printf '%s\n' "$RELEASE_TAG" > "$ARTIFACT_DIR/release-tag.txt"
|
||||
printf '%s\n' "$RELEASE_SHA" > "$ARTIFACT_DIR/release-sha.txt"
|
||||
printf '%s\n' "$RELEASE_NPM_DIST_TAG" > "$ARTIFACT_DIR/release-npm-dist-tag.txt"
|
||||
echo "dir=$ARTIFACT_DIR" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload prepared npm publish bundle
|
||||
@@ -180,12 +196,17 @@ jobs:
|
||||
- name: Validate tag input format
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
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}"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_TAG}" == *"-beta."* && "${RELEASE_NPM_DIST_TAG}" != "beta" ]]; then
|
||||
echo "Beta prerelease tags must publish to npm dist-tag beta."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -249,18 +270,21 @@ jobs:
|
||||
- name: Verify prepared tarball provenance
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
RELEASE_NPM_DIST_TAG: ${{ inputs.npm_dist_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXPECTED_RELEASE_SHA="$(git rev-parse HEAD)"
|
||||
TAG_FILE="preflight-tarball/release-tag.txt"
|
||||
SHA_FILE="preflight-tarball/release-sha.txt"
|
||||
if [[ ! -f "$TAG_FILE" || ! -f "$SHA_FILE" ]]; then
|
||||
NPM_DIST_TAG_FILE="preflight-tarball/release-npm-dist-tag.txt"
|
||||
if [[ ! -f "$TAG_FILE" || ! -f "$SHA_FILE" || ! -f "$NPM_DIST_TAG_FILE" ]]; then
|
||||
echo "Prepared preflight metadata is missing." >&2
|
||||
ls -la preflight-tarball >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
ARTIFACT_RELEASE_TAG="$(tr -d '\r\n' < "$TAG_FILE")"
|
||||
ARTIFACT_RELEASE_SHA="$(tr -d '\r\n' < "$SHA_FILE")"
|
||||
ARTIFACT_RELEASE_NPM_DIST_TAG="$(tr -d '\r\n' < "$NPM_DIST_TAG_FILE")"
|
||||
if [[ "$ARTIFACT_RELEASE_TAG" != "$RELEASE_TAG" ]]; then
|
||||
echo "Prepared preflight tag mismatch: expected $RELEASE_TAG, got $ARTIFACT_RELEASE_TAG" >&2
|
||||
exit 1
|
||||
@@ -269,6 +293,10 @@ jobs:
|
||||
echo "Prepared preflight SHA mismatch: expected $EXPECTED_RELEASE_SHA, got $ARTIFACT_RELEASE_SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$ARTIFACT_RELEASE_NPM_DIST_TAG" != "$RELEASE_NPM_DIST_TAG" ]]; then
|
||||
echo "Prepared preflight npm dist-tag mismatch: expected $RELEASE_NPM_DIST_TAG, got $ARTIFACT_RELEASE_NPM_DIST_TAG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Resolve publish tarball
|
||||
id: publish_tarball
|
||||
@@ -284,9 +312,8 @@ jobs:
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
OPENCLAW_PREPACK_PREPARED: "1"
|
||||
OPENCLAW_NPM_PUBLISH_TAG: ${{ inputs.npm_dist_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
publish_target="${{ steps.publish_tarball.outputs.path }}"
|
||||
|
||||
@@ -301,9 +301,10 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
- `latest` = stable
|
||||
- `beta` = early build for testing
|
||||
|
||||
We ship builds to **beta**, test them, and once a build is solid we **promote
|
||||
that same version to `latest`**. That's why beta and stable can point at the
|
||||
**same version**.
|
||||
Usually, a stable release lands on **beta** first, then an explicit
|
||||
promotion step moves that same version to `latest`. Maintainers can also
|
||||
publish straight to `latest` when needed. That's why beta and stable can
|
||||
point at the **same version** after promotion.
|
||||
|
||||
See what changed:
|
||||
[https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md](https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md)
|
||||
@@ -313,7 +314,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I install the beta version and what is the difference between beta and dev?">
|
||||
**Beta** is the npm dist-tag `beta` (may match `latest`).
|
||||
**Beta** is the npm dist-tag `beta` (may match `latest` after promotion).
|
||||
**Dev** is the moving head of `main` (git); when published, it uses the npm dist-tag `dev`.
|
||||
|
||||
One-liners (macOS/Linux):
|
||||
|
||||
@@ -18,8 +18,11 @@ OpenClaw ships three update channels:
|
||||
The `main` branch is for experimentation and active development. It may contain
|
||||
incomplete features or breaking changes. Do not use it for production gateways.
|
||||
|
||||
We ship builds to **beta**, test them, then **promote a vetted build to `latest`**
|
||||
without changing the version number -- dist-tags are the source of truth for npm installs.
|
||||
We usually ship stable builds to **beta** first, test them there, then run an
|
||||
explicit promotion step that moves the vetted build to `latest` without
|
||||
changing the version number. Maintainers can also publish a stable release
|
||||
directly to `latest` when needed. Dist-tags are the source of truth for npm
|
||||
installs.
|
||||
|
||||
## Switching channels
|
||||
|
||||
@@ -109,7 +112,7 @@ source (config, git tag, git branch, or default).
|
||||
- Keep tags immutable: never move or reuse a tag.
|
||||
- npm dist-tags remain the source of truth for npm installs:
|
||||
- `latest` -> stable
|
||||
- `beta` -> candidate build
|
||||
- `beta` -> candidate build or beta-first stable build
|
||||
- `dev` -> main snapshot (optional)
|
||||
|
||||
## macOS app availability
|
||||
|
||||
@@ -10,7 +10,7 @@ read_when:
|
||||
|
||||
OpenClaw has three public release lanes:
|
||||
|
||||
- stable: tagged releases that publish to npm `latest` and mirror the same version onto `beta` unless `beta` already points at a newer prerelease
|
||||
- stable: tagged releases that publish to npm `beta` by default, or to npm `latest` when explicitly requested
|
||||
- beta: prerelease tags that publish to npm `beta`
|
||||
- dev: the moving head of `main`
|
||||
|
||||
@@ -23,9 +23,9 @@ OpenClaw has three public release lanes:
|
||||
- Beta prerelease version: `YYYY.M.D-beta.N`
|
||||
- Git tag: `vYYYY.M.D-beta.N`
|
||||
- Do not zero-pad month or day
|
||||
- `latest` means the current stable npm release
|
||||
- `beta` means the current beta install target, which may point to either the active prerelease or the latest promoted stable build
|
||||
- Stable and stable correction releases publish to npm `latest` and also retag npm `beta` to that same non-beta version after promotion, unless `beta` already points at a newer prerelease
|
||||
- `latest` means the current promoted stable npm release
|
||||
- `beta` means the current beta install target
|
||||
- Stable and stable correction releases publish to npm `beta` by default; release operators can target `latest` explicitly, or promote a vetted beta build later
|
||||
- Every OpenClaw release ships the npm package and macOS app together
|
||||
|
||||
## Release cadence
|
||||
@@ -49,6 +49,9 @@ OpenClaw has three public release lanes:
|
||||
install path in a fresh temp prefix
|
||||
- Maintainer release automation now uses preflight-then-promote:
|
||||
- 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 a separate manual workflow step
|
||||
- public `macOS Release` is validation-only
|
||||
- real private mac publish must pass successful private mac
|
||||
`preflight_run_id` and `validate_run_id`
|
||||
@@ -75,6 +78,7 @@ OpenClaw has three public release lanes:
|
||||
## Public references
|
||||
|
||||
- [`.github/workflows/openclaw-npm-release.yml`](https://github.com/openclaw/openclaw/blob/main/.github/workflows/openclaw-npm-release.yml)
|
||||
- [`.github/workflows/openclaw-npm-promote-beta.yml`](https://github.com/openclaw/openclaw/blob/main/.github/workflows/openclaw-npm-promote-beta.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)
|
||||
|
||||
@@ -2,7 +2,8 @@ import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { parseReleaseVersion, resolveNpmPublishPlan } from "../openclaw-npm-release-check.ts";
|
||||
import { parseReleaseVersion } from "../openclaw-npm-release-check.ts";
|
||||
import { resolveNpmPublishPlan } from "./npm-publish-plan.mjs";
|
||||
|
||||
export type PluginPackageJson = {
|
||||
name?: string;
|
||||
|
||||
@@ -18,30 +18,21 @@ if [[ -n "${publish_target}" && -f "${publish_target}" ]]; then
|
||||
fi
|
||||
|
||||
package_version="$(node -p "require('./package.json').version")"
|
||||
current_beta_version="$(npm view openclaw dist-tags.beta 2>/dev/null || true)"
|
||||
mapfile -t publish_plan < <(
|
||||
PACKAGE_VERSION="${package_version}" CURRENT_BETA_VERSION="${current_beta_version}" node --import tsx --input-type=module <<'EOF'
|
||||
import {
|
||||
resolveNpmDistTagMirrorAuth,
|
||||
resolveNpmPublishPlan,
|
||||
} from "./scripts/openclaw-npm-release-check.ts";
|
||||
PACKAGE_VERSION="${package_version}" REQUESTED_PUBLISH_TAG="${OPENCLAW_NPM_PUBLISH_TAG:-}" \
|
||||
node --import tsx --input-type=module <<'EOF'
|
||||
import { resolveNpmPublishPlan } from "./scripts/openclaw-npm-release-check.ts";
|
||||
|
||||
const plan = resolveNpmPublishPlan(
|
||||
process.env.PACKAGE_VERSION ?? "",
|
||||
process.env.CURRENT_BETA_VERSION,
|
||||
);
|
||||
const auth = resolveNpmDistTagMirrorAuth();
|
||||
const requestedPublishTag =
|
||||
process.env.REQUESTED_PUBLISH_TAG === "latest" ? "latest" : "beta";
|
||||
const plan = resolveNpmPublishPlan(process.env.PACKAGE_VERSION ?? "", undefined, requestedPublishTag);
|
||||
console.log(plan.channel);
|
||||
console.log(plan.publishTag);
|
||||
console.log(plan.mirrorDistTags.join(","));
|
||||
console.log(auth.source);
|
||||
EOF
|
||||
)
|
||||
|
||||
release_channel="${publish_plan[0]}"
|
||||
publish_tag="${publish_plan[1]}"
|
||||
mirror_dist_tags_csv="${publish_plan[2]:-}"
|
||||
mirror_auth_source="${publish_plan[3]:-none}"
|
||||
publish_cmd=(npm publish)
|
||||
if [[ -n "${publish_target}" ]]; then
|
||||
publish_cmd+=("${publish_target}")
|
||||
@@ -49,58 +40,15 @@ fi
|
||||
publish_cmd+=(--access public --tag "${publish_tag}" --provenance)
|
||||
|
||||
echo "Resolved package version: ${package_version}"
|
||||
echo "Current beta dist-tag: ${current_beta_version:-<missing>}"
|
||||
echo "Resolved release channel: ${release_channel}"
|
||||
echo "Resolved publish tag: ${publish_tag}"
|
||||
echo "Resolved mirror dist-tags: ${mirror_dist_tags_csv:-<none>}"
|
||||
echo "Publish auth: GitHub OIDC trusted publishing"
|
||||
echo "Mirror dist-tag auth source: ${mirror_auth_source}"
|
||||
if [[ -n "${publish_target}" ]]; then
|
||||
echo "Resolved publish target: ${publish_target}"
|
||||
fi
|
||||
|
||||
mirror_auth_token=""
|
||||
case "${mirror_auth_source}" in
|
||||
node-auth-token)
|
||||
mirror_auth_token="${NODE_AUTH_TOKEN:-}"
|
||||
;;
|
||||
npm-token)
|
||||
mirror_auth_token="${NPM_TOKEN:-}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "${mirror_dist_tags_csv}" && -z "${mirror_auth_token}" ]]; then
|
||||
echo "npm dist-tag mirroring requires explicit npm auth via NODE_AUTH_TOKEN or NPM_TOKEN." >&2
|
||||
echo "Refusing publish before npm latest/beta promotion can diverge." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${mirror_dist_tags_csv}" ]]; then
|
||||
mirror_userconfig="$(mktemp)"
|
||||
trap 'rm -f "${mirror_userconfig}"' EXIT
|
||||
chmod 0600 "${mirror_userconfig}"
|
||||
printf '%s\n' "//registry.npmjs.org/:_authToken=${mirror_auth_token}" > "${mirror_userconfig}"
|
||||
|
||||
echo "Validating npm auth for dist-tag mirroring"
|
||||
if ! NPM_CONFIG_USERCONFIG="${mirror_userconfig}" npm whoami >/dev/null; then
|
||||
echo "npm dist-tag auth is invalid; refusing publish before latest/beta diverge." >&2
|
||||
echo "Rotate or replace NODE_AUTH_TOKEN/NPM_TOKEN, then rerun the release workflow." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
printf 'Publish command:'
|
||||
printf ' %q' "${publish_cmd[@]}"
|
||||
printf '\n'
|
||||
|
||||
"${publish_cmd[@]}"
|
||||
|
||||
if [[ -n "${mirror_dist_tags_csv}" ]]; then
|
||||
IFS=',' read -r -a mirror_dist_tags <<< "${mirror_dist_tags_csv}"
|
||||
for dist_tag in "${mirror_dist_tags[@]}"; do
|
||||
[[ -n "${dist_tag}" ]] || continue
|
||||
echo "Mirroring openclaw@${package_version} onto dist-tag ${dist_tag}"
|
||||
NPM_CONFIG_USERCONFIG="${mirror_userconfig}" \
|
||||
npm dist-tag add "openclaw@${package_version}" "${dist_tag}"
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
compareReleaseVersions as compareReleaseVersionsBase,
|
||||
resolveNpmDistTagMirrorAuth as resolveNpmDistTagMirrorAuthBase,
|
||||
parseReleaseVersion as parseReleaseVersionBase,
|
||||
resolveNpmPublishPlan as resolveNpmPublishPlanBase,
|
||||
} from "./lib/npm-publish-plan.mjs";
|
||||
|
||||
type PackageJson = {
|
||||
@@ -82,9 +81,32 @@ export function compareReleaseVersions(left: string, right: string): number | nu
|
||||
|
||||
export function resolveNpmPublishPlan(
|
||||
version: string,
|
||||
currentBetaVersion?: string | null,
|
||||
_currentBetaVersion?: string | null,
|
||||
requestedPublishTag?: "latest" | "beta" | null,
|
||||
): NpmPublishPlan {
|
||||
return resolveNpmPublishPlanBase(version, currentBetaVersion) as NpmPublishPlan;
|
||||
const parsedVersion = parseReleaseVersion(version);
|
||||
if (parsedVersion === null) {
|
||||
throw new Error(`Unsupported release version "${version}".`);
|
||||
}
|
||||
|
||||
const publishTag = requestedPublishTag?.trim() === "latest" ? "latest" : "beta";
|
||||
|
||||
if (parsedVersion.channel === "beta") {
|
||||
if (publishTag !== "beta") {
|
||||
throw new Error("Beta prereleases must publish to the beta dist-tag.");
|
||||
}
|
||||
return {
|
||||
channel: "beta",
|
||||
publishTag: "beta",
|
||||
mirrorDistTags: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
channel: "stable",
|
||||
publishTag,
|
||||
mirrorDistTags: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveNpmDistTagMirrorAuth(params?: {
|
||||
|
||||
@@ -85,37 +85,43 @@ describe("resolveNpmPublishPlan", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("publishes stable releases to latest and mirrors beta", () => {
|
||||
it("publishes stable releases to beta first", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29")).toEqual({
|
||||
channel: "stable",
|
||||
publishTag: "latest",
|
||||
mirrorDistTags: ["beta"],
|
||||
publishTag: "beta",
|
||||
mirrorDistTags: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("mirrors beta for stable correction releases too", () => {
|
||||
it("publishes stable correction releases to beta first too", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29-2")).toEqual({
|
||||
channel: "stable",
|
||||
publishTag: "latest",
|
||||
mirrorDistTags: ["beta"],
|
||||
publishTag: "beta",
|
||||
mirrorDistTags: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not mirror beta when beta already points at a newer prerelease", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29", "2026.4.1-beta.1")).toEqual({
|
||||
it("can publish stable releases directly to latest when requested", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29", undefined, "latest")).toEqual({
|
||||
channel: "stable",
|
||||
publishTag: "latest",
|
||||
mirrorDistTags: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("still mirrors beta when beta points at the same release line", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29", "2026.3.29-beta.2")).toEqual({
|
||||
it("ignores current beta dist-tag state for stable publishes", () => {
|
||||
expect(resolveNpmPublishPlan("2026.3.29", "2026.4.1-beta.1")).toEqual({
|
||||
channel: "stable",
|
||||
publishTag: "latest",
|
||||
mirrorDistTags: ["beta"],
|
||||
publishTag: "beta",
|
||||
mirrorDistTags: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects publishing beta prereleases to latest", () => {
|
||||
expect(() => resolveNpmPublishPlan("2026.3.29-beta.2", undefined, "latest")).toThrow(
|
||||
"Beta prereleases must publish to the beta dist-tag.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveNpmDistTagMirrorAuth", () => {
|
||||
|
||||
Reference in New Issue
Block a user