forked from mirrors/probot
Merge pull request #123 from boneskull/issue/99
enable autodiscovery of plugins; closes #99
This commit is contained in:
commit
f639dfc30d
|
@ -66,7 +66,6 @@ function setupTunnel() {
|
|||
}
|
||||
|
||||
const pkgConf = require('pkg-conf');
|
||||
const resolve = require('resolve').sync;
|
||||
const createProbot = require('../');
|
||||
|
||||
const probot = createProbot({
|
||||
|
@ -77,15 +76,14 @@ const probot = createProbot({
|
|||
});
|
||||
|
||||
pkgConf('probot').then(pkg => {
|
||||
program.args.concat(pkg.plugins || []).map(plugin => {
|
||||
try {
|
||||
const path = resolve(plugin, {basedir: process.cwd()});
|
||||
probot.load(require(path))
|
||||
} catch(err) {
|
||||
console.warn(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
const plugins = require('../lib/plugin')(probot);
|
||||
const requestedPlugins = program.args.concat(pkg.plugins || []);
|
||||
|
||||
// if we have explicitly requested plugins, load them; otherwise use autoloading
|
||||
if (requestedPlugins.length) {
|
||||
plugins.load(requestedPlugins);
|
||||
} else {
|
||||
plugins.autoload();
|
||||
}
|
||||
probot.start();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
module.exports = pluginLoaderFactory;
|
||||
|
||||
function pluginLoaderFactory(probot, opts = {}) {
|
||||
if (!probot) {
|
||||
throw new TypeError('expected probot instance');
|
||||
}
|
||||
|
||||
// We could eventually support a different base dir to load plugins from.
|
||||
const basedir = opts.basedir || process.cwd();
|
||||
// These are mostly to ease testing
|
||||
const autoloader = opts.autoloader || require('load-plugins');
|
||||
const resolver = opts.resolver || require('resolve').sync;
|
||||
|
||||
/**
|
||||
* Resolves a plugin by name from the basedir
|
||||
* @param {string} pluginName - Module name of plugin
|
||||
*/
|
||||
function resolvePlugin(pluginName) {
|
||||
try {
|
||||
return resolver(pluginName, {basedir});
|
||||
} catch (err) {
|
||||
err.message = `Failed to resolve plugin "${pluginName}". Is it installed?
|
||||
Original error message:
|
||||
${err.message}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a plugin via filepath or function
|
||||
* @param {string} pluginName - Plugin name (for error messaging)
|
||||
* @param {string|Function} plugin - Path to plugin module or function
|
||||
*/
|
||||
function loadPlugin(pluginName, plugin) {
|
||||
try {
|
||||
probot.load(typeof plugin === 'string' ? require(plugin) : plugin);
|
||||
} catch (err) {
|
||||
err.message = `Failed to load plugin "${pluginName}". This is a problem with the plugin itself; not probot.
|
||||
Original error message:
|
||||
${err.message}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all accessible plugin modules beginning with "probot-"
|
||||
*/
|
||||
function autoload() {
|
||||
const plugins = autoloader('probot-*');
|
||||
Object.keys(plugins).forEach(pluginName => {
|
||||
loadPlugin(pluginName, plugins[pluginName]);
|
||||
probot.robot.log.info(`Automatically loaded plugin: ${pluginName}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an explicit list of plugin modules
|
||||
* @param {string[]} [pluginNames=[]] - List of plugin module names
|
||||
*/
|
||||
function load(pluginNames = []) {
|
||||
pluginNames.forEach(pluginName => {
|
||||
const pluginPath = resolvePlugin(pluginName);
|
||||
loadPlugin(pluginName, pluginPath);
|
||||
probot.robot.log.debug(`Loaded plugin: ${pluginName}`);
|
||||
});
|
||||
}
|
||||
|
||||
return {load, autoload};
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
"github": "^8.1.0",
|
||||
"github-integration": "^1.0.0",
|
||||
"github-webhook-handler": "^0.6.0",
|
||||
"load-plugins": "^2.1.2",
|
||||
"pkg-conf": "^2.0.0",
|
||||
"raven": "^1.2.0",
|
||||
"resolve": "^1.3.2"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module.exports = function () {}
|
|
@ -0,0 +1,93 @@
|
|||
/* eslint prefer-arrow-callback: off */
|
||||
|
||||
const expect = require('expect');
|
||||
const pluginLoaderFactory = require('../lib/plugin');
|
||||
|
||||
const stubPluginPath = require.resolve('./fixtures/plugin/stub-plugin');
|
||||
const basedir = process.cwd();
|
||||
const nullLogger = {};
|
||||
['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(level => {
|
||||
nullLogger[level] = function () { };
|
||||
});
|
||||
|
||||
describe('plugin loader', function () {
|
||||
let probot;
|
||||
let pluginLoader;
|
||||
let autoloader;
|
||||
let autoplugins;
|
||||
let resolver;
|
||||
|
||||
beforeEach(function () {
|
||||
probot = {
|
||||
load: expect.createSpy(),
|
||||
robot: {
|
||||
log: nullLogger
|
||||
}
|
||||
};
|
||||
|
||||
autoplugins = {
|
||||
probotPlugin: expect.createSpy()
|
||||
};
|
||||
|
||||
autoloader = expect.createSpy().andReturn(autoplugins);
|
||||
|
||||
resolver = expect.createSpy().andReturn(stubPluginPath);
|
||||
});
|
||||
|
||||
describe('factory', function () {
|
||||
describe('when no robot provided', function () {
|
||||
it('should throw a TypeError', function () {
|
||||
expect(pluginLoaderFactory).toThrow(TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when robot provided', function () {
|
||||
it('should return an object', function () {
|
||||
expect(pluginLoaderFactory(probot)).toBeA(Object);
|
||||
});
|
||||
});
|
||||
|
||||
describe('autoload()', function () {
|
||||
beforeEach(() => {
|
||||
pluginLoader = pluginLoaderFactory(probot, {autoloader});
|
||||
});
|
||||
|
||||
it('should ask the autoloader for probot-related plugins', function () {
|
||||
pluginLoader.autoload();
|
||||
expect(autoloader).toHaveBeenCalledWith('probot-*');
|
||||
});
|
||||
|
||||
it('should ask the robot to load the plugins', function () {
|
||||
pluginLoader.autoload();
|
||||
expect(probot.load).toHaveBeenCalledWith(autoplugins.probotPlugin);
|
||||
});
|
||||
});
|
||||
|
||||
describe('load()', function () {
|
||||
beforeEach(() => {
|
||||
pluginLoader = pluginLoaderFactory(probot, {resolver});
|
||||
});
|
||||
|
||||
describe('when supplied no plugin names', function () {
|
||||
it('should do nothing', function () {
|
||||
pluginLoader.load();
|
||||
expect(resolver).toNotHaveBeenCalled();
|
||||
expect(probot.load).toNotHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when supplied plugin name(s)', function () {
|
||||
it('should attempt to resolve plugins by name and basedir', function () {
|
||||
pluginLoader.load(['foo', 'bar']);
|
||||
expect(resolver).toHaveBeenCalledWith('foo', {basedir})
|
||||
.toHaveBeenCalledWith('bar', {basedir});
|
||||
});
|
||||
|
||||
it('should ask the robot to load a plugin at its resolved path', function () {
|
||||
pluginLoader.load(['see-stub-for-resolved-path']);
|
||||
expect(probot.load).toHaveBeenCalledWith(require(stubPluginPath));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue