Skip to content

Commit

Permalink
Merge pull request #15 from privatenumber/develop
Browse files Browse the repository at this point in the history
release
  • Loading branch information
privatenumber authored Sep 2, 2021
2 parents 61ad629 + 5db99e5 commit 02131d9
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 23 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ If using [Lerna](https://lerna.js.org/), add this configuration to the respectiv

That's it! Next time you run `npm publish` or `yarn publish` it will automatically publish to all registries configured in your `package.json` `publishConfig` array.

If the registries require authentication, make sure you authenticate with them all using a single `.npmrc` file (toggling via [`npmrc`](https://www.npmjs.com/package/npmrc) will not work).


## 💁‍♀️ FAQ

### Is it possible to authenticate to multiple npm registries with one `.npmrc`?
### Is it possible to authenticate to multiple npm registries with one `.npmrc` file?

[Yes](https://docs.npmjs.com/logging-in-to-an-npm-enterprise-registry-from-the-command-line#logging-in-with-a-scope-configured-to-point-to-an-npm-enterprise-registry).
[Yes](https://docs.npmjs.com/logging-in-to-an-npm-enterprise-registry-from-the-command-line#logging-in-with-a-scope-configured-to-point-to-an-npm-enterprise-registry). This is actually the preferred way because `npm publish` loads the `.npmrc` file at the beginning, so toggling `.npmrc` files via [`npmrc`](https://www.npmjs.com/package/npmrc) during publish (even in npm-multi-publish) does not take effect.

To login to an enterprise/custom registry:

Expand All @@ -69,13 +71,14 @@ To verify authentication on a specific registry:
$ npm whoami --registry=https://registry.company-name.npme.io
```

If you have certs for the respective registries, you can [add multiple certs to your `.npmrc` file](https://docs.npmjs.com/misc/config#ca).


### How can I manage `.npmrc`s configured for multiple registries?

Use [`npmrc`](https://www.npmjs.com/package/npmrc). When `npm-multi-publish` can't authenticate with a registry, it will wait for you to authenticate (eg. by toggling your npmrc or by logging in).
A `.npmrc` file authenticated with multiple registries should include something like this:
```
//registry-a-url/:_authToken=... # Registry A authentication token
//registry-b-url/:_authToken=... # Registry B authentication token
```

If you have certs for the respective registries, you can [add multiple certs to your `.npmrc` file](https://docs.npmjs.com/misc/config#ca).

### How can I test publishing to a registry?
Use [`verdaccio`](https://github.com/verdaccio/verdaccio) to create a local mock npm registry.
Expand Down
6 changes: 3 additions & 3 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ let state;

if (lifeCycleEvent === 'prepublishOnly') {
await prepublishOnly(state);
}

if (lifeCycleEvent === 'postpublish') {
} else if (lifeCycleEvent === 'postpublish') {
await postpublish(state);
} else if (state) { // Called independently without publish hook - cleanup command for failed publish
await restorePkg(state);
}
})().catch(async error => {
if (state) {
Expand Down
9 changes: 8 additions & 1 deletion lib/prepublish-only.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@ const writeJsonFile = require('write-json-file');
const {
MULTI_PUBLISH_FILE,
readJson,
confirmUnauthenticatedRegistries,
waitTillReachable,
waitTillAuthenticated,
} = require('./utils');

async function prepublishOnly(state) {
if (!state) {
const pkg = await readJson('./package.json');
assert(Array.isArray(pkg.publishConfig), 'publishConfig must be an array');
const {publishConfig} = pkg;

assert(Array.isArray(publishConfig), 'publishConfig must be an array');
assert(publishConfig.length > 0, 'publishConfig must have at least one registry');

await confirmUnauthenticatedRegistries(publishConfig);

state = {
pkg,
publishConfigIdx: 0,
Expand Down
58 changes: 51 additions & 7 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const fs = require('graceful-fs');
const {spawnSync} = require('child_process');
const npmFetch = require('npm-registry-fetch');
const prompts = require('prompts');
const rc = require('rc');
const {promisify} = require('util');
const writeJsonFile = require('write-json-file');

Expand All @@ -13,15 +14,57 @@ const MULTI_PUBLISH_FILE = '._multi-publish';

class Exit extends Error {}

const strictSSL = Boolean(Number.parseInt(process.env.NODE_TLS_REJECT_UNAUTHORIZED, 10));

async function confirmUnauthenticatedRegistries(publishConfig) {
const registries = publishConfig.map(({registry}) => registry);

const npmrc = rc('npm');
const npmrcKeys = Object.keys(npmrc);
const npmrcAuthenticated = npmrcKeys.filter(key => key.startsWith('//') && key.endsWith(':_authToken'));

const unauthenticatedRegistries = registries.filter(registry => {
const registryUrl = registry.replace(/^https?:/, '');
return !npmrcAuthenticated.find(authenticatedRegistry => authenticatedRegistry.startsWith(registryUrl));
});

if (unauthenticatedRegistries.length === 0) {
return;
}

const {confirmed} = await prompts({
name: 'confirmed',
type: 'confirm',
message: (
'Your .npmrc doesn\'t seem to authenticated with the following registries:\n' +
`${unauthenticatedRegistries.map(registry => ' - ' + registry).join('\n')}` +
'\n\n(Authenticate with the following command: `npm login --registry=<registry>`)' +
'\n\nWould you like to continue?'
),
initial: false,
});

if (confirmed === true) {
return;
}

throw new Exit();
}

async function waitTillReachable(registry) {
if (await npmFetch('/-/ping', {registry}).catch(() => false)) {
const isReachable = await npmFetch('/', {
registry,
strictSSL,
}).catch(() => false);

if (isReachable) {
return;
}

const {choice} = await prompts({
name: 'choice',
type: 'select',
message: `Unreachable registry: ${registry}\nIs it behind a VPN?`,
message: `Unreachable registry: ${registry}\n Please check your network settings (eg. VPN).\n`,
choices: [
{
title: 'Retry',
Expand All @@ -39,9 +82,7 @@ async function waitTillReachable(registry) {
return waitTillReachable(registry);
}

if (choice === 'exit') {
throw new Exit();
}
throw new Exit();
}

const isAuth = registry => {
Expand Down Expand Up @@ -81,9 +122,11 @@ async function waitTillAuthenticated(registry) {
return waitTillAuthenticated(registry);
}

if (choice === 'exit') {
throw new Exit();
if (choice === 'proceed') {
return;
}

throw new Exit();
}

async function restorePkg({pkg}) {
Expand All @@ -99,6 +142,7 @@ module.exports = {
MULTI_PUBLISH_FILE,
Exit,
readJson,
confirmUnauthenticatedRegistries,
waitTillReachable,
waitTillAuthenticated,
restorePkg,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"npm-registry-fetch": "^11.0.0",
"pacote": "^11.1.11",
"prompts": "^2.4.0",
"rc": "^1.2.8",
"write-json-file": "^4.3.0"
},
"devDependencies": {
Expand Down
7 changes: 2 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 02131d9

Please sign in to comment.