diff --git a/README.md b/README.md index ca3a352..3b83a04 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ work in progress... * ~~YAML project and server(executors count, etc) configs~~ * ~~Persistent build and console output information~~ * ~~Project relations (blocks, triggers, etc)~~ -* Writes to stderr must not break the build +* ~~Writes to stderr must not break the build~~ * Mail and jabber notifications (with commits, current step and error) * ~~Rename notification strategies according to statuses~~ * Work with git diff --git a/lib/command/base.js b/lib/command/base.js index a9431b3..6b7b3e2 100644 --- a/lib/command/base.js +++ b/lib/command/base.js @@ -7,6 +7,8 @@ function Command(params) { params = params || {}; this.emitIn = params.emitIn; this.emitOut = params.emitOut; + this.emitErr = params.emitErr; + this.attachStderr = params.attachStderr; } exports.Command = Command; diff --git a/lib/command/spawn.js b/lib/command/spawn.js index 24174f1..3ed2c54 100644 --- a/lib/command/spawn.js +++ b/lib/command/spawn.js @@ -20,11 +20,11 @@ inherits(Command, ParentCommand); */ Command.prototype.run = function(params, callback) { var self = this, - stdout = self.collectOut ? '' : null; + stdout = self.collectOut ? '' : null, + stderr = self.attachStderr ? '' : null; if (!params.cmd) return callback(new Error('`cmd` is not set')); if (!params.args) return callback(new Error('`args` is not set')); - callback = _(callback).once(); params.options = params.options || {}; params.options.cwd = params.options.cwd || this.cwd; @@ -40,15 +40,16 @@ Command.prototype.run = function(params, callback) { }); cmd.stderr.on('data', function(data) { - callback(new Error('Spawned command outputs to stderr: ' + data)); - cmd.kill(); + if (self.emitErr) self.emit('stderr', data); + if (self.attachStderr) stderr += data; }); cmd.on('close', function(code) { var err = null; - if (code !== 0) err = new Error( - 'Spawned command exits with non-zero code: ' + code - ); + if (code !== 0) { + err = new Error('Spawned command exits with non-zero code: ' + code); + err.stderr = stderr; + } callback(err, stdout); }); diff --git a/lib/distributor.js b/lib/distributor.js index 2f06096..b796bd8 100644 --- a/lib/distributor.js +++ b/lib/distributor.js @@ -74,7 +74,9 @@ Distributor.prototype._runNext = function(callback) { endDate: Date.now(), status: err ? 'error' : 'done', completed: true, - error: err ? err.message : null + error: err ? { + message: err.message, stderr: err.stderr + } : null }, function(err, build) { if (err) { diff --git a/lib/executor/local.js b/lib/executor/local.js index a7de3cd..665079d 100644 --- a/lib/executor/local.js +++ b/lib/executor/local.js @@ -92,7 +92,9 @@ Executor.prototype._runStep = function(params, callback) { var command = createCommand( _({ emitIn: true, - emitOut: true + emitOut: true, + emitErr: true, + attachStderr: true }).extend(params) ); @@ -104,6 +106,10 @@ Executor.prototype._runStep = function(params, callback) { self.emit('data', String(data)); }); + command.on('stderr', function(data) { + self.emit('data', 'stderr: ' + String(data)); + }); + command.run(params, this.slot()) }, callback diff --git a/static/js/app/components/builds/view.jade b/static/js/app/components/builds/view.jade index edb2128..8c6f8f0 100644 --- a/static/js/app/components/builds/view.jade +++ b/static/js/app/components/builds/view.jade @@ -41,7 +41,11 @@ if this.state.build div if this.state.build.error | Error: - span= this.state.build.error + span= this.state.build.error.message + + if this.state.build.error.stderr + div stderr: + pre= this.state.build.error.stderr .row .col-md-8 diff --git a/test/commands/shell.js b/test/commands/shell.js index 751ccad..49ed5ce 100644 --- a/test/commands/shell.js +++ b/test/commands/shell.js @@ -8,7 +8,8 @@ describe('Shell command', function() { var shellCommand; it('Should be created without errors', function() { shellCommand = new ShellCommand({ - emitOut: true + emitOut: true, + attachStderr: true }); }); @@ -40,9 +41,12 @@ describe('Shell command', function() { shellCommand.run({cmd: 'echo1 "Hello world"'}, function(err) { expect(err).ok(); expect(err).an(Error); - // messages are slightly different across the OSes + // messages and codes are slightly different across the OSes // e.g. at linux and macos - expect(err.message).match(/echo1:.*not found/); + expect(err.message).match( + /^Spawned command exits with non-zero code: \d+/ + ); + expect(err.stderr).match(/echo1:.*not found/); expect(std.err).equal(''); expect(std.out).equal(''); done(); diff --git a/test/distributor/main.js b/test/distributor/main.js index 6ebe22a..69d8e86 100644 --- a/test/distributor/main.js +++ b/test/distributor/main.js @@ -15,7 +15,7 @@ describe('Distributor main', function() { expect(distributor.queue).length(conditions.queue.length); expect(build.status).equal(conditions.build.status); if (build.status === 'error') { - expect(build.error).eql(conditions.build.error.message); + expect(build.error.message).eql(conditions.build.error.message); } }; @@ -117,7 +117,7 @@ describe('Distributor main', function() { var changes = updateBuildSpy.getCall(2).args[1]; expect(changes.status).equal('error'); expect(changes.completed).equal(true); - expect(changes.error).equal('Some error'); + expect(changes.error.message).equal('Some error'); }); it('update build called 3 times in total', function() {