Repository navigation
Conversation
… approval Publishing the GitHub Release used to put the version straight onto npm. Between the tag and a version anyone can install there was no human step and no chance to look at the tarball. The publish step also had a quieter fault: JS-DevTools/npm-publish treats a version that is already on the registry as success. The v1.0.0 re-run on 2026-09-07 logged "already published" and finished green, so a release could appear to ship when nothing had been sent. The publish job now runs `npm stage publish` instead. npm uploads the tarball, with provenance, to its stage queue, and the version stays uninstallable until a maintainer approves it with 2FA (`npm stage approve <id>` or the Staged Packages tab on npmjs.com). The action is dropped because it wraps `npm publish` and has no staging option. Every way of not staging now fails the job: - npm refuses a version the registry already has, and nothing here passes --force, which would skip that check; - a run that exits zero without printing a stage id is failed by the step itself, and the id is then read back with `npm stage view`; - an npm older than 11.15.0, which has no `npm stage`, fails with a message saying so, rather than depending on what `lts/*` happens to ship. The job no longer sets registry-url or NODE_AUTH_TOKEN. With registry-url, setup-node writes a token line into .npmrc and exports a placeholder that npm presents instead of doing the OIDC exchange. Without it, the job authenticates through trusted publishing alone and holds no npm token. The stage id is parsed from npm's "+ name@version (staged with id ...)" line, not from --json, because the JSON shape differs between npm 11.19 (keyed by workspace) and 12.1 (keyed by package name). The text line is the same in both. The README gains a Release section covering the approval commands. Closes #20
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.
What and why
Publishing the GitHub Release used to put the version straight onto npm, with no human step between the tag and a version anyone can install. The publish step also hid a failure:
JS-DevTools/npm-publishtreats a version already on the registry as success. The v1.0.0 re-run on 2026-09-07 (run 34143303465) logged@the-rabbit-hole/docs-theme@1.0.0 already published.and finished green, the same pattern eslint-config showed tonight.The publish job now runs
npm stage publishinstead of the action. npm uploads the tarball with provenance to its stage queue, and the version stays uninstallable until a maintainer approves it with 2FA. The action is dropped because it wrapsnpm publishand has no staging option.Fails loudly. Every way of not staging turns the run red:
You cannot publish over the previously published versions). Nothing passes--force, which is the only thing that skips that check (npmlib/commands/publish.js).staged with id <uuid>line fails the step. The id is then read back withnpm stage view.lts/*ships.ENEEDAUTH.No token.
registry-urlandNODE_AUTH_TOKENare gone. Withregistry-urlset, setup-node writes a token line into.npmrcand exports a placeholder, which npm presents instead of doing the OIDC exchange (the fault #19 worked around). Without it, the job authenticates through trusted publishing alone.Why the text line, not
--json. The--jsonoutput ofnpm stage publishis shaped differently in npm 11.19 (keyed by workspace) and 12.1 (keyed by package name). The+ name@version (staged with id ...)line is identical in both.npm features used, checked against npm's docs on 2026-09-30:
npm stage:publishneeds no 2FA and accepts--provenance,--accessand--tag.view,listanddownloadalso need no 2FA.approveandrejectdo.Provenance. npm's source builds the sigstore bundle into the same payload that goes to the stage route (
libnpmpublishbuildMetadata, thenPOST /-/stage/package/<name>), and--provenanceis passed explicitly. npm's docs don't say whether the approved version keeps that attestation. This can only be checked against the first approved version (v1.1.0) withnpm view @the-rabbit-hole/docs-theme@1.1.0 dist.attestations. That check stays open on the issue.Closes #20
Verification
npm run lint,actionlinton every workflow,shellcheckon the step body)npm test, 23 passed;npm run typecheck)npm run build)How this was verified
stagestep'srun:body out of the workflow withyqand runs it for six cases. Version already published, staged with no id, no auth, and npm 11.14.9 all exit non-zero. A good stage passes on npm 11.19.0 and 12.1.0, writingstage-idtoGITHUB_OUTPUT, callingnpm stage view <id>, and putting the approve and reject commands in the job summary. Before the change, the harness failed because the workflow had no stage step.--dry-run(which never uploads), against the artifact file set built locally: 1.1.0 prints+ @the-rabbit-hole/docs-theme@1.1.0 (staged)and exits 0. With the version set to 1.0.0 it printsYou cannot publish over the previously published versions: 1.0.0.and exits 1.Test does not run on this PR, because its
paths-ignorecovers.github/**and**.md. The first real run is the v1.1.0 release.Before the v1.1.0 release, on npmjs.com for
@the-rabbit-hole/docs-theme: add a trusted publisher forthe-rabbit-hole-tech/docs-themewith workflowaction-deploy-npm.yaml, set to stage-only. Then set Publishing access to disallow tokens, and remove theNPM_TOKENrepo secret, which this workflow no longer reads. v1.0.0 was published under thebugs5382user, not the OIDC identity, so there may be no trusted publisher yet. Without one, this job fails withENEEDAUTH.