Compare commits

...
4 Commits
8 changed files with 83 additions and 24 deletions
+2 -2
View File
@@ -17,9 +17,9 @@
"dependencies": {
"@semantic-release/commit-analyzer": "^3.0.1",
"@semantic-release/condition-travis": "^6.0.0",
"@semantic-release/error": "^2.0.0",
"@semantic-release/error": "^2.1.0",
"@semantic-release/last-release-npm": "^2.0.0",
"@semantic-release/release-notes-generator": "^4.0.0",
"@semantic-release/release-notes-generator": "^5.0.0",
"chalk": "^2.3.0",
"commander": "^2.11.0",
"debug": "^3.1.0",
+2 -3
View File
@@ -1,5 +1,4 @@
const program = require('commander');
const SemanticReleaseError = require('@semantic-release/error');
const logger = require('./lib/logger');
function list(values) {
@@ -49,11 +48,11 @@ module.exports = async () => {
} catch (err) {
// If error is a SemanticReleaseError then it's an expected exception case (no release to be done, running on a PR etc..) and the cli will return with 0
// Otherwise it's an unexpected error (configuration issue, code issue, plugin issue etc...) and the cli will return 1
if (err instanceof SemanticReleaseError) {
if (err.semanticRelease) {
logger.log(`%s ${err.message}`, err.code);
} else {
process.exitCode = 1;
logger.error(err);
logger.error('An error occurred while running semantic-release: %O', err);
}
}
};
+8 -3
View File
@@ -6,13 +6,18 @@ const chalk = require('chalk');
module.exports = {
log(...args) {
const [format, ...rest] = args;
console.log(`${chalk.grey('[Semantic release]:')} ${format}`, ...rest.map(arg => chalk.magenta(arg)));
console.log(
`${chalk.grey('[Semantic release]:')}${typeof format === 'string'
? ` ${format.replace(/%[^%]/g, seq => chalk.magenta(seq))}`
: ''}`,
...(typeof format === 'string' ? [] : [format]).concat(rest)
);
},
error(...args) {
const [format, ...rest] = args;
console.error(
`${chalk.grey('[Semantic release]:')} ${chalk.red(format instanceof Error ? format.stack : format)}`,
...rest.map(arg => chalk.red(arg instanceof Error ? arg.stack : arg))
`${chalk.grey('[Semantic release]:')}${typeof format === 'string' ? ` ${chalk.red(format)}` : ''}`,
...(typeof format === 'string' ? [] : [format]).concat(rest)
);
},
};
+3 -1
View File
@@ -1,3 +1,5 @@
module.exports = function(config, options, cb) {
cb(new Error('a'));
const error = new Error('a');
error.errorProperty = 'errorProperty';
cb(error);
};
+14
View File
@@ -0,0 +1,14 @@
const SemanticReleaseError = require('@semantic-release/error');
class InheritedError extends SemanticReleaseError {
constructor(message, code, newProperty) {
super(message);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.code = code;
}
}
module.exports = function(config, options, cb) {
cb(new InheritedError('Inherited error', 'EINHERITED'));
};
+31 -3
View File
@@ -17,6 +17,7 @@ const env = {
const cli = require.resolve('../bin/semantic-release');
const noop = require.resolve('../src/lib/plugin-noop');
const pluginError = require.resolve('./fixtures/plugin-error-a');
const pluginInheritedError = require.resolve('./fixtures/plugin-error-inherited');
test.before(async t => {
await mockServer.start();
@@ -35,7 +36,6 @@ test.beforeEach(async t => {
});
test.afterEach.always(async t => {
console.log();
// Restore process.env
process.env = Object.assign({}, t.context.env);
// Restore the current working directory
@@ -574,7 +574,7 @@ test.serial('Run via JS API', async t => {
await semanticRelease({githubToken, verifyConditions: [noop, noop], debug: true});
t.true(t.context.log.calledWithMatch(/Published Github release: /, new RegExp(`release-url/${version}`)));
t.true(t.context.log.calledWithMatch(/Publishing version %s to npm registry %s/, version, registry.uri));
t.true(t.context.log.calledWithMatch(/Publishing version .* to npm registry/, version, registry.uri));
// Verify package.json and has been updated
t.is((await readJson('./package.json')).version, version);
@@ -615,7 +615,7 @@ test.serial('Returns and error code if NPM token is invalid', async t => {
t.is(code, 1);
});
test.serial('Log unexpected errors from plugins', async t => {
test.serial('Log unexpected errors from plugins and exit with 1', async t => {
const packageName = 'test-module-9';
const repo = 'test-repo';
// Create a git repository, set the current working directory at the root of the repo
@@ -634,11 +634,39 @@ test.serial('Log unexpected errors from plugins', async t => {
await gitCommits(['feat: Initial commit']);
t.log('$ semantic-release');
let {stderr, code} = await execa(cli, [], {env, reject: false});
// Verify the type and message are logged
t.regex(stderr, /Error: a/);
// Verify the the stacktrace is logged
t.regex(stderr, new RegExp(pluginError));
// Verify the Error properties are logged
t.regex(stderr, /errorProperty: 'errorProperty'/);
t.is(code, 1);
});
test.serial('Log errors inheriting SemanticReleaseError and exit with 0', async t => {
const packageName = 'test-module-10';
const repo = 'test-repo';
// Create a git repository, set the current working directory at the root of the repo
t.log('Create git repository and package.json');
await gitRepo();
// Create package.json in repository root
await writeJson('./package.json', {
name: packageName,
version: '0.0.0-dev',
repository: {url: `git+https://github.com/${repo}/${packageName}`},
release: {githubUrl: mockServer.url, verifyConditions: pluginInheritedError},
});
/** Initial release **/
t.log('Commit a feature');
await gitCommits(['feat: Initial commit']);
t.log('$ semantic-release');
let {stdout, code} = await execa(cli, [], {env, reject: false});
// Verify the type and message are logged
t.regex(stdout, /EINHERITED Inherited error/);
t.is(code, 0);
});
test.serial('CLI returns error code and prints help if called with a command', async t => {
t.log('$ semantic-release pre');
let {stdout, code} = await execa(cli, ['pre'], {env, reject: false});
+19 -8
View File
@@ -1,5 +1,5 @@
import test from 'ava';
import {stub} from 'sinon';
import {stub, match} from 'sinon';
import logger from '../src/lib/logger';
test.beforeEach(t => {
@@ -20,18 +20,29 @@ test.serial('Basic log', t => {
t.true(t.context.error.calledWithMatch(/.*test error/));
});
test.serial('Log object', t => {
const obj = {a: 1, b: '2'};
logger.log(obj);
logger.error(obj);
t.true(t.context.log.calledWithMatch(match.string, obj));
t.true(t.context.error.calledWithMatch(match.string, obj));
});
test.serial('Log with string formatting', t => {
logger.log('test log %s', 'log value');
logger.error('test error %s', 'error value');
t.true(t.context.log.calledWithMatch(/.*test log %s/, 'log value'));
t.true(t.context.error.calledWithMatch(/.*test error %s/, 'error value'));
t.true(t.context.log.calledWithMatch(/.*test log/, 'log value'));
t.true(t.context.error.calledWithMatch(/.*test error/, 'error value'));
});
test.serial('Log with error stacktrace', t => {
logger.error(new Error('error message'));
logger.error('test error %s', new Error('other error message'));
test.serial('Log with error stacktrace and properties', t => {
const error = new Error('error message');
logger.error(error);
const otherError = new Error('other error message');
logger.error('test error %O', otherError);
t.true(t.context.error.calledWithMatch(/.*test error %s/, /Error: other error message(\s|.)*?logger\.test\.js/));
t.true(t.context.error.calledWithMatch(/Error: error message(\s|.)*?logger\.test\.js/));
t.true(t.context.error.calledWithMatch(match.string, error));
t.true(t.context.error.calledWithMatch(/.*test error/, otherError));
});
+4 -4
View File
@@ -2,12 +2,12 @@ import test from 'ava';
import SemanticReleaseError from '@semantic-release/error';
import verify from '../src/lib/verify-pkg';
test.only('Verify name and repository', t => {
test('Verify name and repository', t => {
// Call the verify module with package
t.notThrows(() => verify({name: 'package', repository: {url: 'http://github.com/whats/up.git'}}));
});
test.only('Return error for missing package name', t => {
test('Return error for missing package name', t => {
// Call the verify module with package
const error = t.throws(() => verify({repository: {url: 'http://github.com/whats/up.git'}}));
// Verify error code and type
@@ -15,7 +15,7 @@ test.only('Return error for missing package name', t => {
t.true(error instanceof SemanticReleaseError);
});
test.only('Return error for missing repository', t => {
test('Return error for missing repository', t => {
// Call the verify module with package
const error = t.throws(() => verify({name: 'package'}));
// Verify error code and type
@@ -23,7 +23,7 @@ test.only('Return error for missing repository', t => {
t.true(error instanceof SemanticReleaseError);
});
test.only('Return error for missing repository url', t => {
test('Return error for missing repository url', t => {
// Call the verify module with package
const error = t.throws(() => verify({name: 'package', repository: {}}));
// Verify error code and type