Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a spawn method for logging stdout and stderr. #2

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
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});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/\]//

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, how did I not catch that. Sorry!

}
);
```

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.
81 changes: 61 additions & 20 deletions lib/drush-node.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var _ = require('lodash');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var Deferred = require('promised-io/promise').Deferred;

var Drush = {
execOptions: {
maxBuffer: 256 * 1024 * 1024
log: false
}
};

Expand Down Expand Up @@ -53,10 +54,12 @@ 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.
Expand All @@ -65,40 +68,78 @@ Drush.version = function () {
*
* @return Promise
*/
Drush.exec = function (command, options) {
Drush.exec = function (args, options) {
options = options || {};
if (typeof args === 'string') {
args = args.split(' ');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to use a library, like yargs to parse this out since -a -b and -a -b and -a -b are all equivalent but would render different results here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooray for those looking the same... the point is different whitespace would render different results. Also something like -a "wrapped string" would cause problems.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. I'm fixing this now.

}
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);

// Handle echo and cat options.
if (options.echo || options.cat) {
var command = options.echo ? 'echo' : 'cat';
var cmd = spawn(command, options[command].split(' '));

if (options.cat) {
cmd = 'cat ' + options.cat + ' | ' + cmd;
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);
}

def.resolve(stdout);
});
drush.stderr.on('data', function (data) {
output += data;
if (log) {
console.log('' + data);
}
});
drush.on('close', function (code) {
if (code !== 0) {
return def.reject('drush process exited with code ' + code);
}
def.resolve(output);
});

return def.promise;
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'] = {
});
}
};