Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement Jenkins#consoleStream

  • Loading branch information...
commit a020e365530998e5e25ccfcc222291cf083a8f02 1 parent d1fc78b
@5long 5long authored
Showing with 67 additions and 36 deletions.
  1. +10 −0 lib/console_stream.js
  2. +33 −11 lib/jenkins.js
  3. +24 −25 test/jenkins.js
View
10 lib/console_stream.js
@@ -0,0 +1,10 @@
+var inherits = require('util').inherits,
+ Stream = require('stream').Stream;
+
+function ConsoleStream() {}
+inherits(ConsoleStream, Stream);
+
+ConsoleStream.prototype.readable = true;
+ConsoleStream.prototype.writable = false;
+
+module.exports = ConsoleStream;
View
44 lib/jenkins.js
@@ -5,7 +5,8 @@ var _ = require('underscore'),
dgram = require('dgram'),
feedparser = require('feedparser'),
request = require('request'),
- xml2js = require('xml2js');
+ xml2js = require('xml2js'),
+ ConsoleStream = require('./console_stream');
/**
* class Jenkins
@@ -72,30 +73,36 @@ Jenkins.prototype.build = function (jobName, params, cb) {
};
/**
- * Display build progress console output.
+ * Read a Jenkins job console output as a readable stream.
* Jenkins uses the following headers:
* - x-more-data to determine whether there are still more content coming through
* - x-text-size to determine the starting point of progressive text output
- * Uses process.stdout.write instead of console.log because console.log adds an empty line.
*
* @param {String} jobName: Jenkins job name
* @param {Object} opts: optional interval in milliseconds
* @param {Function} cb: standard cb(err, result) callback
+ * @return {ConsoleStream} Returns readable stream
*/
-Jenkins.prototype.console = function (jobName, opts, cb) {
+Jenkins.prototype.consoleStream = function(jobName, opts, cb) {
if (!cb) {
- cb = opts;
+ if (typeof opts === 'function') {
+ cb = opts;
+ opts = undefined;
+ } else {
+ cb = function(){};
+ }
}
const INTERVAL = 1000;
var url = this.url + '/job/' + jobName + '/lastBuild/logText/progressiveText',
+ stream = new ConsoleStream(),
self = this;
this.opts.queryStrings = { start: 0 }; // the first chunk
function _success(result, cb) {
if (result.body) {
- console.log(result.body);
+ stream.emit('data', result.body);
}
// stream while there are more data
async.whilst(
@@ -117,15 +124,14 @@ Jenkins.prototype.console = function (jobName, opts, cb) {
} else {
result = _result;
if (_result.body) {
- console.log(_result.body);
+ stream.emit('data', _result.body);
}
- setTimeout(function () {
- cb();
- }, (opts && opts.interval) ? opts.interval : INTERVAL);
+ setTimeout(cb, (opts && opts.interval) ? opts.interval : INTERVAL);
}
});
},
function (err) {
+ stream.emit('end');
cb(err);
}
);
@@ -138,7 +144,23 @@ Jenkins.prototype.console = function (jobName, opts, cb) {
this.opts.handlers[200] = _success;
this.opts.handlers[404] = _notFound;
- bag.http.request('get', url, this.opts, cb);
+ process.nextTick(function() {
+ bag.http.request('get', url, self.opts, cb);
+ });
+
+ return stream;
+};
+
+/**
+ * Display build progress console output.
+ *
+ * @param {String} jobName: Jenkins job name
+ * @param {Object} opts: optional interval in milliseconds
+ * @param {Function} cb: standard cb(err, result) callback
+ */
+Jenkins.prototype.console = function (jobName, opts, cb) {
+ var stream = this.consoleStream(jobName, opts, cb);
+ stream.pipe(process.stdout, {end: false});
};
/**
View
49 test/jenkins.js
@@ -133,7 +133,7 @@ buster.testCase('jenkins - build', {
}
});
-buster.testCase('jenkins - console', {
+buster.testCase('jenkins - consoleStream', {
setUp: function () {
this.mockConsole = this.mock(console);
},
@@ -145,27 +145,27 @@ buster.testCase('jenkins - console', {
};
this.stub(bag, 'http', { request: mockRequest });
var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', function (err, result) {
+ jenkins.consoleStream('job1', function (err, result) {
assert.equals(err.message, 'Job job1 does not exist');
assert.equals(result, undefined);
done();
});
},
'should display a single console output when there is no more text': function (done) {
- this.mockConsole.expects('log')
- .once().withExactArgs('Job started by Foo');
var mockRequest = function (method, url, opts, cb) {
assert.equals(method, 'get');
assert.equals(url, 'http://localhost:8080/job/job1/lastBuild/logText/progressiveText');
opts.handlers[200]({ statusCode: 200, body: 'Job started by Foo', headers: { 'x-more-data': 'false' } }, cb);
};
this.stub(bag, 'http', { request: mockRequest });
- var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', function (err, result) {
+ var jenkins = new Jenkins('http://localhost:8080'),
+ read = [];
+ jenkins.consoleStream('job1', function (err, result) {
assert.equals(err, undefined);
assert.equals(result, undefined);
+ assert.equals(read, ['Job started by Foo']);
done();
- });
+ }).on('data', read.push.bind(read));
},
'should not display anything if there is only a single console output with no value (e.g. build step sleeping for several seconds)': function (done) {
this.mockConsole.expects('log').never();
@@ -176,17 +176,15 @@ buster.testCase('jenkins - console', {
};
this.stub(bag, 'http', { request: mockRequest });
var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', function (err, result) {
+ jenkins.consoleStream('job1', function (err, result) {
assert.equals(err, undefined);
assert.equals(result, undefined);
done();
+ }).on('data', function() {
+ assert(false, "There should be no data");
});
},
'should display console output until there is no more text': function (done) {
- this.mockConsole.expects('log')
- .once().withExactArgs('Console output 1');
- this.mockConsole.expects('log')
- .once().withExactArgs('Console output 2');
// only first request uses bag.http.request
var mockBagRequest = function (method, url, opts, cb) {
assert.equals(method, 'get');
@@ -205,16 +203,16 @@ buster.testCase('jenkins - console', {
headers: { 'x-more-data': 'false', 'x-text-size': 20 }
});
this.stub(bag, 'http', { request: mockBagRequest, proxy: function(url) { return 'http://someproxy'; }});
- var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', { interval: 1 }, function (err, result) {
+ var jenkins = new Jenkins('http://localhost:8080'),
+ read = [];
+ jenkins.consoleStream('job1', { interval: 1 }, function (err, result) {
assert.equals(err, undefined);
assert.equals(result, undefined);
+ assert.equals(read, ['Console output 1', 'Console output 2']);
done();
- });
+ }).on('data', read.push.bind(read));
},
'should not display chunked console output when result body is undefined': function (done) {
- this.mockConsole.expects('log')
- .once().withExactArgs('Console output 1');
// only first request uses bag.http.request
var mockBagRequest = function (method, url, opts, cb) {
assert.equals(method, 'get');
@@ -232,16 +230,15 @@ buster.testCase('jenkins - console', {
headers: { 'x-more-data': 'false', 'x-text-size': 20 }
});
this.stub(bag, 'http', { request: mockBagRequest, proxy: function () { return undefined; }});
- var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', { interval: 1 }, function (err, result) {
+ var jenkins = new Jenkins('http://localhost:8080'),
+ read = [];
+ jenkins.consoleStream('job1', { interval: 1 }, function (err, result) {
assert.equals(err, undefined);
assert.equals(result, undefined);
done();
- });
+ }).on('data', read.push.bind(read));
},
'should pass error when chunking console output has an error': function (done) {
- this.mockConsole.expects('log')
- .once().withExactArgs('Console output 1');
// only first request uses bag.http.request
var mockBagRequest = function (method, url, opts, cb) {
assert.equals(method, 'get');
@@ -256,12 +253,14 @@ buster.testCase('jenkins - console', {
var mockRequest = this.mock(request);
mockRequest.expects('get').once().callsArgWith(1, new Error('someerror'));
this.stub(bag, 'http', { request: mockBagRequest, proxy: function () { return undefined; }});
- var jenkins = new Jenkins('http://localhost:8080');
- jenkins.console('job1', { interval: 1 }, function (err, result) {
+ var jenkins = new Jenkins('http://localhost:8080'),
+ read = [];
+ jenkins.consoleStream('job1', { interval: 1 }, function (err, result) {
assert.equals(err.message, 'someerror');
assert.equals(result, undefined);
+ assert.equals(read, ['Console output 1']);
done();
- });
+ }).on('data', read.push.bind(read));
}
});
Please sign in to comment.
Something went wrong with that request. Please try again.