Skip to content

ci: npm publish workflow via OIDC trusted publishing - #101

Merged
aswhitehouse merged 5 commits into
mainfrom
ci/npm-trusted-publishing
Jul 30, 2026
Merged

ci: npm publish workflow via OIDC trusted publishing#101
aswhitehouse merged 5 commits into
mainfrom
ci/npm-trusted-publishing

Conversation

@sgriffiths

@sgriffiths sgriffiths commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What changed

Adds .github/workflows/npm-publish.yml — an npm publish workflow mirroring the PyPI one (publish.yml): manual workflow_dispatch, tag ↔ npm/package.json version verification when dispatched on a v* tag, npm ci + tests, then npm publish with no long-lived token — auth comes from the job's OIDC identity via npm trusted publishing, which also generates provenance attestations automatically. prepublishOnly already runs the TypeScript build.

Why

v1.6.0 is tagged and live on PyPI, but @prime-vector/open-agent-spec is still 1.5.1 on npm: the package has a single maintainer (@aswhitehouse) and publishing currently requires that personal login on a local machine. This puts npm releases on the same footing as PyPI — dispatched from CI against the tag, no secrets to rotate, provenance included.

One-time setup needed (@aswhitehouse)

The workflow authenticates only after a trusted publisher is configured for the package (needs your npm login, ~2 minutes):

  1. npmjs.com → @prime-vector/open-agent-specSettingsTrusted Publisher → GitHub Actions
  2. Organization/user: prime-vector · Repository: open-agent-spec · Workflow filename: npm-publish.yml · Environment: npm
  3. Allowed actions: select npm publish — configurations created after 2026-05-20 must explicitly allow at least one action
  4. Save. No token generation, nothing to store in GitHub secrets.

Then publish 1.6.0 by running the Publish to npm workflow from the Actions tab with v1.6.0 as the ref (or gh workflow run npm-publish.yml --ref v1.6.0).

If you'd rather ship 1.6.0 immediately and set this up after, the manual path still works: git checkout v1.6.0 && cd npm && npm ci && npm publish from your logged-in machine — this PR is about not needing that next time.

How tested

  • All existing tests pass (pytest tests/) — workflow-only change, no runtime code touched
  • New tests added (if applicable) — n/a

actionlint-style review by eye against the existing publish.yml structure; the publish step itself is only exercisable once the trusted publisher exists (verification step and version check will fail fast if misconfigured).

Type of change

  • Refactoring / CI / tooling

Checklist

  • Self-review completed

Mirrors publish.yml (PyPI): manual workflow_dispatch, tag/version
verification when dispatched on a v* tag, then npm publish with no
long-lived token — authentication comes from the job's OIDC identity
once a trusted publisher is configured for the package on npmjs.com.
Requires npm >= 11.5.1, so the workflow updates the bundled npm.
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
open-agent-spec Ready Ready Preview, Comment Jul 28, 2026 9:50pm

Request Review

A branch dispatch would let unreviewed code reach the registry; require
a v* tag ref instead of skipping verification on non-tag refs.
@sgriffiths

sgriffiths commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Two hardenings followin:

Hardened in 55b1fc5: the workflow now hard-fails on any non-v*-tag ref instead of skipping verification — a branch dispatch can't publish unreviewed code. Belt-and-braces: the npm environment is also restricted to v* tag deployments (repo Settings → Environments), and the same tag-only policy is now on the pypi environment for parity. Fork PRs can't trigger workflow_dispatch at all, and OIDC binds the trusted publisher to this exact repo + workflow, so a fork's copy presents the wrong identity to npm.

Ask (@aswhitehouse): alongside the trusted-publisher setup, could you add Scott as a package owner once he has an npm account — npm owner add <username> @prime-vector/open-agent-spec? Single-maintainer registry access is the bus factor this PR is chipping away at; owner access also lets either of you manage the trusted publisher config.

… off tags

npm/ has no Jest test files yet (the node runtime is certified via the
conformance harness), so npm test exits 1 with 'No tests found' —
typecheck with tsc --noEmit until tests land. Bring publish.yml to the
same tag-only posture as npm-publish.yml: refuse non-v* refs instead of
skipping verification.
@sgriffiths

Copy link
Copy Markdown
Contributor Author

Both issues taken, in a7f7494:

1. Chicken-and-egg — confirmed: workflow_dispatch --ref v1.6.0 runs the workflow file from the tag, which predates it. For 1.6.0 the manual one-shot is the path (git checkout v1.6.0 && cd npm && npm ci && npm publish from your logged-in machine); the workflow takes over from the next tag. Not retargeting a published release tag for this — moving v1.6.0 after the GitHub release and PyPI publish would be worse than one last manual publish.

2. npm test — confirmed, zero Jest files under npm/ and ci.yml never runs it (node runtime is certified via the conformance harness). Swapped the step to npm run lint (tsc --noEmit) with a comment to flip back to npm test when tests exist. Preferred that over --passWithNoTests, which would green-light an empty suite forever.

Minor — took the parity suggestion now rather than post-merge: publish.yml (PyPI) also hard-fails off non-v* refs, matching this workflow and the tag-only deployment policies now on both environments.

@sgriffiths

Copy link
Copy Markdown
Contributor Author

Scott's npm account is live: sgriffiths. So the owner-add is copy-paste ready:

npm owner add sgriffiths @prime-vector/open-agent-spec

Full checklist for closing out the 1.6.0 npm side (all yours, @aswhitehouse):

  1. npm owner add sgriffiths @prime-vector/open-agent-spec
  2. One-shot publish for 1.6.0: git fetch --tags && git checkout v1.6.0 && cd npm && npm ci && npm publish
  3. Trusted publisher config on npmjs.com (package Settings → Trusted Publisher → GitHub Actions: prime-vector / open-agent-spec / npm-publish.yml / environment npm)
  4. Review/merge this PR — from the next tag, publishes run from CI with provenance and no local login

@aswhitehouse aswhitehouse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One compatibility fix is needed before this can publish through OIDC: the current npm trusted-publishing requirements are npm CLI >= 11.5.1 and Node >= 22.14.0, but this workflow pins Node 20. Updating npm globally does not satisfy the Node requirement. Please move the publish job to Node 22.14+ (preferably the current Node 24 example used by npm docs); while touching this, the official example now uses actions/setup-node@v6 and recommends package-manager-cache: false for release builds. The package repository URL already matches, permissions/environment/tag checks look sound, and CI is green.

Also, the one-time npm setup instructions should mention selecting the required allowed action npm publish: trusted-publisher configurations created after May 20, 2026 must explicitly select at least one allowed action. Once those are addressed, this looks ready.

OIDC trusted publishing requires Node >= 22.14 as well as npm >= 11.5.1;
Node 20 fails the floor regardless of the npm upgrade. Follow the npm
docs' current example: Node 24, setup-node@v6, package-manager-cache off
so release builds never restore a stale dependency cache.
@sgriffiths

Copy link
Copy Markdown
Contributor Author

Both taken in 15c2767:

  1. Node floor — moved to actions/setup-node@v6 with Node 24 and package-manager-cache: false, per the npm docs example you cited. Kept the npm install -g npm@latest step as belt-and-braces for runner-image lag; with Node 24 it should be a no-op most days.
  2. Allowed actions — the one-time setup instructions in the PR body now include selecting npm publish as the required allowed action (post-2026-05-20 configuration requirement).

Ready for re-review.

Both packages install a global oa; PATH order decides which runs. One
line in the npm README so dual-installers aren't surprised.
@sgriffiths

Copy link
Copy Markdown
Contributor Author

Refinements from a second review pass (none blocking) — the earlier checklist had the trusted-publisher step too early, since this workflow file must exist on main before npmjs.com can bind to it. Corrected sequence (@aswhitehouse):

  1. Merge this PR (so npm-publish.yml is on main)
  2. npm owner add sgriffiths @prime-vector/open-agent-spec
  3. Dry-run, then manual one-shot for 1.6.0: git fetch --tags && git checkout v1.6.0 && cd npm && npm ci && npm publish --dry-run — expect ~43 files: README.md, bin/oa.js, dist/ only (verified locally: 19.3 kB tarball, no src/, no lockfile) — then npm publish
  4. Trusted publisher config on npmjs.com: prime-vector / open-agent-spec / npm-publish.yml / environment npm / allowed action npm publish
  5. From the next tag, publishes run from CI with provenance

Two honesty notes, actioned: the manual 1.6.0 npm artifact won't carry OIDC provenance attestations (unlike the PyPI 1.6.0 publish) — that's accepted for this one-shot and fixed from the next release. And the v1.6.0 GitHub release notes no longer say "one certified set": they now state each runtime is certified for the surface it implements, with npm explicitly refusing tools:/sandbox:. Also added a one-line note to the npm README (30393a7): both CLIs install a global oa, PATH order wins.

@aswhitehouse aswhitehouse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@aswhitehouse
aswhitehouse merged commit 7c67148 into main Jul 30, 2026
4 checks passed
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.

3 participants