UNPKG

cleverstack-cli

Version:
287 lines (245 loc) 7.88 kB
var Promise = require('bluebird') , path = require('path') , async = require('async') , fs = require('fs') , spawn = require('child_process').spawn , readline = require('readline') , os = require('os') , isWin = /^win32/.test(os.platform()); /** * Mimmicks a small grunt-cli utility * * @return {gruntCLI} * @private */ function gruntCLI() { this.tasks = []; this.option = function(key) { return process.env[ key ]; }; this.registerTask = function(cmd) { this.tasks.push(cmd); }; this.loadNpmTasks = function() {}; } /** * Finds the correct Gruntfile name * * @param {String} pathSrc - Directory path where the Gruntfile resides. * @return {Promise} * @api public */ var findGruntFile = exports.findGruntFile = function(pathSrc) { return new Promise(function(resolve, reject) { async.detect([ path.join(pathSrc, 'Gruntfile.js'), path.join(pathSrc, 'gruntfile.js'), path.join(pathSrc, 'Grunt.js'), path.join(pathSrc, 'grunt.js') ], fs.exists, function(filePath) { if (typeof filePath === 'undefined') { return reject('Gruntfile within ' + pathSrc + ' could not be found.'); } resolve(filePath); }); }); }; /** * We're essentially creating a small grunt utility to bypass * grunt functions... this is better than requiring grunt * entirely and trying to regex parse... * * @param {String} filePath * @return {Object} * @api public */ var gruntUtility = exports.gruntUtility = function(filePath) { var cli = new gruntCLI() , file = require(filePath)(cli); if (Array.isArray(file) && typeof file[ file.length-1 ] === 'function') { file[ file.length-1 ].call(file, cli); } return cli.tasks; }; /** * Reads/parses Grunt tasks within pathSrc * * @param {String} pathSrc - Source to the Gruntfile * @param {Boolean} [silence=true] - Silences errors returned from reading the Gruntfile * @return {Promise} * @api public */ var readTasks = exports.readTasks = function(pathSrc, silence) { return new Promise(function(resolve, reject) { if (typeof silence === 'undefined') { silence = true; } findGruntFile(pathSrc) .then(function(filePath) { var tasks = gruntUtility(filePath); resolve([ tasks, pathSrc ]); }) .catch(function (err) { if (err.match(/^Gruntfile within/) !== null && silence === true) { return resolve([ [], pathSrc ]); } reject(err); }); }); }; /** * Runs a grunt task within projectFolder * * @param {String} projectFolder Path to the project's seed folder * @param {String} cmd Command to run within grunt * @return {Promise} Returns a promise from bluebird * @api private */ function runTask(projectFolder, command) { var args = [].slice.call(arguments, 1); return new Promise(function(resolve, reject) { var cmd = !isWin ? 'grunt' : 'grunt.cmd' , env = process.env , opts = {env: env, cwd: projectFolder, stdio: 'inherit'} , isPrompt = /prompt/i.test(args[0]) , paths = process.env.NODE_PATH ? [process.env.NODE_PATH] : []; // Pass the --verbose call down to grunt as well if (!!program.verbose) { args.push('--verbose'); var logMsg = 'Spawning posix child process to run '+lib.colors.lightGreen(cmd+' '+args.join(' ')) + '...'; lib.utils.info(logMsg).running(logMsg); } paths.push(path.resolve(path.join(projectFolder, 'lib')) + path.sep); paths.push(path.resolve(path.join(projectFolder, 'modules')) + path.sep); env.NODE_PATH = paths.join(!!isWin ? ';' : ':'); var posixProc = spawn(cmd, args, opts) , exitHdlr = process.kill.bind(process, posixProc) , error = ''; process.on('SIGTERM', exitHdlr); posixProc.on('error', function(err) { error += err; }); posixProc.on('close', function(code) { process.removeListener('SIGTERM', exitHdlr); if (code !== 0 || !!error) { return reject(error); } if (command.indexOf('prompt')) { lib.utils.progress(); lib.utils.startBar(function() { resolve(); }); } else { resolve(); } }); }); } exports.runTask = runTask; /** * Runs DB migrations from grunt * * @param {String} projectFolder Path to the project's folder * @return {Promise} Returns a promise from bluebird * @api private */ function runDBMigrations(projectFolder) { lib.utils.warn('Running database migrations...'); return new Promise(function(resolve, reject) { var env = process.env , paths = process.env.NODE_PATH ? [process.env.NODE_PATH] : []; paths.push(path.resolve(path.join(projectFolder, 'lib')) + path.sep); paths.push(path.resolve(path.join(projectFolder, 'modules')) + path.sep); env.NODE_PATH = paths.join(!!isWin ? ';' : ':'); // check for NODE_ENV json config file if it doesn't exist then revert to local if (!fs.existsSync(path.join(projectFolder, 'config', env.NOD_ENV + '.json'))) { env.NODE_ENV = 'local'; } var opts = {env: env, cwd: projectFolder} , args = ['db'] , cmd = !isWin ? 'grunt' : 'grunt.cmd'; if (!!program.verbose) { opts.stdio = 'inherit'; } else { args.push('--silent'); } var proc = spawn(cmd, args, opts) , error = ''; proc.on('error', function(err) { error += err; }); proc.on('close', function(code) { if (code !== 0 || !!error) { return reject(error); } lib.utils.progress(); resolve(); }); }); } /** * Runs Grunt tasks specifically for CleverStack * * @param {String} projectFolder Path to the project's backend folder * @param {String} modulePath Path to the module (where to look for the grunt file) * @return {Promise} Promise from bluebird * @api public */ exports.runTasks = function(projectFolder, modulePath) { return new Promise(function(resolve, reject) { var originalCwd = process.cwd(); process.chdir(projectFolder); readTasks(modulePath) .spread(function(tasks) { process.chdir(originalCwd); if (tasks.length) { lib.utils.warn(' Running grunt tasks for module ' + modulePath.split(path.sep).pop() + '...'); } Promise .all(tasks) .then(function() { if (tasks.indexOf('readme') > -1) { lib.utils.expandProgress(1); return runTask(projectFolder, 'readme'); } return true; }) .then(function() { return new Promise(function(resolve, reject) { async.forEachSeries( tasks, function(task, callback) { if (/^prompt:clever.*/.test(task)) { lib.utils.expandProgress(1); lib.utils.stopBar(function() { runTask(projectFolder, task) .then(function() { callback(null); }) .catch(callback); }); } else { callback(null); } }, function(err) { !err ? resolve() : reject(err); } ); }); }) .then(function() { if (tasks.indexOf('db') > -1) { lib.utils.expandProgress(1); return runDBMigrations(projectFolder); } return true; }) .then(function() { resolve(); }, function(err) { reject(err); }); }); }); };