nci/lib/project.js

326 lines
7.1 KiB
JavaScript
Raw Normal View History

2014-12-02 21:21:32 +00:00
'use strict';
2015-04-12 22:19:42 +00:00
var Steppy = require('twostep').Steppy,
2014-12-02 21:21:32 +00:00
fs = require('fs'),
2015-04-12 22:19:42 +00:00
path = require('path'),
2015-05-18 17:52:01 +00:00
_ = require('underscore'),
2015-12-02 20:24:45 +00:00
utils = require('./utils'),
2015-12-28 20:34:08 +00:00
SpawnCommand = require('./command/spawn').Command,
2016-01-05 11:18:20 +00:00
validateParams = require('./validateParams'),
EventEmitter = require('events').EventEmitter,
inherits = require('util').inherits;
2014-12-02 21:21:32 +00:00
/**
* Projects collection contains all currently loaded projects and provides
* operations for manipulating with them.
* All projects stored on disk in `baseDir` and loaded to memory so
* they can be received (by `get`, `getAll` and other methods) in a sync way.
* Note that id for the particular project is a `name` of that project.
2016-01-05 11:18:20 +00:00
*/
function ProjectsCollection(params) {
this.db = params.db;
this.reader = params.reader;
this.baseDir = params.baseDir;
this.configs = [];
}
exports.ProjectsCollection = ProjectsCollection;
inherits(ProjectsCollection, EventEmitter);
2014-12-02 21:21:32 +00:00
/**
* Validate and return given config.
*
* @param {Object} config
* @param {Function} callback(err,config)
2014-12-02 21:21:32 +00:00
*/
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype.validateConfig = function(config, callback) {
2015-12-28 20:34:08 +00:00
Steppy(
function() {
validateParams(config, {
type: 'object',
properties: {
2016-01-09 13:43:47 +00:00
name: {
type: 'string',
pattern: /^(\w|-)+$/
},
2015-12-28 20:34:08 +00:00
scm: {
type: 'object',
required: true,
properties: {
type: {enum: ['git', 'mercurial'], required: true},
repository: {type: 'string', required: true},
rev: {type: 'string', required: true}
}
},
steps: {
type: 'array',
required: true,
items: {
type: 'object',
properties: {
cmd: {type: 'string', required: true},
name: {type: 'string'},
type: {enum: ['shell']},
2015-12-28 20:34:08 +00:00
shell: {type: 'string'}
}
}
}
},
additionalProperties: true
});
this.pass(null);
},
function(err) {
if (err) {
err.message = (
'Error during validation of project "' + config.name +
'": ' + err.message
);
}
callback(err, config);
}
);
2014-12-02 21:21:32 +00:00
};
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype._getProjectPath = function(name) {
return path.join(this.baseDir, name);
};
2015-04-12 22:19:42 +00:00
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype._loadConfig = function(dir, callback) {
var self = this;
2014-12-02 21:21:32 +00:00
Steppy(
function() {
2016-01-05 11:18:20 +00:00
self.reader.load(dir, 'config', this.slot());
2014-12-02 21:21:32 +00:00
},
function(err, config) {
2015-08-21 20:38:20 +00:00
// convert steps object to array
if (!_(config.steps).isArray() && _(config.steps).isObject()) {
config.steps = _(config.steps).map(function(val, name) {
var step;
if (_(val).isObject()) {
step = val;
} else {
step = {cmd: val};
}
step.name = name;
return step;
});
}
// apply defaults
_(config.steps).each(function(step) {
2015-05-20 20:20:51 +00:00
if (!step.type) step.type = 'shell';
2015-12-28 20:34:08 +00:00
if (!step.name && step.cmd) step.name = utils.prune(step.cmd, 40);
});
this.pass(config);
},
2014-12-02 21:21:32 +00:00
callback
);
};
2016-01-05 11:18:20 +00:00
/**
* Load project to collection.
* `projectLoaded` event with loaded config as argument will be emitted after
* load.
*
* @param {String} name
* @param {Function} [callback(err)]
2016-01-05 11:18:20 +00:00
*/
ProjectsCollection.prototype.load = function(name, callback) {
callback = callback || _.noop;
2016-01-05 11:18:20 +00:00
var self = this,
dir = self._getProjectPath(name);
Steppy(
function() {
if (self.get(name)) {
throw new Error(
'Can`t load already loaded project "' + name + '"'
);
}
2016-01-05 11:18:20 +00:00
self._loadConfig(dir, this.slot());
},
function(err, config) {
config.name = name;
config.dir = dir;
self.validateConfig(config, this.slot());
},
function(err, config) {
self.configs.push(config);
self.emit('projectLoaded', config);
this.pass(null);
},
2014-12-02 21:21:32 +00:00
callback
);
};
/**
* Load all projects (from `this.baseDir`).
* Calls `load` for every project in a base dir.
*
* @param {Function} [callback(err)]
*/
ProjectsCollection.prototype.loadAll = function(callback) {
callback = callback || _.noop;
var self = this;
Steppy(
function() {
fs.readdir(self.baseDir, this.slot());
},
function(err, dirs) {
var loadGroup = this.makeGroup();
_(dirs).each(function(dir) {
self.load(dir, loadGroup.slot());
});
},
callback
);
};
/**
* Unload project from collection
* `projectUnloaded` event with unloaded config as argument will be emitted
* after unload.
*
* @param {String} name
* @param {Function} [callback(err)]
*/
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype.unload = function(name, callback) {
callback = callback || _.noop;
var self = this;
2014-12-02 21:21:32 +00:00
Steppy(
function() {
2016-01-05 11:18:20 +00:00
var index = _(self.configs).findIndex(function(config) {
return config.name === name;
});
if (index === -1) {
throw new Error('Can`t unload not loaded project: "' + name + '"');
}
var unloadedConfig = self.configs.splice(index, 1)[0];
self.emit('projectUnloaded', unloadedConfig);
this.pass(null);
2014-12-02 21:21:32 +00:00
},
2016-01-05 11:18:20 +00:00
callback
);
};
/**
* Get project config by name.
* Returns config object or undefined if project is not found.
*
* @param {String} name
*/
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype.get = function(name) {
return _(this.configs).findWhere({name: name});
};
/**
* Get configs for all currently loaded projects.
* Returns array of config objects.
*/
ProjectsCollection.prototype.getAll = function() {
2016-01-05 11:18:20 +00:00
return this.configs;
};
/**
* Get project configs which match to predicate.
* Returns array of config objects or empty array if there is no matched
* project.
*
* @param {Function} predicate
2016-01-05 11:18:20 +00:00
*/
ProjectsCollection.prototype.filter = function(predicate) {
return _(this.configs).filter(predicate);
2014-12-02 21:21:32 +00:00
};
2015-07-26 13:05:54 +00:00
/**
* Remove project by name.
* Calls `unload`, removes project from disk and db.
*
* @param {String} name
* @param {Function} [callback(err)]
*/
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype.remove = function(name, callback) {
callback = callback || _.noop;
2016-01-05 11:18:20 +00:00
var self = this;
2015-12-02 20:24:45 +00:00
Steppy(
function() {
2016-01-05 11:18:20 +00:00
self.db.builds.find({
start: {projectName: name, descCreateDate: ''}
2015-12-02 20:24:45 +00:00
}, this.slot());
new SpawnCommand().run({cmd: 'rm', args: [
2016-01-05 11:18:20 +00:00
'-Rf', self._getProjectPath(name)
2015-12-02 20:24:45 +00:00
]}, this.slot());
2016-01-05 11:18:20 +00:00
self.unload(name, this.slot());
2015-12-02 20:24:45 +00:00
},
function(err, builds) {
if (builds.length) {
2016-01-05 11:18:20 +00:00
self.db.builds.del(builds, this.slot());
2015-12-02 20:24:45 +00:00
var logLinesRemoveGroup = this.makeGroup();
_(builds).each(function(build) {
2016-01-05 11:18:20 +00:00
self.db.logLines.remove({
2015-12-02 20:24:45 +00:00
start: {buildId: build.id}
}, logLinesRemoveGroup.slot());
});
} else {
this.pass(null, null);
}
},
callback
);
};
2015-12-04 19:54:21 +00:00
/**
* Rename project.
* Renames project on disk and db, also changes name for loaded project.
*
* @param {String} name
2016-01-09 19:23:39 +00:00
* @param {String} newName
* @param {Function} [callback(err)]
*/
2016-01-05 11:18:20 +00:00
ProjectsCollection.prototype.rename = function(name, newName, callback) {
callback = callback || _.noop;
2016-01-05 11:18:20 +00:00
var self = this;
2015-12-04 19:54:21 +00:00
Steppy(
function() {
fs.rename(
2016-01-05 11:18:20 +00:00
self._getProjectPath(name),
self._getProjectPath(newName),
2015-12-04 19:54:21 +00:00
this.slot()
);
2016-01-05 11:18:20 +00:00
self.db.builds.multiUpdate(
{start: {projectName: name, descCreateDate: ''}},
2015-12-04 19:54:21 +00:00
function(build) {
2016-01-05 11:18:20 +00:00
build.project.name = newName;
2015-12-04 19:54:21 +00:00
return build;
},
this.slot()
);
},
2016-01-05 11:18:20 +00:00
function() {
// just update currently loaded project name by link
self.get(name).name = newName;
this.pass(null);
},
2015-12-04 19:54:21 +00:00
callback
);
};