diff --git a/tasks/git_changelog_generate.js b/tasks/git_changelog_generate.js
index 84cc13e..6dab922 100644
--- a/tasks/git_changelog_generate.js
+++ b/tasks/git_changelog_generate.js
@@ -12,8 +12,6 @@ var debug = require('debug')('changelog');
var _ = require('lodash');
var q = require('q');
-var defaults = require('./defaults');
-
//ALLOWED_COMMITS = '^fix|^feat|^docs|BREAKING',
//git-describe - Show the most recent tag that is reachable from a commit
@@ -22,445 +20,29 @@ var Changelog = function Changelog() {
this.setDefaults();
};
-Changelog.prototype.setDefaults = function setDefaults() {
- debug('setting defaults');
- this.options = {};
- this.cmd = {
- gitTag: 'git describe --tags --abbrev=0',
- gitRepoUrl: 'git config --get remote.origin.url',
- gitLog: null,
- gitLogNoTag: null
- };
- this.header = '%s\n# %s (%s)\n\n';
- this.emptyComponent = '$$';
- this.links = null;
- this.provider = null;
-};
-
-Changelog.prototype.message = function message() {
- debug('adding 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) {
- debug('initializing options');
- this.setDefaults();
-
- this.options = _.defaults(params, defaults);
- this.options.msg = '';
-
- 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.getProviderLinks = function getProviderLinks() {
- debug('getting provider links');
- // This is just in case they differ their urls at some point in the future.
- // Also brings the posibility of adding more providers
- var providerLinks = {
- github: {
- issue: '[#%s](' + this.options.repo_url + '/issues/%s)',
- commit: '[%s](' + this.options.repo_url + '/commit/%s)'
- },
- bitbucket: {
- issue: '[#%s](' + this.options.repo_url + '/issues/%s)',
- commit: '[%s](' + this.options.repo_url + '/commits/%s)'
- }
- };
-
- this.provider = this.options.repo_url.indexOf('github.com') !== -1 ? 'github' :'bitbucket';
- this.links = providerLinks[this.provider];
-};
-
-Changelog.prototype.getGitLogCommands = function getGitLogCommands() {
- debug('getting log commands');
- this.cmd.gitLog = 'git log ' + this.options.branch_name + ' --grep="%s" -E --format=%s %s..HEAD';
- this.cmd.gitLogNoTag = 'git log ' + this.options.branch_name + ' --grep="%s" -E --format=%s';
-};
-
-Changelog.prototype.init = function init(params) {
- debug('initializing ...');
- var self = this;
- var deferred = q.defer();
-
- this.initOptions(params);
-
- this.getRepoUrl().then(function(url) {
- var provider;
-
- self.options.repo_url = url;
- self.message('remote', self.options.repo_url);
-
- self.getProviderLinks();
- self.getGitLogCommands();
-
- deferred.resolve(self.options);
- })
- .catch(function(err) {
- 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) {
- debug('parsing raw commit');
- 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));
- }
- });
-
- msg.hash = lines.shift();
- msg.subject = lines.shift();
-
- match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
- if (match) {
- msg.breaking = match[1];
- }
-
- msg.body = lines.join('\n');
- match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
-
- 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.subject = match[2];
-
- return msg;
- }
-
- msg.type = match[1];
- msg.component = match[2];
- msg.subject = match[3];
-
- return msg;
-};
-
-Changelog.prototype.linkToIssue = function linkToIssue(issue) {
- debug('generating link to issue');
- return format(this.links.issue, issue, issue);
-};
-
-Changelog.prototype.linkToCommit = function linkToCommit(hash) {
- debug('generating link to commit');
- return format(this.links.commit, hash.substr(0, 8), hash);
-};
-
-Changelog.prototype.currentDate = function currentDate() {
- debug('getting current date');
- var now = new Date();
- var pad = function(i) {
- return ('0' + i).substr(-2);
- };
-
- return format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
-};
-
-Changelog.prototype.printSection = function printSection(stream, title, section, printCommitLinks) {
- debug('printing section ...');
- printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
- var components = Object.keys(section).sort();
-
- if (!components.length) {
- return;
- }
-
- stream.write(format('\n## %s\n\n', title));
-
- components.forEach(function(name) {
- var prefix = '-';
- var nested = section[name].length > 1;
-
- if (name !== this.emptyComponent) {
- if (nested) {
- stream.write(format('- **%s:**\n', name));
- prefix = ' -';
- } else {
- prefix = format('- **%s:**', name);
- }
- }
-
- section[name].forEach(function(commit) {
- if (printCommitLinks) {
- stream.write(format('%s %s\n (%s', prefix, commit.subject, this.linkToCommit(commit.hash)));
-
- if (commit.closes.length) {
- stream.write(',\n ' + commit.closes.map(this.linkToIssue, this).join(', '));
- }
- stream.write(')\n');
- } else {
- stream.write(format('%s %s\n', prefix, commit.subject));
- }
- }, this);
- }, this);
-
- stream.write('\n');
-};
-
-Changelog.prototype.printSalute = function printSalute(stream) {
- debug('printing salute');
- 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 readGitLog(git_log_command, from) {
- debug('reading git log ...');
- var self = this;
- var deferred = q.defer();
-
- git_log_command = git_log_command === this.cmd.gitLog ? 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==');
-
- this.log('Executing : ', git_log_command);
-
- debug('executing git log command');
- child.exec(git_log_command , {timeout: 1000}, function(code, stdout, stderr) {
- debug('returning from git log command');
- var commits = [];
- stdout.split('\n==END==\n').forEach(function(rawCommit) {
- var commit = self.parseRawCommit(rawCommit);
- if (commit) {
- commits.push(commit);
- }
- });
-
- deferred.resolve(commits);
- });
-
- return deferred.promise;
-};
-
-Changelog.prototype.writeChangelog = function writeChangelog(stream, commits) {
- debug('writing change log');
- var deferred = q.defer();
- var sections = {
- fix: {},
- feat: {},
- breaks: {},
- style: {},
- refactor: {},
- test: {},
- chore: {},
- docs: {}
- };
-
- sections.breaks[this.emptyComponent] = [];
-
- this.organizeCommits(commits, sections);
-
- stream.on('open', function() {
- stream.write(format(this.header, 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[this.emptyComponent].length > 0 ) {
- this.printSection(stream, 'Breaking Changes', sections.breaks, false);
- }
-
- this.printSalute(stream);
- stream.end();
- stream.on('finish', function() {
- deferred.resolve();
- });
-
- }.bind(this));
-
- return deferred.promise;
-};
-
-Changelog.prototype.organizeCommits = function organizeCommits(commits, sections) {
- debug('organizaing commits');
- commits.forEach(function(commit) {
- var section = sections[commit.type];
- var component = commit.component || this.emptyComponent;
-
- if (section) {
- section[component] = section[component] || [];
- section[component].push(commit);
- }
-
- 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);
-
- return sections;
-};
-
-Changelog.prototype.getPreviousTag = function getPreviousTag() {
- debug('getting previous tag');
- var deferred = q.defer();
-
- 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
- debug('calling git tag command');
- child.exec(this.cmd.gitTag, function(code, stdout, stderr) {
- debug('returning from git tag');
- if (code) {
- deferred.reject();
- } else {
- deferred.resolve(stdout.replace('\n', ''));
- }
- });
- }
-
- return deferred.promise;
-};
-
-Changelog.prototype.getRepoUrl = function getRepoUrl() {
- debug('getting repo url');
- var deferred = q.defer();
-
- 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
- debug('calling git repo url command');
- child.exec(this.cmd.gitRepoUrl, function(code, stdout, stderr) {
- debug('returning git repo url command');
- if (code) {
- deferred.reject();
- } else {
- stdout = stdout.replace('\n', '').replace('.git', '');
- deferred.resolve(stdout);
- }
- });
- }
-
- return deferred.promise;
-};
-
-Changelog.prototype.checkPath = function chekPath(dirname, done) {
- fs.stat(dirname, function (err, stats) {
- if (err) {
- if (err.code === 'ENOENT') {
- this.checkPath(path.dirname(dirname), function (err) {
- if (err) {
- throw err;
- } else {
- fs.mkdir(dirname, function (err) {
- if (err) {
- throw err;
- }
- done();
- });
- }
- });
- } else {
- throw err;
- }
- } else if (stats.isDirectory()) {
- done();
- } else {
- throw new Error(dirname + ' exists and is not a directory');
- }
- }.bind(this));
-};
-
-Changelog.prototype.getStream = function getStream(filename) {
- debug('getting stream ...');
- var deferred = q.defer();
- var stream;
-
- if (filename) {
- this.checkPath(path.dirname(filename), function() {
- deferred.resolve(fs.createWriteStream(filename));
- });
- } else {
- deferred.resolve(process.stdout);
- }
-
- return deferred.promise;
-};
-
-Changelog.prototype.generate = function generate(params) {
- debug('generating ...');
- var self = this;
- var deferred = q.defer();
-
- this.init(params).then(function() {
- return self.getPreviousTag();
- }).then(function(tag) {
- var readGitLog;
-
- if (typeof(tag) !== 'undefined' && tag !== false) {
- self.log('Reading git log since', tag);
- self.message('since tag', tag);
- readGitLog = self.readGitLog.bind(self, self.cmd.gitLog, tag);
- } else {
- self.log('Reading git log since the beggining');
- self.message('since beggining');
- readGitLog = self.readGitLog.bind(self, self.cmd.gitLogNoTag);
- }
-
- readGitLog().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.getStream(self.options.file).then(function(stream) {
- self.writeChangelog(stream, commits).then(function() {
- 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;
-};
-
-Changelog.prototype.log = function log() {
- if (this.options.debug) {
- console.log.apply(null, arguments);
- }
-};
-
-Changelog.prototype.warn = function warn() {
- this.log('WARNING:', format.apply(null, arguments));
-};
+Changelog.prototype.init = require('./lib/init');
+Changelog.prototype.initOptions = require('./lib/init-options');
+Changelog.prototype.setDefaults = require('./lib/set-defaults');
+Changelog.prototype.message = require('./lib/message');
+Changelog.prototype.getProviderLinks = require('./lib/get-provider-links');
+Changelog.prototype.getGitLogCommands = require('./lib/get-gitlog-commands');
+Changelog.prototype.parseRawCommit = require('./lib/parse-raw-commit');
+Changelog.prototype.linkToIssue = require('./lib/link-to-issue');
+Changelog.prototype.linkToCommit = require('./lib/link-to-commit');
+Changelog.prototype.currentDate = require('./lib/current-date');
+Changelog.prototype.printSection = require('./lib/print-section');
+Changelog.prototype.printSalute = require('./lib/print-salute');
+Changelog.prototype.readGitLog = require('./lib/read-gitlog');
+Changelog.prototype.writeChangelog = require('./lib/write-change-log');
+Changelog.prototype.organizeCommits = require('./lib/organize-commits');
+Changelog.prototype.getPreviousTag = require('./lib/get-previous-tag');
+Changelog.prototype.getRepoUrl = require('./lib/get-repo-url');
+Changelog.prototype.checkPath = require('./lib/check-path');
+Changelog.prototype.getStream = require('./lib/get-stream');
+Changelog.prototype.generate = require('./lib/generate');
+
+Changelog.prototype.log = require('./lib/log');
+Changelog.prototype.warn = require('./lib/warn');
var changelog = new Changelog();
diff --git a/tasks/lib/check-path.js b/tasks/lib/check-path.js
new file mode 100644
index 0000000..7e055a9
--- /dev/null
+++ b/tasks/lib/check-path.js
@@ -0,0 +1,39 @@
+'use strict';
+
+var debug = require('debug')('changelog:checkPath');
+var fs = require('fs');
+
+function makePathDone(done, err) {
+ if (err) {
+ throw err;
+ }
+ done();
+}
+
+function makePath(dirname, done, err) {
+ if (err) {
+ throw err;
+ } else {
+ fs.mkdir(dirname, makePathDone.bind(null, done));
+ }
+}
+
+function processPath(dirname, done, err, stats) {
+ if (err) {
+ if (err.code === 'ENOENT') {
+ this.checkPath(path.dirname(dirname), makePath.bind(null, dirname, done));
+ } else {
+ throw err;
+ }
+ } else if (stats.isDirectory()) {
+ done();
+ } else {
+ throw new Error(dirname + ' exists and is not a directory');
+ }
+}
+
+function checkPath(dirname, done) {
+ fs.stat(dirname, processPath.bind(this, dirname, done));
+}
+
+module.exports = checkPath;
diff --git a/tasks/lib/current-date.js b/tasks/lib/current-date.js
new file mode 100644
index 0000000..2e98c0f
--- /dev/null
+++ b/tasks/lib/current-date.js
@@ -0,0 +1,16 @@
+'use strict';
+
+var debug = require('debug')('changelog:currentDate');
+var format = require('util').format;
+
+function pad(i) {
+ return ('0' + i).substr(-2);
+}
+
+function currentDate() {
+ debug('getting current date');
+ var now = new Date();
+ return format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
+}
+
+module.exports = currentDate;
diff --git a/tasks/lib/generate.js b/tasks/lib/generate.js
new file mode 100644
index 0000000..dccd7cc
--- /dev/null
+++ b/tasks/lib/generate.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var debug = require('debug')('changelog:generate');
+var q = require('q');
+
+function writeChangelogDone(deferred) {
+ deferred.resolve(this.options);
+}
+
+function writeCommitsToStream(deferred, commits, stream) {
+ this.writeChangelog(stream, commits)
+ .then(writeChangelogDone.bind(this, deferred));
+}
+
+function generateFromCommits(deferred, commits) {
+ this.message('parsed commits', commits.length);
+ this.log('Parsed', commits.length, 'commits');
+ this.log('Generating changelog to', this.options.file || 'stdout', '(', this.options.version, ')');
+
+ this.getStream(this.options.file)
+ .then(writeCommitsToStream.bind(this, deferred, commits));
+}
+
+function handleReadGitLogError(err) {
+ console.log('error', err);
+}
+
+function generateFromTag(deferred, tag) {
+ var readGitLog;
+
+ if (typeof(tag) !== 'undefined' && tag !== false) {
+ this.log('Reading git log since', tag);
+ this.message('since tag', tag);
+ readGitLog = this.readGitLog.bind(this, this.cmd.gitLog, tag);
+ } else {
+ this.log('Reading git log since the beggining');
+ this.message('since beggining');
+ readGitLog = this.readGitLog.bind(this, this.cmd.gitLogNoTag);
+ }
+
+ readGitLog()
+ .then(generateFromCommits.bind(this, deferred))
+ .catch(handleReadGitLogError);
+}
+
+function handleGenerateError(deferred, err) {
+ console.log('Error generating changelog ', err);
+ deferred.reject(err);
+}
+
+function generate(params) {
+ debug('generating ...');
+ var self = this;
+ var deferred = q.defer();
+
+ this.init(params)
+ .then(this.getPreviousTag.bind(this))
+ .then(generateFromTag.bind(this, deferred))
+ .catch(handleGenerateError.bind(null, deferred));
+
+ return deferred.promise;
+}
+
+module.exports = generate;
diff --git a/tasks/lib/get-gitlog-commands.js b/tasks/lib/get-gitlog-commands.js
new file mode 100644
index 0000000..7743f03
--- /dev/null
+++ b/tasks/lib/get-gitlog-commands.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var debug = require('debug')('changelog:getGitLogCommands');
+
+function getGitLogCommands() {
+ debug('getting log commands');
+ this.cmd.gitLog = 'git log ' + this.options.branch_name + ' --grep="%s" -E --format=%s %s..HEAD';
+ this.cmd.gitLogNoTag = 'git log ' + this.options.branch_name + ' --grep="%s" -E --format=%s';
+}
+
+module.exports = getGitLogCommands;
diff --git a/tasks/lib/get-previous-tag.js b/tasks/lib/get-previous-tag.js
new file mode 100644
index 0000000..71210c5
--- /dev/null
+++ b/tasks/lib/get-previous-tag.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var debug = require('debug')('changelog:getPreviousTag');
+var child = require('child_process');
+var q = require('q');
+
+function cmdDone(deferred, code, stdout, stderr) {
+ debug('returning from git tag');
+ if (code) {
+ deferred.reject();
+ } else {
+ deferred.resolve(stdout.replace('\n', ''));
+ }
+}
+
+function getPreviousTag() {
+ debug('getting previous tag');
+ var deferred = q.defer();
+
+ 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
+ debug('calling git tag command');
+ child.exec(this.cmd.gitTag, cmdDone.bind(null, deferred));
+ }
+
+ return deferred.promise;
+}
+
+module.exports = getPreviousTag;
diff --git a/tasks/lib/get-provider-links.js b/tasks/lib/get-provider-links.js
new file mode 100644
index 0000000..8df88f9
--- /dev/null
+++ b/tasks/lib/get-provider-links.js
@@ -0,0 +1,24 @@
+'use strict';
+
+var debug = require('debug')('changelog:getProviderLinks');
+
+function getProviderLinks() {
+ debug('getting provider links');
+ // This is just in case they differ their urls at some point in the future.
+ // Also brings the posibility of adding more providers
+ var providerLinks = {
+ github: {
+ issue: '[#%s](' + this.options.repo_url + '/issues/%s)',
+ commit: '[%s](' + this.options.repo_url + '/commit/%s)'
+ },
+ bitbucket: {
+ issue: '[#%s](' + this.options.repo_url + '/issues/%s)',
+ commit: '[%s](' + this.options.repo_url + '/commits/%s)'
+ }
+ };
+
+ this.provider = this.options.repo_url.indexOf('github.com') !== -1 ? 'github' :'bitbucket';
+ this.links = providerLinks[this.provider];
+}
+
+module.exports = getProviderLinks;
diff --git a/tasks/lib/get-repo-url.js b/tasks/lib/get-repo-url.js
new file mode 100644
index 0000000..e11e822
--- /dev/null
+++ b/tasks/lib/get-repo-url.js
@@ -0,0 +1,32 @@
+'use strict';
+
+var debug = require('debug')('changelog:getRepoUrl');
+var child = require('child_process');
+var q = require('q');
+
+function cmdDone(deferred, code, stdout, stderr) {
+ debug('returning git repo url command');
+ if (code) {
+ deferred.reject();
+ } else {
+ stdout = stdout.replace('\n', '').replace('.git', '');
+ deferred.resolve(stdout);
+ }
+}
+
+function getRepoUrl() {
+ debug('getting repo url');
+ var deferred = q.defer();
+
+ 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
+ debug('calling git repo url command');
+ child.exec(this.cmd.gitRepoUrl, cmdDone.bind(null, deferred));
+ }
+
+ return deferred.promise;
+}
+
+module.exports = getRepoUrl;
diff --git a/tasks/lib/get-stream.js b/tasks/lib/get-stream.js
new file mode 100644
index 0000000..283a805
--- /dev/null
+++ b/tasks/lib/get-stream.js
@@ -0,0 +1,26 @@
+'use strict';
+
+var debug = require('debug')('changelog:getStream');
+var fs = require('fs');
+var path = require('path');
+var q = require('q');
+
+function checkPathDone(deferred, filename) {
+ deferred.resolve(fs.createWriteStream(filename));
+}
+
+function getStream(filename) {
+ debug('getting stream ...');
+ var deferred = q.defer();
+ var stream;
+
+ if (filename) {
+ this.checkPath(path.dirname(filename), checkPathDone.bind(null, deferred, filename));
+ } else {
+ deferred.resolve(process.stdout);
+ }
+
+ return deferred.promise;
+}
+
+module.exports = getStream;
diff --git a/tasks/lib/init-options.js b/tasks/lib/init-options.js
new file mode 100644
index 0000000..430a830
--- /dev/null
+++ b/tasks/lib/init-options.js
@@ -0,0 +1,22 @@
+'use strict';
+
+var debug = require('debug')('changelog:initOptions');
+var _ = require('lodash');
+
+var defaults = require('../defaults');
+
+function initOptions(params) {
+ debug('initializing options');
+ this.setDefaults();
+
+ this.options = _.defaults(params, defaults);
+ this.options.msg = '';
+ 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);
+
+}
+
+module.exports = initOptions;
diff --git a/tasks/lib/init.js b/tasks/lib/init.js
new file mode 100644
index 0000000..e441208
--- /dev/null
+++ b/tasks/lib/init.js
@@ -0,0 +1,37 @@
+'use strict';
+
+var debug = require('debug')('changelog:init');
+var q = require('q');
+
+function getRepoSuccess(deferred, url) {
+ var provider;
+
+ this.options.repo_url = url;
+ this.message('remote', this.options.repo_url);
+
+ this.getProviderLinks();
+ this.getGitLogCommands();
+
+ deferred.resolve(this.options);
+}
+
+function getRepoFailure(deferred, err) {
+ this.message('not remote');
+ deferred.reject("Sorry, you doesn't have configured any origin remote or passed a `repo_url` config value");
+}
+
+function init(params) {
+ debug('initializing ...');
+ var self = this;
+ var deferred = q.defer();
+
+ this.initOptions(params);
+
+ this.getRepoUrl()
+ .then(getRepoSuccess.bind(this, deferred))
+ .catch(getRepoFailure.bind(this, deferred));
+
+ return deferred.promise;
+}
+
+module.exports = init;
diff --git a/tasks/lib/link-to-commit.js b/tasks/lib/link-to-commit.js
new file mode 100644
index 0000000..b98244d
--- /dev/null
+++ b/tasks/lib/link-to-commit.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var debug = require('debug')('changelog:linkToCommit');
+var format = require('util').format;
+
+function linkToCommit(hash) {
+ debug('generating link to commit');
+ return format(this.links.commit, hash.substr(0, 8), hash);
+}
+
+module.exports = linkToCommit;
diff --git a/tasks/lib/link-to-issue.js b/tasks/lib/link-to-issue.js
new file mode 100644
index 0000000..6da7e8b
--- /dev/null
+++ b/tasks/lib/link-to-issue.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var debug = require('debug')('changelog:linkToIssue');
+var format = require('util').format;
+
+function linkToIssue(issue) {
+ debug('generating link to issue');
+ return format(this.links.issue, issue, issue);
+}
+
+module.exports = linkToIssue;
diff --git a/tasks/lib/log.js b/tasks/lib/log.js
new file mode 100644
index 0000000..6c67fc8
--- /dev/null
+++ b/tasks/lib/log.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var debug = require('debug')('changelog:log');
+
+function log() {
+ if (this.options.debug) {
+ console.log.apply(null, arguments);
+ }
+}
+
+module.exports = log;
diff --git a/tasks/lib/message.js b/tasks/lib/message.js
new file mode 100644
index 0000000..1268b4b
--- /dev/null
+++ b/tasks/lib/message.js
@@ -0,0 +1,14 @@
+'use strict';
+
+var debug = require('debug')('changelog:message');
+
+function message() {
+ debug('adding message');
+ Array.prototype.slice.call(arguments).forEach(function(value, index) {
+ this.options.msg += (index ? ': ' : '') + value;
+ }, this);
+
+ this.options.msg += ';';
+}
+
+module.exports = message;
diff --git a/tasks/lib/organize-commits.js b/tasks/lib/organize-commits.js
new file mode 100644
index 0000000..8e1efa8
--- /dev/null
+++ b/tasks/lib/organize-commits.js
@@ -0,0 +1,31 @@
+'use strict';
+
+var debug = require('debug')('changelog:organizeCommits');
+var format = require('util').format;
+
+function organizeCommit(sections, commit) {
+ var section = sections[commit.type];
+ var component = commit.component || this.emptyComponent;
+
+ if (section) {
+ section[component] = section[component] || [];
+ section[component].push(commit);
+ }
+
+ 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: []
+ });
+ }
+}
+
+function organizeCommits(commits, sections) {
+ debug('organizaing commits');
+ commits.forEach(organizeCommit.bind(this, sections), this);
+ return sections;
+}
+
+module.exports = organizeCommits;
diff --git a/tasks/lib/parse-raw-commit.js b/tasks/lib/parse-raw-commit.js
new file mode 100644
index 0000000..f979633
--- /dev/null
+++ b/tasks/lib/parse-raw-commit.js
@@ -0,0 +1,56 @@
+'use strict';
+
+var debug = require('debug')('changelog:parseRawCommit');
+
+function parseLine(msg, line) {
+ var match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
+ if (match) {
+ msg.closes.push(parseInt(match[1], 10));
+ }
+}
+
+function parseRawCommit(raw) {
+ debug('parsing raw commit');
+ if (!raw) {
+ return null;
+ }
+
+ var lines = raw.split('\n');
+ var msg = {}, match;
+
+ msg.closes = [];
+ msg.breaks = [];
+
+ lines.forEach(parseLine.bind(null, msg));
+
+ msg.hash = lines.shift();
+ msg.subject = lines.shift();
+
+ match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
+ if (match) {
+ msg.breaking = match[1];
+ }
+
+ msg.body = lines.join('\n');
+ match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
+
+ 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.subject = match[2];
+
+ return msg;
+ }
+
+ msg.type = match[1];
+ msg.component = match[2];
+ msg.subject = match[3];
+
+ return msg;
+}
+
+module.exports = parseRawCommit;
diff --git a/tasks/lib/print-salute.js b/tasks/lib/print-salute.js
new file mode 100644
index 0000000..fc75f9f
--- /dev/null
+++ b/tasks/lib/print-salute.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var debug = require('debug')('changelog:printSalute');
+
+function printSalute(stream) {
+ debug('printing salute');
+ 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** ');
+}
+
+module.exports = printSalute;
diff --git a/tasks/lib/print-section.js b/tasks/lib/print-section.js
new file mode 100644
index 0000000..bf64820
--- /dev/null
+++ b/tasks/lib/print-section.js
@@ -0,0 +1,51 @@
+'use strict';
+
+var debug = require('debug')('changelog:printSection');
+var format = require('util').format;
+
+function printCommit(stream, printCommitLinks, prefix, commit) {
+ if (printCommitLinks) {
+ stream.write(format('%s %s\n (%s', prefix, commit.subject, this.linkToCommit(commit.hash)));
+
+ if (commit.closes.length) {
+ stream.write(',\n ' + commit.closes.map(this.linkToIssue, this).join(', '));
+ }
+ stream.write(')\n');
+ } else {
+ stream.write(format('%s %s\n', prefix, commit.subject));
+ }
+}
+
+function printComponent(stream, section, printCommitLinks, name) {
+ var prefix = '-';
+ var nested = section[name].length > 1;
+
+ if (name !== this.emptyComponent) {
+ if (nested) {
+ stream.write(format('- **%s:**\n', name));
+ prefix = ' -';
+ } else {
+ prefix = format('- **%s:**', name);
+ }
+ }
+
+ section[name].forEach(printCommit.bind(this, stream, printCommitLinks, prefix), this);
+}
+
+function printSection(stream, title, section, printCommitLinks) {
+ debug('printing section ...');
+ printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
+ var components = Object.keys(section).sort();
+
+ if (!components.length) {
+ return;
+ }
+
+ stream.write(format('\n## %s\n\n', title));
+
+ components.forEach(printComponent.bind(this, stream, section, printCommitLinks), this);
+
+ stream.write('\n');
+}
+
+module.exports = printSection;
diff --git a/tasks/lib/read-gitlog.js b/tasks/lib/read-gitlog.js
new file mode 100644
index 0000000..92869d2
--- /dev/null
+++ b/tasks/lib/read-gitlog.js
@@ -0,0 +1,46 @@
+'use strict';
+
+var debug = require('debug')('changelog:readGitLog');
+var format = require('util').format;
+var child = require('child_process');
+var q = require('q');
+
+function processRawCommit(commits, rawCommit) {
+ var commit = this.parseRawCommit(rawCommit);
+ if (commit) {
+ commits.push(commit);
+ }
+}
+
+function cmdDone(deferred, code, stdout, stderr) {
+ debug('returning from git log command');
+ var commits = [];
+
+ stdout
+ .split('\n==END==\n')
+ .forEach(processRawCommit.bind(this, commits), this);
+
+ deferred.resolve(commits);
+}
+
+function gitLogCommand(git_log_command, from) {
+ if (git_log_command === this.cmd.gitLog) {
+ return format(git_log_command, this.options.grep_commits, '%H%n%s%n%b%n==END==', from)
+ } else {
+ return format(git_log_command, this.options.grep_commits, '%H%n%s%n%b%n==END==');
+ }
+}
+
+function readGitLog(git_log_command, from) {
+ debug('reading git log ...');
+ var deferred = q.defer();
+
+ git_log_command = gitLogCommand.call(this, git_log_command, from);
+ this.log('Executing : ', git_log_command);
+ debug('executing git log command');
+ child.exec(git_log_command , {timeout: 1000}, cmdDone.bind(this, deferred));
+
+ return deferred.promise;
+}
+
+module.exports = readGitLog;
diff --git a/tasks/lib/set-defaults.js b/tasks/lib/set-defaults.js
new file mode 100644
index 0000000..7bbdf3b
--- /dev/null
+++ b/tasks/lib/set-defaults.js
@@ -0,0 +1,20 @@
+'use strict';
+
+var debug = require('debug')('changelog:setDefaults');
+
+function setDefaults() {
+ debug('setting defaults');
+ this.options = {};
+ this.cmd = {
+ gitTag: 'git describe --tags --abbrev=0',
+ gitRepoUrl: 'git config --get remote.origin.url',
+ gitLog: null,
+ gitLogNoTag: null
+ };
+ this.header = '%s\n# %s (%s)\n\n';
+ this.emptyComponent = '$$';
+ this.links = null;
+ this.provider = null;
+}
+
+module.exports = setDefaults;
diff --git a/tasks/lib/warn.js b/tasks/lib/warn.js
new file mode 100644
index 0000000..7fdc5d4
--- /dev/null
+++ b/tasks/lib/warn.js
@@ -0,0 +1,10 @@
+'use strict';
+
+var debug = require('debug')('changelog:warn');
+var format = require('util').format;
+
+function warn() {
+ this.log('WARNING:', format.apply(null, arguments));
+}
+
+module.exports = warn;
diff --git a/tasks/lib/write-change-log.js b/tasks/lib/write-change-log.js
new file mode 100644
index 0000000..3021e0c
--- /dev/null
+++ b/tasks/lib/write-change-log.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var debug = require('debug')('changelog:writeChangelog');
+var format = require('util').format;
+var q = require('q');
+
+function sendToStream(stream, sections, deferred) {
+ stream.write(format(this.header, 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[this.emptyComponent].length > 0 ) {
+ this.printSection(stream, 'Breaking Changes', sections.breaks, false);
+ }
+
+ this.printSalute(stream);
+ stream.end();
+ stream.on('finish', deferred.resolve);
+}
+
+function writeChangelog(stream, commits) {
+ debug('writing change log');
+ var deferred = q.defer();
+ var sections = {
+ fix: {},
+ feat: {},
+ breaks: {},
+ style: {},
+ refactor: {},
+ test: {},
+ chore: {},
+ docs: {}
+ };
+
+ sections.breaks[this.emptyComponent] = [];
+ this.organizeCommits(commits, sections);
+ stream.on('open', sendToStream.bind(this, stream, sections, deferred));
+
+ return deferred.promise;
+}
+
+module.exports = writeChangelog;