diff --git a/README.md b/README.md index ac186f2..d76ef25 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ work in progress... * Ability to change build parameters from ui (at least target branch) * Semantic versioning and plugins * Safe id and build numbers generation +* Better tests coverage ## Roadmap diff --git a/app.js b/app.js index 74fbe7f..4d72ec7 100644 --- a/app.js +++ b/app.js @@ -7,7 +7,8 @@ var http = require('http'), fs = require('fs'), Steppy = require('twostep').Steppy, _ = require('underscore'), - reader = require('./lib/reader'); + reader = require('./lib/reader'), + notifier = require('./lib/notifier'); var staticServer = new nodeStatic.Server('./static'); var server = http.createServer(function(req, res, next) { @@ -34,6 +35,7 @@ var app = { app.lib = {}; app.lib.reader = reader; +app.lib.notifier = notifier; Steppy( function() { @@ -58,6 +60,7 @@ Steppy( // register plugins require('./lib/reader/yaml').register(app); + require('./lib/notifier/console').register(app); reader.load(app.config.paths.data, 'config', this.slot()); }, @@ -66,6 +69,8 @@ Steppy( console.log('Server config:', JSON.stringify(app.config, null, 4)); + notifier.init(app.config.notify, this.slot()); + // init resources require('./resources')(app); }, diff --git a/data/config.yaml b/data/config.yaml index c3ae012..88837b5 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -2,3 +2,5 @@ nodes: - type: local maxExecutorsCount: 1 + +notify: {} diff --git a/data/projects/project1/config.yaml b/data/projects/project1/config.yaml index 587821d..d3eb448 100644 --- a/data/projects/project1/config.yaml +++ b/data/projects/project1/config.yaml @@ -6,15 +6,26 @@ scm: repository: ./test/repos/mercurial rev: default +notify: + on: + - success + - fail + to: + console: + # email: + # - oleg.korobenko@gmail.com + # jabber: + # - oleg.korobenko@gmail.com + steps: - cmd: > - echo "long multiline string" && - sleep 2 && - echo "is not a problem when you're using yaml" && - echo "cur dir is `pwd`" - - name: sleep - cmd: sleep 4 + echo "long multiline string" && + sleep 2 && + echo "is not a problem when you're using yaml" && + echo "cur dir is `pwd`" + # - name: sleep + # cmd: sleep 4 - cmd: echo 1 > 1.txt - - cmd: sleep 4 + # - cmd: sleep 4 - cmd: echo 2 > 2.txt - cmd: cat 1.txt 2.txt diff --git a/lib/distributor.js b/lib/distributor.js index da449f2..1a0432a 100644 --- a/lib/distributor.js +++ b/lib/distributor.js @@ -4,7 +4,8 @@ var Steppy = require('twostep').Steppy, _ = require('underscore'), Node = require('./node').Node, EventEmitter = require('events').EventEmitter, - inherits = require('util').inherits; + inherits = require('util').inherits, + notifier = require('./notifier'); function Distributor(params) { @@ -72,6 +73,7 @@ Distributor.prototype._runNext = function(callback) { error: err ? err.message : null }, function(err, build) { + notifier.send(build); // try to run next project from the queue self._runNext(stepCallback); } diff --git a/lib/notifier/base.js b/lib/notifier/base.js new file mode 100644 index 0000000..c21520f --- /dev/null +++ b/lib/notifier/base.js @@ -0,0 +1,18 @@ +'use strict'; + +function Notifier() { +} + +exports.Notifier = Notifier; + +Notifier.prototype.init = function(params, callback) { + callback(); +}; + +/* + * {Object} params.notifyReson + * {Object} params.build + */ +Notifier.prototype.send = function(params, callback) { + callback(); +}; diff --git a/lib/notifier/console.js b/lib/notifier/console.js new file mode 100644 index 0000000..7966fff --- /dev/null +++ b/lib/notifier/console.js @@ -0,0 +1,26 @@ +'use strict'; + +var BaseNotifier = require('./base').Notifier, + inherits = require('util').inherits; + +function Notifier() { +} + +inherits(Notifier, BaseNotifier); + +exports.register = function(app) { + app.lib.notifier.register('console', Notifier); +}; + +exports.Notifier = Notifier; + +Notifier.prototype.send = function(params, callback) { + var build = params.build; + console.log( + 'NOTIFY on %s: build #%s of project %s is %s', + params.notifyReason.strategy, + build.number, + build.project.name, + build.status + ); +}; diff --git a/lib/notifier/index.js b/lib/notifier/index.js new file mode 100644 index 0000000..c4bf281 --- /dev/null +++ b/lib/notifier/index.js @@ -0,0 +1,72 @@ +'use strict'; + +var Steppy = require('twostep').Steppy, + _ = require('underscore'), + utils = require('../utils'); + +var constructors = {}, + instances = {}; + +exports.register = function(type, constructor) { + constructors[type] = constructor; +}; + +exports.init = function(params, callback) { + Steppy( + function() { + var initGroup = this.makeGroup(); + _(constructors).each(function(Constructor, type) { + instances[type] = new Constructor(); + instances[type].init(params[type], initGroup.slot()); + }); + }, + callback + ); +}; + +/* + * Check if this completed build should be notified, then notify + */ +exports.send = function(build, callback) { + callback = callback || utils.logErrorCallback; + 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(); + } + + var strategy = _(notify.on).find(function(strategy) { + if (strategy === 'success') { + return build.status === 'done'; + } else if (strategy === 'fail') { + return build.status === 'error'; + } + }); + + // Nothing to notify about + if (!strategy) { + return callback(); + } + + var notifyGroup = this.makeGroup(); + _(notify.to).each(function(recipients, type) { + if (type in instances) { + instances[type].send({ + build: build, + notifyReason: {strategy: strategy} + }, notifyGroup.slot()); + } else { + throw new Error('Unknown notifier: ' + type); + } + }); + }, + callback + ); +}; diff --git a/lib/utils.js b/lib/utils.js index 3981aef..2d05193 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -10,3 +10,7 @@ exports.prune = function(str, length) { return result.replace(/ $/, words.length ? '...' : ''); }; + +exports.logErrorCallback = function(err) { + if (err) console.error(err.stack || err); +};