Repository navigation
[DO NOT MERGE] ci: stage npm releases via OIDC instead of publishing directly - #239
evenstensberg wants to merge 1 commit into
Conversation
Replace `changeset publish` with a script that runs `npm stage publish` through the OIDC trusted publisher, so every release needs a maintainer's 2FA approval before it goes live. The script still prints "New tag:" so changesets/action pushes the tag and creates the GitHub release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #239 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 3 3
Lines 18 18
Branches 2 2
=========================================
Hits 18 18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
WalkthroughThe release command now runs a staging script instead of Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to
|
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
ac33b2d3-9467-42a7-b0a0-130fcdf526b4
📒 Files selected for processing: 3
.github/workflows/release.ymlpackage.jsonscripts/stage-publish.mjs
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.
| } else if (isTagged()) { | ||
| log(`${tag} already exists, ${name}@${version} is staged, skipping.`); | ||
| } else { | ||
| run("npm", ["stage", "publish"], { stdio: "inherit" }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Pass an npm dist-tag for prerelease versions.
If Changesets produces a version such as 5.1.0-beta.1, this command uses npm’s default latest tag. npm stage publish rejects prerelease versions without an explicit tag. Select the Changesets prerelease tag and pass --tag when staging that version. Otherwise, the prerelease workflow stops before it creates a stage. (docs.npmjs.com)
| run("npm", ["stage", "publish"], { stdio: "inherit" }); | ||
| run("git", ["tag", tag]); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Recover a stage when tag publication fails.
If staging succeeds but git tag or changesets/action’s tag push fails, npm retains the staged version. A fresh run then finds no published version and no remote tag. It attempts to stage the same version again, which npm rejects. Reconcile an existing pending stage before staging; when the remote tag is absent, let the run create the tag and emit New tag: without another upload. (docs.npmjs.com)
| run("git", ["tag", tag]); | ||
|
|
||
| log(`Staged ${name}@${version}, approve it with 2FA to publish.`); | ||
| log(`New tag: ${tag}`); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Delay the public release signal until approval.
If a maintainer has not approved the stage, this New tag: line still makes changesets/action push v<version>, create a GitHub release, and report published=true. npm does not make the version installable until approval. A rejected stage therefore leaves a public release for a version that never went live. Use a separate staging signal, then create the release after approval. (raw.githubusercontent.com)
Summary
Releases now go to the npm stage queue through the existing OIDC trusted publisher, instead of being published directly. A maintainer has to approve each version with 2FA before it goes live.
changeset publishalways runs a plainnpm publishand has no staging option. So Changesets still handles versioning and the release PR, and the publish step now runs a small script.Changes
scripts/stage-publish.mjs(new): replaceschangeset publish.v<version>tag already exists on origin (meaning it's staged and waiting for approval). This keeps later pushes tomainfrom trying to stage the same version again.npm stage publish, creates thev<version>tag locally and printsNew tag: v<version>. changesets/action looks for that line in single-package repos, then pushes the tag and creates the GitHub release as before.package.json:releasenow runs the script.release.yml: comments and a step name only. Staging needs npm 11.15.0 or later and Node 22.14.0 or later; the workflow already uses Nodelts/*and updates tonpm@latest.Reviewer notes / follow-ups
npm publishfrom CI is rejected.npm stage list coffee-loader, thennpm stage approve <stage-id>, or use the Staged Packages tab on npmjs.com. Approval stays blocked until npm's malware scan finishes.Testing
5.0.4: it skips as expected.npmandgitstubbed and an unpublished version: it callsnpm stage publishandgit tag, then printsNew tag:.🤖 Generated with Claude Code
Summary by CodeRabbit