diff --git a/app.js b/app.js index bfc125c..9473912 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,8 @@ var http = require('http'), _ = require('underscore'), reader = require('./lib/reader'), notifier = require('./lib/notifier'), - project = require('./lib/project'); + project = require('./lib/project'), + chokidar = require('chokidar'); var staticServer = new nodeStatic.Server('./static'); var server = http.createServer(function(req, res, next) { @@ -73,11 +74,64 @@ Steppy( notifier.init(app.config.notify, this.slot()); }, function() { + // load all projects for the first time project.loadAll(app.config.paths.projects, this.slot()); }, function(err, projects) { + // note that `app.projects` is live variable app.projects = projects; - console.log('Loaded projects: ', _(projects).pluck('name')); + console.log('Loaded projects: ', _(app.projects).pluck('name')); + + // start file watcher for reloading projects on change + var syncProject = function(filename, fileInfo) { + var baseDir = app.config.paths.projects, + projectName = path.relative( + baseDir, + path.dirname(filename) + ); + + var projectIndex = _(app.projects).findIndex(function(project) { + return project.name === projectName; + }); + + if (projectIndex !== -1) { + console.log('Unload project: "' + projectName + '"'); + app.projects.splice(projectIndex, 1); + } + + // on add or change (info is falsy on unlink) + if (fileInfo) { + console.log('Load project "' + projectName + '" on change'); + project.load(baseDir, projectName, function(err, project) { + if (err) { + return console.error( + 'Error during load project "' + projectName + '": ', + err.stack || err + ); + } + app.projects.push(project); + console.log( + 'Project "' + projectName + '" loaded:', + JSON.stringify(project, null, 4) + ); + + }); + } + }; + + // NOTE: currently after add remove and then add same file events will + // not be emitted + var watcher = chokidar.watch( + path.join(app.config.paths.projects, '*', 'config.*'), + {ignoreInitial: true} + ); + watcher.on('add', syncProject); + watcher.on('change', syncProject); + watcher.on('unlink', syncProject); + + watcher.on('error', function(err) { + console.error('File watcher error occurred: ', err.stack || err); + }); // init resources require('./resources')(app); diff --git a/gulpfile.js b/gulpfile.js index 01ee8ca..6e3ff05 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -30,7 +30,7 @@ gulp.task('develop', function() { return nodemon({ ignore: ['static/**/*.js', 'app/**/*.js', 'node_modules/**'], script: 'app.js', - ext: 'js,json,yaml' + ext: 'js' }); }); diff --git a/lib/distributor.js b/lib/distributor.js index 10a2012..53f2bf1 100644 --- a/lib/distributor.js +++ b/lib/distributor.js @@ -21,7 +21,7 @@ function Distributor(params) { callback(null, build); }; - self.projectsHash = _(params.projects).indexBy('name'); + self.projects = params.projects; } inherits(Distributor, EventEmitter); @@ -106,7 +106,13 @@ Distributor.prototype._onBuildComplete = function(err, build, callback) { _(after).each(function(item) { if (!item.status || item.status === build.status) { self.run({ - projectName: item.project + projectName: item.project, + initiator: { + type: 'build', + id: build.id, + number: build.number, + project: {name: build.project.name} + }, }, triggerAfterGroup.slot()); } }); @@ -163,9 +169,10 @@ Distributor.prototype.run = function(params, callback) { project; Steppy( function() { - project = self.projectsHash[params.projectName]; + project = _(self.projects).findWhere({name: params.projectName}); self._updateBuild({}, { project: project, + initiator: params.initiator, params: params.params, createDate: Date.now(), status: 'queued', diff --git a/lib/reader/json.js b/lib/reader/json.js index 591ec95..a6c6e4f 100644 --- a/lib/reader/json.js +++ b/lib/reader/json.js @@ -1,8 +1,10 @@ 'use strict'; -var inherits = require('util').inherits, +var Steppy = require('twostep').Steppy, + inherits = require('util').inherits, ParentReader = require('./base').Reader, - path = require('path'); + path = require('path'), + fs = require('fs'); function Reader() { @@ -16,6 +18,14 @@ exports.Reader = Reader; Reader.prototype.ext = 'json'; Reader.prototype._load = function(dir, name, callback) { - var content = require(path.join(dir, name + '.' + this.ext)); - callback(null, content); + var self = this; + Steppy( + function() { + fs.readFile(path.join(dir, name + '.' + self.ext), 'utf8', this.slot()) + }, + function(err, content) { + this.pass(JSON.parse(content)); + }, + callback + ); }; diff --git a/package.json b/package.json index a4e79cb..fa52fae 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "homepage": "https://github.com/okv/nci", "dependencies": { + "chokidar": "1.0.3", "data.io": "0.3.0", "jade": "1.9.2", "js-yaml": "3.3.1", diff --git a/resources/projects.js b/resources/projects.js index fa5598c..b228a0d 100644 --- a/resources/projects.js +++ b/resources/projects.js @@ -110,7 +110,10 @@ module.exports = function(app) { resource.use('run', function(req, res) { var projectName = req.data.projectName; console.log('Run the project: %s', projectName); - distributor.run({projectName: projectName}, function(err, build) { + distributor.run({ + projectName: projectName, + initiator: {type: 'user'} + }, function(err, build) { console.log('>>> err, build = ', err && err.stack || err, build); }); res.send(); diff --git a/static/js/app/components/builds/view.jade b/static/js/app/components/builds/view.jade index 1251b17..274c85e 100644 --- a/static/js/app/components/builds/view.jade +++ b/static/js/app/components/builds/view.jade @@ -1,8 +1,19 @@ if this.state.build h2 - span Build # + span= this.state.build.project.name + span build # span= this.state.build.number + div + | Initiated by + - var initiator = this.state.build.initiator; + if initiator.type === 'user' + span user + else if initiator.type === 'build' + span= initiator.project.name + span build # + span= initiator.number + if this.state.build.startDate div | Started at diff --git a/test/distributor.js b/test/distributor.js index 0d52980..5329710 100644 --- a/test/distributor.js +++ b/test/distributor.js @@ -62,7 +62,8 @@ describe('Distributor', function() { it('build should be queued', function() { var changes = updateBuildSpy.getCall(0).args[1]; expect(changes).only.have.keys( - 'project', 'params', 'createDate', 'status', 'completed' + 'project', 'initiator', 'params', 'createDate', 'status', + 'completed' ); expect(changes.status).equal('queued'); expect(changes.completed).equal(false);