From 02a86da23afa1e8a135c1707a49ac2f6863c0fc4 Mon Sep 17 00:00:00 2001 From: Onur Solmaz Date: Sat, 14 Mar 2026 13:01:15 +0100 Subject: [PATCH] ci: preserve manual npm release approval delays --- .github/workflows/openclaw-npm-release.yml | 1 + docs/reference/RELEASING.md | 3 ++- scripts/openclaw-npm-release-check.ts | 12 ++++++++++-- test/openclaw-npm-release-check.test.ts | 11 +++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.github/workflows/openclaw-npm-release.yml b/.github/workflows/openclaw-npm-release.yml index 20af84f2b2c..48d97baf268 100644 --- a/.github/workflows/openclaw-npm-release.yml +++ b/.github/workflows/openclaw-npm-release.yml @@ -129,6 +129,7 @@ jobs: env: RELEASE_TAG: ${{ inputs.tag }} RELEASE_MAIN_REF: origin/main + RELEASE_SKIP_CALVER_WINDOW: "1" run: | set -euo pipefail RELEASE_SHA=$(git rev-parse HEAD) diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index f929d16e5f7..30c73027102 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -94,7 +94,8 @@ Historical note: - [ ] Confirm git status is clean; commit and push as needed. - [ ] Confirm npm trusted publishing is configured for the `openclaw` package. -- [ ] Push the matching git tag to trigger `.github/workflows/openclaw-npm-release.yml`. +- [ ] Push the matching git tag to trigger the preview run in `.github/workflows/openclaw-npm-release.yml`. +- [ ] Run `OpenClaw NPM Release` manually with the same tag to publish after `npm-release` environment approval. - Stable tags publish to npm `latest`. - Beta tags publish to npm `beta`. - The workflow rejects tags that do not match `package.json`, are not on `main`, or whose CalVer date is more than 2 UTC calendar days away from the release date. diff --git a/scripts/openclaw-npm-release-check.ts b/scripts/openclaw-npm-release-check.ts index fcd2dc8e7e1..5b1fba71e98 100644 --- a/scripts/openclaw-npm-release-check.ts +++ b/scripts/openclaw-npm-release-check.ts @@ -162,11 +162,13 @@ export function collectReleaseTagErrors(params: { releaseSha?: string; releaseMainRef?: string; now?: Date; + enforceCalverWindow?: boolean; }): string[] { const errors: string[] = []; const releaseTag = params.releaseTag.trim(); const packageVersion = params.packageVersion.trim(); const now = params.now ?? new Date(); + const enforceCalverWindow = params.enforceCalverWindow ?? true; const parsedVersion = parseReleaseVersion(packageVersion); if (parsedVersion === null) { @@ -196,7 +198,7 @@ export function collectReleaseTagErrors(params: { ); } - if (parsedVersion !== null) { + if (parsedVersion !== null && enforceCalverWindow) { const dayDistance = utcCalendarDayDistance(parsedVersion.date, now); if (dayDistance > MAX_CALVER_DISTANCE_DAYS) { const nowLabel = now.toISOString().slice(0, 10); @@ -230,12 +232,16 @@ function loadPackageJson(): PackageJson { function main(): number { const pkg = loadPackageJson(); + const skipCalverWindow = process.env.RELEASE_SKIP_CALVER_WINDOW === "1"; + const now = new Date(); const metadataErrors = collectReleasePackageMetadataErrors(pkg); const tagErrors = collectReleaseTagErrors({ packageVersion: pkg.version ?? "", releaseTag: process.env.RELEASE_TAG ?? "", releaseSha: process.env.RELEASE_SHA, releaseMainRef: process.env.RELEASE_MAIN_REF, + now, + enforceCalverWindow: !skipCalverWindow, }); const errors = [...metadataErrors, ...tagErrors]; @@ -251,7 +257,9 @@ function main(): number { const dayDistance = parsedVersion === null ? "unknown" - : String(utcCalendarDayDistance(parsedVersion.date, new Date())); + : skipCalverWindow + ? "skipped" + : String(utcCalendarDayDistance(parsedVersion.date, now)); console.log( `openclaw-npm-release-check: validated ${channel} release ${pkg.version} (${dayDistance} day UTC delta).`, ); diff --git a/test/openclaw-npm-release-check.test.ts b/test/openclaw-npm-release-check.test.ts index 66cf7d9b5cf..9d8c663f23c 100644 --- a/test/openclaw-npm-release-check.test.ts +++ b/test/openclaw-npm-release-check.test.ts @@ -66,6 +66,17 @@ describe("collectReleaseTagErrors", () => { ).toContainEqual(expect.stringContaining("must be within 2 days")); }); + it("allows manual publish validation to skip the CalVer freshness window", () => { + expect( + collectReleaseTagErrors({ + packageVersion: "2026.3.10", + releaseTag: "v2026.3.10", + now: new Date("2026-03-20T00:00:00Z"), + enforceCalverWindow: false, + }), + ).toEqual([]); + }); + it("rejects tags that do not match the current release format", () => { expect( collectReleaseTagErrors({