Skip to content

chore: add release workflow using npm trusted publishing - #403

Draft
captbaritone wants to merge 3 commits into
mainfrom
add-release-workflow
Draft

captbaritone wants to merge 3 commits into
mainfrom
add-release-workflow

Conversation

@captbaritone

@captbaritone captbaritone commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Adds a Release workflow so publishing happens from CI with npm trusted publishing (OIDC) instead of a maintainer's local npm token. No NPM_TOKEN secret is used or needed.

How it works

Merging to main is the only gate. On every push to main the workflow reads the version from package.json and asks npm whether that version already exists. If it does, the run is a no-op. If it does not, it runs the test suite, builds the package, publishes it with provenance, creates the vX.Y.Z tag, and opens a GitHub release.

So a release is simply a merged version bump — no tagging or npm publish by hand. Because the decision is derived from npm's state rather than from the event, re-running a failed publish is safe.

A version containing a - publishes under next rather than latest, so the pipeline can be rehearsed with something like 0.11.1-rc.0.

Why main-triggered rather than tag-triggered

A tag can point at any commit, including one that never landed on main, and tags are not covered by branch protection. A tag-triggered release would let anyone with write access publish an unreviewed commit, bypassing whatever protection main has. Keying off main means the published commit is always one that went through the branch.

Security notes

  • The publish job holds the OIDC token and deliberately never runs npm ci, so no dependency lifecycle scripts execute while the token is available. It only publishes the artifact the build job produced.
  • permissions: {} at the top level, with each job granted the minimum it needs.
  • Provenance means every release carries a signed, publicly verifiable attestation of the repo, commit and workflow that produced it.
  • This widens who can effectively publish, from npm package owners to anyone who can land a commit on main. Branch protection on main (require a PR, require status checks; no required approvals needed) is what makes that boundary meaningful, and is worth adding alongside this.

Notes

  • The npm package's trusted publisher must be configured on npmjs.com pointing at this repo and release.yml. No GitHub environment is required.
  • .node-version is pinned to v24 because trusted publishing needs npm >= 11.5.1. Node 22 still bundles npm 10.9.4, which is too old and would fail at publish time.
  • CONTRIBUTING.md now documents the PR: * label requirement, since gen-changelog.js throws on any merged PR missing one.

Test plan

  • npm run test passes (lint, tsc, 86 tests, prettier, cspell)
  • Workflow YAML parses; embedded shell passes bash -n
  • Version-check logic verified against published/unpublished, index-0, and single-version (bare string) responses from npm view
  • dist-tag selection verified for stable and prerelease versions
  • End-to-end publish, best rehearsed with a prerelease once the trusted publisher is configured

Publishes via GitHub Actions with OIDC provenance instead of a local npm
token, following the pattern already used by graphql/graphql-js.
@codecov

codecov Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (903ebe0) to head (9d6e95c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #403   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines          748       748           
  Branches        48        48           
=========================================
  Hits           748       748           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@captbaritone
captbaritone marked this pull request as draft September 18, 2026 17:54
A tag can point at any commit, so a tag-triggered release bypasses branch
protection entirely. Keying off the version in package.json means the
published commit is always one that landed on main.
@captbaritone captbaritone mentioned this pull request Sep 21, 2026
Every previous release body uses the gen-changelog format driven by the
'PR: *' labels, which --generate-notes would have abandoned. Generating in
the build job means a PR missing its label fails before anything is
published.
@benjie

benjie commented Sep 22, 2026

Copy link
Copy Markdown
Member

Trusted publishing is now ready to go npm-side. I've only allowed for it to do "npm stage publish" right now (you need to approve the release via npm), we can change this if need be

Comment on lines +22 to +30
uses: actions/checkout@v4
with:
# gen-changelog.js shells out to 'git rev-list' against release tags,
# so it needs full history rather than the default shallow clone.
fetch-depth: 0
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These versions are a little old, I think v7 is latest?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants