Repository: semantic-release/npm
Pull request #10 (2017-11-30 06:44:33)

Support legacy authentication
We are supporting legacy npm authentication as we use [npm-registry-couchapp](https://github.com/npm/npm-registry-couchapp) for integration tests which doesn't support token authentication.
But it wasn't intended to be used in production.

It seems there is several private npm registry solution that supports only legacy authentication.
See semantic-release/semantic-release#500 and #6.

This PR improve our support of the legacy auth and mention it in the documentation.

Fix semantic-release/semantic-release#500

pvdlg: I keep getting a lot of `Couldn't start npm-docker-couchdb after 2 min`.
It happened to me 3 or 4 times. Each time on Node 9. While the Node 8 build was passing...

That's going to be a tricky one to figure out...
gr2m: yeah debugging CI is the worst :(
pvdlg: I check the availability of the the registry [here](https://github.com/semantic-release/npm/blob/master/test/helpers/npm-registry.js#L34).
Basically I wait for `/registry/_design/app` to return a 200...So not sure what could go wrong...

Patch:
diff --git a/README.md b/README.md
index d96afa1..5c311b2 100644
--- a/README.md
+++ b/README.md
@@ -20,11 +20,26 @@ Publish the package on the `npm` registry.
 
 ## Configuration
 
-For each plugin, the `npm` authentication token has to be configured with the environment variable `NPM_TOKEN`.
+### Environment variables
 
-All the plugins are based on `npm` and will use the configuration from `.npmrc`. Any parameter returned by `npm config list` will be used by each plugin.
+The `npm` authentication configuration is **required** and can be set via environment variables.
 
-The registry and dist-tag can be configured in the `package.json` and will take precedence on the configuration in `.npmrc`:
+Both the [token](https://docs.npmjs.com/getting-started/working_with_tokens) and the legacy (`username`, `password` and `email`) authentication are supported. It is recommended to use the [token](https://docs.npmjs.com/getting-started/working_with_tokens) authentication. The legacy authentication is supported as the alternative npm registries [Artifactory](https://www.jfrog.com/open-source/#os-arti) and [npm-registry-couchapp](https://github.com/npm/npm-registry-couchapp) only supports that form of authentication at this point.
+
+| Variable       | Description                                              
+| -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
+| `NPM_TOKEN`    | Npm token created via [npm token create](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) |
+| `NPM_USERNAME` | Npm username created via [npm adduser](https://docs.npmjs.com/cli/adduser) or on [npmjs.com](https://www.npmjs.com)           |
+| `NPM_PASSWORD` | Password of the npm user.                                                                                                     |
+| `NPM_EMAIL`    | Email address associated with the npm user                                                                                    |
+
+Use either `NPM_TOKEN` for token authentication or `NPM_USERNAME`, `NPM_PASSWORD` and `NPM_EMAIL` for legacy authentication
+
+### Options
+
+The plugins are based on `npm` and will use the configuration from [`.npmrc`](https://docs.npmjs.com/files/npmrc). See [npm config](https://docs.npmjs.com/misc/config) for the option list.
+
+The [`registry`](https://docs.npmjs.com/misc/registry) and [`dist-tag`](https://docs.npmjs.com/cli/dist-tag) can be configured in the `package.json` and will take precedence over the configuration in `.npmrc`:
 ```json
 {
   "publishConfig": {
@@ -33,6 +48,9 @@ The registry and dist-tag can be configured in the `package.json` and will take
   }
 }
 ```
+
+### Usage
+
 The plugins are used by default by [semantic-release](https://github.com/semantic-release/semantic-release) so no specific configuration is requiered to use them.
 
 Each individual plugin can be disabled, replaced or used with other plugins in the `package.json`:
diff --git a/index.js b/index.js
index 862d6b1..09ac6de 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,4 @@
+const setLegacyToken = require('./lib/set-legacy-token');
 const getPkg = require('./lib/get-pkg');
 const verifyNpm = require('./lib/verify');
 const publishNpm = require('./lib/publish');
@@ -6,12 +7,14 @@ const getLastReleaseNpm = require('./lib/get-last-release');
 let verified;
 
 async function verifyConditions(pluginConfig, {logger}) {
+  setLegacyToken();
   const pkg = await getPkg();
   await verifyNpm(pkg, logger);
   verified = true;
 }
 
 async function getLastRelease(pluginConfig, {logger}) {
+  setLegacyToken();
   // Reload package.json in case a previous external step updated it
   const pkg = await getPkg();
   if (!verified) {
@@ -22,6 +25,7 @@ async function getLastRelease(pluginConfig, {logger}) {
 }
 
 async function publish(pluginConfig, {nextRelease: {version}, logger}) {
+  setLegacyToken();
   // Reload package.json in case a previous external step updated it
   const pkg = await getPkg();
   if (!verified) {
diff --git a/lib/set-legacy-token.js b/lib/set-legacy-token.js
new file mode 100644
index 0000000..e9ded7b
--- /dev/null
+++ b/lib/set-legacy-token.js
@@ -0,0 +1,8 @@
+module.exports = () => {
+  // Set the environment variable `LEGACY_TOKEN` when user use the legacy auth, so it can be resolved by npm CLI
+  if (process.env.NPM_USERNAME && process.env.NPM_PASSWORD && process.env.NPM_EMAIL) {
+    process.env.LEGACY_TOKEN = Buffer.from(`${process.env.NPM_USERNAME}:${process.env.NPM_PASSWORD}`, 'utf8').toString(
+      'base64'
+    );
+  }
+};
diff --git a/lib/set-npmrc-auth.js b/lib/set-npmrc-auth.js
index 902016a..f86e14b 100644
--- a/lib/set-npmrc-auth.js
+++ b/lib/set-npmrc-auth.js
@@ -11,12 +11,7 @@ module.exports = async (registry, logger) => {
     return;
   }
   if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
-    // Using the old auth token format is not considered part of the public API
-    // This might go away anytime (i.e. once we have a better testing strategy)
-    await appendFile(
-      './.npmrc',
-      `\n_auth = ${Buffer.from(`${NPM_USERNAME}:${NPM_PASSWORD}`, 'utf8').toString('base64')}\nemail = \${NPM_EMAIL}`
-    );
+    await appendFile('./.npmrc', `\n_auth = ${Buffer.from(`\${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`)}`);
     logger.log('Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to .npmrc.');
   } else if (NPM_TOKEN) {
     await appendFile('./.npmrc', `\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);


Test patch:
diff --git a/test/integration.test.js b/test/integration.test.js
index d219c8d..9e8c211 100644
--- a/test/integration.test.js
+++ b/test/integration.test.js
@@ -11,11 +11,11 @@ let processStderr;
 let processStdout;
 
 test.before(async () => {
-  // Start the local NPM registry
-  await npmRegistry.start();
   // Disable npm logger during tests
   processStderr = stub(process.stderr, 'write');
   processStdout = stub(process.stdout, 'write');
+  // Start the local NPM registry
+  await npmRegistry.start();
 });
 
 test.beforeEach(t => {
diff --git a/test/set-npmrc-auth.test.js b/test/set-npmrc-auth.test.js
index 5dd4fd0..938fe71 100644
--- a/test/set-npmrc-auth.test.js
+++ b/test/set-npmrc-auth.test.js
@@ -45,12 +45,7 @@ test.serial('Set auth with "NPM_USERNAME", "NPM_PASSWORD" and "NPM_EMAIL"', asyn
   await setNpmrcAuth('http://custom.registry.com', t.context.logger);
 
   const npmrc = (await readFile('.npmrc')).toString();
-  t.regex(
-    npmrc,
-    new RegExp(
-      `_auth = ${Buffer.from('npm_username:npm_pasword', 'utf8').toString('base64')}\\W+email = \\\${NPM_EMAIL}`
-    )
-  );
+  t.is(npmrc, `\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
 
   t.true(t.context.log.calledWith('Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to .npmrc.'));
 });