ci: harden npm release pipeline - #14
Merged
Merged
Conversation
Publish job now stages the release (npm stage publish) for manual 2FA approval and installs no dependencies; build moved to a separate job. Least-privilege permissions, persist-credentials off, zizmor linting, 3-day dependency cooldown.
size-limit report 📦
|
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.
Overview
Reworks the release pipeline so no publishing credential exists to steal, releases can only originate from one workflow, and every release still requires a manual 2FA approval from the maintainer. Follows the secure-npm-package guide.
Problem Statement
The previous publish workflow ran the build, the install, and the publish in a single job holding
id-token: write. Any dependency in that job — including the full dev toolchain and its transitive packages — could have publishednanotagson its own. Publishing was also fully automatic: a tag push released straight to npm with no human gate. Supporting workflows kept credentials in the checkout, ran with default (write) token permissions, and one job pulled an unpinnedpnpm@latestat runtime.Solution Approach
publish.ymlintotest,build, andpublishjobs. Onlypublishgetsid-token: write, and it installs no dependencies, uses no cache, and consumes the build output as an artifact.npm stage publish --ignore-scripts: CI stages the release and it goes live only after the maintainer approves it with their 2FA key, so a compromised pipeline cannot ship a version on its own.permissionsper job,persist-credentials: falseon every checkout, and refreshed all SHA-pinned actions; the bundle-size job no longer activates an unpinned pnpm.minimumReleaseAge), which blocks the large majority of malicious releases since most are pulled within hours.Breaking Changes
None for consumers of the package. For maintainers, the release is no longer automatic: after pushing a version tag, the staged release must be approved in Staged Packages on npmjs.com or via
npm stage approve.Testing