Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
93 changes: 67 additions & 26 deletions lib/drush-node.js
Original file line number Diff line number Diff line change
@@ -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 || {});
Expand All @@ -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();
Expand All @@ -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;

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 0 additions & 1 deletion test/drush_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,3 @@ exports['drush'] = {
});
}
};