nci
Version:
Flexible, open source continuous integration server written in node.js
157 lines (134 loc) • 3.7 kB
JavaScript
;
var Steppy = require('twostep').Steppy,
_ = require('underscore'),
logger = require('../logger')('notifier'),
BaseNotifierTransport = require('./transport/base').Transport,
ConsoleNotifierTransport = require('./transport/console').Transport;
var constructors = {
console: ConsoleNotifierTransport
};
exports.register = function(type, constructor) {
constructors[type] = constructor;
};
exports.BaseNotifierTransport = BaseNotifierTransport;
function Notifier(params) {
this.db = params.db;
this.instances = {};
}
exports.Notifier = Notifier;
Notifier.prototype.init = function(params, callback) {
var self = this;
Steppy(
function() {
var initGroup = this.makeGroup();
_(constructors).each(function(Constructor, type) {
self.instances[type] = new Constructor();
self.instances[type].init(params[type], initGroup.slot());
});
},
callback
);
};
// Returns previous (by number) build from the same project
Notifier.prototype._getPrevBuild = function(build, callback) {
var self = this;
Steppy(
function() {
var envName = build.env && build.env.name;
// get id of prev build
self.db.builds.find({
start: {
projectName: build.project.name,
descCreateDate: ''
},
filter: function(prevBuild) {
var prevEnvName = prevBuild.env && prevBuild.env.name;
return (
(prevBuild.id < build.id) &&
(!envName || envName === prevEnvName)
);
},
limit: 1
}, this.slot());
},
function(err, builds) {
if (builds.length) {
this.pass(builds[0]);
} else {
this.pass(null);
}
},
callback
);
};
/*
* Check if that's completed build should be notified, then notify
*/
Notifier.prototype.send = function(build, callback) {
callback = callback || function(err) {
if (err) {
logger.error('Error during send:', err.stack || err);
}
};
var self = this;
Steppy(
function() {
if (!build.completed) {
throw new Error('Build should be completed before notify');
}
var notify = build.project.notify;
// TODO: move to project validation during load
if (!notify || !notify.on || !notify.to) {
return callback();
}
this.pass(notify);
// get previous build (for some strategies)
if (_(notify.on).intersection(['change', 'fix']).length) {
self._getPrevBuild(build, this.slot());
} else {
this.pass(null);
}
},
function(err, notify, prevBuild) {
var strategy = _(notify.on).find(function(strategy) {
if (strategy === 'done') {
return build.status === 'done';
} else if (strategy === 'error') {
return build.status === 'error';
} else if (strategy === 'canceled') {
return build.status === 'canceled';
} else if (strategy === 'change') {
// notify on status change or about first build
return prevBuild ? build.status !== prevBuild.status: true;
} else if (strategy === 'fix') {
// notify on fix or about first build if it's correct
if (prevBuild) {
return prevBuild.status === 'error' && build.status === 'done';
} else {
return build.status === 'done';
}
}
});
// Nothing to notify about
if (!strategy) {
return callback();
}
var notifyGroup = this.makeGroup();
_(notify.to).each(function(recipients, type) {
logger.log(
'Notify about ' + build.project.name + ' build #' +
build.number + ' "' + strategy + '" via ' + type
);
if (type in self.instances) {
self.instances[type].send({
build: build,
notifyReason: {strategy: strategy}
}, notifyGroup.slot());
} else {
throw new Error('Unknown notifier: ' + type);
}
});
},
callback
);
};