Skip to content

Commit

Permalink
chore(changelog): improve the changelog generator (#1087)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdurieux authored and surli committed Jan 5, 2017
1 parent bf0652c commit ac4988a
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 148 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -26,3 +26,6 @@ maven-eclipse.xml
spooned-classes/
spooned/
doc/_jekyll/_site/

# Nodejs
node_modules/
362 changes: 214 additions & 148 deletions doc/_release/changelog_generator/changelog.js
Expand Up @@ -2,165 +2,229 @@ var log = require('git-log-parser');
var through2 = require('through2');
var request = require('request');

var SPOON_REPOSITORY = "https://github.com/INRIA/spoon/";
// hide merge commits
log.fields.merge = "P";

var prefix = {
"feat": {title:"New feature", commits: []},
"fix": {title:"Fixes", commits: []},
"doc": {title:"Documentation", commits: []},
"style": {title:"Code style", commits: []},
"refactor": {title:"Refactoring", commits: []},
"perf": {title:"Performance", commits: []},
"test": {title:"Tests", commits: []},
"chore": {title:"Other", commits: []},
"unknown": {title:"Unknown", commits: []}
};
var SPOON_REPOSITORY = "https://github.com/INRIA/spoon/";

var count = 0;
var prs = {};
/**
* Return the usage of the script
* @returns {string}
*/
function usage() {
return "Invalid script usage! node changelog.js <previous-spoon-version>";
}

if (process.argv.length == 2) {
console.error("Invalid script usage! node changelog.js <previous-spoon-version>");
return;
/**
* Main function of the script
*/
function main() {
if (process.argv.length == 2) {
console.error(usage());
return;
}
parseCommitLog(process.argv[2], function (data) {});
}
var SPOON_VERSION = process.argv[2];

log.parse({
"_": "spoon-core-" + SPOON_VERSION + "..master"
})
.pipe(through2.obj(function (chunk, enc, callback) {
chunk.subject = chunk.subject.replace("[ci skip]", "").trim();
count ++;
var found = false;
if(chunk.merge) {
var mergeSplit = chunk.merge.split(" ");

if(mergeSplit.length == 2) {
chunk.prId = mergeSplit[1];
chunk.origin = mergeSplit[0];
} else {
chunk.prId = mergeSplit[0];
}
/**
* Parse the log of spoon
* @param the last version of spoon (ex: 5.5.0)
* @param callback(data): the callback function with one argument that contains the categorized commits
*/
function parseCommitLog(version, callback) {
var categorizedCommits = {
"feat": {
title:"New features",
commits: []
},
"fix": {
title:"Bug Fixes",
commits: []
},
"doc": {
title:"Documentation",
commits: []
},
"style": {
title:"Code style",
commits: []
},
"refactor": {
title:"Refactoring",
commits: []
},
"perf": {
title:"Performance",
commits: []
},
"test": {
title:"Tests",
commits: []
},
"chore": {
title:"Other",
commits: []
},
"unknown": {
title:"Unknown",
commits: []
}
};
log
.parse({
"_": "spoon-core-" + version + "..master"
})
.pipe(through2.obj(function (commit, enc, callback) {
commit.subject = commit.subject.replace("[ci skip]", "").trim();

prs[chunk.prId] = chunk;
}
var url = SPOON_REPOSITORY + "branch_commits/" + chunk.commit.long;
request(url, function (error, response, body) {
var mMerge = chunk.subject.match("Merge pull request (#[0-9]+)");
if(mMerge) {
chunk.pr = mMerge[1];
found = true;
} else if(prs[chunk.commit.long]) {
chunk.pr = prs[chunk.commit.long].pr;
found = true;
}
if (!error && response.statusCode == 200) {
var match = body.match(/">#([0-9]+)<\/a>/);
if (match) {
chunk.pr = "#" + match[1];
found = true;
}
}
var found = false;

var foundPrefix = false;
for(var pref in prefix) {
var index = chunk.subject.toLowerCase().indexOf(pref);
if(index > -1 && index < 2) {
prefix[pref].commits.push(chunk);
foundPrefix = true;
var m = chunk.subject.substring(0, chunk.subject.indexOf(":") - 1)
if(m) {
if(m.indexOf("(") > 0) {
m = m.substring(m.indexOf("(") + 1)
chunk.category = m;
}
}
break;
}
}
if (!foundPrefix && chunk.subject.indexOf("Merge pull request") == -1) {
prefix["unknown"].commits.push(chunk);
}
var regex = /(#[0-9]+)/;
var m = chunk.subject.match(regex);
if(m) {
if (m[0] != chunk.pr) {
chunk.issue = m[0]
}
} else {
m = chunk.body.match(regex);
if(m) {
if (m[0] != chunk.pr) {
chunk.issue = m[0]
}
}
}
callback(null, JSON.stringify(prefix, undefined, 2));
});
})).on("data", function () {
}).on("end", function () {
// print the changelog
for(var i in prefix) {
var c = prefix[i];
if (c.commits.length == 0) {
continue;
}
console.log("\n");
console.log("# " + c.title + "\n");
var commits = c.commits.sort(function (a, b) {
if(a.category == b.category) {
return 0;
}
if(a.category > b.category) {
return 1;
}
return -1
});
var categories = {};
for (var j = 0; j < commits.length; j++) {
var commit = commits[j];
var category = commit.category;
if (!category) {
category = "none";
}
if (!categories[category]) {
categories[category] = []
}
categories[category].push(commit);
}
for (category in categories) {
var output = "";
if (category != "none") {
output += "* **" + category + "**";
var mMerge = commit.subject.match("Merge pull request (#[0-9]+)");
if(mMerge) {
commit.pr = mMerge[1];
found = true;
}

var category = getCommitCategory(commit, categorizedCommits);
if (category != null) {
categorizedCommits[category].commits.push(commit);
process.stdout.write("\033[2K" + categorizedCommits[category]['title'] + ": " + printCommit(commit) + '\033[0G');
}
if (categories[category].length > 1) {
output += "\n";
for (k in categories[category]) {
var commit = categories[category][k];
if (category != "none") {
output += " ";
}
output += "* " + printCommit(commit) + "\n";
}
} else {
if (category != "none") {
output += " : ";
} else {
output += "* ";
}
output += printCommit(categories[category][0]);

if (!found) {
var url = SPOON_REPOSITORY + "branch_commits/" + commit.commit.long;
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
var match = body.match(/">#([0-9]+)<\/a>/);
if (match) {
commit.pr = "#" + match[1];
found = true;
}
}
callback(null, null);
});
} else {
callback(null, null);
}
console.log(output.trim());
}
}
});
}))
.on("data", function () {})
.on("end", function () {
// clear progression
console.log("\033[2K");

var authors = {};

// print the changelog
for(var i in categorizedCommits) {
var c = categorizedCommits[i];
if (c.commits.length == 0) {
continue;
}
console.log("\n");
console.log("# " + c.title + "\n");
var commits = c.commits.sort(function (a, b) {
if(a.category == b.category) {
return 0;
}
if(a.category > b.category) {
return 1;
}
return -1
});
var categories = {};
for (var j = 0; j < commits.length; j++) {
var commit = commits[j];
if (authors[commit.author.name] == null) {
authors[commit.author.name] = {
"pr": 0
};
}
authors[commit.author.name]['pr'] ++;
var category = commit.category;
if (!category) {
category = "none";
}
if (!categories[category]) {
categories[category] = []
}
categories[category].push(commit);
}
for (category in categories) {
var output = "";
if (category != "none") {
output += "* **" + category + "**";
}
if (categories[category].length > 1) {
output += "\n";
for (k in categories[category]) {
var commit = categories[category][k];
if (category != "none") {
output += " ";
}
output += "* " + printCommit(commit) + "\n";
}
} else {
if (category != "none") {
output += ": ";
} else {
output += "* ";
}
output += printCommit(categories[category][0]);
}
console.log(output.trim());
}
}

// Print authors of the release
console.log("\n# Authors");
console.log("| Name | Nb Commit |");
console.log("|---------|-----------|");
var names = [];
for (var author in authors) {
names[names.length] = author;
}
names = names.sort(function (a, b) {
return authors[b]['pr'] - authors[a]['pr'];
});
for (var index in names) {
var author = names[index];
console.log("| " + author + " | " + authors[author]['pr'] + " |");
}
if (callback) {
callback(categorizedCommits);
}
});
}

/**
* Get the category if the commit
* @param commit
*/
function getCommitCategory (commit, categorizedCommits) {
for(var pref in categorizedCommits) {
var index = commit.subject.toLowerCase().indexOf(pref);
if(index > -1 && index < 2) {
var m = commit.subject.substring(0, commit.subject.indexOf(":") - 1);
if(m) {
if(m.indexOf("(") > 0) {
m = m.substring(m.indexOf("(") + 1)
commit.category = m;
}
}
return pref;
}
}
// ignore merge commit
if (commit.subject.indexOf("Merge pull request") > -1) {
return null;
}
if (commit.subject.indexOf("Merge remote-tracking branch") > -1) {
return null;
}
return "unknown";
}

function printCommit(commit) {
var issue = "";
if(commit.issue) {
issue = " (Issue: " + commit.issue + ")";
}
var pr = "";
if(commit.pr) {
pr = " (PR: " + commit.pr + ")";
Expand All @@ -174,5 +238,7 @@ function printCommit(commit) {
if (firstCharacter == firstCharacter.toLowerCase()) {
subject = firstCharacter.toUpperCase() + subject.substring(1);
}
return subject + issue + pr;
return subject + pr;
}

main();

0 comments on commit ac4988a

Please sign in to comment.