From 1f4fbec3f39fc33defdc24b2a8e8b71343a32f72 Mon Sep 17 00:00:00 2001 From: 1bcMax Date: Tue, 18 Aug 2026 20:41:36 -0700 Subject: [PATCH] ci: gate the npm publish on lint, typecheck, tests and a tag/VERSION match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit publish.yml triggers on a GitHub Release and ran nothing but a build before uploading to npm. That is how 3.13.1 shipped: it bumped package.json and src/version.ts but left VERSION on 3.13.0 with no changelog entry, and the version-consistency suite that catches both never ran until the push-triggered CI job afterwards — by which point npm had the broken tree and npm, like PyPI, does not allow replacing a published version. Adds the same gate the Python SDK's publish workflow has had since 1.8.1 died the same way: verify the release tag matches VERSION, then lint, typecheck and the unit suite, all before build/pack/publish. Simulated against the real tags: v3.13.1 fails the tag check (VERSION said 3.13.0), v3.13.2 passes. This is ci.yml's 22 leg, not full CI — 20 and 24 still run only on push, so a version-incompatible API is caught there. brand-numbers is deliberately left out: it compares marketing counts in the docs, and a stale model number is not a reason to block a bugfix or security release. --- .github/workflows/publish.yml | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d14a589..382a32f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,6 +28,48 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile + + # ── Release gate ────────────────────────────────────────────────────── + # Everything below this line runs before anything is published, because + # npm (like PyPI) will not let you replace a published version. + # + # 3.13.1 is why this exists. It bumped package.json and src/version.ts but + # left VERSION on 3.13.0 and never recorded the release in the changelog. + # The version-consistency suite catches both — but this workflow triggers + # on a GitHub Release and ran none of it, so the broken tree went to npm + # and the failure only surfaced on the push-triggered CI run afterwards. + # + # This is the 22 leg of ci.yml, not full CI: 20 and 24 still run only on + # push, so a version-incompatible API is caught there, not here. The + # brand-numbers check is deliberately NOT a release gate — it compares + # marketing counts in the docs, and a stale model number is not a reason + # to block a bugfix or security release. + + # A release tag that disagrees with the package version means the wrong + # tree is being published. The "already published" check below would stop + # a duplicate, but only after the release is cut; fail here instead. + - name: Verify the release tag matches VERSION + run: | + tag="${{ github.event.release.tag_name }}" + declared="v$(tr -d '[:space:]' < VERSION)" + if [ "$tag" != "$declared" ]; then + echo "Release tag $tag does not match VERSION ($declared)." >&2 + exit 1 + fi + echo "Release tag $tag matches VERSION." + + - name: Lint + run: pnpm run lint + + - name: Typecheck + run: pnpm run typecheck + + # Covers VERSION <-> package.json <-> src/version.ts and the changelog + # entry, via test/unit/version-consistency.test.ts. + - name: Test + run: pnpm run test --run + # ────────────────────────────────────────────────────────────────────── + - run: pnpm run build # pack/publish stay on npm: they read package.json, not a lockfile, and