feat: npm stage#9201
Conversation
|
Why would download not require 2fa but view require it? Either both should, or neither, I’d expect. |
@ljharb that was a mistake, fixed! |
|
(i'm assuming that all the "no"s still require one factor) |
All staged content routes will require authentication with access to that package. |
Adds staged publishing support with subcommands: publish, list, view, approve, reject, and download. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The logTar utility wraps JSON output under the package name key, so tests need to access out[pkg] instead of out directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update smoke-tests/tap-snapshots/test/index.js.test.cjs to match current CLI output after rebasing on latest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ns (#54) ## Summary This PR introduces six new public endpoints for supporting managing staged package versions. These endpoint support viewing, filtering, approving, rejecting and inspecting staged package versions on the npm registry. #### E1. GET https://registry.npmjs.org/-/stage Gets the list of all staged package versions that the user has access to #### E2. POST https://registry.npmjs.org/-/stage/package/{package-name} Creates a staged package version #### E3. GET https://registry.npmjs.org/-/stage/{stage-id} Gets a specific staged package version #### E4. DELETE https://registry.npmjs.org/-/stage/{stage-id} Reject a staged staged package version #### E5. POST https://registry.npmjs.org/-/stage/{stage-id}/approve Approve a staged package version to published #### E6. GET https://registry.npmjs.org/-/stage/{stage-id}/tarball Gets a staged package version tarball ## References - for more info on npm stage: npm/cli#9201
JSON output no longer disables credential redaction globally. Previously, all --json commands would skip redactLog(), which could leak embedded credentials from registry URLs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Regarding the The JSON output buffer now redacts each item individually by default. Callsites can opt out of redaction by passing We could not simply add stageId as a separate buffer entry because Note: publish |
The JSON output buffer now redacts each buffered item individually
based on its meta. Callsites opt out with { redact: false }.
This allows stage publish to include stageId (a UUID) unredacted
while other buffer items remain redacted by default.
Also guards against actorType: null rendering as "(null)" in
stage view output.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirrors the pattern used by dist-tag, access, and owner commands to provide subcommand suggestions (publish, list, view, approve, reject, download) when pressing TAB after `npm stage`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the common failure modes users will hit: - Invalid UUID format (validateUUID throws) - 404 (stage-id not found or already approved/rejected) - 403 (not an owner) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a test case for the falsy actorType path on line 36 of lib/utils/key-values.js, bringing branch coverage to 100%. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mutate stageId directly onto res instead of spreading, matching the existing transparencyLogUrl pattern. Spreading dropped non-enumerable Response getters (headers, status, url). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove local variable aliases for this.#command, using the getter directly everywhere for consistent style. Also adds clarifying comment for sync tar usage in download.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change static name from 'stage' to 'publish' to accurately reflect the subcommand identity within the stage command group. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use pkgContents.name and include version in the download filename for better identification of staged tarballs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify that stage publish intentionally bypasses otplease since 2FA is deferred to the approve/reject step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update expected filenames to include version, matching the updated download filename logic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary Adds permission flags to trust create operations. Users must now specify at least one of `--allow-publish` or `--allow-stage-publish` (alias: `--allow-staged-publish`) when creating trust configurations. ## Changes - Add `--allow-publish` and `--allow-stage-publish` flags to all trust provider commands (GitHub, GitLab, CircleCI) - Require at least one permission flag when creating trust configurations - Include permissions in the request body and display output - Add `PERMISSIONS` constants for permission values - Update tests and completion snapshots for new flags ## Related - #9201 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve conflict in lib/commands/trust/circleci.js by keeping both imports: validateUUID from the new shared utils/validate-uuid.js (introduced on this branch) and trustDefinitions from trust-cmd.js (added on latest, needed for allow-publish/allow-stage-publish). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
This usually means the cherry-pick had conflicts. Please create a manual backport: git fetch origin release/v11
git checkout -b backport/v11/9201 origin/release/v11
git cherry-pick -x 254809e318ee0046092d07d68a99154c3f672147
# resolve any conflicts, then:
git push origin backport/v11/9201Error details |
Backports #9201 to `release/v11`. Original commit: 254809e ## Conflict resolutions - **docs/lib/content/nav.yml** and **lib/utils/cmd-list.js**: kept `shrinkwrap`, `star`, and `stars` (still present on `release/v11`) and added `stage` in alphabetical order. On `latest`, those three commands were dropped in separate commits not part of this backport. - **lib/utils/tar.js**: combined both sides — kept the `key == null ? tarball : { [key]: tarball }` form from `release/v11` and added the new `redact` / `META` handling from the stage commit. - **Snapshots** (`test/lib/docs.js.test.cjs`, `test/lib/npm.js.test.cjs`, `smoke-tests/test/index.js.test.cjs`, `tap-snapshots/test/lib/commands/publish.js.test.cjs`): regenerated locally via `npm test` and `npm run snap -w smoke-tests`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`npm stage download <id> --json` currently emits the package contents
under a literal `"undefined"` key because `logTar` is called without a
`key` option.
### Before
```json
{
"undefined": {
"name": "polo-meow-meow-meow",
"version": "1.0.3",
...
}
}
```
### After
```json
{
"polo-meow-meow-meow": {
"name": "polo-meow-meow-meow",
"version": "1.0.3",
...
}
}
```
This matches the JSON shape of `npm publish --json` and `npm pack
--json`.
### Background
The `key == null` fallback in `lib/utils/tar.js` (that would have
rendered a bare object when no key was passed) was removed from `latest`
in #9247 ("fix: sync json output of pack and publish") as a `BREAKING
CHANGE`. Per that PR:
> BREAKING CHANGE: the --json output of `npm pack` and `npm publish`
have changed. They are now always consistent, and in the same format.
>
> Previously, `npm pack` would output an array of entries and `npm
publish` an object. The `npm publish` object also changed forms
depending on if workspaces were being published.
>
> Now, the output is always an object with the package name as the top
level index.
When #9201 (npm stage) landed, it added a new `logTar` caller in
`lib/commands/stage/download.js` that did not pass a `key`, silently
violating the v12 contract established in #9247 and producing the
`"undefined"` wrapper. This PR brings the new caller into compliance.
### Repro
```
npm stage download <stage-id> --json
```
A follow-up backports this to `release/v11` for consistent output across
branches: #9381.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Description > AI-agent disclosure: this PR was authored by an automated agent (Claude Code) and has not been line-by-line reviewed by a human before submission. Switches every `publish_package_*` job in `.github/workflows/build-status.yml` from `pnpm publish` to [`npm stage publish`](npm/cli#9201). With staged publishing, CI uploads the tarball into a holding area on the registry instead of releasing it immediately — a maintainer then has to approve the staged version (with 2FA) before it becomes installable. End users still receive the same artifacts on npm, but the release step is no longer fully automated: there is now an explicit approval gate between a tag being pushed and the package going live. This is a follow-up to #7011 (which already removed the OTP prompt from the publish call); together they move the workflow toward npm's recommended staged-release flow. Each publish job now: - installs `npm@latest` so the runner-provided npm is upgraded before publishing — the `stage` subcommand landed in npm 11.15.0 and is not yet bundled with Node 24's default npm; - runs both `npm install -g` and `npm stage publish` from the parent directory (`cd ..`), because the repo's `package.json` pins pnpm via `devEngines` and would otherwise block npm from executing inside the workspace; - no longer needs the `pnpm/action-setup` step or the pnpm-specific `--no-git-checks` flag, both of which were dropped from the seven affected jobs. Non-publish jobs were intentionally left alone and still install pnpm — only the release plumbing changes. Because the diff only touches the workflow file, there are no source, dependency, or published-artifact changes, no semver impact to flag, and no changeset to add; the new code path is exercised the next time a `v*` tag is pushed. ## Checklist — _Don't delete this checklist and make sure you do the following before opening the PR_ - [x] I have a full understanding of every line in this PR — whether the code was hand-written, AI-generated, copied from external sources or produced by any other tool - [x] I flagged the impact of my change (minor / patch / major) either by running `pnpm run bump` or by following the instructions from the changeset bot - [x] I kept this PR focused on a single concern and did not bundle unrelated changes - [x] I followed the [gitmoji](https://gitmoji.dev/) specification for the name of the PR, including the package scope (e.g. `🐛(vitest) Something...`) when the change targets a package other than `fast-check` - [x] I added relevant tests and they would have failed without my PR (when applicable) Co-authored-by: Claude <noreply@anthropic.com>
Introducing
npm stage🎉A new command for staged publishing — allowing package maintainers to decouple the act of publishing from proof-of-presence (2FA), making automated workflows more secure.
🔗 Docs
Why Staged Publishing?
With
npm stage publish, an automated workflow can stage a package version without a 2FA prompt. The maintainer can then review and approve the staged package at their convenience, providing 2FA only at the approval step. This keeps proof-of-presence in the loop while keeping CI/CD fully automated.Subcommands
npm stage publish [<package-spec>]npm stage list [<package-spec>]npm stage view <stage-id>npm stage approve <stage-id>npm stage reject <stage-id>npm stage download <stage-id>How It Works
npm stage publishusing any token type (no 2FA needed). The package version is held in a pending state, not publicly available.npm stage listto see pending staged packages, andnpm stage view <id>ornpm stage download <id>to inspect them.npm stage approve <id>(with 2FA) to publish, ornpm stage reject <id>to discard.Key Behaviors
npm publishcontinues to work alongside staged publishing.npm stage publishhas full parity withnpm publish(respects"private": true, workspace support, etc).Future Work
npm trust, with--allow-publishand--allow-stage-publishflags to control whether a trust relationship can be used fornpm publish,npm stage publish, or both.