ci(release): make npm trusted publishing work + post-publish verification

Merged
YA
2mo ago
+80-0

LIVE RECEIPTS

@wave-av/cli 1.0.8 on npm latest prints --version as 1.0.0 (hardcoded,
never matched the real published version) and wave status silently
exited 0 on auth/health failure. The fix for both is already on main
(the 1.0.9 work) with its own unit tests — this PR does not touch that
code. What was still missing: release.yml (from #44) has no working
Trusted Publishing path and no proof that a publish actually reached npm
correctly.

Measured locally against node --version bundled by actions/setup-node's
Node 22:

$ node -v && npm -v
v22.14.0
10.9.2

npm's own docs require npm >= 11.5.1 for OIDC trusted publishing.
10.9.2 is below that floor, so the OIDC branch in the existing workflow
could never actually run — every publish was silently falling through to
the NPM_TOKEN fallback, and would fail outright the moment that secret
is absent (which, per the live smoke that motivated this wave, it is).

Local dry-run of the new apiEndpoint extraction logic this PR adds to
verify-publish (proven against this repo's own build, not the published
package, since publishing is an operator crossing):

$ npm run build && node dist/index.js status --output json
WAVE CLI Status
  Auth:     Not authenticated
  ...
  Endpoint: https://api.wave.online
{
  "project": "default",
  "authenticated": false,
  "apiEndpoint": "https://api.wave.online",
  ...
}

$ OUT="$(node dist/index.js status --output json 2>&1 || true)"
$ echo "$OUT" | node -e "...parse from first '{'..."
apiEndpoint=https://api.wave.online

actionlint .github/workflows/release.yml — clean, no findings.

ROOT CAUSE

Two gaps left the OIDC path non-functional and unverified:

  1. actions/setup-node@v7 with node-version: "22" installs whatever npm
    Node 22 bundles (10.9.2) — below npm's own 11.5.1 floor for trusted
    publishing. mcp-server's release.yml already had to solve this with
    an explicit npm install -g npm@11.19.0 step; cli's did not.
  2. The release job's own smoke test packs and installs a local tarball
    before publish — real coverage of the build, but no step proves the
    package that actually landed on the registry is reachable and correct.

WHAT CHANGED

.github/workflows/release.yml:

  • npm upgrade step before the publish attempt: pins npm@11.19.0,
    then asserts the resulting version is >= 11.5.1 (fails loudly instead
    of silently degrading to the token fallback).
  • New verify-publish job, needs: release:
    • Polls npm view @wave-av/cli@<tag> (8x, 15s apart) until the registry
      confirms the exact tagged version is indexed.
    • Installs that version from the real registry into a throwaway
      project and asserts npx wave --version prints the tagged version.
    • Runs wave status --output json, extracts the JSON block (the
      command also prints a human summary first; extraction starts from the
      first {), and asserts apiEndpoint is exactly
      https://api.wave.online — the receipt that the published binary
      targets the real API, never the wave.online marketing site.

The NPM_TOKEN fallback from #44 is unchanged and still applies whenever
that secret is set, independent of whichever npm version is active.

No source under src/ changed. --version reading package.json at
runtime and wave status exiting non-zero on failure are both already on
main with tests in src/cli.test.ts and
src/commands/status/index.test.ts.

GATES (local, tails)

  • npm test (vitest): Test Files 4 passed (4), Tests 11 passed (11).
  • npm run build: ESM dist/index.js 158.82 KB — succeeds in 36ms.
  • actionlint .github/workflows/release.yml: clean.
  • npm run lint: fails locally — eslint is referenced in the lint
    script but is not a declared dependency in this checkout
    (sh: eslint: command not found); pre-existing on main, unrelated to
    this change, and not a step release.yml runs.
  • npm run type-check: fails locally with pre-existing src/types
    module-resolution errors unrelated to this change (that directory does
    not exist in this checkout); tsup's esbuild-based build does not do
    full type resolution so it is unaffected, and no workflow in this repo
    invokes type-check today (grep confirms only a comment mentions it).
    Flagging as a known gap, not something this PR silently worked around.

OPERATOR STEPS

One-time npm Trusted Publisher registration (this repo has no
NPM_TOKEN secret today per the live smoke, so the OIDC path is the only
one that will work until one is set):

  1. Sign in to https://www.npmjs.com, go to the @wave-av/cli package ->
    Settings -> Trusted Publisher -> "Add GitHub Actions publisher".
  2. Fill in exactly:
    • Organization/user: wave-av
    • Repository: cli
    • Workflow filename: release.yml
    • Environment: (leave blank — this workflow does not scope publish to
      a GitHub Environment)
  3. Save. No token is generated or stored.

To cut the 1.0.9 release once this PR is merged and the Trusted Publisher
above is registered (operator-run, not this agent — package.json on
main is already 1.0.9, matching the fix already merged):

git -C ~/wave-av/cli fetch origin
git -C ~/wave-av/cli tag v1.0.9 origin/main
git -C ~/wave-av/cli push origin v1.0.9

That tag push triggers release.yml. Watch both the release job and
the new verify-publish job in the Actions tab — a green verify-publish
is the live receipt that npx @wave-av/cli@1.0.9 --version prints 1.0.9
and defaults to https://api.wave.online.

🤖 Generated with Claude Code
https://claude.ai/code/session_01MPeHryYVubEwzmnnf8pykK


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


[!NOTE]
Low Risk
CI-only workflow changes with no application source or runtime behavior changes; failures only block a release pipeline if the live package is wrong or not yet visible on npm.

Overview
Adds a verify-publish job that runs after publish so a green release is not only “npm publish exited 0” but the tagged build is actually on the public registry.

The job derives the expected version from the release tag, polls npm view (up to eight attempts, 15s apart) until that exact version is indexed, then installs @wave-av/cli from registry.npmjs.org in a throwaway project and fails if npx wave --version does not match the tag. A final step runs wave status --output json, parses the trailing JSON block, and asserts the default apiEndpoint is https://api.wave.online (not the marketing host).

Reviewed by Cursor Bugbot for commit 465054e58dc9ff459788506eacf78db065a95ea8. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by Sourcery

Harden the npm release workflow for trusted publishing and verify that each published CLI package is live and correctly configured.

New Features:

  • Add post-publish verification that confirms the tagged CLI version is available from npm, reports the expected version, and targets the production API endpoint.

Bug Fixes:

  • Ensure the release workflow can use npm OIDC trusted publishing by enforcing a compatible npm version before publishing.

CI:

  • Harden the release workflow with an npm version prerequisite and live registry smoke checks after publishing.

Tests:

  • Verify the published package from the real npm registry rather than only testing the pre-publish tarball.