diff --git a/README.md b/README.md index 16a33e3..43a5063 100644 --- a/README.md +++ b/README.md @@ -9,44 +9,51 @@ var drush = require('drush-node'); drush.init().then( function () { + // Executes `drush updb` and logs the output after the command has completed. drush.exec('updb') .then( function (res) { console.log(res); } ); + + // Executes `drush cc all` and logs the output as the command is running. + drush.exec('cc all', {log: true}); } ); ``` The module is built on top of [promised-io](https://github.com/kriszyp/promised-io) -and exec returns a promise object. Chaining commands can therefore be conveniently -done as follows: +and exec returns a promise object. Chaining commands can therefore be +conveniently done as follows: ```javascript var group = require('promised-io/promise').all([ - drush.init(), + drush.init({log: true}), drush.exec('updb'), drush.exec('fra'), drush.exec('cc all') ]); group.then(function (res) { - console.log(res.join("\r")); + console.log('All commands completed.'); }); ``` -You must call `Drush.init()` before executing other commands, but as long -as the `Drush` object remains in scope you only need to call it once. +You must call `drush.init()` before executing other commands, but as long +as the `drush` object remains in scope you only need to call it once. ### Advanced options -You may pass additional options to the underlaying [exec commands](http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) +You may pass additional options to the underlaying [spawn commands](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) by calling init with a hash of appropriate options: ```javascript -drush.init({ maxBuffer: 256 * 1024 * 1024 }) +drush.init({ detached: true }) ``` -*Note*: You may need to increase the buffer size for commands that return a lot -of output. +*Note*: The `log` option is not an option for the spawn command, but can be +included when calling drush.init(). Specifying this option when calling +drush.init() prevents the need to specify it on every individual call to +drush.exec() in cases where you want all output for every command to be logged +to the terminal. diff --git a/lib/drush-node.js b/lib/drush-node.js index 886ae3c..b6c955e 100644 --- a/lib/drush-node.js +++ b/lib/drush-node.js @@ -1,18 +1,20 @@ var _ = require('lodash'); var exec = require('child_process').exec; +var spawn = require('child_process').spawn; +var argv = require('node-argv'); var Deferred = require('promised-io/promise').Deferred; var Drush = { execOptions: { - maxBuffer: 256 * 1024 * 1024 + log: false } }; /** - * Initializes the drush object by determining the path to the drush - * executable. + * Initializes the drush object by determining path to the drush executable. * - * @return Promise. + * @param {Object} execOptions - child_process.spawn Asynchronous Process Creation options. + * @returns {Promise}. */ Drush.init = function (execOptions) { this.execOptions = _.merge(this.execOptions, execOptions || {}); @@ -32,7 +34,7 @@ Drush.init = function (execOptions) { /** * Fetches the Drush version on the host machine. * - * @return Promise. + * @returns {Promise} upon completion. */ Drush.version = function () { var def = new Deferred(); @@ -53,56 +55,95 @@ Drush.version = function () { /** * Execute a drush command. * - * @param {string} command - * The drush command to execute. + * @param {string|array} args + * The drush command to execute either as a string or an array of arguments, + * e.g. 'cc drush' or ['cc', 'drush']. * @param {object} options * A hash of options to add to the command, can contain: + * - log: flag to log the output of the drush command. * - alias: the drush alias, e.g. "@self" to execute the command with. * - simulate: boolean, simulates all relevant actions. * - uri: the URI of the drupal site to use. * - echo: text to echo to the drush command. * - cat: a file to cat to the drush command. * - * @return Promise + * @returns {Promise} upon completion. */ -Drush.exec = function (command, options) { +Drush.exec = function (args, options) { options = options || {}; + if (typeof args === 'string') { + args = argv(args, {}).input; + } + args = args || []; + var log = typeof options.log !== 'undefined' ? options.log : this.execOptions.log; var def = new Deferred(); - var cmd = Drush.command; - var prop = ''; + var output = ''; + // Prepend the alias argument. if (options.alias) { - cmd += ' ' + options.alias; + args = [options.alias].concat(args); } + // Add simulate flag. if (options.simulate) { - cmd += ' -s'; + args.push('-s'); } + // Add uri arguments. if (options.uri) { - cmd += ' -l ' + options.uri; + args.push('-l'); + args.push(options.uri); } - cmd += ' ' + command + ' -y'; + // Add -y flag to prevent prompts from hanging. + args.push('-y'); - if (options.echo) { - cmd = 'echo ' + options.echo + ' | ' + cmd; - } + // Initialize drush child process. + var drush = spawn(Drush.command, args, this.execOptions); - if (options.cat) { - cmd = 'cat ' + options.cat + ' | ' + cmd; + // Handle echo and cat options. + if (options.echo || options.cat) { + var command = options.echo ? 'echo' : 'cat'; + var cmd = spawn(command, options[command].split(' ')); + + cmd.stdout.on('data', function (data) { + drush.stdin.write(data); + }); + cmd.stderr.on('data', function (data) { + if (log) { + console.log('' + data); + } + }); + cmd.on('close', function (code) { + if (code !== 0) { + return def.reject(command + ' process exited with code ' + code); + } + drush.stdin.end(); + }); } - exec(cmd, this.execOptions, function (err, stdout, stderr) { - if (err) { - return def.reject(err); + // Listen to stdout and stderr streams and resolve the promise when the drush + // process closes. + drush.stdout.on('data', function (data) { + output += data; + if (log) { + console.log(data.toString('utf8').trim()); + } + }); + drush.stderr.on('data', function (data) { + output += data; + if (log) { + console.log(data.toString('utf8').trim()); } - - def.resolve(stdout); + }); + drush.on('close', function (code) { + if (code !== 0) { + return def.reject('drush process exited with code ' + code); + } + def.resolve(output); }); return def.promise; }; module.exports = Drush; - diff --git a/package.json b/package.json index 942776b..2c5c09d 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,9 @@ } ], "dependencies": { - "promised-io": "^0.3.4", - "lodash": "^2.4.1" + "lodash": "^2.4.1", + "node-argv": "0.0.7", + "promised-io": "^0.3.4" }, "devDependencies": { "grunt": "^0.4.4", diff --git a/test/drush_test.js b/test/drush_test.js index f0a08e4..9266d59 100644 --- a/test/drush_test.js +++ b/test/drush_test.js @@ -49,4 +49,3 @@ exports['drush'] = { }); } }; -