* only modify root package.json file

previously this was modifying every package.json from node_modules as well

* refactor the build task

export the build, define helpers outside of the task

* allow custom kibana and build versions

both can be passed via flags

* allow custom build globs to be specified

* move build into its own module

* add simple option parsing tests

* update readme

* move dependency file appending into the action

* put source and target into variables

* move config file loading into a module

* refactor test_server_action slightly

be more explicit about the files option overwriting the plugin settings

* move default build patterns to plugin config

allows the setting to be overridden via the config file

* fix dirname on relative includes

trim any leading '../' off the path when moving it into the build target

* move node_module dirs into plugin_config module, use existing promises

* rename file_config => config_file

Original commit: elastic/kibana-plugin-helpers@743e4a37c2
This commit is contained in:
Joe Fleming 2016-12-21 21:37:57 -07:00 committed by Spencer
parent 4cc81e2c89
commit ceb52252be
8 changed files with 248 additions and 149 deletions

View file

@ -16,11 +16,11 @@ $ plugin-helpers help
Commands:
start Start kibana and have it include this plugin
build Build a distributable archive
test Run the server and browser tests
test:browser [options] Run the browser tests in a real web browser
test:server [files...] Run the server tests using mocha
start Start kibana and have it include this plugin
build [options] [files...] Build a distributable archive
test Run the server and browser tests
test:browser [options] Run the browser tests in a real web browser
test:server [files...] Run the server tests using mocha
Options:

View file

@ -31,11 +31,17 @@ enableCollectingUnknownOptions(
);
program
.command('build')
.command('build [files...]')
.description('Build a distributable archive')
.on('--help', docs('build'))
.action(taskRunner(function () {
run('build');
.option('-b, --build-version <version>', 'Version for the build output')
.option('-k, --kibana-version <version>', 'Kibana version for the build output')
.action(taskRunner(function (command, files) {
run('build', {
buildVersion: command.buildVersion,
kibanaVersion: command.kibanaVersion,
files: files,
});
}));
program

View file

@ -0,0 +1,30 @@
var resolve = require('path').resolve;
var readFileSync = require('fs').readFileSync;
var configFiles = [ '.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json' ];
var configCache = {};
module.exports = function (root) {
if (!root) root = process.cwd();
if (configCache[root]) {
return configCache[root];
}
// config files to read from, in the order they are merged together
var config = configCache[root] = {};
configFiles.forEach(function (configFile) {
try {
var content = JSON.parse(readFileSync(resolve(root, configFile)));
config = Object.assign(config, content);
} catch (e) {
// noop
}
});
// if the kibanaRoot is set, use resolve to ensure correct resolution
if (config.kibanaRoot) config.kibanaRoot = resolve(root, config.kibanaRoot);
return config;
};

View file

@ -1,29 +1,32 @@
var resolve = require('path').resolve;
var readFileSync = require('fs').readFileSync;
var configFile = require('./config_file');
module.exports = function (root) {
if (!root) root = process.cwd();
var pkg = require(resolve(root, 'package.json'));
// config files to read from, in the order they are merged together
var configFiles = [ '.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json' ];
var config = {};
var config = configFile(root);
configFiles.forEach(function (configFile) {
try {
var content = JSON.parse(readFileSync(resolve(root, configFile)));
config = Object.assign(config, content);
} catch (e) {
// noop
}
});
var buildSourcePatterns = [
'package.json',
'index.js',
'{lib,public,server,webpackShims}/**/*',
];
// if the kibanaRoot is set, use resolve to ensure correct resolution
if (config.kibanaRoot) config.kibanaRoot = resolve(root, config.kibanaRoot);
// add dependency files
var deps = Object.keys(pkg.dependencies || {});
if (deps.length === 1) {
buildSourcePatterns.push(`node_modules/${ deps[0] }/**/*`);
} else if (deps.length) {
buildSourcePatterns.push(`node_modules/{${ deps.join(',') }}/**/*`);
}
return Object.assign({
root: root,
kibanaRoot: resolve(root, '../kibana'),
serverTestPatterns: ['server/**/__tests__/**/*.js'],
buildSourcePatterns: buildSourcePatterns,
id: pkg.name,
pkg: pkg,
version: pkg.version,

View file

@ -1,122 +1,39 @@
var vfs = require('vinyl-fs');
var zip = require('gulp-zip');
var map = require('through2-map').obj;
var rename = require('gulp-rename');
var join = require('path').join;
var inquirer = require('inquirer');
var execFileSync = require('child_process').execFileSync;
module.exports = function (plugin) {
return new Promise(function (resolve, reject) {
var createBuild = require('./create_build');
function main() {
var kibanaVersion = (plugin.pkg.kibana && plugin.pkg.kibana.version) || plugin.pkg.version;
var deps = Object.keys(plugin.pkg.dependencies || {});
var buildId = `${plugin.id}-${plugin.version}`;
module.exports = function (plugin, run, options) {
options = options || {};
var buildVersion = plugin.version;
var kibanaVersion = (plugin.pkg.kibana && plugin.pkg.kibana.version) || plugin.pkg.version;
var buildFiles = plugin.buildSourcePatterns;
if (kibanaVersion === 'kibana') {
askForKibanaVersion(function (customKibanaVersion) {
build(buildId, deps, customKibanaVersion);
});
} else {
build(buildId, deps, kibanaVersion);
}
}
// allow source files to be overridden
if (options.files && options.files.length) {
buildFiles = options.files;
}
function askForKibanaVersion(cb) {
inquirer.prompt([
{
type: 'input',
name: 'kibanaVersion',
message: 'What version of Kibana are you building for?'
}
]).then(function (answers) {
cb(answers.kibanaVersion);
});
}
// allow options to override plugin info
if (options.buildVersion) buildVersion = options.buildVersion;
if (options.kibanaVersion) kibanaVersion = options.kibanaVersion;
function toBuffer(string) {
if (typeof Buffer.from === 'function') {
return Buffer.from(string, 'utf8');
} else {
// this was deprecated in node v5 in favor
// of Buffer.from(string, encoding)
return new Buffer(string, 'utf8');
}
}
function gitInfo() {
try {
var LOG_SEPARATOR = '||';
var commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
cwd: plugin.root,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
});
var logLine = execFileSync('git', ['log', '--pretty=%h' + LOG_SEPARATOR + '%cD', '-n', '1'], {
cwd: plugin.root,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
}).split(LOG_SEPARATOR);
return {
count: commitCount.trim(),
sha: logLine[0].trim(),
date: logLine[1].trim(),
};
} catch (e) {
return {};
}
}
function build(buildId, deps, kibanaVersion) {
var files = [
'package.json',
'index.js',
'{lib,public,server,webpackShims}/**/*'
];
if (deps.length === 1) {
files.push(`node_modules/${ deps[0] }/**/*`);
} else if (deps.length) {
files.push(`node_modules/{${ deps.join(',') }}/**/*`);
}
vfs
.src(files, { cwd: plugin.root, base: plugin.root })
// modify the package.json file
.pipe(map(function (file) {
if (file.basename === 'package.json') {
var pkg = JSON.parse(file.contents.toString('utf8'));
// rewrite the target kibana version while the
// file is on it's way to the archive
if (!pkg.kibana) pkg.kibana = {};
pkg.kibana.version = kibanaVersion;
// append build info
pkg.build = {
git: gitInfo(),
date: new Date().toString()
};
file.contents = toBuffer(JSON.stringify(pkg, null, 2));
}
return file;
}))
// put all files inside the correct directoried
.pipe(rename(function nestFileInDir(path) {
path.dirname = join('kibana', plugin.id, path.dirname);
}))
.pipe(zip(`${buildId}.zip`))
.pipe(vfs.dest(join(plugin.root, 'build')))
.on('end', resolve);
}
main();
});
if (kibanaVersion === 'kibana') {
return askForKibanaVersion().then(function (customKibanaVersion) {
return createBuild(plugin, buildVersion, customKibanaVersion, buildFiles);
});
} else {
return createBuild(plugin, buildVersion, kibanaVersion, buildFiles);
}
};
function askForKibanaVersion(cb) {
return inquirer.prompt([
{
type: 'input',
name: 'kibanaVersion',
message: 'What version of Kibana are you building for?'
}
]).then(function (answers) {
return answers.kibanaVersion;
});
}

View file

@ -1,24 +1,78 @@
/*eslint-env jest*/
const resolve = require('path').resolve;
const fs = require('fs');
const del = require('del');
const buildAction = require('./build_action');
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/test_plugin');
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build');
const PLUGIN = require('../../lib/plugin_config')(PLUGIN_FIXTURE);
const noop = function () {};
describe('build_action', () => {
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
describe('creating build zip', function () {
const buildAction = require('./build_action');
it('creates a zip in the build directory', () => {
return buildAction(PLUGIN).then(() => {
if (!fs.existsSync(resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip'))) {
throw new Error('expected the plugin to build a zip file');
}
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
it('creates a zip in the build directory', () => {
return buildAction(PLUGIN).then(() => {
var buildFile = resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip');
if (!fs.existsSync(buildFile)) {
throw new Error('Build file not found: ' + buildFile);
}
});
});
});
});
describe('calling create_build', () => {
let mockBuild;
let buildAction;
beforeEach(() => {
jest.resetModules();
mockBuild = jest.fn(() => Promise.resolve());
jest.mock('./create_build', () => mockBuild);
buildAction = require('./build_action');
});
it('takes optional build version', function () {
const options = {
buildVersion: '1.2.3',
kibanaVersion: '4.5.6',
};
return buildAction(PLUGIN, noop, options).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
const [ plugin, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
expect(buildVersion).toBe('1.2.3');
expect(kibanaVersion).toBe('4.5.6');
});
});
it('uses default file list without files option', function () {
return buildAction(PLUGIN).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
const [ plugin, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
PLUGIN.buildSourcePatterns.forEach(file => expect(files).toContain(file));
});
});
it('uses only files passed in', function () {
const options = {
files: [
'index.js',
'LICENSE.txt',
'plugins/**/*',
'{server,public}/**/*'
]
};
return buildAction(PLUGIN, noop, options).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
const [ plugin, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
options.files.forEach(file => expect(files).toContain(file));
});
});
});
});

View file

@ -0,0 +1,84 @@
var join = require('path').join;
var execFileSync = require('child_process').execFileSync;
var vfs = require('vinyl-fs');
var zip = require('gulp-zip');
var map = require('through2-map').obj;
var rename = require('gulp-rename');
module.exports = function createBuild(plugin, buildVersion, kibanaVersion, files) {
var buildId = `${plugin.id}-${buildVersion}`;
var buildSource = plugin.root;
var buildTarget = join(plugin.root, 'build');
return new Promise(function (resolve) {
vfs
.src(files, { cwd: buildSource, base: buildSource })
// modify the package.json file
.pipe(map(function (file) {
if (file.basename === 'package.json' && file.dirname === buildSource) {
var pkg = JSON.parse(file.contents.toString('utf8'));
// rewrite the target kibana version while the
// file is on it's way to the archive
if (!pkg.kibana) pkg.kibana = {};
pkg.kibana.version = kibanaVersion;
pkg.version = buildVersion;
// append build info
pkg.build = {
git: gitInfo(buildSource),
date: new Date().toString()
};
file.contents = toBuffer(JSON.stringify(pkg, null, 2));
}
return file;
}))
// put all files inside the correct directories
.pipe(rename(function nestFileInDir(path) {
var nonRelativeDirname = path.dirname.replace(/^(\.\.\/?)+/g, '');
path.dirname = join('kibana', plugin.id, nonRelativeDirname);
}))
.pipe(zip(`${buildId}.zip`))
.pipe(vfs.dest(buildTarget))
.on('end', resolve);
});
};
function toBuffer(string) {
if (typeof Buffer.from === 'function') {
return Buffer.from(string, 'utf8');
} else {
// this was deprecated in node v5 in favor
// of Buffer.from(string, encoding)
return new Buffer(string, 'utf8');
}
}
function gitInfo(rootPath) {
try {
var LOG_SEPARATOR = '||';
var commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
});
var logLine = execFileSync('git', ['log', '--pretty=%h' + LOG_SEPARATOR + '%cD', '-n', '1'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
}).split(LOG_SEPARATOR);
return {
count: commitCount.trim(),
sha: logLine[0].trim(),
date: logLine[1].trim(),
};
} catch (e) {
return {};
}
}

View file

@ -3,12 +3,17 @@ var delimiter = require('path').delimiter;
var execFileSync = require('child_process').execFileSync;
module.exports = function (plugin, run, options) {
options = options || {};
var kibanaBins = resolve(plugin.kibanaRoot, 'node_modules/.bin');
var mochaSetupJs = resolve(plugin.kibanaRoot, 'test/mocha_setup.js');
options = options || {};
var testPaths = plugin.serverTestPatterns;
// allow server test files to be overridden
if (options.files && options.files.length) {
testPaths = options.files;
}
var cmd = 'mocha';
var testPaths = (options.files && options.files.length) ? options.files : plugin.serverTestPatterns;
var args = ['--require', mochaSetupJs].concat(testPaths);
var path = `${kibanaBins}${delimiter}${process.env.PATH}`;