Skip to content

ci(release): add automated npm publishing with OIDC trusted publishing#170

Merged
backnotprop merged 1 commit intobacknotprop:mainfrom
rcdailey:ci-npm-publish
Feb 24, 2026
Merged

ci(release): add automated npm publishing with OIDC trusted publishing#170
backnotprop merged 1 commit intobacknotprop:mainfrom
rcdailey:ci-npm-publish

Conversation

@rcdailey
Copy link
Contributor

Summary

Adds automated npm publishing to the release workflow for @plannotator/opencode and @plannotator/pi-extension, using npm's OIDC trusted publishing (no long-lived secrets required). Also adds CI smoke testing on pull requests and a manual dry-run trigger.

What changed

The release.yml workflow now has three trigger events and a new npm-publish job:

Triggers

  • Tag push (v*): Full release. Builds binaries, creates GitHub Release, and publishes both packages to npm with provenance attestations. This is the existing behavior plus npm publishing.
  • Pull request (to main): Smoke test. Runs the full build pipeline and packs the npm packages without publishing. Catches build breakage before merge. The release job is skipped (no tag).
  • Manual dispatch (workflow_dispatch): On-demand validation. Has a "dry run" checkbox (default: checked) that controls whether npm publish actually uploads. Useful for verifying the pipeline end-to-end.

Dry-run logic

A workflow-level DRY_RUN env var is derived once and used everywhere:

Trigger DRY_RUN npm behavior GitHub Release
Tag push false Real publish Created
Pull request true bun pm pack only Skipped
Manual, default true bun pm pack only Skipped
Manual, unchecked false Real publish Skipped (no tag)

npm publishing approach

bun publish does not yet support OIDC or provenance (oven-sh/bun#15601), so the job uses a two-step approach:

  1. bun pm pack builds the tarball and resolves workspace:* references
  2. npm publish *.tgz --provenance --access public publishes via OIDC with provenance attestation

This keeps the build on bun while using npm CLI for the publish step, which supports trusted publishing natively.

Other changes

  • Action versions pinned to full SHA (supply chain hardening)
  • id-token: write permission added at workflow level for OIDC
  • release job gated at job level with if: startsWith(github.ref, 'refs/tags/') instead of always running

Setup required before first publish

Configure trusted publishers on npmjs.com

For each package (@plannotator/opencode and @plannotator/pi-extension):

  1. Go to npmjs.com > your package > Settings > Trusted Publisher
  2. Select GitHub Actions
  3. Fill in:
    • Organization or user: backnotprop
    • Repository: plannotator
    • Workflow filename: release.yml
    • Environment name: (leave blank)

Full instructions: npm trusted publishing docs

That's it

No NPM_TOKEN secret or any other repository configuration is needed. OIDC trusted publishing uses short-lived tokens generated per workflow run, authenticated by GitHub's OIDC provider. Provenance attestations are generated automatically, giving consumers a verified link from the published package back to the source commit and workflow that built it (npm provenance docs).

Testing

Validated on my fork (rcdailey/plannotator) using dry-run mode. Both packages pack successfully with correct contents and resolved workspace dependencies.

@backnotprop
Copy link
Owner

Ty for this, I'm flying back home and will review a bit later today

Adds an npm-publish job to the release workflow that publishes
@plannotator/opencode and @plannotator/pi-extension to npm with
OIDC trusted publishing and provenance attestation.

- Add pull_request and workflow_dispatch triggers (with dry-run input)
- Derive DRY_RUN env var; publish only on tag pushes or explicit opt-in
- Pin all action refs to SHA for supply-chain security
- Gate the release job on tag refs to avoid spurious GH releases
- Add id-token: write permission at workflow and job level for OIDC
@backnotprop
Copy link
Owner

Before merging: configure npm trusted publishing (OIDC) for both packages on npmjs.com, otherwise the publish step will 403.

  1. Go to https://www.npmjs.com/package/@plannotator/opencode → Settings → Trusted Publishing → GitHub Actions
  2. Set: Organization/user = backnotprop, Repository = plannotator, Workflow = release.yml
  3. Repeat for https://www.npmjs.com/package/@plannotator/pi-extension

Optional: enable "Require 2FA and disallow tokens" on each package to lock publishing to OIDC-only.

@backnotprop
Copy link
Owner

^ this is my task

@backnotprop backnotprop merged commit e8ad0d5 into backnotprop:main Feb 24, 2026
3 checks passed
@backnotprop
Copy link
Owner

backnotprop commented Feb 24, 2026

@rcdailey how did you determine the hashes for each of the GitHub actions?

What would you recommend as a process moving forward for updating these?

@rcdailey
Copy link
Contributor Author

Hey thanks for the merge.

To your question: In this case, I did npx -y actions-up@latest on my local machine. It's a nice, easy way to do adhoc upgrades. Long term, though, I highly recommend Renovate. It will upgrade GH actions, NPM packages, whatever you can think of. I use it for all my projects.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants