feat: Expect plugins to return Promises

BREAKING CHANGE: Each plugin is expected to return an async function or a Promise returning function. The callback parameter is not passed to plugins anymore.
This commit is contained in:
Pierre Vanduynslager 2017-11-23 04:34:21 -05:00
parent d4c7605f68
commit 5bec59b26b
9 changed files with 37 additions and 60 deletions

View File

@ -210,8 +210,6 @@ module.exports = function (pluginConfig, config, callback) {}
- `options`: `semantic-release` options like `debug`, or `branch` - `options`: `semantic-release` options like `debug`, or `branch`
- `pkg`: Parsed `package.json` - `pkg`: Parsed `package.json`
- For certain plugins the `config` object contains even more information. See below. - For certain plugins the `config` object contains even more information. See below.
- `callback`: If an error occurs pass it as first argument. Otherwise pass your result as second argument.
### `analyzeCommits` ### `analyzeCommits`

View File

@ -1,4 +1,4 @@
const {promisify, inspect} = require('util'); const {inspect} = require('util');
const {isString, isObject, isFunction, noop, cloneDeep} = require('lodash'); const {isString, isObject, isFunction, noop, cloneDeep} = require('lodash');
const importFrom = require('import-from'); const importFrom = require('import-from');
@ -14,9 +14,9 @@ module.exports = (pluginType, pluginConfig, logger, validator) => {
let func; let func;
if (isFunction(plugin)) { if (isFunction(plugin)) {
func = promisify(plugin.bind(null, cloneDeep(config))); func = plugin.bind(null, cloneDeep(config));
} else if (isObject(plugin) && plugin[pluginType] && isFunction(plugin[pluginType])) { } else if (isObject(plugin) && plugin[pluginType] && isFunction(plugin[pluginType])) {
func = promisify(plugin[pluginType].bind(null, cloneDeep(config))); func = plugin[pluginType].bind(null, cloneDeep(config));
} else { } else {
throw new Error( throw new Error(
`The ${pluginType} plugin must be a function, or an object with a function in the property ${pluginType}.` `The ${pluginType} plugin must be a function, or an object with a function in the property ${pluginType}.`

View File

@ -1,21 +1,8 @@
module.exports = { module.exports = {
verifyConditions(config, options, cb) { verifyConditions: () => {},
cb(); getLastRelease: () => {},
}, analyzeCommits: () => {},
verifyRelease: () => {},
getLastRelease(config, options, cb) { generateNotes: () => {},
cb(); publish: () => {},
},
analyzeCommits(config, options, cb) {
cb();
},
verifyRelease(config, options, cb) {
cb();
},
generateNotes(config, options, cb) {
cb();
},
publish(config, options, cb) {
cb();
},
}; };

View File

@ -9,6 +9,6 @@ class InheritedError extends SemanticReleaseError {
} }
} }
module.exports = function(config, options, cb) { module.exports = () => {
cb(new InheritedError('Inherited error', 'EINHERITED')); throw new InheritedError('Inherited error', 'EINHERITED');
}; };

View File

@ -1,5 +1,5 @@
module.exports = function(config, options, cb) { module.exports = () => {
const error = new Error('a'); const error = new Error('a');
error.errorProperty = 'errorProperty'; error.errorProperty = 'errorProperty';
cb(error); throw error;
}; };

View File

@ -1,3 +1 @@
module.exports = (config, options, cb) => { module.exports = () => {};
cb(null);
};

View File

@ -1,3 +1 @@
module.exports = function(pluginConfig, options, cb) { module.exports = (pluginConfig, options) => ({pluginConfig, options});
cb(null, {pluginConfig, options});
};

View File

@ -1,4 +1,3 @@
import {callbackify} from 'util';
import test from 'ava'; import test from 'ava';
import {writeJson} from 'fs-extra'; import {writeJson} from 'fs-extra';
import proxyquire from 'proxyquire'; import proxyquire from 'proxyquire';
@ -57,12 +56,12 @@ test.serial('Plugins are called with expected values', async t => {
const options = { const options = {
branch: 'master', branch: 'master',
verifyConditions: [callbackify(verifyConditions1), callbackify(verifyConditions2)], verifyConditions: [verifyConditions1, verifyConditions2],
getLastRelease: callbackify(getLastRelease), getLastRelease,
analyzeCommits: callbackify(analyzeCommits), analyzeCommits,
verifyRelease: callbackify(verifyRelease), verifyRelease,
generateNotes: callbackify(generateNotes), generateNotes,
publish: callbackify(publish), publish,
}; };
const pkg = {name, version: '0.0.0-dev'}; const pkg = {name, version: '0.0.0-dev'};
normalizeData(pkg); normalizeData(pkg);
@ -140,12 +139,12 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre
const options = { const options = {
branch: 'master', branch: 'master',
verifyConditions: callbackify(stub().resolves()), verifyConditions: stub().resolves(),
getLastRelease: callbackify(stub().resolves(lastRelease)), getLastRelease: stub().resolves(lastRelease),
analyzeCommits: callbackify(stub().resolves(nextRelease.type)), analyzeCommits: stub().resolves(nextRelease.type),
verifyRelease: callbackify(stub().resolves()), verifyRelease: stub().resolves(),
generateNotes: callbackify(generateNotes), generateNotes,
publish: [callbackify(publish1), callbackify(publish2)], publish: [publish1, publish2],
}; };
await writeJson('./package.json', {}); await writeJson('./package.json', {});
@ -188,12 +187,12 @@ test.serial('Dry-run skips verifyConditions and publish', async t => {
const options = { const options = {
dryRun: true, dryRun: true,
branch: 'master', branch: 'master',
verifyConditions: callbackify(verifyConditions), verifyConditions,
getLastRelease: callbackify(getLastRelease), getLastRelease,
analyzeCommits: callbackify(analyzeCommits), analyzeCommits,
verifyRelease: callbackify(verifyRelease), verifyRelease,
generateNotes: callbackify(generateNotes), generateNotes,
publish: callbackify(publish), publish,
}; };
const pkg = {name, version: '0.0.0-dev'}; const pkg = {name, version: '0.0.0-dev'};
normalizeData(pkg); normalizeData(pkg);

View File

@ -1,4 +1,3 @@
import {callbackify} from 'util';
import test from 'ava'; import test from 'ava';
import {noop} from 'lodash'; import {noop} from 'lodash';
import {stub, match} from 'sinon'; import {stub, match} from 'sinon';
@ -39,7 +38,7 @@ test('Normalize and load plugin that retuns multiple functions', t => {
test('Wrap plugin in a function that validate the output of the plugin', async t => { test('Wrap plugin in a function that validate the output of the plugin', async t => {
const pluginFunction = stub().resolves(1); const pluginFunction = stub().resolves(1);
const plugin = normalize('', callbackify(pluginFunction), t.context.logger, { const plugin = normalize('', pluginFunction, t.context.logger, {
validator: output => output === 1, validator: output => output === 1,
message: 'The output must be 1', message: 'The output must be 1',
}); });
@ -53,7 +52,7 @@ test('Wrap plugin in a function that validate the output of the plugin', async t
test('Plugin is called with "pluginConfig" (omitting "path") and input', async t => { test('Plugin is called with "pluginConfig" (omitting "path") and input', async t => {
const pluginFunction = stub().resolves(); const pluginFunction = stub().resolves();
const conf = {path: callbackify(pluginFunction), conf: 'confValue'}; const conf = {path: pluginFunction, conf: 'confValue'};
const plugin = normalize('', conf, t.context.logger); const plugin = normalize('', conf, t.context.logger);
await plugin('param'); await plugin('param');
@ -61,9 +60,8 @@ test('Plugin is called with "pluginConfig" (omitting "path") and input', async t
}); });
test('Prevent plugins to modify "pluginConfig"', async t => { test('Prevent plugins to modify "pluginConfig"', async t => {
const pluginFunction = stub().callsFake((pluginConfig, options, cb) => { const pluginFunction = stub().callsFake(pluginConfig => {
pluginConfig.conf.subConf = 'otherConf'; pluginConfig.conf.subConf = 'otherConf';
cb();
}); });
const conf = {path: pluginFunction, conf: {subConf: 'originalConf'}}; const conf = {path: pluginFunction, conf: {subConf: 'originalConf'}};
const plugin = normalize('', conf, t.context.logger); const plugin = normalize('', conf, t.context.logger);
@ -73,9 +71,8 @@ test('Prevent plugins to modify "pluginConfig"', async t => {
}); });
test('Prevent plugins to modify its input', async t => { test('Prevent plugins to modify its input', async t => {
const pluginFunction = stub().callsFake((pluginConfig, options, cb) => { const pluginFunction = stub().callsFake((pluginConfig, options) => {
options.param.subParam = 'otherParam'; options.param.subParam = 'otherParam';
cb();
}); });
const input = {param: {subParam: 'originalSubParam'}}; const input = {param: {subParam: 'originalSubParam'}};
const plugin = normalize('', pluginFunction, t.context.logger); const plugin = normalize('', pluginFunction, t.context.logger);