-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Verify latest release
- I verified that the issue exists in the latest pnpm release
pnpm version
v10.3.0
Which area(s) of pnpm are affected? (leave empty if unsure)
CLI
Link to the code that reproduces this issue or a replay of the bug
No response
Reproduction steps
Create the following package.json in a fresh directory:
{
"packageManager": "pnpm@10.3.0",
"name": "@myscope/mypkg",
"version": "0.1.0"
}And run pnpm publish --config.@myscope:registry=https://myscoperegistry.example.com/npm --//myscoperegistry.example.com/npm:_authToken=MYTOKENHERE
Then we see the following although we explicitly specify the custom scoped registry via CLI options:
npm notice Publishing to https://registry.npmjs.org/ with tag latest and default access
Describe the Bug
(Versions to reproduce: pnpm 10.3.0, npm 10.9.2)
I have a scoped npm package I want to publish via CI. Let's say it is @myscope/mypkg.
The most simplest way to pubilsh this package would be via the following "pnpm publish" command:
pnpm publish --@myscope:registry=https://myscoperegistry.example.com/npm --//myscoperegistry.example.com/npm:_authToken=MYTOKENHEREHowever, this does not work with the following error:
ERROR Unknown option: '@myscope:registry'
Did you mean 'registry'? Use "--config.unknown=value" to force an unknown option.
This behavior is documented so it is expected.
However, the problem is that we cannot use --config.@myscope:registry=https://myscoperegistry.example.com/npm neither.
When I run following command:
pnpm publish --config.@myscope:registry=https://myscoperegistry.example.com/npm --//myscoperegistry.example.com/npm:_authToken=MYTOKENHEREIt simply does not use https://myscoperegistry.example.com/npm when publishing.
Proposed solution
We see that --//myscoperegistry.example.com/npm:_authToken=MYTOKENHERE is not treated as an unknown option by pnpm's CLI option validation logic.
For consistency and usability, I think it makes sense to let --@myscope:registry=https://myscoperegistry.example.com/npm pass the validation logic as well.
Also regardless of whether we implement this, I think we should make sure --config.unknown=value propagates to npm CLI invocation used internally in "pnpm publish"
Alternatives considered
There are some working alternatives but none of them are ideal.
Why not npm_config_@myscope:registry environment variable
As explained in the documentation, we can use environment variable to pass the validation logic.
However, the name of the environment variable includes special characters (@ and :) and this environment variable is not reliably passed down to the underlying npm publish invocation for some environment.
To clarify, I found in some environment, the following does not output anything:
env npm_config_@myscope:registry=myval /bin/sh -c env|grep @myscopeThis matters since "the npm binary" (node_modules/.bin/npm) is a shell script with #!/bin/sh to invoke node /path/to/npm-cli.js.
When /bin/sh drops this environment variable, npm cannot recognize this option.
So far I confirmed that this behavior is reproducible when /bin/sh is symlinked to dash. It's also reproducible by installing dash manually (verified on macOS 15.2, dash 0.5.12 installed via Homebrew):
env npm_config_@myscope:registry=myval dash -c env|grep @myscopeWhy not publishConfig in package.json
Using publishConfig like the following actually works fine:
{
"name": "@myscope/mypkg",
"version": "0.1.0",
"publishConfig": {
"@myscope:registry": "https://myscoperegistry.example.com/npm"
}
}
However, this is not ideal when we want to separate all the concerns around "how/where to publish" into a centralized place, which should be "the publish pipieline job".
For example, we might want to use different CI pipelines to publish the same package to different npm registries such as one for regular internal registry, the other for experimental publish.
Why not "pnpm config set" or "npm config set"
We can use these command to set @myscope:registry via ~/.npmrc. This is totally fine when we use isolated CI envionment for each job
However, this is not ideal in general. If we run concurrent jobs on the same physical host which shares ~/.npmrc, it can cause race conditions when different jobs try to publish the same package for different registries.
Why not temporarily rewriting publishConfig or local .npmrc just before the publish
This actually works. However this is not ideal simply because it modifies the file temporarily, which is error prone.
For example, we might accidentally commit the modified file. Also this requires --no-git-checks option.
Expected Behavior
Specifying --config.@myscope:registry=https://myscoperegistry.example.com/npm in pnpm publish should override the scoped registry. Also we might want to allow specifying --@myscope:registry=https://myscoperegistry.example.com/npm without --config. prefix
Which Node.js version are you using?
v23.6.1
Which operating systems have you used?
- macOS
- Windows
- Linux
If your OS is a Linux based, which one it is? (Include the version if relevant)
No response