CLI fixes for installing and removing services.

This commit is contained in:
Braydon Fuller 2015-09-02 18:30:35 -04:00
parent 5e532d4b78
commit 52e80039d8
8 changed files with 77 additions and 66 deletions

View File

@ -58,20 +58,23 @@ function main() {
});
program
.command('add <modules...>')
.command('add <services...>')
.alias('install')
.description('Install a module for the current node')
.action(function(modules){
.description('Install a service for the current node')
.action(function(services){
var configInfo = findConfig(process.cwd());
if (!configInfo) {
throw new Error('Could not find configuration, see `bitcore-node create --help`');
}
var opts = {
path: configInfo.path,
modules: modules
services: services
};
add(opts, function() {
console.log('Successfully added module(s):', modules.join(', '));
add(opts, function(err) {
if (err) {
throw err;
}
console.log('Successfully added services(s):', services.join(', '));
});
}).on('--help', function() {
console.log(' Examples:');
@ -82,20 +85,23 @@ function main() {
});
program
.command('remove <modules...>')
.command('remove <services...>')
.alias('uninstall')
.description('Uninstall a module for the current node')
.action(function(modules){
.description('Uninstall a service for the current node')
.action(function(services){
var configInfo = findConfig(process.cwd());
if (!configInfo) {
throw new Error('Could not find configuration, see `bitcore-node create --help`');
}
var opts = {
path: configInfo.path,
modules: modules
services: services
};
remove(opts, function() {
console.log('Successfully removed module(s):', modules.join(', '));
remove(opts, function(err) {
if (err) {
throw err;
}
console.log('Successfully removed services(s):', services.join(', '));
});
}).on('--help', function() {
console.log(' Examples:');

View File

@ -147,11 +147,6 @@ Node.prototype._instantiateService = function(service) {
config.node = this;
var mod = new service.module(config);
$.checkState(
mod instanceof BaseService,
'Unexpected module instance type for service:' + service.name
);
// include in loaded services
this.services[service.name] = mod;

View File

@ -94,6 +94,11 @@ function add(options, done) {
if (err) {
return next(err);
}
// TODO: get the name of the package from the updated package.json
// to be able to support other types of installation such as
// hosted git urls
// add service to bitcore-node.json
addConfig(bitcoreConfigPath, service, next);
});

View File

@ -10,12 +10,12 @@ var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
/**
* Will remove a module from bitcore-node.json
* Will remove a service from bitcore-node.json
* @param {String} configFilePath - The absolute path to the configuration file
* @param {String} module - The name of the module
* @param {String} service - The name of the module
* @param {Function} done
*/
function removeConfig(configFilePath, module, done) {
function removeConfig(configFilePath, service, done) {
$.checkArgument(path.isAbsolute(configFilePath), 'An absolute path is expected');
fs.readFile(configFilePath, function(err, data) {
if (err) {
@ -23,17 +23,17 @@ function removeConfig(configFilePath, module, done) {
}
var config = JSON.parse(data);
$.checkState(
Array.isArray(config.modules),
'Configuration file is expected to have a modules array.'
Array.isArray(config.services),
'Configuration file is expected to have a services array.'
);
// remove the module from the configuration
for (var i = 0; i < config.modules.length; i++) {
if (config.modules[i] === module) {
config.modules.splice(i, 1);
// remove the service from the configuration
for (var i = 0; i < config.services.length; i++) {
if (config.services[i] === service) {
config.services.splice(i, 1);
}
}
config.modules = _.unique(config.modules);
config.modules.sort(function(a, b) {
config.services = _.unique(config.services);
config.services.sort(function(a, b) {
return a > b;
});
fs.writeFile(configFilePath, JSON.stringify(config, null, 2), done);
@ -41,16 +41,16 @@ function removeConfig(configFilePath, module, done) {
}
/**
* Will uninstall a Node.js module and remove from package.json.
* Will uninstall a Node.js service and remove from package.json.
* @param {String} configDir - The absolute configuration directory path
* @param {String} module - The name of the module
* @param {String} service - The name of the service
* @param {Function} done
*/
function uninstallModule(configDir, module, done) {
function uninstallService(configDir, service, done) {
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
$.checkArgument(_.isString(module), 'A string is expected for the module argument');
$.checkArgument(_.isString(service), 'A string is expected for the service argument');
var child = spawn('npm', ['uninstall', module, '--save'], {cwd: configDir});
var child = spawn('npm', ['uninstall', service, '--save'], {cwd: configDir});
child.stdout.on('data', function(data) {
process.stdout.write(data);
@ -62,7 +62,7 @@ function uninstallModule(configDir, module, done) {
child.on('close', function(code) {
if (code !== 0) {
return done(new Error('There was an error uninstalling module: ' + module));
return done(new Error('There was an error uninstalling service(s): ' + service));
} else {
return done();
}
@ -70,26 +70,26 @@ function uninstallModule(configDir, module, done) {
}
/**
* Will remove a Node.js module if it is installed.
* Will remove a Node.js service if it is installed.
* @param {String} configDir - The absolute configuration directory path
* @param {String} module - The name of the module
* @param {String} service - The name of the service
* @param {Function} done
*/
function removeModule(configDir, module, done) {
function removeService(configDir, service, done) {
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
$.checkArgument(_.isString(module), 'A string is expected for the module argument');
$.checkArgument(_.isString(service), 'A string is expected for the service argument');
// check if the module is installed
// check if the service is installed
npm.load(function(err) {
if (err) {
return done(err);
}
npm.commands.ls([module], true /*silent*/, function(err, data, lite) {
npm.commands.ls([service], true /*silent*/, function(err, data, lite) {
if (err) {
return done(err);
}
if (lite.dependencies) {
uninstallModule(configDir, module, done);
uninstallService(configDir, service, done);
} else {
done();
}
@ -99,10 +99,10 @@ function removeModule(configDir, module, done) {
}
/**
* Will remove the Node.js module and from the bitcore-node configuration.
* Will remove the Node.js service and from the bitcore-node configuration.
* @param {String} options.cwd - The current working directory
* @param {String} options.dirname - The bitcore-node configuration directory
* @param {Array} options.modules - An array of strings of module names
* @param {Array} options.services - An array of strings of service names
* @param {Function} done - A callback function called when finished
*/
function remove(options, done) {
@ -112,10 +112,10 @@ function remove(options, done) {
_.isString(options.path) && path.isAbsolute(options.path),
'An absolute path is expected'
);
$.checkArgument(Array.isArray(options.modules));
$.checkArgument(Array.isArray(options.services));
var configPath = options.path;
var modules = options.modules;
var services = options.services;
var bitcoreConfigPath = path.resolve(configPath, 'bitcore-node.json');
var packagePath = path.resolve(configPath, 'package.json');
@ -127,15 +127,15 @@ function remove(options, done) {
}
async.eachSeries(
modules,
function(module, next) {
// if the module is installed remove it
removeModule(configPath, module, function(err) {
services,
function(service, next) {
// if the service is installed remove it
removeService(configPath, service, function(err) {
if (err) {
return next(err);
}
// remove module to bitcore-node.json
removeConfig(bitcoreConfigPath, module, next);
// remove service to bitcore-node.json
removeConfig(bitcoreConfigPath, service, next);
});
}, done
);

View File

@ -20,11 +20,15 @@ log.debug = function() {};
* }
* ]
* @param {Function} req - The require function to use
* @param {Array} cwd - The local path (for requiring services)
* @param {Object} config
* @param {Array} config.services - An array of strings of service names.
* @returns {Array}
*/
function setupServices(req, config) {
function setupServices(req, cwd, config) {
module.paths.push(path.resolve(cwd, './node_modules'));
var services = [];
if (config.services) {
for (var i = 0; i < config.services.length; i++) {
@ -160,7 +164,7 @@ function registerExitHandlers(proc, node) {
function start(options) {
var fullConfig = _.clone(options.config);
fullConfig.services = setupServices(require, options.config);
fullConfig.services = setupServices(require, options.path, options.config);
fullConfig.datadir = path.resolve(options.path, options.config.datadir);
var node = new BitcoreNode(fullConfig);

View File

@ -47,14 +47,14 @@
"async": "^1.3.0",
"bindings": "^1.2.1",
"bitcore": "^0.13.0",
"colors": "^1.1.2",
"body-parser": "^1.13.3",
"colors": "^1.1.2",
"commander": "^2.8.1",
"errno": "^0.1.4",
"express": "^4.13.3",
"leveldown": "^1.4.1",
"levelup": "^1.2.1",
"liftoff": "^2.1.0",
"express": "^4.13.3",
"memdown": "^1.0.0",
"mkdirp": "0.5.0",
"nan": "1.3.0",

View File

@ -15,7 +15,7 @@ describe('#remove', function() {
var testDir = path.resolve(basePath, 'temporary-test-data');
var startConfig = {
name: 'My Node',
modules: ['a', 'b', 'c']
services: ['a', 'b', 'c']
};
var startPackage = {};
@ -56,7 +56,7 @@ describe('#remove', function() {
it('will give an error if expected files do not exist', function(done) {
remove({
path: path.resolve(testDir, 's0'),
modules: ['b']
services: ['b']
}, function(err) {
should.exist(err);
err.message.match(/^Invalid state/);
@ -64,7 +64,7 @@ describe('#remove', function() {
});
});
it('will update bitcore-node.json modules', function(done) {
it('will update bitcore-node.json services', function(done) {
var spawn = sinon.stub().returns({
stdout: {
on: sinon.stub()
@ -89,12 +89,12 @@ describe('#remove', function() {
});
removetest({
path: path.resolve(testDir, 's0/s1/'),
modules: ['b']
services: ['b']
}, function(err) {
should.not.exist(err);
var configPath = path.resolve(testDir, 's0/s1/bitcore-node.json');
var config = JSON.parse(fs.readFileSync(configPath));
config.modules.should.deep.equal(['a', 'c']);
config.services.should.deep.equal(['a', 'c']);
done();
});
});
@ -125,10 +125,10 @@ describe('#remove', function() {
removetest({
path: path.resolve(testDir, 's0/s1/'),
modules: ['b']
services: ['b']
}, function(err) {
should.exist(err);
err.message.should.equal('There was an error uninstalling module: b');
err.message.should.equal('There was an error uninstalling service(s): b');
done();
});
});

View File

@ -9,6 +9,7 @@ var start = require('../../lib/scaffold/start');
describe('#start', function() {
describe('#setupServices', function() {
var cwd = process.cwd();
var setupServices = proxyquire('../../lib/scaffold/start', {}).setupServices;
it('will require an internal module', function() {
function InternalService() {}
@ -28,7 +29,7 @@ describe('#start', function() {
}
}
};
var services = setupServices(testRequire, config);
var services = setupServices(testRequire, cwd, config);
services[0].name.should.equal('internal');
services[0].config.should.deep.equal({param: 'value'});
services[0].module.should.equal(InternalService);
@ -53,7 +54,7 @@ describe('#start', function() {
var config = {
services: ['local']
};
var services = setupServices(testRequire, config);
var services = setupServices(testRequire, cwd, config);
services[0].name.should.equal('local');
services[0].module.should.equal(LocalService);
});
@ -78,7 +79,7 @@ describe('#start', function() {
var config = {
services: ['local']
};
var services = setupServices(testRequire, config);
var services = setupServices(testRequire, cwd, config);
services[0].name.should.equal('local');
services[0].module.should.equal(LocalService);
});
@ -91,7 +92,7 @@ describe('#start', function() {
services: ['bitcoind']
};
(function() {
setupServices(testRequire, config);
setupServices(testRequire, cwd, config);
}).should.throw('Could not load service');
});
});