nci/lib/build.js
2016-01-09 22:23:39 +03:00

222 lines
5.8 KiB
JavaScript

'use strict';
var Steppy = require('twostep').Steppy,
_ = require('underscore'),
EventEmitter = require('events').EventEmitter,
inherits = require('util').inherits;
/**
* Facade entity which accumulates operations with currently running and
* db saved builds.
*/
function BuildsCollection(params) {
this.db = params.db;
this.distributor = params.distributor;
this._proxyDistributorEvent('buildUpdate', 'buildUpdated');
this._proxyDistributorEvent('buildCancel', 'buildCanceled');
this._proxyDistributorEvent('buildLogLines', 'buildLogLines');
}
exports.BuildsCollection = BuildsCollection;
inherits(BuildsCollection, EventEmitter);
BuildsCollection.prototype._proxyDistributorEvent = function(source, dest) {
var self = this;
self.distributor.on(source, function() {
self.emit.apply(self, [dest].concat(_(arguments).toArray()));
});
};
/**
* Create build by running given project.
* - `params.projectName` - project to build
* - `params.withScmChangesOnly` - if true then build will be started only if
* there is scm changes for project
* - `params.queueQueued` - if true then currently queued project can be queued
* again
* - `params.initiator` - contains information about initiator of the build,
* must contain `type` property e.g. when one build triggers another:
* initiator: {type: 'build', id: 123, number: 10, project: {name: 'project1'}
*
* @param {Object} params
* @param {Function} [callback(err)]
*/
BuildsCollection.prototype.create = function(params, callback) {
this.distributor.run(params, callback);
};
/**
* Cancel build by id.
* Note that only queued build can be canceled currently.
*
* @param {Number} id
* @param {Function} [callback(err)]
*/
BuildsCollection.prototype.cancel = function(id, callback) {
this.distributor.cancel(id, callback);
};
/**
* Get build by id.
*
* @param {Number} id
* @param {Function} callback(err,build)
*/
BuildsCollection.prototype.get = function(id, callback) {
this.db.builds.find({start: {id: id}}, function(err, builds) {
callback(err, builds && builds[0]);
});
};
/**
* Get log lines for the given build.
* - `params.buildId` - target build
* - `params.from` - if set then lines from that number will be returned
* - `params.to` - if set then lines to that number will be returned
*
* @param {Object} params
* @param {Function} callback(err,logLinesData)
*/
BuildsCollection.prototype.getLogLines = function(params, callback) {
var self = this;
Steppy(
function() {
var findParams = {
start: {buildId: params.buildId},
end: {buildId: params.buildId}
};
if (params.from) findParams.start.number = params.from;
if (params.to) findParams.end.number = params.to;
var count = params.from && params.to ? params.to - params.from + 1: 0;
self.db.logLines.find(findParams, this.slot());
this.pass(count);
},
function(err, logLines, count) {
this.pass({
lines: logLines,
isLast: count ? logLines.length < count : true
});
},
callback
);
};
BuildsCollection.prototype.getLogLinesTail = function(params, callback) {
var self = this;
Steppy(
function() {
var findParams = {
reverse: true,
start: {buildId: params.buildId},
limit: params.limit
};
self.db.logLines.find(findParams, this.slot());
},
function(err, logLines) {
var lines = logLines.reverse(),
total = logLines.length ? logLines[logLines.length - 1].number : 0;
this.pass({lines: lines, total: total});
},
callback
);
};
/**
* Calculate average build duration for the given builds.
*
* @param {Object[]} builds
*/
BuildsCollection.prototype.getAvgBuildDuration = function(builds) {
var durationsSum = _(builds).reduce(function(sum, build) {
return sum + (build.endDate - build.startDate);
}, 0);
return Math.round(durationsSum / builds.length);
};
/**
* Get builds sorted by date in descending order.
* - `params.projectName` - optional project filter
* - `params.status` - optional status filter, can be used only when
* `params.projectName` is set. When used builds in the result will contain
* only following fields: id, number, startDate, endDate
* - `params.limit` - maximum builds count to get
*
* @param {Object} params
* @param {Function} callback(err,builds)
*/
BuildsCollection.prototype.getRecent = function(params, callback) {
var self = this;
Steppy(
function() {
var findParams = {start: {}, limit: params.limit};
// such condition for match one of projections:
// projectName, descCreateDate
// projectName, status, descCreateDate
// or just descCreateDate projection if project name is not set
if (params.projectName) {
findParams.start.projectName = params.projectName;
if (params.status) findParams.start.status = params.status;
}
findParams.start.descCreateDate = '';
self.db.builds.find(findParams, this.slot());
},
callback
);
};
/**
* Get info about current done builds streak.
* - `params.projectName` - optional project filter
*
* @param {Object} params
* @param {Function} callback(err,doneStreak)
*/
BuildsCollection.prototype.getDoneStreak = function(params, callback) {
var self = this;
Steppy(
function() {
var start = {};
if (params.projectName) start.projectName = params.projectName;
start.descCreateDate = '';
// tricky but effective streak counting inside filter goes below
var doneBuildsStreakCallback = _(this.slot()).once(),
doneBuildsStreak = {buildsCount: 0};
self.db.builds.find({
start: start,
filter: function(build) {
// error exits streak
if (build.status === 'error') {
doneBuildsStreakCallback(null, doneBuildsStreak);
return true;
}
if (build.status === 'done') {
doneBuildsStreak.buildsCount++;
}
},
limit: 1
}, function(err) {
doneBuildsStreakCallback(err, doneBuildsStreak);
});
},
callback
);
};