Repository navigation
2.7: Publish releases directly by default, keep staging behind an input - #72
Merged
Merged
Conversation
The release job now publishes every release directly unless its caller asks for staging, so a version is live when its run ends and nobody has to promote it. A new optional workflow_call input, publish-mode, picks the arm: "direct" (the default) or "staged". Every caller pins @main and passes nothing, so every caller gets direct with no change on its side. publish-floor.mjs reads the input (PUBLISH_MODE, or --mode by hand) and stays the one place the arm is decided; every step condition still reads its mode output, and the publish command is byte-identical. On the direct arm it still identifies the publish tool and reads its version, but owes no staging floor and does not ask the registry, because neither changes the arm. On "staged" its behaviour is unchanged. Any other value, the empty string included, is refused before anything is packed. The README section is now "Direct publishing, and staging on request", with what a direct publish needs from NPM_TOKEN (a granular token, "Read and write (publish and stage)", with Bypass 2FA, or npm answers E403/EOTP), the 90-day cap on write tokens, and npm's stated January 2027 end for direct publishing with granular tokens, each with its source and the date read. Claude-Session: https://claude.ai/code/session_01U97ssbeo9C37pqPiWMQ1BU
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 changed
The shared release workflow now publishes directly by default, so a release is live when its run ends and nobody has to promote it. Staging stays available behind a new optional input.
release.yml: a newworkflow_callinput,publish-mode(string, default"direct"; the other accepted value is"staged"). It reachespublish-floor.mjsas thePUBLISH_MODEenvironment variable, so the value is never shell text. The publish step'spublish:expression is unchanged. It still runspnpm run releaseon everymodeexceptstaged, andpublish-floor.mjsnow reportsmode: directby default, for a package that is already on npm too. Every step that readssteps.publish-floor.outputs.mode(stage report, promotion wait, promoted-arm release, direct-arm release, install gate) still reads that one value, so on the default arm the staged steps are skipped and the direct-arm release and install gate run.scripts/publish-floor.mjs:direct: still identifies the publish tool and reads its version, and refuses if it cannot. It owes no staging floor and does not query the registry, because neither changes the arm. The annotation is a notice, not a warning.staged: behaves exactly asmaindoes today.--modenorPUBLISH_MODE, it uses the default,direct.test/publish-floor.test.mjs: the existing staged-arm cases now ask forstagedexplicitly. New cases cover the default, the direct arm (no floor owed, registry never queried, tool still read) and the refusal of unknown values, both as unit tests and end to end through the real script. The input's declared default is pinned to the script'sDEFAULT_PUBLISH_MODE. The input reaches only the floor gate, and only as an env var.test/environment-gate.test.mjs: AC11 now admitspublish-modeas an optional input with default"direct".NPM_TOKEN, that npm caps write tokens at 90 days, that npm has said it will remove direct publishing with granular tokens in January 2027, and how a caller opts back into staging. Each claim cites its source and the date we read it.Callers. All 14 library callers pin
release.yml@mainand pass onlypackage-name(read from each caller's.github/workflows/release.ymlonmain, 2026-10-08). They get the direct default with no change on their side.scripts/reference-publish.mjsclassifies the new input asinput-addedwithact: false: "the inputpublish-modewas added, defaulting to "direct". A caller that passes nothing is unaffected."How it was checked
Before this change the suite had 1019 tests, all passing. With the code change and no test edits, 15 of them failed, all of them cases that expected the staged arm by default.
We did not run any release, publish or
workflow_dispatch.Research (read 2026-10-08)
npm publishfrom CI with a granular token. "When the Bypass 2FA option is set to true, this setting takes precedence over account-level and package-level 2FA settings for package and automation actions such as publishing." Bypass 2FA "is set to false by default at token creation". A "Read and write (stage only)" token "cannot publish new package versions directly". https://docs.npmjs.com/about-access-tokens (last edited 2026-09-10). Without Bypass 2FA, npm answers a CI publish with E403 or EOTP.--provenanceflag needed. Provenance is optional to npm and is enabled byNPM_CONFIG_PROVENANCE, which the publish step already sets for public callers, together withid-token: write. https://docs.npmjs.com/generating-provenance-statements (last edited 2026-05-04). We checked this on the registry:@cosyte/hl7@0.0.10, which this workflow published directly on 2026-08-07, carries an SLSA provenance attestation (npm view @cosyte/hl7@0.0.10 dist.attestations).npm publish, you can submit packages to a staging area withnpm stage publish." Promotion needs 2FA: "You will be prompted for 2FA verification whether you approve the package in the CLI or on npmjs.com." https://docs.npmjs.com/staged-publishing (last edited 2026-09-29).Scope notes
publish-floor.mjs, so the publish command and the steps that depend on it cannot disagree.staged-publish.mjs,promotion.mjsand thefinalizejob are unchanged.finalizealso covers a direct publish whose release step never finished, which was already true before this change.NPM_TOKENbeing allowed to publish without 2FA. If it fails with E403 or EOTP, the token needs replacing (granular, "Read and write (publish and stage)", Bypass 2FA). That is a step for the owner on npmjs.com, and this PR does not address it.https://claude.ai/code/session_01U97ssbeo9C37pqPiWMQ1BU