Repository: semantic-release/semantic-release
Pull request #676 (2018-02-15 06:08:27)

fix: verify branch first
Verify the branch is the release branch before doing any other verification.
Otherwise the plugin and Git authentication are verified when building on other branches. If the branch is in a cloned repo (if building pushes from PR's) the environment variables will not be available and semantic-release will wrongly report the authentication error.


Patch:
diff --git a/index.js b/index.js
index bb1f73f..e7c77d7 100644
--- a/index.js
+++ b/index.js
@@ -29,10 +29,17 @@ async function run(options, plugins) {
     return;
   }
 
-  if (!await verify(options, branch, logger)) {
-    return;
+  if (branch !== options.branch) {
+    logger.log(
+      `This test run was triggered on the branch ${branch}, while semantic-release is configured to only publish from ${
+        options.branch
+      }, therefore a new version won’t be published.`
+    );
+    return false;
   }
 
+  await verify(options);
+
   logger.log('Run automated release from branch %s', options.branch);
 
   logger.log('Call plugin %s', 'verify-conditions');
diff --git a/lib/verify.js b/lib/verify.js
index f0a85a8..3b1e171 100644
--- a/lib/verify.js
+++ b/lib/verify.js
@@ -3,7 +3,7 @@ const AggregateError = require('aggregate-error');
 const {isGitRepo, verifyAuth, verifyTagName} = require('./git');
 const getError = require('./get-error');
 
-module.exports = async (options, branch, logger) => {
+module.exports = async options => {
   const errors = [];
 
   if (!await isGitRepo()) {
@@ -29,15 +29,4 @@ module.exports = async (options, branch, logger) => {
   if (errors.length > 0) {
     throw new AggregateError(errors);
   }
-
-  if (branch !== options.branch) {
-    logger.log(
-      `This test run was triggered on the branch ${branch}, while semantic-release is configured to only publish from ${
-        options.branch
-      }, therefore a new version won’t be published.`
-    );
-    return false;
-  }
-
-  return true;
 };


Test patch:
diff --git a/test/index.test.js b/test/index.test.js
index f1b7310..2059538 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -302,9 +302,9 @@ test.serial('Log all "verifyConditions" errors', async t => {
     './lib/logger': t.context.logger,
     'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
   });
-  const errors = await t.throws(semanticRelease(options));
+  const errors = [...(await t.throws(semanticRelease(options)))];
 
-  t.deepEqual(Array.from(errors), [error1, error2, error3]);
+  t.deepEqual(errors, [error1, error2, error3]);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 2', 'ERR2']);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 3', 'ERR3']);
   t.deepEqual(t.context.error.args[t.context.error.args.length - 1], [
@@ -345,9 +345,9 @@ test.serial('Log all "verifyRelease" errors', async t => {
     './lib/logger': t.context.logger,
     'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
   });
-  const errors = await t.throws(semanticRelease(options));
+  const errors = [...(await t.throws(semanticRelease(options)))];
 
-  t.deepEqual(Array.from(errors), [error1, error2]);
+  t.deepEqual(errors, [error1, error2]);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 1', 'ERR1']);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 2', 'ERR2']);
   t.is(fail.callCount, 1);
@@ -428,9 +428,9 @@ test.serial('Dry-run skips fail', async t => {
     './lib/logger': t.context.logger,
     'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
   });
-  const errors = await t.throws(semanticRelease(options));
+  const errors = [...(await t.throws(semanticRelease(options)))];
 
-  t.deepEqual(Array.from(errors), [error1, error2]);
+  t.deepEqual(errors, [error1, error2]);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 1', 'ERR1']);
   t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 2', 'ERR2']);
   t.is(fail.callCount, 0);
@@ -785,7 +785,7 @@ test.serial('Throw SemanticReleaseError if repositoryUrl is not set and cannot b
     './lib/logger': t.context.logger,
     'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
   });
-  const errors = Array.from(await t.throws(semanticRelease()));
+  const errors = [...(await t.throws(semanticRelease()))];
 
   // Verify error code and type
   t.is(errors[0].code, 'ENOREPOURL');
diff --git a/test/verify.test.js b/test/verify.test.js
index d8cfd42..78d8892 100644
--- a/test/verify.test.js
+++ b/test/verify.test.js
@@ -1,5 +1,4 @@
 import test from 'ava';
-import {stub} from 'sinon';
 import tempy from 'tempy';
 import verify from '../lib/verify';
 import {gitRepo} from './helpers/git-utils';
@@ -9,17 +8,13 @@ const envBackup = Object.assign({}, process.env);
 // Save the current working diretory
 const cwd = process.cwd();
 
-test.beforeEach(t => {
+test.beforeEach(() => {
   // Delete environment variables that could have been set on the machine running the tests
   delete process.env.GIT_CREDENTIALS;
   delete process.env.GH_TOKEN;
   delete process.env.GITHUB_TOKEN;
   delete process.env.GL_TOKEN;
   delete process.env.GITLAB_TOKEN;
-  // Stub the logger functions
-  t.context.log = stub();
-  t.context.error = stub();
-  t.context.logger = {log: t.context.log, error: t.context.error};
 });
 
 test.afterEach.always(() => {
@@ -32,7 +27,7 @@ test.afterEach.always(() => {
 test.serial('Throw a AggregateError', async t => {
   await gitRepo();
 
-  const errors = Array.from(await t.throws(verify({}, 'master', t.context.logger)));
+  const errors = [...(await t.throws(verify({})))];
 
   t.is(errors[0].name, 'SemanticReleaseError');
   t.is(errors[0].code, 'ENOREPOURL');
@@ -46,7 +41,7 @@ test.serial('Throw a SemanticReleaseError if does not run on a git repository',
   const dir = tempy.directory();
   process.chdir(dir);
 
-  const errors = Array.from(await t.throws(verify({}, 'master', t.context.logger)));
+  const errors = [...(await t.throws(verify({})))];
 
   t.is(errors[0].name, 'SemanticReleaseError');
   t.is(errors[0].code, 'ENOGITREPO');
@@ -56,7 +51,7 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" is not valid', asyn
   const repositoryUrl = await gitRepo(true);
   const options = {repositoryUrl, tagFormat: `?\${version}`};
 
-  const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
+  const errors = [...(await t.throws(verify(options, 'master', t.context.logger)))];
 
   t.is(errors[0].name, 'SemanticReleaseError');
   t.is(errors[0].code, 'EINVALIDTAGFORMAT');
@@ -66,7 +61,7 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" does not contains t
   const repositoryUrl = await gitRepo(true);
   const options = {repositoryUrl, tagFormat: 'test'};
 
-  const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
+  const errors = [...(await t.throws(verify(options, 'master', t.context.logger)))];
 
   t.is(errors[0].name, 'SemanticReleaseError');
   t.is(errors[0].code, 'ETAGNOVERSION');
@@ -76,22 +71,15 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" contains multiple "
   const repositoryUrl = await gitRepo(true);
   const options = {repositoryUrl, tagFormat: `\${version}v\${version}`};
 
-  const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
+  const errors = [...(await t.throws(verify(options)))];
 
   t.is(errors[0].name, 'SemanticReleaseError');
   t.is(errors[0].code, 'ETAGNOVERSION');
 });
 
-test.serial('Return "false" if the current branch is not the once configured', async t => {
-  const repositoryUrl = await gitRepo(true);
-  const options = {repositoryUrl, tagFormat: `v\${version}`, branch: 'master'};
-
-  t.false(await verify(options, 'other', t.context.logger));
-});
-
 test.serial('Return "true" if all verification pass', async t => {
   const repositoryUrl = await gitRepo(true);
   const options = {repositoryUrl, tagFormat: `v\${version}`, branch: 'master'};
 
-  t.true(await verify(options, 'master', t.context.logger));
+  await t.notThrows(verify(options));
 });