diff --git a/tasks/git_changelog_generate.js b/tasks/git_changelog_generate.js
index 76d7911..85aff6d 100644
--- a/tasks/git_changelog_generate.js
+++ b/tasks/git_changelog_generate.js
@@ -3,293 +3,313 @@
* https://github.com/rafinskipg/git-changelog
*/
+var fs = require('fs');
var child = require('child_process');
-var defaults = require('./defaults');
+var format = require('util').format;
+
var _ = require('lodash');
-var fs = require('fs');
-var util = require('util');
var q = require('q');
-var OPTS = {};
-var PROVIDER, GIT_LOG_CMD, GIT_NOTAG_LOG_CMD,
- //ALLOWED_COMMITS = '^fix|^feat|^docs|BREAKING',
- //git-describe - Show the most recent tag that is reachable from a commit
- GIT_TAG_CMD = 'git describe --tags --abbrev=0',
- HEADER_TPL = '%s\n# %s (%s)\n\n',
- LINK_ISSUE,
- LINK_COMMIT,
- GIT_REPO_URL_CMD = 'git config --get remote.origin.url',
- EMPTY_COMPONENT = '$$';
+var defaults = require('./defaults');
+
+//ALLOWED_COMMITS = '^fix|^feat|^docs|BREAKING',
+//git-describe - Show the most recent tag that is reachable from a commit
+var GIT_TAG_CMD = 'git describe --tags --abbrev=0';
+var HEADER_TPL = '%s\n# %s (%s)\n\n';
+var GIT_REPO_URL_CMD = 'git config --get remote.origin.url';
+var EMPTY_COMPONENT = '$$';
+var GIT_LOG_CMD;
+var GIT_NOTAG_LOG_CMD;
+var LINK_ISSUE;
+var LINK_COMMIT;
// I have to clean that mess
-function initOptions(params){
- OPTS = _.defaults(params, defaults);
- OPTS.msg = 'name: '+ OPTS.app_name +';';
- OPTS.msg += 'file: '+ OPTS.file +';';
- OPTS.msg += 'grep_commits: '+ OPTS.grep_commits +';';
- OPTS.msg += 'debug: '+ OPTS.debug +';';
- OPTS.msg += 'version: '+ OPTS.version +';';
-}
+var Changelog = function Changelog() {
+ this.options = {};
+ this.options.msg = '';
+};
+
+Changelog.prototype.message = function message() {
+ Array.prototype.slice.call(arguments).forEach(function(value, index) {
+ this.options.msg += (index ? ': ' : '') + value;
+ }, this);
+
+ this.options.msg += ';';
+};
+
+Changelog.prototype.initOptions = function initOptions(params) {
+ this.options = _.defaults(params, defaults);
-var init = function(params){
+ this.message('name', this.options.app_name);
+ this.message('file', this.options.file);
+ this.message('grep_commits', this.options.grep_commits);
+ this.message('debug', this.options.debug);
+ this.message('version', this.options.version);
+};
+
+Changelog.prototype.init = function init(params) {
+ var self = this;
var deferred = q.defer();
- initOptions(params);
+ this.initOptions(params);
+
+ this.getRepoUrl().then(function(url) {
+ var provider;
+
+ self.options.repo_url = url;
+ self.message('remote', self.options.repo_url);
- getRepoUrl().then(function(url){
- OPTS.repo_url = url;
- OPTS.msg += 'remote: '+ OPTS.repo_url+';';
-
//G \ B \ ---
- PROVIDER = OPTS.repo_url.indexOf('github.com') !== -1 ? 'G' :'B';
+ provider = self.options.repo_url.indexOf('github.com') !== -1 ? 'G' :'B';
//Log commits
- GIT_LOG_CMD = 'git log ' + OPTS.branch_name + ' --grep="%s" -E --format=%s %s..HEAD';
- GIT_NOTAG_LOG_CMD = 'git log ' + OPTS.branch_name + ' --grep="%s" -E --format=%s';
+ GIT_LOG_CMD = 'git log ' + self.options.branch_name + ' --grep="%s" -E --format=%s %s..HEAD';
+ GIT_NOTAG_LOG_CMD = 'git log ' + self.options.branch_name + ' --grep="%s" -E --format=%s';
//This is just in case they differ their urls at some point in the future. Also brings the posibility of adding more providers
LINK_ISSUE = ({
- G: '[#%s]('+OPTS.repo_url+'/issues/%s)',
- B : '[#%s]('+OPTS.repo_url+'/issues/%s)'})
- [PROVIDER];
+ G: '[#%s]('+self.options.repo_url+'/issues/%s)',
+ B : '[#%s]('+self.options.repo_url+'/issues/%s)'})
+ [provider];
- LINK_COMMIT = ({
- G: '[%s]('+OPTS.repo_url+'/commit/%s)',
- B: '[%s]('+OPTS.repo_url+'/commits/%s)'})
- [PROVIDER];
+ LINK_COMMIT = ({
+ G: '[%s]('+self.options.repo_url+'/commit/%s)',
+ B: '[%s]('+self.options.repo_url+'/commits/%s)'})
+ [provider];
- deferred.resolve(OPTS);
+ deferred.resolve(self.options);
})
- .catch(function(){
- OPTS.msg += 'not remote;';
+ .catch(function() {
+ self.message('not remote');
deferred.reject("Sorry, you doesn't have configured any origin remote or passed a `repo_url` config value");
});
return deferred.promise;
};
+Changelog.prototype.parseRawCommit = function parseRawCommit(raw) {
+ if (!raw) {
+ return null;
+ }
-var parseRawCommit = function(raw) {
- if (!raw) { return null; }
-
- var lines = raw.split('\n');
- var msg = {}, match;
-
- msg.closes = [];
- msg.breaks = [];
-
- lines.forEach(function(line) {
- match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
- if (match) { msg.closes.push(parseInt(match[1], 10)); }
- });
+ var lines = raw.split('\n');
+ var msg = {}, match;
- msg.hash = lines.shift();
- msg.subject = lines.shift();
+ msg.closes = [];
+ msg.breaks = [];
- match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
+ lines.forEach(function(line) {
+ match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
if (match) {
- msg.breaking = match[1];
+ msg.closes.push(parseInt(match[1], 10));
}
+ });
+ msg.hash = lines.shift();
+ msg.subject = lines.shift();
- msg.body = lines.join('\n');
- match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
+ match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
+ if (match) {
+ msg.breaking = match[1];
+ }
- if(!match){
- match = msg.subject.match(/^(.*)\:\s(.*)$/);
- if(!match){
- warn('Incorrect message: %s %s', msg.hash, msg.subject);
- return null;
- }
- msg.type = match[1];
- msg.subject = match[2];
+ msg.body = lines.join('\n');
+ match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
- return msg;
+ if (!match) {
+ match = msg.subject.match(/^(.*)\:\s(.*)$/);
+ if (!match) {
+ this.warn('Incorrect message: %s %s', msg.hash, msg.subject);
+ return null;
}
-
msg.type = match[1];
- msg.component = match[2];
- msg.subject = match[3];
+ msg.subject = match[2];
+
return msg;
-};
+ }
+ msg.type = match[1];
+ msg.component = match[2];
+ msg.subject = match[3];
-var linkToIssue = function(issue) {
- return util.format(LINK_ISSUE, issue, issue);
+ return msg;
};
-
-var linkToCommit = function(hash) {
- return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
+Changelog.prototype.linkToIssue = function linkToIssue(issue) {
+ return format(LINK_ISSUE, issue, issue);
};
+Changelog.prototype.linkToCommit = function linkToCommit(hash) {
+ return format(LINK_COMMIT, hash.substr(0, 8), hash);
+};
-var currentDate = function() {
- var now = new Date();
- var pad = function(i) {
- return ('0' + i).substr(-2);
- };
+Changelog.prototype.currentDate = function currentDate() {
+ var now = new Date();
+ var pad = function(i) {
+ return ('0' + i).substr(-2);
+ };
- return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
+ return format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
};
+Changelog.prototype.printSection = function printSection(stream, title, section, printCommitLinks) {
+ printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
+ var components = Object.keys(section).sort();
+
+ if (!components.length) {
+ return;
+ }
-var printSection = function(stream, title, section, printCommitLinks) {
- printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
- var components = Object.getOwnPropertyNames(section).sort();
+ stream.write(format('\n## %s\n\n', title));
- if (!components.length) {return; }
+ components.forEach(function(name) {
+ var prefix = '-';
+ var nested = section[name].length > 1;
- stream.write(util.format('\n## %s\n\n', title));
+ if (name !== EMPTY_COMPONENT) {
+ if (nested) {
+ stream.write(format('- **%s:**\n', name));
+ prefix = ' -';
+ } else {
+ prefix = format('- **%s:**', name);
+ }
+ }
- components.forEach(function(name) {
- var prefix = '-';
- var nested = section[name].length > 1;
+ section[name].forEach(function(commit) {
+ if (printCommitLinks) {
+ stream.write(format('%s %s\n (%s', prefix, commit.subject, this.linkToCommit(commit.hash)));
- if (name !== EMPTY_COMPONENT) {
- if (nested) {
- stream.write(util.format('- **%s:**\n', name));
- prefix = ' -';
- } else {
- prefix = util.format('- **%s:**', name);
- }
+ if (commit.closes.length) {
+ stream.write(',\n ' + commit.closes.map(this.linkToIssue).join(', '));
}
+ stream.write(')\n');
+ } else {
+ stream.write(format('%s %s\n', prefix, commit.subject));
+ }
+ }, this);
+ }, this);
- section[name].forEach(function(commit) {
- if (printCommitLinks) {
- stream.write(util.format('%s %s\n (%s', prefix, commit.subject, linkToCommit(commit.hash)));
-
- if (commit.closes.length) {
- stream.write(',\n ' + commit.closes.map(linkToIssue).join(', '));
- }
- stream.write(')\n');
- } else {
- stream.write(util.format('%s %s\n', prefix, commit.subject));
- }
- });
- });
+ stream.write('\n');
+};
- stream.write('\n');
+Changelog.prototype.printSalute = function printSalute(stream) {
+ stream.write('\n\n---\n');
+ stream.write('*Generated with [git-changelog](https://github.com/rafinskipg/git-changelog). If you have any problem or suggestion, create an issue.* :) **Thanks** ');
};
-var printSalute = function(stream){
- stream.write('\n\n---\n');
- stream.write('*Generated with [git-changelog](https://github.com/rafinskipg/git-changelog). If you have any problem or suggestion, create an issue.* :) **Thanks** ');
-}
+Changelog.prototype.readGitLog = function prototype( git_log_command, from) {
+ var self = this;
+ var deferred = q.defer();
-var readGitLog = function( git_log_command, from) {
- var deferred = q.defer();
+ git_log_command = git_log_command === GIT_LOG_CMD ? format(git_log_command, this.options.grep_commits, '%H%n%s%n%b%n==END==', from) : format(git_log_command, this.options.grep_commits, '%H%n%s%n%b%n==END==');
- git_log_command = git_log_command === GIT_LOG_CMD ? util.format(git_log_command, OPTS.grep_commits, '%H%n%s%n%b%n==END==', from) : util.format(git_log_command, OPTS.grep_commits, '%H%n%s%n%b%n==END==');
-
- log('Executing : ', git_log_command);
+ this.log('Executing : ', git_log_command);
- child.exec(git_log_command , {timeout: 1000}, function(code, stdout, stderr) {
+ child.exec(git_log_command , {timeout: 1000}, function(code, stdout, stderr) {
+ var commits = [];
- var commits = [];
+ stdout.split('\n==END==\n').forEach(function(rawCommit) {
+ var commit = self.parseRawCommit(rawCommit);
+ if (commit) {
+ commits.push(commit);
+ }
+ });
- stdout.split('\n==END==\n').forEach(function(rawCommit) {
+ deferred.resolve(commits);
+ });
- var commit = parseRawCommit(rawCommit);
- if (commit) {commits.push(commit);}
- });
+ return deferred.promise;
+};
- deferred.resolve(commits);
- });
+Changelog.prototype.writeChangelog = function writeChangelog(stream, commits) {
+ var sections = {
+ fix: {},
+ feat: {},
+ breaks: {},
+ style: {},
+ refactor: {},
+ test: {},
+ chore: {},
+ docs: {}
+ };
+
+ sections.breaks[EMPTY_COMPONENT] = [];
+
+ this.organizeCommitsInSections(commits, sections);
+
+ stream.write(format(HEADER_TPL, this.options.version, this.options.app_name, this.options.version, this.currentDate()));
+ this.printSection(stream, 'Bug Fixes', sections.fix);
+ this.printSection(stream, 'Features', sections.feat);
+ this.printSection(stream, 'Refactor', sections.refactor, false);
+ this.printSection(stream, 'Style', sections.style, false);
+ this.printSection(stream, 'Test', sections.test, false);
+ this.printSection(stream, 'Chore', sections.chore, false);
+ this.printSection(stream, 'Documentation', sections.docs, false);
+ if (sections.breaks[EMPTY_COMPONENT].length > 0 ) {
+ this.printSection(stream, 'Breaking Changes', sections.breaks, false);
+ }
- return deferred.promise;
+ this.printSalute(stream);
};
+Changelog.prototype.organizeCommitsInSections = function organizeCommitsInSections(commits, sections) {
+ commits.forEach(function(commit) {
+ var section = sections[commit.type];
+ var component = commit.component || EMPTY_COMPONENT;
-var writeChangelog = function(stream, commits) {
- var sections = {
- fix: {},
- feat: {},
- breaks: {},
- style: {},
- refactor: {},
- test: {},
- chore: {},
- docs: {}
- };
-
- sections.breaks[EMPTY_COMPONENT] = [];
-
- organizeCommitsInSections(commits, sections)
-
- stream.write(util.format(HEADER_TPL, OPTS.version, OPTS.app_name, OPTS.version, currentDate()));
- printSection(stream, 'Bug Fixes', sections.fix);
- printSection(stream, 'Features', sections.feat);
- printSection(stream, 'Refactor', sections.refactor, false);
- printSection(stream, 'Style', sections.style, false);
- printSection(stream, 'Test', sections.test, false);
- printSection(stream, 'Chore', sections.chore, false);
- printSection(stream, 'Documentation', sections.docs, false);
- if(sections.breaks[EMPTY_COMPONENT].length > 0 ) {
- printSection(stream, 'Breaking Changes', sections.breaks, false);
+ if (section) {
+ section[component] = section[component] || [];
+ section[component].push(commit);
}
- printSalute(stream);
-};
+ if (commit.breaking) {
+ sections.breaks[component] = sections.breaks[component] || [];
+ sections.breaks[component].push({
+ subject: format("due to %s,\n %s", this.linkToCommit(commit.hash), commit.breaking),
+ hash: commit.hash,
+ closes: []
+ });
+ }
+ }, this);
-var organizeCommitsInSections = function(commits, sections){
- commits.forEach(function(commit) {
- var section = sections[commit.type];
- var component = commit.component || EMPTY_COMPONENT;
+ return sections;
+};
- if (section) {
- section[component] = section[component] || [];
- section[component].push(commit);
- }
+Changelog.prototype.getPreviousTag = function getPreviousTag() {
+ var deferred = q.defer();
- if (commit.breaking) {
- sections.breaks[component] = sections.breaks[component] || [];
- sections.breaks[component].push({
- subject: util.format("due to %s,\n %s", linkToCommit(commit.hash), commit.breaking),
- hash: commit.hash,
- closes: []
- });
- }
+ if (this.options.tag) {
+ deferred.resolve(this.options.tag);
+ } else if (this.options.tag === false) {
+ deferred.resolve(false);
+ } else {
+ //IF we dont find a previous tag, we get all the commits from the beggining - The bigbang of the code
+ child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
+ if (code ) {
+ deferred.resolve();
+ } else {
+ deferred.resolve(stdout.replace('\n', ''));
+ }
});
- return sections;
-}
-
-
-
-var getPreviousTag = function() {
- var deferred = q.defer();
- if(OPTS.tag){
- deferred.resolve(OPTS.tag);
- }else if(OPTS.tag === false){
- deferred.resolve(false);
- }else{
- //IF we dont find a previous tag, we get all the commits from the beggining - The bigbang of the code
- child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
- if (code ){ deferred.resolve();
- }else{
- deferred.resolve(stdout.replace('\n', ''));
- }
- });
- }
+ }
- return deferred.promise;
+ return deferred.promise;
};
-
-var getRepoUrl = function() {
+Changelog.prototype.getRepoUrl = function getRepoUrl() {
var deferred = q.defer();
- if(OPTS.repo_url){
- deferred.resolve(OPTS.repo_url);
- }else{
+ if (this.options.repo_url) {
+ deferred.resolve(this.options.repo_url);
+ } else {
//IF we dont find a previous tag, we get all the commits from the beggining - The bigbang of the code
child.exec(GIT_REPO_URL_CMD, function(code, stdout, stderr) {
- if (code){
+ if (code) {
deferred.reject();
} else {
stdout = stdout.replace('\n', '').replace('.git', '');
- deferred.resolve(stdout);
+ deferred.resolve(stdout);
}
});
}
@@ -297,63 +317,59 @@ var getRepoUrl = function() {
return deferred.promise;
};
-
-var generate = function(params) {
+Changelog.prototype.generate = function generate(params) {
+ var self = this;
var deferred = q.defer();
- init(params)
- .then(function(){
- return getPreviousTag()
- })
- .then(function(tag) {
- var fn ;
-
- if(typeof(tag) !== 'undefined' && tag !== false){
- log('Reading git log since', tag);
- OPTS.msg += 'since tag: '+ tag +';';
- fn = function(){ return readGitLog(GIT_LOG_CMD, tag);};
- }else{
- log('Reading git log since the beggining');
- OPTS.msg += 'since beggining;';
- fn = function(){ return readGitLog(GIT_NOTAG_LOG_CMD);};
+
+ this.init(params).then(function() {
+ return self.getPreviousTag();
+ }).then(function(tag) {
+ var fn;
+
+ if (typeof(tag) !== 'undefined' && tag !== false) {
+ self.log('Reading git log since', tag);
+ self.message('since tag', tag);
+
+ fn = function() {
+ return self.readGitLog(GIT_LOG_CMD, tag);
}
- fn().then(function(commits) {
- OPTS.msg += 'parsed commits:'+ commits.length +';';
- log('Parsed', commits.length, 'commits');
- log('Generating changelog to', OPTS.file || 'stdout', '(', OPTS.version, ')');
- writeChangelog(OPTS.file ? fs.createWriteStream(OPTS.file) : process.stdout, commits);
- deferred.resolve(OPTS);
- })
- .catch(function(err){
- console.log('error', err);
- })
-
- })
- .catch(function(err){
+ }else{
+ self.log('Reading git log since the beggining');
+ self.message('since beggining');
+
+ fn = function() {
+ return self.readGitLog(GIT_NOTAG_LOG_CMD);
+ }
+ }
+
+ fn().then(function(commits) {
+ self.message('parsed commits', commits.length);
+ self.log('Parsed', commits.length, 'commits');
+ self.log('Generating changelog to', self.options.file || 'stdout', '(', self.options.version, ')');
+
+ self.writeChangelog(self.options.file ? fs.createWriteStream(self.options.file) : process.stdout, commits);
+
+ deferred.resolve(self.options);
+ }).catch(function(err) {
+ console.log('error', err);
+ });
+ }).catch(function(err) {
console.log('Error generating changelog ', err);
deferred.reject(err);
- })
+ });
return deferred.promise;
};
-
-function log(){
- if(OPTS.debug){
+Changelog.prototype.log = function log() {
+ if (this.options.debug) {
console.log.apply(console, arguments);
}
-}
-
-var warn = function() {
- if(OPTS.debug){
- console.log('WARNING:', util.format.apply(null, arguments));
- }
};
+Changelog.prototype.warn = function warn() {
+ this.log('WARNING:', format.apply(null, arguments));
+};
-// publish for testing
-exports.parseRawCommit = parseRawCommit;
-exports.organizeCommitsInSections = organizeCommitsInSections;
-exports.generate = generate;
-exports.getRepoUrl = getRepoUrl;
-
+module.exports = new Changelog();