nci/app.js

250 lines
6.4 KiB
JavaScript
Raw Normal View History

2015-04-10 19:23:52 +00:00
'use strict';
var env = process.env.NODE_ENV || 'development',
db = require('./db'),
http = require('http'),
2015-05-12 21:07:03 +00:00
nodeStatic = require('node-static'),
path = require('path'),
2015-05-18 18:25:08 +00:00
fs = require('fs'),
Steppy = require('twostep').Steppy,
_ = require('underscore'),
2015-05-21 21:09:16 +00:00
reader = require('./lib/reader'),
2015-06-13 18:59:32 +00:00
notifier = require('./lib/notifier'),
2015-06-14 13:40:20 +00:00
project = require('./lib/project'),
2015-07-05 18:03:58 +00:00
libLogger = require('./lib/logger'),
EventEmitter = require('events').EventEmitter;
2015-04-10 19:23:52 +00:00
var app = new EventEmitter(),
logger = libLogger('app'),
2015-12-06 20:46:24 +00:00
httpApi;
2015-07-05 18:03:58 +00:00
var staticPath = path.join(__dirname, 'static'),
2015-12-08 19:09:46 +00:00
staticServer = new nodeStatic.Server(staticPath),
staticDataServer;
var server = http.createServer(function(req, res) {
if (req.url.indexOf('/api/') === 0) {
return httpApi(req, res);
}
2015-12-08 19:09:46 +00:00
if (new RegExp('^/projects/(\\w|-)+/workspace').test(req.url)) {
return staticDataServer.serve(req, res);
}
2015-04-10 20:17:03 +00:00
if (req.url.indexOf('/data.io.js') === -1) {
2015-12-17 19:41:51 +00:00
if (/(js|css|fonts|images)/.test(req.url)) {
2015-04-10 19:23:52 +00:00
staticServer.serve(req, res);
} else {
// serve index for all app pages
if (env === 'development') {
var jade = require('jade');
// Compile a function
var index = jade.compileFile(__dirname + '/views/index.jade');
res.write(index({env: env}));
res.end();
} else {
// serve index for all other pages (/builds/:id, etc)
fs.createReadStream(path.join(staticPath, 'index.html'))
.pipe(res);
}
2015-04-10 19:23:52 +00:00
}
}
});
var socketio = require('socket.io')(server);
var dataio = require('./dataio')(socketio);
2015-04-10 19:23:52 +00:00
app.server = server;
app.dataio = dataio;
2015-04-10 19:23:52 +00:00
2015-05-18 20:27:02 +00:00
app.lib = {};
app.lib.reader = reader;
2015-05-21 21:09:16 +00:00
app.lib.notifier = notifier;
2015-07-05 18:03:58 +00:00
app.lib.logger = libLogger;
2015-05-12 21:07:03 +00:00
2015-07-20 20:57:41 +00:00
var configDefaults = {
2015-07-20 21:01:55 +00:00
notify: {},
2015-08-18 21:29:30 +00:00
http: {host: '127.0.0.1', port: 3000, url: 'http://127.0.0.1:3000'}
2015-07-20 20:57:41 +00:00
};
var completeUncompletedBuilds = function(callback) {
Steppy(
function() {
db.builds.find({
start: {descCreateDate: ''},
limit: 100
}, this.slot());
},
function(err, lastBuilds) {
var uncompletedBuilds = _(lastBuilds).filter(function(lastBuild) {
return !lastBuild.completed;
});
var completeGroup = this.makeGroup();
if (uncompletedBuilds.length) {
var queuedAndOtherUncompletedBuilds = _(uncompletedBuilds).partition(
function(uncompletedBuild) {
return uncompletedBuild.status === 'queued';
}
);
var queuedBuilds = queuedAndOtherUncompletedBuilds[0];
uncompletedBuilds = queuedAndOtherUncompletedBuilds[1];
if (queuedBuilds.length) {
logger.log(
'remove queued builds: %s',
_(queuedBuilds).pluck('id').join(', ')
);
db.builds.del(queuedBuilds, completeGroup.slot());
}
if (uncompletedBuilds.length) {
logger.log(
'complete with interrupt error uncompleted builds: %s',
_(uncompletedBuilds).pluck('id').join(', ')
);
_(uncompletedBuilds).each(function(uncompletedBuild) {
var endDate = (
uncompletedBuild.startDate ||
uncompletedBuild.createDate
);
var sumDuration = _(uncompletedBuild.stepTimings).reduce(
function(sum, timing) {
return sum + timing.duration;
},
0
) || 0;
endDate += sumDuration;
db.builds.update(
{id: uncompletedBuild.id},
{
endDate: endDate,
status: 'error',
completed: true,
error: {message: 'interrupted by server restart'}
},
completeGroup.slot()
);
});
}
}
},
callback
);
};
2015-05-18 18:25:08 +00:00
Steppy(
function() {
app.config = {};
app.config.paths = {};
// path to root dir (with projects, builds etc)
app.config.paths.data = path.join(process.cwd(), 'data');
2015-12-08 19:09:46 +00:00
staticDataServer = new nodeStatic.Server(app.config.paths.data);
2015-05-18 18:25:08 +00:00
app.config.paths.projects = path.join(app.config.paths.data, 'projects');
2015-07-12 12:14:28 +00:00
app.config.paths.db = path.join(app.config.paths.data, 'db');
app.config.paths.preload = path.join(app.config.paths.data, 'preload.json');
2015-11-22 07:55:44 +00:00
var dbDirExistsCallback = this.slot();
fs.exists(app.config.paths.db, function(isExists) {
dbDirExistsCallback(null, isExists);
});
var preloadExistsCallback = this.slot();
fs.exists(app.config.paths.preload, function(isExists) {
preloadExistsCallback(null, isExists);
2015-05-18 18:25:08 +00:00
});
},
2015-11-22 07:55:44 +00:00
function(err, isDbDirExists, isPreloadExists) {
if (isDbDirExists) {
2015-05-18 20:27:02 +00:00
this.pass(null);
2015-11-22 07:55:44 +00:00
} else {
fs.mkdir(app.config.paths.db, this.slot());
2015-05-18 18:25:08 +00:00
}
2015-05-12 21:07:03 +00:00
if (isPreloadExists) {
var preload = require(app.config.paths.preload);
// register rc plugins
_(preload.plugins).each(function(plugin) {
logger.log('Preload plugin "%s"', plugin);
require(plugin).register(app);
});
}
2015-05-18 20:27:02 +00:00
reader.load(app.config.paths.data, 'config', this.slot());
},
function(err, mkdirResult, config) {
2015-05-18 18:25:08 +00:00
_(app.config).defaults(config);
2015-07-20 20:57:41 +00:00
_(app.config).defaults(configDefaults);
2015-05-18 18:25:08 +00:00
2015-07-05 18:03:58 +00:00
logger.log('Server config:', JSON.stringify(app.config, null, 4));
2015-05-18 18:25:08 +00:00
var dbBackend = require(app.config.storage.backend);
// monkey patch memdown to allow save empty strings which is correct
// at general but occasionally not allowed at _checkKey
// https://github.com/Level/abstract-leveldown/issues/74
if (app.config.storage.backend === 'memdown') {
dbBackend.prototype._checkKey = _.noop;
}
db.init(app.config.paths.db, {db: dbBackend}, this.slot());
2015-06-13 18:59:32 +00:00
},
function() {
2015-06-14 13:40:20 +00:00
// load all projects for the first time
2015-06-13 18:59:32 +00:00
project.loadAll(app.config.paths.projects, this.slot());
completeUncompletedBuilds(this.slot());
2015-06-13 18:59:32 +00:00
},
function(err, projects) {
2015-06-14 13:40:20 +00:00
// note that `app.projects` is live variable
2015-06-13 18:59:32 +00:00
app.projects = projects;
2015-07-05 18:03:58 +00:00
logger.log('Loaded projects: ', _(app.projects).pluck('name'));
2015-06-14 13:40:20 +00:00
require('./distributor').init(app, this.slot());
},
function(err, distributor) {
app.distributor = distributor;
// register other plugins
require('./lib/notifier/console').register(app);
_(app.config.plugins).each(function(plugin) {
logger.log('Load plugin "%s"', plugin);
require(plugin).register(app);
});
2015-12-06 20:46:24 +00:00
httpApi = require('./httpApi')(app);
notifier.init(app.config.notify, this.slot());
require('./projectsWatcher').init(app, this.slot());
2015-05-21 21:09:16 +00:00
2015-07-12 08:39:22 +00:00
require('./scheduler').init(app, this.slot());
// notify about first project loading
_(app.projects).each(function(project) {
app.emit('projectLoaded', project);
});
2015-05-18 20:27:02 +00:00
// init resources
2015-05-18 18:25:08 +00:00
require('./resources')(app);
},
function(err) {
2015-07-20 21:01:55 +00:00
var host = app.config.http.host,
port = app.config.http.port;
logger.log('Start http server on %s:%s', host, port);
app.server.listen(port, host);
},
2015-05-18 18:25:08 +00:00
function(err) {
if (err) throw err;
}
);