ci(publish)!: migrate NPM publication to OIDC trusted publishing (#104)#105
Merged
Conversation
NPM now requires trusted publishing (OIDC), which binds a package to one exact workflow filename. Alpha (ci_reusable.yaml) and latest (publish-latest.yaml) were published from two different workflows with a long-lived NPM_TOKEN, so both paths are now broken. Consolidate both publish paths into a single tokenless workflow: - Add .github/workflows/publish.yaml: push to main -> alpha (npm publish --tag alpha), workflow_dispatch -> latest via semantic-release (semantic_version 25, which bundles @semantic-release/npm >= 13.1 with OIDC support) plus a GitHub release. Uses permissions: id-token: write and no NPM_TOKEN. - Extract the shared setup-node + npm upgrade + ci + build + test steps into a local composite action (.github/actions/build-test) reused by both the publish workflow and PR CI, so build/test is defined once and runs once per runner. Bumps Node to 22 and npm to >= 11.5.1 as required by trusted publishing. - Delete publish-latest.yaml (superseded). - Reduce ci_reusable.yaml to build/test only (drops NPM_TOKEN, NODE_AUTH_TOKEN, PUBLISH_NPM and all publish steps) and ci.yaml to a PR-only trigger. Requires a one-time manual step: configure the GitHub Actions trusted publisher for @bosonprotocol/chat-sdk on npmjs.com pointing at publish.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Migrates NPM publishing to OIDC trusted publishing by consolidating alpha (on push to main) and latest (manual workflow_dispatch via semantic-release) into a single workflow, and de-tokenizing CI/publish by removing NPM_TOKEN-based auth.
Changes:
- Added a unified
publish.yamlworkflow that handles alpha publishes on push and latest releases on manual dispatch (semantic-release). - Introduced a shared composite action to standardize Node setup, npm upgrade, install, build, and test across CI and publishing.
- Simplified CI workflows to PR-only and removed legacy publish workflows/logic and token usage.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/publish.yaml |
New unified OIDC-based publishing workflow for both alpha (push) and latest (dispatch via semantic-release). |
.github/actions/build-test/action.yml |
New composite action to centralize Node/npm setup + install/build/test steps. |
.github/workflows/publish-latest.yaml |
Removes the legacy token-based manual release workflow in favor of the unified publish workflow. |
.github/workflows/ci_reusable.yaml |
Strips publishing logic and switches CI to use the shared composite action plus PR-only formatting/linting steps. |
.github/workflows/ci.yaml |
Reduces CI triggering to PR-only and delegates to the reusable build/test workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… commit
actions/checkout leaves a detached HEAD on push events, so the bare `git push`
in the alpha step would fail ("not currently on a branch"). And `git commit
--amend` rewrote the version-bump commit without moving the tag npm had just
created, so the pushed tag pointed at the pre-amend (orphaned) commit.
Check out `main` explicitly via the checkout `ref`, and bake `[skip ci]` into
the `npm version` commit message (`-m "%s [skip ci]"`) so the commit and tag are
created together in one step. Push both with `git push --follow-tags`.
Addresses PR #105 review comment r3354854034.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Installing npm@latest makes CI and publishing non-deterministic: a new npm release could break builds or publishing with no change in the repo. Pin to a known-good version that satisfies the trusted-publishing requirement (>= 11.5.1) and bump it intentionally when needed. Addresses PR #105 review comment r3354854090. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
actions/setup-node already caches the npm download cache when cache: "npm" is set, so the separate actions/cache step for ~/.npm duplicated that work and only added restore/save overhead and confusion. Rely on setup-node's built-in caching. Addresses PR #105 review comment r3354854115. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The publish job mutates and pushes main (version bump + tags) and publishes to npm, so overlapping runs — two pushes to main close together, or a manual workflow_dispatch while a push publish is still running — can race and cause non-fast-forward push failures or inconsistent prerelease sequencing. Add a workflow-level concurrency group keyed on the ref with cancel-in-progress: false so publish runs are serialized and a run is never cancelled mid-publish. Addresses PR #105 review comment r3355066601. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Checking out `ref: main` meant a run triggered by a specific push could build,
test, bump and publish a *newer* main if other pushes landed before the job
started — making publishes non-reproducible and inviting push conflicts.
Check out `${{ github.sha }}` and recreate a local `main` branch at that commit
(`git checkout -B main`), which keeps the run pinned to the triggering commit
while still avoiding the detached HEAD that would break `git push`. Push
explicitly to `origin main` since the recreated branch has no upstream.
Addresses PR #105 review comment r3355066656.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The major-only selector "22" does not guarantee the >= 22.14 patch level that OIDC trusted publishing requires — on a fresh runner or a stale toolcache it can resolve to an earlier 22.x, which would make publishing fail unexpectedly. Pin the default to 22.14.0 (still overridable via the node-version input). Addresses PR #105 review comment r3355066675. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`git checkout -B main` creates the branch without an upstream. The alpha path compensates with an explicit `origin main` push, but semantic-release performs its own `git push` on the workflow_dispatch (latest) path and can fail with "no upstream branch". Configure branch.main.remote/merge so the branch tracks origin/main, making both publish paths robust. Addresses PR #105 review comment r3355705468. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The git author identity was only set on push events, but the workflow_dispatch (latest) path runs semantic-release with @semantic-release/git, which creates changelog/version commits and fails without a configured author. Move the author config out of the alpha-only section so it runs unconditionally. Addresses PR #105 review comment r3355705521. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously PR CI ran `prettier --write` and `eslint --fix` *after* the composite's build+test, so build/test exercised the pre-fix code and the auto-fixers (which always exit 0) gated nothing — reducing confidence in CI. Add a `run-checks` input to the build-test composite that runs `npm run lint` (non-mutating, no --fix) after `npm ci` but before build+test, and enable it from ci_reusable.yaml (PR CI). Lint is now a real gate and build/test run on the actual committed code. Publish runs leave run-checks at its default (off). The mutating `prettier --write` step is dropped from CI: it never gated anything and whitespace formatting does not affect build/test. A real `prettier --check` gate is deferred — it currently fails on pre-existing unrelated files (incl. the semantic-release-managed CHANGELOG.md) and is a separate cleanup. Addresses PR #105 review comment r3355705568. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to r3355705568: add a real (non-mutating) formatting gate alongside lint. New `prettier:check` script runs `prettier --check` scoped to first-party `src/` and `tests/` (both currently clean), wired into the build-test composite so it runs before build/test when run-checks is enabled (PR CI). Kept scoped rather than repo-wide because `prettier --check .` fails on pre-existing unrelated files (CHANGELOG.md — managed by semantic-release — plus docs/ and scripts/); formatting those is a separate cleanup. Addresses PR #105 review comment r3355705568. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ci_reusable.yaml runs via workflow_call, and `ref: github.event.pull_request. head.ref` is the wrong revision to check out: it targets the PR head branch tip rather than the merge commit, and for fork PRs the head ref doesn't exist in this repo so checkout fails. Drop the explicit ref so actions/checkout uses its default (github.sha) — the PR merge commit in the caller context, which also works for fork PRs. Addresses PR #105 review comment r3356088977. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The inline comment claimed the explicit `origin main` target was needed because the branch had no upstream after `checkout -B`, but the upstream is now set (branch.main.remote/merge) a few steps earlier. Update the comment to reflect that the explicit target is just being explicit; behavior is unchanged. Addresses PR #105 review comment r3356089027. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The build-test composite runs Node 22.14.0 / npm 11.5.1 (the latter required by OIDC trusted publishing), but the Volta pins still selected Node 20.19.0 / npm 8.11.0, so contributors using Volta ran a different runtime than CI and publishing — a "works locally, fails in CI" gap. Bump the Volta pins to match, so local dev, PR CI and publish all use the same toolchain. Addresses PR #105 review comment r3356297523. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
levalleux-ludo
added a commit
that referenced
this pull request
Jun 4, 2026
PR #105's squash commit message contained a skip directive, so the push to main skipped all workflows including the new publish workflow. This empty commit re-triggers it to run the alpha publish. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 2.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #104.
Problem
NPM now requires trusted publishing (OIDC), which binds a package to one exact workflow filename. The repo currently publishes from two different workflows using a long-lived
NPM_TOKEN, so both publish paths are broken:ci_reusable.yaml(auto, push tomain)publish-latest.yaml(manualworkflow_dispatch, semantic-release)Change
Consolidate both publish paths into a single tokenless workflow.
.github/workflows/publish.yamlpush→ alpha (npm publish --tag alpha);workflow_dispatch→ latest via semantic-release + GitHub release.permissions: id-token: write, noNPM_TOKEN..github/actions/build-test/action.ymlsetup-node(Node 22) → upgrade npm (≥ 11.5.1) → cache →npm ci→ build → test. Shared by the publish workflow and PR CI..github/workflows/publish-latest.yaml.github/workflows/ci_reusable.yamlprettier/lint:fix. DropsNPM_TOKEN/NODE_AUTH_TOKEN/PUBLISH_NPMand all publish steps..github/workflows/ci.yamlWhy these specifics
npm install -g npm@latest.setup-node+registry-urlwith no token env is the documented pattern, and provenance is automatic.@semantic-release/npm≥ 13.1.0, bundled in semantic-release ≥ v25.0.1 →semantic_versionbumped24→25.npm publishreusesdist/with no rebuild (avoids the double-build a reusable-workflowneeds:gate would cause).Before this can publish, configure the trusted publisher on npmjs.com for @bosonprotocol/chat-sdk → Settings → Trusted Publisher → GitHub Actions:
bosonprotocolchat-sdkpublish.yaml(exact, case-sensitive)npm publishThe first publish fails with
ENEEDAUTHuntil this exists. TheNPM_TOKENrepo secret becomes unused and can be removed afterward.Verification
- uses: ./.github/actions/build-testresolves.NPM_TOKEN/NODE_AUTH_TOKEN/PUBLISH_NPMusage remains.mainpublishes a newalphaversion (with provenance); a manual run of Publish - Chat SDK publisheslatestand creates a GitHub release.🤖 Generated with Claude Code