Repository: semantic-release/npm
Pull request #25 (2017-12-21 17:46:44)

Skip npm auth verification if npmPublish is false
Follow up to #12

See [comment](https://github.com/shroudedcode/semantic-release-apm/issues/4#issuecomment-353403804)


Patch:
diff --git a/index.js b/index.js
index dac2ee8..19f447e 100644
--- a/index.js
+++ b/index.js
@@ -1,18 +1,17 @@
 const {castArray} = require('lodash');
 const setLegacyToken = require('./lib/set-legacy-token');
 const getPkg = require('./lib/get-pkg');
-const verifyNpm = require('./lib/verify');
+const verifyNpmConfig = require('./lib/verify-config');
+const verifyNpmAuth = require('./lib/verify-auth');
 const publishNpm = require('./lib/publish');
 const getLastReleaseNpm = require('./lib/get-last-release');
 
 let verified;
 
-async function verifyConditions(pluginConfig, {options, logger}) {
+async function verifyConditions(pluginConfig, {options: {publish, getLastRelease}, logger}) {
   // If the npm publish plugin is used and has `npmPublish`, `tarballDir` or `pkgRoot` configured, validate them now in order to prevent any release if the configuration is wrong
-  if (options.publish) {
-    const publishPlugin = castArray(options.publish).find(
-      config => config.path && config.path === '@semantic-release/npm'
-    );
+  if (publish) {
+    const publishPlugin = castArray(publish).find(config => config.path && config.path === '@semantic-release/npm');
     if (publishPlugin && publishPlugin.npmPublish) {
       pluginConfig.npmPublish = publishPlugin.npmPublish;
     }
@@ -23,9 +22,18 @@ async function verifyConditions(pluginConfig, {options, logger}) {
       pluginConfig.pkgRoot = publishPlugin.pkgRoot;
     }
   }
-  setLegacyToken();
+
   const pkg = await getPkg(pluginConfig.pkgRoot);
-  await verifyNpm(pluginConfig, pkg, logger);
+  await verifyNpmConfig(pluginConfig, pkg, logger);
+
+  // Verify the npm authentication only if `npmPublish` is not false and if the npm plugin is used as `getLastRelease`
+  if (
+    pluginConfig.npmPublish !== false ||
+    (getLastRelease && (getLastRelease === '@semantic-release/npm' || getLastRelease.path === '@semantic-release/npm'))
+  ) {
+    setLegacyToken();
+    await verifyNpmAuth(pluginConfig, pkg, logger);
+  }
   verified = true;
 }
 
@@ -42,7 +50,8 @@ async function getLastRelease(pluginConfig, {options, logger}) {
         pluginConfig.pkgRoot = publishPlugin.pkgRoot;
       }
     }
-    await verifyNpm(pluginConfig, pkg, logger);
+    await verifyNpmConfig(pluginConfig, pkg, logger);
+    await verifyNpmAuth(pluginConfig, pkg, logger);
     verified = true;
   }
   return getLastReleaseNpm(pkg, logger);
@@ -53,7 +62,8 @@ async function publish(pluginConfig, {nextRelease: {version}, logger}) {
   // Reload package.json in case a previous external step updated it
   const pkg = await getPkg(pluginConfig.pkgRoot);
   if (!verified) {
-    await verifyNpm(pluginConfig, pkg, logger);
+    await verifyNpmConfig(pluginConfig, pkg, logger);
+    await verifyNpmAuth(pluginConfig, pkg, logger);
     verified = true;
   }
   await publishNpm(pluginConfig, pkg, version, logger);
diff --git a/lib/verify-auth.js b/lib/verify-auth.js
new file mode 100644
index 0000000..9486989
--- /dev/null
+++ b/lib/verify-auth.js
@@ -0,0 +1,14 @@
+const execa = require('execa');
+const SemanticReleaseError = require('@semantic-release/error');
+const getRegistry = require('./get-registry');
+const setNpmrcAuth = require('./set-npmrc-auth');
+
+module.exports = async (pluginConfig, pkg, logger) => {
+  const registry = await getRegistry(pkg.publishConfig, pkg.name);
+  await setNpmrcAuth(registry, logger);
+  try {
+    await execa('npm', ['whoami', '--registry', registry]);
+  } catch (err) {
+    throw new SemanticReleaseError('Invalid npm token.', 'EINVALIDNPMTOKEN');
+  }
+};
diff --git a/lib/verify.js b/lib/verify-config.js
similarity index 57%
rename from lib/verify.js
rename to lib/verify-config.js
index d585734..5f0bf02 100644
--- a/lib/verify.js
+++ b/lib/verify-config.js
@@ -1,10 +1,7 @@
 const {isString, isUndefined, isBoolean} = require('lodash');
-const execa = require('execa');
 const SemanticReleaseError = require('@semantic-release/error');
-const getRegistry = require('./get-registry');
-const setNpmrcAuth = require('./set-npmrc-auth');
 
-module.exports = async ({npmPublish, tarballDir, pkgRoot}, pkg, logger) => {
+module.exports = async ({npmPublish, tarballDir, pkgRoot}) => {
   if (!isUndefined(npmPublish) && !isBoolean(npmPublish)) {
     throw new SemanticReleaseError('The "npmPublish" options, if defined, must be a Boolean.', 'EINVALIDNPMPUBLISH');
   }
@@ -16,12 +13,4 @@ module.exports = async ({npmPublish, tarballDir, pkgRoot}, pkg, logger) => {
   if (!isUndefined(pkgRoot) && !isString(pkgRoot)) {
     throw new SemanticReleaseError('The "pkgRoot" options, if defined, must be a String.', 'EINVALIDPKGROOT');
   }
-
-  const registry = await getRegistry(pkg.publishConfig, pkg.name);
-  await setNpmrcAuth(registry, logger);
-  try {
-    await execa('npm', ['whoami', '--registry', registry]);
-  } catch (err) {
-    throw new SemanticReleaseError('Invalid npm token.', 'EINVALIDNPMTOKEN');
-  }
 };


Test patch:
diff --git a/test/integration.test.js b/test/integration.test.js
index 52ffa39..2a73204 100644
--- a/test/integration.test.js
+++ b/test/integration.test.js
@@ -48,6 +48,13 @@ test.after.always(async () => {
   await npmRegistry.stop();
 });
 
+test.serial('Skip npm auth verification if "npmPublish" is false', async t => {
+  process.env.NPM_TOKEN = 'wrong_token';
+  const pkg = {name: 'published', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
+  await outputJson('./package.json', pkg);
+  await t.notThrows(t.context.m.verifyConditions({npmPublish: false}, {options: {}, logger: t.context.logger}));
+});
+
 test.serial('Throws error if NPM token is invalid', async t => {
   process.env.NPM_TOKEN = 'wrong_token';
   const pkg = {name: 'published', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
@@ -62,6 +69,50 @@ test.serial('Throws error if NPM token is invalid', async t => {
   t.regex(npmrc, /:_authToken/);
 });
 
+test.serial(
+  'Throws error if NPM token is invalid if "npmPublish" is false and npm plugin used for "getLastRelease"',
+  async t => {
+    process.env.NPM_TOKEN = 'wrong_token';
+    const pkg = {name: 'published', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
+    await outputJson('./package.json', pkg);
+    const error = await t.throws(
+      t.context.m.verifyConditions(
+        {npmPublish: false},
+        {options: {getLastRelease: '@semantic-release/npm'}, logger: t.context.logger}
+      )
+    );
+
+    t.true(error instanceof SemanticReleaseError);
+    t.is(error.code, 'EINVALIDNPMTOKEN');
+    t.is(error.message, 'Invalid npm token.');
+
+    const npmrc = (await readFile('.npmrc')).toString();
+    t.regex(npmrc, /:_authToken/);
+  }
+);
+
+test.serial(
+  'Throws error if NPM token is invalid if "npmPublish" is false and npm plugin used for "getLastRelease" as an object',
+  async t => {
+    process.env.NPM_TOKEN = 'wrong_token';
+    const pkg = {name: 'published', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
+    await outputJson('./package.json', pkg);
+    const error = await t.throws(
+      t.context.m.verifyConditions(
+        {npmPublish: false},
+        {options: {getLastRelease: {path: '@semantic-release/npm'}}, logger: t.context.logger}
+      )
+    );
+
+    t.true(error instanceof SemanticReleaseError);
+    t.is(error.code, 'EINVALIDNPMTOKEN');
+    t.is(error.message, 'Invalid npm token.');
+
+    const npmrc = (await readFile('.npmrc')).toString();
+    t.regex(npmrc, /:_authToken/);
+  }
+);
+
 test.serial('Verify npm auth and package', async t => {
   Object.assign(process.env, npmRegistry.authEnv);
   const pkg = {name: 'valid-token', publishConfig: {registry: npmRegistry.url}};
diff --git a/test/verify.test.js b/test/verify-config.test.js
similarity index 81%
rename from test/verify.test.js
rename to test/verify-config.test.js
index faa03ee..f4cd953 100644
--- a/test/verify.test.js
+++ b/test/verify-config.test.js
@@ -1,6 +1,6 @@
 import test from 'ava';
 import {stub} from 'sinon';
-import verify from '../lib/verify';
+import verify from '../lib/verify-config';
 
 test.beforeEach(t => {
   // Stub the logger functions
@@ -8,6 +8,10 @@ test.beforeEach(t => {
   t.context.logger = {log: t.context.log};
 });
 
+test('Verify "npmPublish", "tarballDir" and "pkgRoot" options', async t => {
+  await t.notThrows(verify({npmPublish: true, tarballDir: 'release', pkgRoot: 'dist'}, {}, t.context.logger));
+});
+
 test('Throw SemanticReleaseError if "npmPublish" option is not a Boolean', async t => {
   const npmPublish = 42;
   const error = await t.throws(verify({npmPublish}, {}, t.context.logger));