2015-03-17 21:13:37 +00:00
|
|
|
'use strict';
|
|
|
|
|
2015-03-23 21:16:00 +00:00
|
|
|
var Steppy = require('twostep').Steppy,
|
|
|
|
path = require('path'),
|
2015-05-03 21:53:11 +00:00
|
|
|
_ = require('underscore'),
|
|
|
|
EventEmitter = require('events').EventEmitter,
|
2016-02-26 20:43:38 +00:00
|
|
|
fs = require('fs'),
|
2015-07-05 18:03:58 +00:00
|
|
|
inherits = require('util').inherits;
|
2015-03-17 21:13:37 +00:00
|
|
|
|
|
|
|
function Executor(params) {
|
2015-03-23 21:16:00 +00:00
|
|
|
this.project = params.project;
|
|
|
|
this.cwd = path.join(this.project.dir, 'workspace');
|
2015-03-17 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.Executor = Executor;
|
|
|
|
|
2015-05-03 21:53:11 +00:00
|
|
|
inherits(Executor, EventEmitter);
|
|
|
|
|
2015-05-20 20:49:53 +00:00
|
|
|
Executor.prototype.throttledEmit = _(function() {
|
|
|
|
this.emit.apply(this, arguments);
|
2015-12-25 21:14:21 +00:00
|
|
|
}).throttle(500);
|
2015-05-20 20:49:53 +00:00
|
|
|
|
2015-03-17 21:13:37 +00:00
|
|
|
Executor.prototype._getSources = function(params, callback) {
|
2016-02-26 20:43:38 +00:00
|
|
|
var self = this,
|
|
|
|
scm;
|
|
|
|
Steppy(
|
|
|
|
function() {
|
|
|
|
self._getChanges(params, this.slot());
|
|
|
|
},
|
|
|
|
function(err, data) {
|
|
|
|
scm = data.scm;
|
|
|
|
this.pass(data.changes);
|
|
|
|
scm.update(data.rev, this.slot());
|
|
|
|
},
|
|
|
|
function(err, changes) {
|
|
|
|
scm.getCurrent(this.slot());
|
|
|
|
this.pass(changes);
|
|
|
|
scm.getRev(params.rev, this.slot());
|
|
|
|
},
|
|
|
|
function(err, currentRev, changes, latestRev) {
|
|
|
|
this.pass({
|
|
|
|
rev: currentRev,
|
|
|
|
changes: changes,
|
|
|
|
isLatest: currentRev.id === latestRev.id
|
|
|
|
});
|
|
|
|
},
|
|
|
|
callback
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
Executor.prototype._runStep = function(step, callback) {
|
|
|
|
var self = this,
|
|
|
|
params = _(step).clone();
|
|
|
|
|
|
|
|
Steppy(
|
|
|
|
function() {
|
|
|
|
if (params.type !== 'shell') {
|
|
|
|
throw new Error('Unknown step type: ' + params.type);
|
|
|
|
}
|
|
|
|
// set command cwd to executor cwd
|
|
|
|
params.cwd = self.cwd;
|
|
|
|
var command = self._createCommand(
|
|
|
|
_({
|
|
|
|
emitIn: true,
|
|
|
|
emitOut: true,
|
|
|
|
emitErr: true,
|
|
|
|
attachStderr: true
|
|
|
|
}).extend(params)
|
|
|
|
);
|
|
|
|
|
|
|
|
command.on('stdin', function(data) {
|
|
|
|
self.emit('data', '> ' + String(data));
|
|
|
|
});
|
|
|
|
|
|
|
|
command.on('stdout', function(data) {
|
|
|
|
self.emit('data', String(data));
|
|
|
|
});
|
|
|
|
|
|
|
|
command.on('stderr', function(data) {
|
|
|
|
self.emit('data', 'stderr: ' + String(data));
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: should be fixed properly, currently it's quick fix for
|
|
|
|
// NODE_ENV which affects npm install/prune calls
|
|
|
|
params.options = params.options || {};
|
|
|
|
params.options.env = params.options.env || process.env;
|
|
|
|
delete params.options.env.NODE_ENV;
|
|
|
|
|
|
|
|
command.run(params, this.slot())
|
|
|
|
},
|
|
|
|
callback
|
|
|
|
);
|
2015-03-17 21:13:37 +00:00
|
|
|
};
|
|
|
|
|
2016-02-26 20:43:38 +00:00
|
|
|
Executor.prototype._getChanges = function(params, callback) {
|
|
|
|
var self = this,
|
|
|
|
scm, isFirstRun, oldRev;
|
|
|
|
Steppy(
|
|
|
|
function() {
|
|
|
|
var slot = this.slot();
|
|
|
|
fs.exists(self.cwd, function(exists) {
|
|
|
|
slot(null, exists);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
function(err, exists) {
|
|
|
|
var scmParams = {type: params.type};
|
|
|
|
if (exists) {
|
|
|
|
scmParams.cwd = self.cwd;
|
|
|
|
isFirstRun = false;
|
|
|
|
} else {
|
|
|
|
scmParams.repository = params.repository;
|
|
|
|
isFirstRun = true;
|
|
|
|
}
|
|
|
|
scm = self._createScm(scmParams);
|
2015-03-17 21:13:37 +00:00
|
|
|
|
2016-02-26 20:43:38 +00:00
|
|
|
scm.on('stdin', function(data) {
|
|
|
|
self.emit('data', '> ' + String(data));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isFirstRun) {
|
|
|
|
this.pass(null);
|
|
|
|
} else {
|
|
|
|
scm.getCurrent(this.slot());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function(err, id) {
|
|
|
|
oldRev = id;
|
|
|
|
|
|
|
|
if (isFirstRun) {
|
|
|
|
scm.clone(self.cwd, params.rev, this.slot());
|
|
|
|
} else {
|
|
|
|
scm.pull(params.rev, this.slot())
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
scm.getChanges(oldRev && oldRev.id, params.rev, this.slot());
|
|
|
|
},
|
|
|
|
function(err, changes) {
|
|
|
|
var target = self._getTarget(params.rev, changes);
|
|
|
|
this.pass({
|
|
|
|
scm: scm,
|
|
|
|
oldRev: oldRev,
|
|
|
|
rev: target.rev,
|
|
|
|
changes: target.changes
|
|
|
|
});
|
|
|
|
},
|
|
|
|
callback
|
|
|
|
);
|
2015-03-17 21:13:37 +00:00
|
|
|
};
|
|
|
|
|
2015-07-11 10:41:01 +00:00
|
|
|
// Does current project scm has new changes to build
|
|
|
|
Executor.prototype.hasScmChanges = function(callback) {
|
2016-02-26 20:43:38 +00:00
|
|
|
this._getChanges(this.project.scm, function(err, data) {
|
|
|
|
callback(err, !err && data.changes.length > 0);
|
|
|
|
});
|
2015-07-11 10:41:01 +00:00
|
|
|
};
|
|
|
|
|
2015-03-17 21:13:37 +00:00
|
|
|
Executor.prototype.run = function(params, callback) {
|
2015-03-23 21:16:00 +00:00
|
|
|
var self = this,
|
2015-07-11 21:27:02 +00:00
|
|
|
project = _({}).extend(self.project, params),
|
|
|
|
getSourcesTiming = {name: 'get sources'},
|
|
|
|
stepTimings = [],
|
|
|
|
getSourcesStart = Date.now();
|
|
|
|
|
2015-03-17 21:13:37 +00:00
|
|
|
Steppy(
|
|
|
|
function() {
|
2015-07-11 21:27:02 +00:00
|
|
|
self.throttledEmit('currentStep', getSourcesTiming.name);
|
2015-03-23 21:16:00 +00:00
|
|
|
self._getSources(project.scm, this.slot());
|
2015-03-17 21:13:37 +00:00
|
|
|
},
|
2015-05-09 16:59:27 +00:00
|
|
|
function(err, scmData) {
|
2015-07-11 21:27:02 +00:00
|
|
|
getSourcesTiming.duration = Date.now() - getSourcesStart;
|
|
|
|
stepTimings.push(getSourcesTiming);
|
|
|
|
|
2015-05-09 16:59:27 +00:00
|
|
|
self.emit('scmData', scmData);
|
2015-07-11 21:27:02 +00:00
|
|
|
|
2015-05-05 23:11:28 +00:00
|
|
|
var funcs = project.steps.map(function(step, index) {
|
2015-03-17 21:13:37 +00:00
|
|
|
return function() {
|
2015-07-11 21:27:02 +00:00
|
|
|
var start = Date.now(),
|
|
|
|
stepCallback = this.slot();
|
|
|
|
|
2015-05-20 20:49:53 +00:00
|
|
|
self.throttledEmit('currentStep', step.name);
|
2015-07-11 21:27:02 +00:00
|
|
|
|
|
|
|
var timing = {name: step.name};
|
|
|
|
self._runStep(step, function(err) {
|
|
|
|
timing.duration = Date.now() - start;
|
|
|
|
stepTimings.push(timing);
|
|
|
|
self.emit('stepTimingsChange', stepTimings);
|
|
|
|
stepCallback(err);
|
|
|
|
});
|
2015-03-17 21:13:37 +00:00
|
|
|
};
|
|
|
|
});
|
2015-07-11 21:27:02 +00:00
|
|
|
|
2015-03-17 21:13:37 +00:00
|
|
|
funcs.push(this.slot());
|
|
|
|
Steppy.apply(this, funcs);
|
|
|
|
},
|
|
|
|
callback
|
|
|
|
);
|
2015-06-23 19:18:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Returns target rev and filtered changes according to `catchRev`
|
|
|
|
Executor.prototype._getTarget = function(rev, changes) {
|
|
|
|
var result = {rev: rev, changes: changes},
|
|
|
|
catchRev = this.project.catchRev;
|
|
|
|
|
|
|
|
if (catchRev) {
|
|
|
|
// reverse before search
|
|
|
|
changes = changes.reverse();
|
|
|
|
|
|
|
|
var index;
|
|
|
|
|
|
|
|
var comment = catchRev.comment;
|
|
|
|
if (comment) {
|
|
|
|
index = _(changes).findIndex(function(change) {
|
|
|
|
if (_(comment).isRegExp()) {
|
|
|
|
return comment.test(change.comment);
|
|
|
|
} else {
|
|
|
|
return comment === change.comment;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-06-28 10:23:34 +00:00
|
|
|
var tag = catchRev.tag;
|
|
|
|
if (tag) {
|
|
|
|
index = _(changes).findIndex(function(change) {
|
|
|
|
if (change.tags) {
|
|
|
|
if (_(tag).isRegExp()) {
|
|
|
|
return _(change.tags).find(function(changeTag) {
|
|
|
|
return tag.test(changeTag);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return _(change.tags).contains(tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-06-23 19:18:13 +00:00
|
|
|
if (index !== -1) {
|
|
|
|
result.rev = changes[index].id;
|
2015-06-24 21:54:33 +00:00
|
|
|
result.changes = changes.slice(0, index + 1);
|
2015-06-23 19:18:13 +00:00
|
|
|
result.changes.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
// reverse back before return
|
|
|
|
changes = changes.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|