Files
angular.js/scripts/compare-master-to-stable.js
2016-11-29 13:25:56 +00:00

168 lines
3.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
'use strict';
var util = require('util');
var cp = require('child_process');
var Q = require('q');
var _ = require('lodash');
var semver = require('semver');
var exec = function(cmd) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(cmd);
var fullCmd = util.format.apply(util, args);
return Q.nfcall(cp.exec, fullCmd).then(function(out) {
return out[0].split('\n');
});
};
};
var andThen = function(fn, after) {
return /** @this */ function() {
return fn.apply(this, arguments).then(after);
};
};
var oneArg = function(fn) {
return function(arg) {
return fn(arg);
};
};
var oneLine = function(lines) {
return lines[0].trim();
};
var noArgs = function(fn) {
return function() {
return fn();
};
};
var identity = function(i) { return i; };
// like Q.all, but runs the commands in series
// useful for ensuring env state (like which branch is checked out)
var allInSeries = function(fn) {
return function(args) {
var results = [];
var def;
while (args.length > 0) {
(function(arg) {
if (def) {
def = def.then(function() {
return fn(arg);
});
} else {
def = fn(arg);
}
def = def.then(function(res) {
results.push(res);
});
})(args.pop());
}
return def.then(function() {
return results;
});
};
};
var compareBranches = function(left, right) {
console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
console.log(_(left.log).
difference(right.log).
map(function(line) {
return left.full[left.log.indexOf(line)]; // lol O(n^2)
}).
value().
join('\n'));
};
var checkout = oneArg(exec('git checkout %s'));
var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
var getTags = noArgs(exec('git tag'));
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));
// remember this so we can restore state
var currentBranch;
getCurrentBranch().
then(function(branch) {
currentBranch = branch;
}).
then(getTags).
then(function(tags) {
return tags.
filter(semver.valid).
map(semver.clean).
sort(semver.rcompare);
}).
then(function(tags) {
var major = semver(tags[0]).major;
return tags.
filter(function(ver) {
return semver(ver).major === major;
});
}).
then(function(tags) {
return _(tags).
groupBy(function(tag) {
return tag.split('.')[1];
}).
map(function(group) {
return _.first(group);
}).
map(function(tag) {
return 'v' + tag;
}).
value();
}).
then(function(tags) {
var master = tags.pop();
var stable = tags.pop();
return [
{ name: stable.replace(/\d+$/, 'x'), tag: stable },
{ name: 'master', tag: master}
];
}).
then(allInSeries(function(branch) {
return checkout(branch.name).
then(function() {
return getTheLog(branch.tag);
}).
then(function(log) {
return log.
filter(identity);
}).
then(function(log) {
branch.full = log.map(function(line) {
line = line.split(' ');
var sha = line.shift();
var msg = line.join(' ');
return sha + ((/fix\([^)]+\):/i.test(msg)) ? ' * ' : ' ') + msg;
});
branch.log = log.map(function(line) {
return line.substr(41);
});
return branch;
});
})).
then(function(pairs) {
compareBranches(pairs[0], pairs[1]);
console.log('\n');
compareBranches(pairs[1], pairs[0]);
return pairs;
}).
then(function() {
return checkout(currentBranch);
}).
catch(function(e) {
console.log(e.stack);
});