From 521851542ef4333a68bb7dd7b8b3d2560da56ee5 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:05:08 +0100 Subject: [PATCH 01/35] Add utils functions --- src/utils.js | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/utils.js diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 00000000..d0cfd336 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,109 @@ +'use strict'; + +var chalk = require('chalk'); + +/** + * Print a task name in a custom format + * + * @since 0.5.0 + * + * @param {string} name The name of the task + */ +function printTask(name) { + process.stdout.write(chalk.blue(name + ' task:\n===================================\n')); +} +/** + * Outputs the task status + * + * @since 0.5.0 + * + * @param {string} taskName The task name + * + * @return {Function} The function to be fired when is loaded + */ +function task(taskName) { + var time = process.hrtime(); + process.stdout.write(chalk.green(taskName) + ': .'); + + var si = setInterval(function() { + process.stdout.write('.'); + }, 1000); + + return function (message) { + var diff = process.hrtime(time); + + process.stdout.write(message || '' + chalk.yellow(' (' + ((diff[0] * 1e9 + diff[1]) * 1e-9).toFixed(2) + ' secs)\n')); + clearInterval(si); + }; +} + +/** + * Check if e value is between a min and a max + * + * @since 0.5.0 + * + * @param {number} value + * @param {number} min + * @param {number} max + * + * @return {Boolean} + */ +function isInRange(value, min, max) { + return !Math.floor((value - min) / (max - min)); +} + +/** + * Transforms a dasherize string into a camel case one. + * + * @since 0.3.2 + * + * @param {string} value The dasherize string + * + * @return {string} The camel case string + */ +function dashToCamelCase(value) { + return value.replace(/-([a-z])/g, function (match) { + return match[1].toUpperCase(); + }); +} + +/** + * Create a literal object of the node module options + * + * @since 0.1.0 + * + * @param {Array} args The array of arguments (the module arguments start from index 2) + * + * @return {Object} The object containg the key/value options + */ +function getOptions(args) { + var settings = {}; + + for(var i=2;i Date: Thu, 9 Jun 2016 16:05:40 +0100 Subject: [PATCH 02/35] Add github info function to retrieve user and repo informations --- src/github-info.js | 98 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/github-info.js diff --git a/src/github-info.js b/src/github-info.js new file mode 100644 index 00000000..a713fafd --- /dev/null +++ b/src/github-info.js @@ -0,0 +1,98 @@ +'use strict'; + +var exec = require('child_process').exec; +var chalk = require('chalk'); +var Promise = Promise || require('es6-promise').Promise; + +/** + * Execute a command in the bash and run a callback + * + * @since 0.5.0 + * + * @param {string} command The command to execute + * @param {Function} callback The callback which returns the stdout + * + * @return {Promise} + */ +function executeCommand(command, callback) { + return new Promise(function (resolve, reject) { + exec(command, function(err, stdout, stderr){ + if(err || stderr) { + reject(err || stderr); + } else { + resolve(stdout.replace('\n', '')); + } + }); + }) + .then(callback) + .catch(function (error) { + console.log(chalk.red(error)); + }); +} + +/** + * Get user informations + * + * @since 0.5.0 + * + * @param {Function} callback + * + * @return {Promise} The promise that resolves user informations ({ user: username}) + */ +function user(callback) { + return executeCommand('git config user.name', function (user) { + return { + user: user + }; + }) + .then(callback); +} + +/** + * Get repo informations + * + * @since 0.5.0 + * + * @param {Function} callback + * + * @return {Promise} The promise that resolves repo informations ({user: user, name: name}) + */ +function repo(callback) { + return executeCommand('git config remote.origin.url', function (repo) { + var repoPath = repo + .replace(/([^:]*:)|\.[^.]+$/g, '') + .split('/'); + var user = repoPath[0]; + var name = repoPath[1]; + + return { + username: user, + repo: name + }; + }) + .then(callback); +} + +/** + * Get token informations + * + * @since 0.5.0 + * + * @param {Function} callback + * + * @return {Promise} The promise that resolves token informations ({token: token}) + */ +function token(callback) { + return executeCommand('gitapp getToken', function (token) { + return { + token: token + }; + }) + .then(callback); +} + +module.exports = { + user: user, + repo: repo, + token: token +}; \ No newline at end of file From a4f079c63461b087df6d6046f1f1c53d8f4637a5 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:06:25 +0100 Subject: [PATCH 03/35] Update the main file so is able to get the action dynamically --- github-release-notes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/github-release-notes.js b/github-release-notes.js index 563bc44a..48812e7b 100644 --- a/github-release-notes.js +++ b/github-release-notes.js @@ -2,5 +2,8 @@ var GithubReleaseNotes = require('./src/index'); var gren = new GithubReleaseNotes(); +var utils = require('./src/util'); -gren.release(); \ No newline at end of file +var action = utils.getOptions(process.argv)['action']; + +gren.init(action || 'release'); \ No newline at end of file From 5a80af5000c2b4d4a865f66132c4f6d4ac24be25 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:07:02 +0100 Subject: [PATCH 04/35] Add a CHANGELOG.md file --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..21c84318 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,70 @@ +# Changelog + +## 0.4.0 (3/2/2016) + +- Changed option to --include-message +- Fixed default option +- Fixed typo +- Modifications to the options for messages to use lower case. + +Changed the casing of the include messages option +- Updated README.md +- Fixes after comments from Alex Canessa: + +1 - Since it is a new feature, the version should be 0.4.0 + +2 - I prefer to keep the default to Commits, to avoid a breaking change. +- Updated package.json version +- Added options for filtering messages + +Updated README.md +- NPM v0.3..3 +- Fix the release maker, when there are no previous releases + + --- + +## v0.3.2 (7/11/2015) + +- Version 0.3.1 +- Clean the code. Add the avoid message feature +- Fix the [ReferenceError: tags is not defined] error +- Package.json to reflect the real version +- Force option +- Release function returns a boolean. Stable version +- Refactoring of the promises, to return a promise from the .release() function +- Update README.md +- Divide the source code from the exec, creating the release() function +- Update README.md +- Release 0.2.0 +- Bit of refactoring +- [fix]: Change the prototype constructor +- [fix]: exporting the module +- src => lib +- Init the function and exports the constructor +- Install to start +- Move the lib into ./lib and require to allow the install from npm +- Update readme.md to show the npm version + + --- + +## v0.1.0 (12/10/2015) + +- [fix]: Avoid the double deleting if the first message is a merge +- Update the readme.md +- Reorder the functions, Release notes made between the latest tag and the latest release +- Change the default settings, fix the prepareRelease function binding the tags +- Fix the package.json file +- Remove global option +- Prefer Global option +- Change path to the bin +- Make the node global +- Update README.md +- update readme file +- Update the readme file +- Prefix option +- Documentation and prepareRelease function +- Error catching +- Update the readme.md file +- Release notes main js +- Package.json +- Add gitignore \ No newline at end of file From afdde16a8fbd8d88d0b3fadb4664098590591397 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:07:54 +0100 Subject: [PATCH 05/35] Update the index.js file to have the changelog function, use user's local informations and use issues as data --- src/index.js | 759 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 639 insertions(+), 120 deletions(-) diff --git a/src/index.js b/src/index.js index 5b910075..4dc3386b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,29 @@ 'use strict'; +var utils = require('./utils'); +var githubInfo = require('./github-info'); var Github = require('github-api'); +var fs = require('fs'); +var chalk = require('chalk'); +var Promise = Promise || require('es6-promise').Promise; + +var defaults = { + timeWrap: 'latest', // || history + changelogFilename: 'CHANGELOG.md', + dataSource: 'issues', // || commits + draft: false, + force: false, + prefix: '', + prerelease: false, + timeMeter: 'releaseDates', // || milestones, + dateZero: new Date(0) +}; /** * Create a release from a given tag (in the options) * + * @since 0.1.0 + * * @param {GithubReleaseNotes} gren The gren object * @param {Object} releaseOptions The options to build the release: * { @@ -16,18 +35,23 @@ var Github = require('github-api'); * "prerelease": false * } */ -function makeRelease(gren, releaseOptions) { +function createRelease(gren, releaseOptions) { + var loaded = utils.task('Preparing the release'); + return new Promise(function (resolve, reject) { - gren.repo.makeRelease(releaseOptions, function (err, release) { + gren.repo.createRelease(releaseOptions, function (err, release) { + loaded(); + if(err) { var responseText = JSON.parse(err.request.responseText); - console.error( - responseText.message + '\n' - + responseText.errors[0].code - ); + console.log(chalk.red( + responseText.message + '\n' + + responseText.errors[0].code + )); reject(false); - } else { - console.log(release.tag_name + ' successfully created!'); + } else { + console.log(chalk.green('\n\n' + release.name + ' has successfully created!')); + resolve(true); } }); @@ -35,19 +59,30 @@ function makeRelease(gren, releaseOptions) { } /** - * Return a string with a - to be a bullet list (used for a mapping) - * - * @param {string} message + * Creates the options to make the release * - * @return {string} + * @since 0.2.0 + * + * @param {GithubReleaseNotes} gren The gren object + * @param {Object[]} tags The collection of tags */ -function createBody(message) { - return '- ' + message; +function prepareRelease(gren, block) { + var releaseOptions = { + tag_name: block.release, + name: gren.options.prefix + block.release, + body: block.body, + draft: gren.options.draft, + prerelease: gren.options.prerelease + }; + + return createRelease(gren, releaseOptions); } /** * Transforms the commits to commit messages * + * @since 0.1.0 + * * @param {Object[]} commits The array of object containing the commits * * @return {String[]} @@ -57,56 +92,12 @@ function commitMessages(commits) { return commitObject.commit.message; }); } -{ - - key: function(message) { return message.method }, -} - -/** - * Creates the options to make the release - * - * @param {GithubReleaseNotes} gren The gren object - * @param {Object[]} tags The collection of tags - * @param {string[]} commitMessages The commit messages to create the release body - */ -function prepareRelease(gren, tagName, commitMessages) { - var body = commitMessages - .slice(0, -1) - .filter(function (message) { - var messageType = gren.options.includeMessages; - var filterMap = { - merges: function(message) { - return message.match(/^merge/i); - }, - commits: function(message) { - return !message.match(/^merge/i); - }, - all: function(message) { return true; } - }; - - if(filterMap[messageType]) { - return filterMap[messageType](message); - } - - return filterMap.commits(message); - }) - .map(createBody) - .join('\n'); - - var releaseOptions = { - tag_name: tagName, - name: (gren.options.prefix || '') + tagName, - body: body, - draft: gren.options.draft || false, - prerelease: gren.options.prerelease || false - }; - - return makeRelease(gren, releaseOptions); -} /** * Gets all the commits between two dates * + * @since 0.1.0 + * * @param {GithubReleaseNotes} gren The gren object * @param {string} since The since date in ISO * @param {string} until The until date in ISO @@ -114,13 +105,18 @@ function prepareRelease(gren, tagName, commitMessages) { * @return {Promise} The promise which resolves the [Array] commit messages */ function getCommitsBetweenTwo(gren, since, until) { + var loaded = utils.task('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until))); + var options = { since: since, - until: until + until: until, + per_page: 100 }; return new Promise(function (resolve, reject) { - gren.repo.getCommits(options, function (err, commits) { + gren.repo.listCommits(options, function (err, commits) { + loaded(); + if(err) { reject(err); } else { @@ -130,148 +126,635 @@ function getCommitsBetweenTwo(gren, since, until) { }); } +/** + * Get all the tags of the repo + * + * @since 0.1.0 + * + * @param {GithubReleaseNotes} gren The gren object + * + * @return {Promise} + */ +function getLastTags(gren, releaseTagName) { + var loaded = utils.task('Getting latest tag'); + + return new Promise(function (resolve, reject) { + gren.repo.listTags(function (err, tags) { + loaded(); + + if(err) { + reject(err); + } else { + var filteredTags = tags.filter(function(tag, index) { + return index === 0 || (releaseTagName ? tag.name === releaseTagName : index === tags.length-1); + }); + + resolve(filteredTags); + } + }); + }); +} + /** * Get the dates of the last two tags * + * @since 0.1.0 + * * @param {GithubReleaseNotes} gren The gren object * @param {Object[]} tags List of all the tags in the repo * * @return {Promise[]} The promises which returns the dates */ -function getTagDates(gren, lastTag, lastRelease) { - return [lastTag, lastRelease].map(function (tag) { +function getTagDates(gren, tags) { + var loaded = utils.task('Getting the tag dates ranges'); + + return tags.map(function (tag) { return new Promise(function (resolve, reject) { - gren.repo.getCommit('master', tag.commit.sha, function (err, commit) { + gren.repo.getCommit(tag.commit.sha, function (err, commit) { + loaded(); + if(err) { reject(err); } else { - resolve(commit.committer.date); + resolve({ + name: tag.name, + date: commit.committer.date + }); } }); }); - }) + }); } /** - * Get all the tags of the repo + * Get all releases + * + * @since 0.5.0 + * + * @param {Object[]} releases A list of release Objects + * + * @return {Array} The list of the dates + */ +function getReleaseDates(gren, releases) { + return [].concat(releases).map(function (release) { + return { + id: release.id, + name: release.name, + tag_name: release.tag_name, + date: release.created_at, + body: release.body || null + }; + }).concat(releases.length === 1 || gren.options.timeWrap === 'history' && {id: 0, date: gren.options.dateZero} || []); +} + +/** + * Get all releases * + * @since 0.5.0 + * * @param {GithubReleaseNotes} gren The gren object * - * @return {Promise} + * @return {Promise} The promise which resolves an array of releases */ -function getLastTags(gren, releaseTagName) { +function getListReleases(gren) { + var loaded = utils.task('Getting the list of releases'); + return new Promise(function (resolve, reject) { - gren.repo.listTags(function (err, tags) { - if(err) { + gren.repo.listReleases(function (err, releases) { + loaded(); + + if(err && err.request.status !== 404) { reject(err); } else { - var filteredTags = tags.filter(function(tag, index) { - return index === 0 || (releaseTagName ? tag.name === releaseTagName : index === tags.length-1 ); - }); - - resolve(filteredTags); + if(err && err.request.status === 404) { + resolve(false); + } else { + process.stdout.write(releases.length + ' releases found\n'); + resolve(releases); + } } }); }); } +/** + * Get the latest two releases + * + * @since 0.5.0 + * + * @param {GithubReleaseNotes} gren The gren object + * + * @return {Promise} The promise which resolves the tag name of the release + */ +function getLatestTwoRelease(gren) { + return getListReleases(gren) + .then(function(releases) { + return releases.slice(0, 2); + }); +} + /** * Get the latest release * + * @since 0.5.0 + * * @param {GithubReleaseNotes} gren The gren object * * @return {Promise} The promise which resolves the tag name of the release */ function getLatestRelease(gren) { + return getListReleases(gren) + .then(function(releases) { + return releases[0]; + }); +} + +/** + * Return a string with a - to be a bullet list (used for a mapping) + * + * @since 0.1.0 + * + * @param {string} message + * + * @return {string} + */ +function templateCommits(message) { + return '- ' + message; +} + +/** + * Generate the MD template from all the labels of a specific issue + * + * @since 0.5.0 + * + * @param {Object} issue + * + * @return {string} + */ +function templateLabels(issue) { + return issue.labels ? issue.labels.map(function (label) { + return '[**' + label.name + '**] '; + }) + .join('') : '[closed]'; +} + +/** + * Generate the MD template a block + * + * @since 0.5.0 + * + * @param {Object} block ({name: 'v1.2.3', body: []}) + * + * @return {string} + */ +function templateBlock(block) { + var date = new Date(block.date); + + return '## ' + block.release + ' (' + utils.formatDate(date) + ')' + '\n\n' + + block.body; +} + +/** + * Generate the MD template for each issue + * + * @since 0.5.0 + * + * @param {Object} issue + * + * @return {string} + */ +function templateIssue(issue) { + return '- ' + templateLabels(issue) + issue.title + ' [#' + issue.number + '](' + issue.html_url + ')'; +} + +/** + * Generate the Changelog MD template + * + * @since 0.5.0 + * + * @param {Object[]} blocks + * + * @return {string} + */ +function templateChangelog(blocks) { + return '# Changelog\n\n' + + blocks + .map(templateBlock) + .join('\n\n --- \n\n'); +} + +/** + * Return a commit messages generated body + * + * @since 0.5.0 + * + * @param {string} message + * + * @return {string} + */ +function generateCommitsBody(gren, messages) { + return messages + .slice(0, -1) + .filter(function (message) { + var messageType = gren.options.includeMessages; + var filterMap = { + merges: function(message) { + return message.match(/^merge/i); + }, + commits: function(message) { + return !message.match(/^merge/i); + }, + all: function() { return true; } + }; + + if(filterMap[messageType]) { + return filterMap[messageType](message); + } + + return filterMap.commits(message); + }) + .map(templateCommits) + .join('\n'); +} + +/** + * Get the list of all the issues of a specific milestone + * + * @since 0.5.0 + * + * @param {Object[]} issues The list of all the issues + * @param {Object} milestone The milestone whom filter the issues + * + * @return {string} + */ +function getMilestoneIssues(issues, milestone) { + return issues.filter(function(issue) { + return issue.milestone !== null && issue.milestone.id === milestone.id; + }) + .map(templateIssue) + .join('\n'); +} + +/** + * Get all the closed issues from the current repo + * + * @since 0.5.0 + * + * @param {GithubReleaseNotes} gren The gren object + * + * @return {Promise} The promise which resolves the list of the issues + */ +function getClosedIssues(gren) { + var loaded = utils.task('Getting all closed issues'); + return new Promise(function (resolve, reject) { - gren.repo.getLatestRelease(function (err, release) { - if(err && err.request.status !== 404) { + gren.issues.listIssues({ + state: 'closed' + }, function (err, issues) { + loaded(); + + if(err) { reject(err); } else { - if(err && err.request.status === 404) { - resolve(false); - } else { - resolve(release.tag_name); - } + var filteredIssues = issues.filter(function (issue) { + return !issue.pull_request; + }); + + process.stdout.write(filteredIssues.length + ' issues found'); + + resolve(filteredIssues); } }); }); } /** - * Transforms a dasherize string into a camel case one. + * Get all the closed milestones from the current repo + * + * @since 0.5.0 * - * @param {[string]} value The dasherize string + * @param {GithubReleaseNotes} gren The gren object * - * @return {[string]} The camel case string + * @return {Promise} The promise which resolves the list of the milestones */ -function dashToCamelCase(value) { - return value.replace(/-([a-z])/g, function (match) { - return match[1].toUpperCase(); +function getClosedMilestones(gren) { + return new Promise(function (resolve, reject) { + gren.issues.listMilestones({ state: 'closed' }, function (err, milestones) { + if(err) { + reject(err); + } else { + resolve(milestones); + } + }); }); } /** - * Create a literal object of the node module options + * Get the blocks of issues based on release dates + * + * @since 0.5.0 + * + * @param {GithubReleaseNotes} gren + * @param {Array} releaseRanges The array of date ranges + * + * @return {Promise[]} + */ +function getIssueBlocks(gren, releaseRanges) { + console.log('\nCreating the body blocks from issues:'); + + return getClosedIssues(gren) + .then(function (issues) { + return releaseRanges + .map(function (range) { + var body = (!range[0].body || gren.options.force) && + issues.filter(function (issue) { + return utils.isInRange( + Date.parse(issue.closed_at), + Date.parse(range[1].date), + Date.parse(range[0].date) + ); + }) + .map(templateIssue) + .join('\n') || range[0].body + '\n'; + + return { + release: range[0].name, + date: range[0].date, + body: body + }; + }); + }); +} + +/** + * Get the blocks of commits based on release dates + * + * @since 0.5.0 + * + * @param {GithubReleaseNotes} gren + * @param {Array} releaseRanges The array of date ranges + * + * @return {Promise[]} + */ +function getCommitBlocks(gren, releaseRanges) { + console.log('\nCreating the body blocks from commits:'); + + return Promise.all( + releaseRanges + .map(function (range) { + return getCommitsBetweenTwo(gren, range[1].date, range[0].date) + .then(function (commits) { + return { + release: range[0].name, + date: range[0].date, + body: generateCommitsBody(gren, commits) + }; + }); + }) + ); +} + +/** + * Create the ranges of release dates * - * @param {Array} args The array of arguments (the module arguments start from index 2) + * @since 0.5.0 + * + * @param {Array} releaseDates The release dates + * + * @return {Array} + */ +function createReleaseRanges(releaseDates) { + var ranges = []; + var range = 2; + + for(var i = 0; i Date: Thu, 9 Jun 2016 16:08:28 +0100 Subject: [PATCH 06/35] Add jshint and tests to the project --- .gitignore | 1 + .jshintrc | 31 +++++++++++++++++++++++++++++++ .travis.yml | 15 +++++++++++++++ Gruntfile.js | 33 +++++++++++++++++++++++++++++++++ package.json | 10 +++++++++- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 .jshintrc create mode 100644 .travis.yml create mode 100644 Gruntfile.js diff --git a/.gitignore b/.gitignore index 41aa0714..dc4da13c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store +docs/ node_modules/ npm-debug.log \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..55893226 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,31 @@ +{ + "curly": true, + "eqeqeq": true, + "forin": false, + "freeze": true, + "funcscope": false, + "futurehostile": true, + "globals": { + "define": false + }, + "globalstrict": true, + "iterator": false, + "latedef": true, + "maxcomplexity": 6, + "maxdepth": 3, + "maxparams": 4, + "maxstatements": 15, + "noarg": true, + "nocomma": true, + "nonbsp": true, + "nonew": true, + "notypeof": true, + "shadow": true, + "singleGroups": true, + "unused": true, + + "strict": true, + + "node": true, + "esversion": 6 +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..f87719ca --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: + - node_js + +node_js: + - "5.1" + +cache: + directories: + - node_modules + +before_install: + - npm install -g grunt-cli + +script: + - grunt test \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..13869431 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = function(grunt) { + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-jsdoc'); + grunt.loadNpmTasks('grunt-contrib-nodeunit'); + + grunt.initConfig({ + nodeunit: { + all: ['test/**/*.js'] + }, + jshint: { + options: { + jshintrc: '.jshintrc', + }, + src: [ + 'Gruntfile.js', + 'src/**/*.js' + ] + }, + jsdoc : { + dist : { + src: ['src/*.js', 'README.md'], + options: { + destination: 'docs' + } + } + } + }); + + // grunt.registerTask('ship', ['jshint', 'jsdoc']); + grunt.registerTask('test', ['jshint', 'nodeunit']); +}; \ No newline at end of file diff --git a/package.json b/package.json index 616838be..da30bb3c 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,14 @@ }, "homepage": "https://github.com/alexcanessa/github-release-notes#readme", "dependencies": { - "github-api": "git+https://github.com/alexcanessa/github.git" + "chalk": "^1.1.3", + "es6-promise": "^3.2.1", + "github-api": "^2.1.0" + }, + "devDependencies": { + "grunt": "^1.0.1", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-nodeunit": "^1.0.0", + "grunt-jsdoc": "^2.1.0" } } From 71aed38aea2c51cd8f784033e5f35ab1275660d5 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:17:01 +0100 Subject: [PATCH 07/35] Change the dateFormat and the getOptions functions --- src/index.js | 2 +- src/utils.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 4dc3386b..6365c215 100644 --- a/src/index.js +++ b/src/index.js @@ -678,7 +678,7 @@ function generateOptions() { * @constructor */ function GithubReleaseNotes(options) { - this.options = Object.assign({}, defaults, options || utils.getOptions(process.argv)); + this.options = Object.assign({}, defaults, options || utils.getBashOptions(process.argv)); this.repo = null; this.issues = null; } diff --git a/src/utils.js b/src/utils.js index d0cfd336..b78dfa43 100644 --- a/src/utils.js +++ b/src/utils.js @@ -76,7 +76,7 @@ function dashToCamelCase(value) { * * @return {Object} The object containg the key/value options */ -function getOptions(args) { +function getBashOptions(args) { var settings = {}; for(var i=2;i Date: Thu, 9 Jun 2016 16:17:30 +0100 Subject: [PATCH 08/35] Update the exported function name from utils --- src/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.js b/src/utils.js index b78dfa43..15c6a523 100644 --- a/src/utils.js +++ b/src/utils.js @@ -103,7 +103,7 @@ function formatDate(date) { module.exports = { printTask: printTask, task: task, - getOptions: getOptions, + getBashOptions: getBashOptions, isInRange: isInRange, formatDate: formatDate }; \ No newline at end of file From b8a86429507000aa4e0aabd6e729a92ed1a48150 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:46:17 +0100 Subject: [PATCH 09/35] Add tests for the utils functions --- src/utils.js | 9 ++++--- test/utils.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 test/utils.js diff --git a/src/utils.js b/src/utils.js index 15c6a523..555ea003 100644 --- a/src/utils.js +++ b/src/utils.js @@ -62,9 +62,11 @@ function isInRange(value, min, max) { * @return {string} The camel case string */ function dashToCamelCase(value) { - return value.replace(/-([a-z])/g, function (match) { - return match[1].toUpperCase(); - }); + return value + .toLowerCase() + .replace(/-([a-z])/g, function (match) { + return match[1].toUpperCase(); + }); } /** @@ -104,6 +106,7 @@ module.exports = { printTask: printTask, task: task, getBashOptions: getBashOptions, + dashToCamelCase: dashToCamelCase, isInRange: isInRange, formatDate: formatDate }; \ No newline at end of file diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 00000000..8909566e --- /dev/null +++ b/test/utils.js @@ -0,0 +1,65 @@ +var utils = require('../src/utils.js'); + +/* +======== A Handy Little Nodeunit Reference ======== +https://github.com/caolan/nodeunit + +Test methods: +test.expect(numAssertions) +test.done() +Test assertions: +test.ok(value, [message]) +test.equal(actual, expected, [message]) +test.notEqual(actual, expected, [message]) +test.deepEqual(actual, expected, [message]) +test.notDeepEqual(actual, expected, [message]) +test.strictEqual(actual, expected, [message]) +test.notStrictEqual(actual, expected, [message]) +test.throws(block, [error], [message]) +test.doesNotThrow(block, [error], [message]) +test.ifError(value) +*/ + +exports['utils'] = { + setUp: function (done) { + // setup here + done(); + }, + 'Should return the string of the formatted date': function (test) { + test.expect(1); + + test.deepEqual(utils.formatDate(new Date(0)), '01/01/1970', 'Given a date object.'); + test.done(); + }, + 'Should return the options in a key/value format': function (test) { + test.expect(1); + + let bashOptions = utils.getBashOptions([null, null, '--key=value', '--key2=value2']); + + test.deepEqual(JSON.stringify(bashOptions), JSON.stringify({ + key: 'value', + key2: 'value2' + }), 'Given an array of node arguments.'); + test.done(); + }, + 'Should return a camelCase string': function (test) { + test.expect(2); + + test.deepEqual(utils.dashToCamelCase('this-is-a-string'), 'thisIsAString', 'Given a string with dashes.'); + test.deepEqual(utils.dashToCamelCase('tHIs-Is-a-sTriNg'), 'thisIsAString', 'Given a string with random capital letters'); + test.done(); + }, + 'Should return if a number is in between a range': function (test) { + test.expect(7); + + test.deepEqual(utils.isInRange(2, 1, 3), true, 'Given a number in range'); + test.deepEqual(utils.isInRange(1, 2, 3), false, 'Given a number below range'); + test.deepEqual(utils.isInRange(4, 1, 3), false, 'Given a number above range'); + test.deepEqual(utils.isInRange(-1, 1, 3), false, 'Given a number above range, negative'); + test.deepEqual(utils.isInRange(-1, -3, 0), true, 'Given a number in range, negative'); + test.deepEqual(utils.isInRange(2, 2, 5), true, 'Given same number as first range value'); + test.deepEqual(utils.isInRange(5, 2, 5), false, 'Given same number as last range value'); + + test.done(); + } +}; \ No newline at end of file From 344d33bd1f2ab20f78de31cc25c9d8b7877acba0 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 16:54:08 +0100 Subject: [PATCH 10/35] Add strict mode to the tests --- test/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/utils.js b/test/utils.js index 8909566e..53980c86 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,3 +1,5 @@ +'use strict'; + var utils = require('../src/utils.js'); /* From a4283894e238825b6cf361a6db294fcbfc0acb36 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 17:13:07 +0100 Subject: [PATCH 11/35] Change the inclusion of the utils test file --- test/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.js b/test/utils.js index 53980c86..3e74a793 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('../src/utils.js'); +var utils = require('../src/utils'); /* ======== A Handy Little Nodeunit Reference ======== From 274d2e38368986ac829dee06baee443f60098b76 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 9 Jun 2016 17:13:27 +0100 Subject: [PATCH 12/35] Change the name of the index file to gren.js --- github-release-notes.js | 2 +- src/{index.js => gren.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{index.js => gren.js} (100%) diff --git a/github-release-notes.js b/github-release-notes.js index 48812e7b..a7ad82ae 100644 --- a/github-release-notes.js +++ b/github-release-notes.js @@ -1,6 +1,6 @@ 'use strict'; -var GithubReleaseNotes = require('./src/index'); +var GithubReleaseNotes = require('./src/gren'); var gren = new GithubReleaseNotes(); var utils = require('./src/util'); diff --git a/src/index.js b/src/gren.js similarity index 100% rename from src/index.js rename to src/gren.js From 95fdc87e89bc3ec9d378ee899df7898506282206 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 10 Jun 2016 18:07:41 +0100 Subject: [PATCH 13/35] Add the check network functionality, Change the way it gets the token --- Gruntfile.js | 10 ++++--- github-release-notes.js | 11 +++++--- package.json | 6 +++-- src/github-info.js | 2 +- src/gren.js | 59 ++++++++++++++++++++++++++++++----------- src/utils.js | 2 +- 6 files changed, 65 insertions(+), 25 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 13869431..adc072ee 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,14 +20,18 @@ module.exports = function(grunt) { }, jsdoc : { dist : { - src: ['src/*.js', 'README.md'], + src: ['CHANGELOG.md', 'src/*.js'], + readme: 'README.md', + version: true, options: { - destination: 'docs' + destination: 'docs', + template : "node_modules/ink-docstrap/template", + configure : "node_modules/ink-docstrap/template/jsdoc.conf.json" } } } }); - // grunt.registerTask('ship', ['jshint', 'jsdoc']); + grunt.registerTask('ship', ['jshint', 'jsdoc']); grunt.registerTask('test', ['jshint', 'nodeunit']); }; \ No newline at end of file diff --git a/github-release-notes.js b/github-release-notes.js index a7ad82ae..dce81c37 100644 --- a/github-release-notes.js +++ b/github-release-notes.js @@ -2,8 +2,13 @@ var GithubReleaseNotes = require('./src/gren'); var gren = new GithubReleaseNotes(); -var utils = require('./src/util'); +var utils = require('./src/utils'); -var action = utils.getOptions(process.argv)['action']; +var action = utils.getBashOptions(process.argv)['action']; -gren.init(action || 'release'); \ No newline at end of file +gren.init() + .then(function (success) { + if(success) { + return gren[action || 'release'](); + } + }); diff --git a/package.json b/package.json index da30bb3c..ab0883d4 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,14 @@ "dependencies": { "chalk": "^1.1.3", "es6-promise": "^3.2.1", - "github-api": "^2.1.0" + "github-api": "^2.1.0", + "is-online": "^5.1.1" }, "devDependencies": { "grunt": "^1.0.1", "grunt-contrib-jshint": "^1.0.0", "grunt-contrib-nodeunit": "^1.0.0", - "grunt-jsdoc": "^2.1.0" + "grunt-jsdoc": "^2.1.0", + "ink-docstrap": "^1.2.1" } } diff --git a/src/github-info.js b/src/github-info.js index a713fafd..6eca0b38 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -83,7 +83,7 @@ function repo(callback) { * @return {Promise} The promise that resolves token informations ({token: token}) */ function token(callback) { - return executeCommand('gitapp getToken', function (token) { + return executeCommand('echo $CHANGELOG_GITHUB_TOKEN', function (token) { return { token: token }; diff --git a/src/gren.js b/src/gren.js index 6365c215..cd41cbe5 100644 --- a/src/gren.js +++ b/src/gren.js @@ -6,6 +6,7 @@ var Github = require('github-api'); var fs = require('fs'); var chalk = require('chalk'); var Promise = Promise || require('es6-promise').Promise; +var isOnline = require('is-online'); var defaults = { timeWrap: 'latest', // || history @@ -16,12 +17,15 @@ var defaults = { prefix: '', prerelease: false, timeMeter: 'releaseDates', // || milestones, - dateZero: new Date(0) + dateZero: new Date(0), + override: false }; /** * Create a release from a given tag (in the options) * + * @private + * * @since 0.1.0 * * @param {GithubReleaseNotes} gren The gren object @@ -466,7 +470,7 @@ function getIssueBlocks(gren, releaseRanges) { .then(function (issues) { return releaseRanges .map(function (range) { - var body = (!range[0].body || gren.options.force) && + var body = (!range[0].body || gren.options.override) && issues.filter(function (issue) { return utils.isInRange( Date.parse(issue.closed_at), @@ -637,8 +641,9 @@ function createChangelog(gren, body) { if(data.match(newReleaseName)) { console.error(chalk.red('\nThis release is already in the changelog')); - // @TODO: Change to a dedicated option if(gren.options.force) { + createFile(body + data.replace(/^(#\s?\w*\n\n)/g, '')); + } else if(gren.options.override) { createFile(body); } @@ -670,6 +675,26 @@ function generateOptions() { ]); } +/** + * Check if there is connectivity + * + * @since 0.5.0 + * @private + * + * @return {boolean} If there is connectivity or not + */ +function hasNetwork() { + return new Promise(function (resolve, reject) { + isOnline(function (err, online) { + if(err) { + reject(chalk.red(err)); + } + + resolve(online); + }); + }); +} + /** * @param {Object} [options] The options of the module * @@ -690,13 +715,24 @@ function GithubReleaseNotes(options) { * @since 0.5.0 * * @param {function} action + * + * @return {Promise} The generated options + * + * @todo: check the network first */ -GithubReleaseNotes.prototype.init = function(action) { +GithubReleaseNotes.prototype.init = function() { var gren = this; - generateOptions(this.options) - .then(function (OptionData) { - gren.options = Object.assign(...OptionData, gren.options); + return hasNetwork() + .then(function (success) { + if(success) { + return generateOptions(gren, gren.options); + } else { + throw chalk.red('You need to have network connectivity'); + } + }) + .then(function (optionData) { + gren.options = Object.assign(...optionData, gren.options); if(!gren.options.token) { throw chalk.red('You need to provide the token'); @@ -709,10 +745,7 @@ GithubReleaseNotes.prototype.init = function(action) { gren.repo = githubApi.getRepo(gren.options.username, gren.options.repo); gren.issues = githubApi.getIssues(gren.options.username, gren.options.repo); - console.log(chalk.green('Hello ' + gren.options.user + ', sit back while I do the ' + action + ' for you\n')); - - gren[action](); - + return true; }) .catch(function (error) { console.log(error); @@ -735,7 +768,6 @@ GithubReleaseNotes.prototype.release = function() { commits: getCommitBlocks }; - return getLatestRelease(this) .then(function (release) { return getLastTags(gren, release.tag_name || false); @@ -753,9 +785,6 @@ GithubReleaseNotes.prototype.release = function() { .then(function (blocks) { return prepareRelease(gren, blocks[0]); }) - .then(function (success) { - return success && gren.changelog(); - }) .then(function (success) { return success; }) diff --git a/src/utils.js b/src/utils.js index 555ea003..093ac490 100644 --- a/src/utils.js +++ b/src/utils.js @@ -27,7 +27,7 @@ function task(taskName) { var si = setInterval(function() { process.stdout.write('.'); - }, 1000); + }, 100); return function (message) { var diff = process.hrtime(time); From 0c11513e20312f72b32fd0e1aadc9167a27ef0b6 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Sat, 11 Jun 2016 19:41:20 +0100 Subject: [PATCH 14/35] Add @private and @public tags, remove useless comments --- src/github-info.js | 4 ++++ src/utils.js | 6 ++++++ test/utils.js | 24 ------------------------ 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/github-info.js b/src/github-info.js index 6eca0b38..785e2888 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -8,6 +8,7 @@ var Promise = Promise || require('es6-promise').Promise; * Execute a command in the bash and run a callback * * @since 0.5.0 + * @private * * @param {string} command The command to execute * @param {Function} callback The callback which returns the stdout @@ -34,6 +35,7 @@ function executeCommand(command, callback) { * Get user informations * * @since 0.5.0 + * @public * * @param {Function} callback * @@ -52,6 +54,7 @@ function user(callback) { * Get repo informations * * @since 0.5.0 + * @public * * @param {Function} callback * @@ -77,6 +80,7 @@ function repo(callback) { * Get token informations * * @since 0.5.0 + * @public * * @param {Function} callback * diff --git a/src/utils.js b/src/utils.js index 093ac490..b4d77e3c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,6 +6,7 @@ var chalk = require('chalk'); * Print a task name in a custom format * * @since 0.5.0 + * @public * * @param {string} name The name of the task */ @@ -16,6 +17,7 @@ function printTask(name) { * Outputs the task status * * @since 0.5.0 + * @public * * @param {string} taskName The task name * @@ -41,6 +43,7 @@ function task(taskName) { * Check if e value is between a min and a max * * @since 0.5.0 + * @public * * @param {number} value * @param {number} min @@ -56,6 +59,7 @@ function isInRange(value, min, max) { * Transforms a dasherize string into a camel case one. * * @since 0.3.2 + * @public * * @param {string} value The dasherize string * @@ -73,6 +77,7 @@ function dashToCamelCase(value) { * Create a literal object of the node module options * * @since 0.1.0 + * @public * * @param {Array} args The array of arguments (the module arguments start from index 2) * @@ -94,6 +99,7 @@ function getBashOptions(args) { * Format a date into a string * * @since 0.5.0 + * @public * * @param {Date} date * @return {string} diff --git a/test/utils.js b/test/utils.js index 3e74a793..154d11a6 100644 --- a/test/utils.js +++ b/test/utils.js @@ -2,31 +2,7 @@ var utils = require('../src/utils'); -/* -======== A Handy Little Nodeunit Reference ======== -https://github.com/caolan/nodeunit - -Test methods: -test.expect(numAssertions) -test.done() -Test assertions: -test.ok(value, [message]) -test.equal(actual, expected, [message]) -test.notEqual(actual, expected, [message]) -test.deepEqual(actual, expected, [message]) -test.notDeepEqual(actual, expected, [message]) -test.strictEqual(actual, expected, [message]) -test.notStrictEqual(actual, expected, [message]) -test.throws(block, [error], [message]) -test.doesNotThrow(block, [error], [message]) -test.ifError(value) -*/ - exports['utils'] = { - setUp: function (done) { - // setup here - done(); - }, 'Should return the string of the formatted date': function (test) { test.expect(1); From 2f67ed640e9e200313482f908b9baccc237eab69 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Sat, 11 Jun 2016 19:41:52 +0100 Subject: [PATCH 15/35] Add the chance to change an existing release --- src/gren.js | 371 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 227 insertions(+), 144 deletions(-) diff --git a/src/gren.js b/src/gren.js index cd41cbe5..220b34ea 100644 --- a/src/gren.js +++ b/src/gren.js @@ -21,16 +21,54 @@ var defaults = { override: false }; + /** - * Create a release from a given tag (in the options) + * Edit arelease from a given tag (in the options) * + * @since 0.5.0 * @private + * + * @param {GithubReleaseNotes} gren The gren object + * @param {Object} releaseOptions The options to build the release: + * @example + * { + * "tag_name": "v1.0.0", + * "target_commitish": "master", + * "name": "v1.0.0", + * "body": "Description of the release", + * "draft": false, + * "prerelease": false + * } + * + * @return {Promise} + */ +function editRelease(gren, releaseId, releaseOptions) { + var loaded = utils.task('Updating latest release'); + + return new Promise(function (resolve, reject) { + gren.repo.updateRelease(releaseId, releaseOptions, function (err, release) { + loaded(); + + if(err) { + reject(chalk.red(err)); + } else { + console.log(chalk.green('\n\n' + release.name + ' has successfully updated!')); + + resolve(true); + } + }); + }); +} + +/** + * Create a release from a given tag (in the options) * * @since 0.1.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @param {Object} releaseOptions The options to build the release: - * { + * @example { * "tag_name": "v1.0.0", * "target_commitish": "master", * "name": "v1.0.0", @@ -38,6 +76,8 @@ var defaults = { * "draft": false, * "prerelease": false * } + * + * @return {Promise} */ function createRelease(gren, releaseOptions) { var loaded = utils.task('Preparing the release'); @@ -66,9 +106,12 @@ function createRelease(gren, releaseOptions) { * Creates the options to make the release * * @since 0.2.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @param {Object[]} tags The collection of tags + * + * @return {Promise} */ function prepareRelease(gren, block) { var releaseOptions = { @@ -79,67 +122,24 @@ function prepareRelease(gren, block) { prerelease: gren.options.prerelease }; - return createRelease(gren, releaseOptions); -} - -/** - * Transforms the commits to commit messages - * - * @since 0.1.0 - * - * @param {Object[]} commits The array of object containing the commits - * - * @return {String[]} - */ -function commitMessages(commits) { - return commits.map(function (commitObject) { - return commitObject.commit.message; - }); -} - -/** - * Gets all the commits between two dates - * - * @since 0.1.0 - * - * @param {GithubReleaseNotes} gren The gren object - * @param {string} since The since date in ISO - * @param {string} until The until date in ISO - * - * @return {Promise} The promise which resolves the [Array] commit messages - */ -function getCommitsBetweenTwo(gren, since, until) { - var loaded = utils.task('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until))); - - var options = { - since: since, - until: until, - per_page: 100 - }; - - return new Promise(function (resolve, reject) { - gren.repo.listCommits(options, function (err, commits) { - loaded(); - - if(err) { - reject(err); - } else { - resolve(commitMessages(commits)); - } - }); - }); + if(gren.isEditingLatestRelease) { + return editRelease(gren, block.id, releaseOptions); + } else { + return createRelease(gren, releaseOptions); + } } /** * Get all the tags of the repo * * @since 0.1.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * * @return {Promise} */ -function getLastTags(gren, releaseTagName) { +function getLastTags(gren, releases) { var loaded = utils.task('Getting latest tag'); return new Promise(function (resolve, reject) { @@ -150,10 +150,34 @@ function getLastTags(gren, releaseTagName) { reject(err); } else { var filteredTags = tags.filter(function(tag, index) { - return index === 0 || (releaseTagName ? tag.name === releaseTagName : index === tags.length-1); + var previousTag = releases[0].tag_name ? tag.name === releases[0].tag_name : index === tags.length-1; + + return index === 0 || previousTag; + }) + .map(function (tag) { + return { + tag: tag, + releaseId: releases.filter(function (release) { + return release.tag_name === tag.name; + })[0].id || null + }; }); - resolve(filteredTags); + + if(filteredTags.length === 1 && gren.options.override) { + gren.isEditingLatestRelease = true; + + var secondTag = { + tag: tags.filter(function (tag) { + return tag.name === releases[1].tag_name; + })[0], + releaseId: releases[1].id + }; + + resolve(filteredTags.concat(secondTag)); + } else { + resolve(filteredTags); + } } }); }); @@ -163,6 +187,7 @@ function getLastTags(gren, releaseTagName) { * Get the dates of the last two tags * * @since 0.1.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @param {Object[]} tags List of all the tags in the repo @@ -170,18 +195,15 @@ function getLastTags(gren, releaseTagName) { * @return {Promise[]} The promises which returns the dates */ function getTagDates(gren, tags) { - var loaded = utils.task('Getting the tag dates ranges'); - return tags.map(function (tag) { return new Promise(function (resolve, reject) { - gren.repo.getCommit(tag.commit.sha, function (err, commit) { - loaded(); - + gren.repo.getCommit(tag.tag.commit.sha, function (err, commit) { if(err) { reject(err); } else { resolve({ - name: tag.name, + id: tag.releaseId, + name: tag.tag.name, date: commit.committer.date }); } @@ -194,6 +216,7 @@ function getTagDates(gren, tags) { * Get all releases * * @since 0.5.0 + * @private * * @param {Object[]} releases A list of release Objects * @@ -215,6 +238,7 @@ function getReleaseDates(gren, releases) { * Get all releases * * @since 0.5.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @@ -245,6 +269,7 @@ function getListReleases(gren) { * Get the latest two releases * * @since 0.5.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @@ -257,26 +282,11 @@ function getLatestTwoRelease(gren) { }); } -/** - * Get the latest release - * - * @since 0.5.0 - * - * @param {GithubReleaseNotes} gren The gren object - * - * @return {Promise} The promise which resolves the tag name of the release - */ -function getLatestRelease(gren) { - return getListReleases(gren) - .then(function(releases) { - return releases[0]; - }); -} - /** * Return a string with a - to be a bullet list (used for a mapping) * * @since 0.1.0 + * @private * * @param {string} message * @@ -290,6 +300,7 @@ function templateCommits(message) { * Generate the MD template from all the labels of a specific issue * * @since 0.5.0 + * @private * * @param {Object} issue * @@ -306,6 +317,7 @@ function templateLabels(issue) { * Generate the MD template a block * * @since 0.5.0 + * @private * * @param {Object} block ({name: 'v1.2.3', body: []}) * @@ -322,6 +334,7 @@ function templateBlock(block) { * Generate the MD template for each issue * * @since 0.5.0 + * @private * * @param {Object} issue * @@ -335,6 +348,7 @@ function templateIssue(issue) { * Generate the Changelog MD template * * @since 0.5.0 + * @private * * @param {Object[]} blocks * @@ -350,7 +364,8 @@ function templateChangelog(blocks) { /** * Return a commit messages generated body * - * @since 0.5.0 + * @since 0.1.0 + * @private * * @param {string} message * @@ -382,27 +397,88 @@ function generateCommitsBody(gren, messages) { } /** - * Get the list of all the issues of a specific milestone + * Transforms the commits to commit messages + * + * @since 0.1.0 + * @private + * + * @param {Object[]} commits The array of object containing the commits + * + * @return {String[]} + */ +function commitMessages(commits) { + return commits.map(function (commitObject) { + return commitObject.commit.message; + }); +} + +/** + * Gets all the commits between two dates + * + * @since 0.1.0 + * @private + * + * @param {GithubReleaseNotes} gren The gren object + * @param {string} since The since date in ISO + * @param {string} until The until date in ISO + * + * @return {Promise} The promise which resolves the [Array] commit messages + */ +function getCommitsBetweenTwo(gren, since, until) { + process.stdout.write(chalk.green('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until)) + '\n')); + + var options = { + since: since, + until: until, + per_page: 100 + }; + + return new Promise(function (resolve, reject) { + gren.repo.listCommits(options, function (err, commits) { + if(err) { + reject(err); + } else { + resolve(commitMessages(commits)); + } + }); + }); +} + +/** + * Get the blocks of commits based on release dates * * @since 0.5.0 + * @private * - * @param {Object[]} issues The list of all the issues - * @param {Object} milestone The milestone whom filter the issues + * @param {GithubReleaseNotes} gren + * @param {Array} releaseRanges The array of date ranges * - * @return {string} + * @return {Promise[]} */ -function getMilestoneIssues(issues, milestone) { - return issues.filter(function(issue) { - return issue.milestone !== null && issue.milestone.id === milestone.id; - }) - .map(templateIssue) - .join('\n'); +function getCommitBlocks(gren, releaseRanges) { + console.log(chalk.blue('\nCreating the body blocks from commits:')); + + return Promise.all( + releaseRanges + .map(function (range) { + return getCommitsBetweenTwo(gren, range[1].date, range[0].date) + .then(function (commits) { + return { + id: range[0].id, + release: range[0].name, + date: range[0].date, + body: generateCommitsBody(gren, commits) + }; + }); + }) + ); } /** * Get all the closed issues from the current repo * * @since 0.5.0 + * @private * * @param {GithubReleaseNotes} gren The gren object * @@ -424,7 +500,7 @@ function getClosedIssues(gren) { return !issue.pull_request; }); - process.stdout.write(filteredIssues.length + ' issues found'); + process.stdout.write(filteredIssues.length + ' issues found\n'); resolve(filteredIssues); } @@ -432,31 +508,11 @@ function getClosedIssues(gren) { }); } -/** - * Get all the closed milestones from the current repo - * - * @since 0.5.0 - * - * @param {GithubReleaseNotes} gren The gren object - * - * @return {Promise} The promise which resolves the list of the milestones - */ -function getClosedMilestones(gren) { - return new Promise(function (resolve, reject) { - gren.issues.listMilestones({ state: 'closed' }, function (err, milestones) { - if(err) { - reject(err); - } else { - resolve(milestones); - } - }); - }); -} - /** * Get the blocks of issues based on release dates * * @since 0.5.0 + * @private * * @param {GithubReleaseNotes} gren * @param {Array} releaseRanges The array of date ranges @@ -480,8 +536,8 @@ function getIssueBlocks(gren, releaseRanges) { }) .map(templateIssue) .join('\n') || range[0].body + '\n'; - return { + id: range[0].id, release: range[0].name, date: range[0].date, body: body @@ -491,37 +547,51 @@ function getIssueBlocks(gren, releaseRanges) { } /** - * Get the blocks of commits based on release dates + * Get the list of all the issues of a specific milestone * * @since 0.5.0 + * @private * - * @param {GithubReleaseNotes} gren - * @param {Array} releaseRanges The array of date ranges + * @param {Object[]} issues The list of all the issues + * @param {Object} milestone The milestone whom filter the issues * - * @return {Promise[]} + * @return {string} */ -function getCommitBlocks(gren, releaseRanges) { - console.log('\nCreating the body blocks from commits:'); +function getMilestoneIssues(issues, milestone) { + return issues.filter(function(issue) { + return issue.milestone !== null && issue.milestone.id === milestone.id; + }) + .map(templateIssue) + .join('\n'); +} - return Promise.all( - releaseRanges - .map(function (range) { - return getCommitsBetweenTwo(gren, range[1].date, range[0].date) - .then(function (commits) { - return { - release: range[0].name, - date: range[0].date, - body: generateCommitsBody(gren, commits) - }; - }); - }) - ); +/** + * Get all the closed milestones from the current repo + * + * @since 0.5.0 + * @private + * + * @param {GithubReleaseNotes} gren The gren object + * + * @return {Promise} The promise which resolves the list of the milestones + */ +function getClosedMilestones(gren) { + return new Promise(function (resolve, reject) { + gren.issues.listMilestones({ state: 'closed' }, function (err, milestones) { + if(err) { + reject(err); + } else { + resolve(milestones); + } + }); + }); } /** * Create the ranges of release dates * * @since 0.5.0 + * @private * * @param {Array} releaseDates The release dates * @@ -542,6 +612,7 @@ function createReleaseRanges(releaseDates) { * Generate a CHANGELOG.md file based on Time and issues * * @since 0.5.0 + * @private * * @return {Promise[]} */ @@ -570,6 +641,7 @@ function generateReleaseDatesChangelogBody(gren) { * Generate a CHANGELOG.md file based on Milestones and issues * * @since 0.5.0 + * @private * * @return {Promise} */ @@ -607,6 +679,7 @@ function generateMilestonesChangelogBody(gren) { * Create the CHANGELOG.md file * * @since 0.5.0 + * @private * * @param {string} body * @@ -621,7 +694,7 @@ function createChangelog(gren, body) { throw err; } - process.stdout.write('\n' + chalk.green('The changelog file has been saved!')); + process.stdout.write('\n' + chalk.green('The changelog file has been saved!\n')); return true; }); @@ -639,18 +712,23 @@ function createChangelog(gren, body) { var newReleaseName = body.match(/(##\s[\w\.]+)/)[0]; if(data.match(newReleaseName)) { - console.error(chalk.red('\nThis release is already in the changelog')); if(gren.options.force) { - createFile(body + data.replace(/^(#\s?\w*\n\n)/g, '')); + createFile(body + '\n --- \n\n' + data.replace(/^(#\s?\w*\n\n)/g, '')); + + return true; } else if(gren.options.override) { createFile(body); + + return true; } - return; + console.error(chalk.red('\nThis release is already in the changelog\n')); + + return false; } - createFile(body + data.replace(/^(#\s?\w*\n\n)/g, '')); + createFile(body + '\n --- \n\n' + data.replace(/^(#\s?\w*\n\n)/g, '')); }); } @@ -664,6 +742,7 @@ function createChangelog(gren, body) { * Generate the GithubReleaseNotes getting the options from the git config * * @since 0.5.0 + * @private * * @return {Promise[]} */ @@ -699,6 +778,7 @@ function hasNetwork() { * @param {Object} [options] The options of the module * * @since 0.1.0 + * @public * * @constructor */ @@ -706,6 +786,7 @@ function GithubReleaseNotes(options) { this.options = Object.assign({}, defaults, options || utils.getBashOptions(process.argv)); this.repo = null; this.issues = null; + this.isEditingLatestRelease = false; } /** @@ -713,12 +794,11 @@ function GithubReleaseNotes(options) { * a given module method * * @since 0.5.0 + * @public * * @param {function} action * * @return {Promise} The generated options - * - * @todo: check the network first */ GithubReleaseNotes.prototype.init = function() { var gren = this; @@ -756,30 +836,36 @@ GithubReleaseNotes.prototype.init = function() { * Get All the tags, get the dates, get the commits between those dates and prepeare the release * * @since 0.1.0 + * @public * * @return {Promise} */ GithubReleaseNotes.prototype.release = function() { utils.printTask('Release'); + var loaded; var gren = this; var dataSource = { issues: getIssueBlocks, commits: getCommitBlocks }; - return getLatestRelease(this) - .then(function (release) { - return getLastTags(gren, release.tag_name || false); + return getLatestTwoRelease(this) + .then(function (releases) { + return getLastTags(gren, releases || false); }) .then(function (tags) { if(tags.length === 1) { throw chalk.red('The latest tag is the latest release!'); } + loaded = utils.task('Getting the tag dates ranges'); + return Promise.all(getTagDates(gren, tags)); }) - .then(function (releaseDates) { + .then(function (releaseDates) { + loaded(); + return dataSource[gren.options.dataSource](gren, createReleaseRanges(releaseDates)); }) .then(function (blocks) { @@ -791,10 +877,6 @@ GithubReleaseNotes.prototype.release = function() { .catch(function (error) { console.error(error); - if(gren.options.force) { - gren.changelog(); - } - return gren.options.force; }); @@ -804,6 +886,7 @@ GithubReleaseNotes.prototype.release = function() { * Generate the Changelog based on milestones * * @since 0.5.0 + * @public * * @param {string} type The type of changelog */ From 0dc2b3f81a015d41b41a7cbc07f18fd29210224c Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 16 Jun 2016 14:31:43 +0100 Subject: [PATCH 16/35] Remove the milestones functions, saved for next release --- src/gren.js | 88 ++--------------------------------------------------- 1 file changed, 2 insertions(+), 86 deletions(-) diff --git a/src/gren.js b/src/gren.js index 220b34ea..84c90935 100644 --- a/src/gren.js +++ b/src/gren.js @@ -16,7 +16,6 @@ var defaults = { force: false, prefix: '', prerelease: false, - timeMeter: 'releaseDates', // || milestones, dateZero: new Date(0), override: false }; @@ -29,6 +28,7 @@ var defaults = { * @private * * @param {GithubReleaseNotes} gren The gren object + * @param {number} releaseId The id of the release to edit * @param {Object} releaseOptions The options to build the release: * @example * { @@ -546,47 +546,6 @@ function getIssueBlocks(gren, releaseRanges) { }); } -/** - * Get the list of all the issues of a specific milestone - * - * @since 0.5.0 - * @private - * - * @param {Object[]} issues The list of all the issues - * @param {Object} milestone The milestone whom filter the issues - * - * @return {string} - */ -function getMilestoneIssues(issues, milestone) { - return issues.filter(function(issue) { - return issue.milestone !== null && issue.milestone.id === milestone.id; - }) - .map(templateIssue) - .join('\n'); -} - -/** - * Get all the closed milestones from the current repo - * - * @since 0.5.0 - * @private - * - * @param {GithubReleaseNotes} gren The gren object - * - * @return {Promise} The promise which resolves the list of the milestones - */ -function getClosedMilestones(gren) { - return new Promise(function (resolve, reject) { - gren.issues.listMilestones({ state: 'closed' }, function (err, milestones) { - if(err) { - reject(err); - } else { - resolve(milestones); - } - }); - }); -} - /** * Create the ranges of release dates * @@ -637,44 +596,6 @@ function generateReleaseDatesChangelogBody(gren) { }); } -/** - * Generate a CHANGELOG.md file based on Milestones and issues - * - * @since 0.5.0 - * @private - * - * @return {Promise} - */ -function generateMilestonesChangelogBody(gren) { - return Promise.all([getClosedMilestones(gren), getClosedIssues(gren)]) - .then(function (data) { - var milestones = data[0]; - var issues = data[1]; - - // @TODO: Sort by date rather than reorder it - milestones.reverse(); - - return milestones.map(function(milestone) { - var date = new Date(milestone.closed_at); - var stringDate = date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear(); - - return { - title: milestone.title, - date: stringDate, - body: getMilestoneIssues(issues, milestone) - }; - }); - }) - .then(function (milestones) { - return templateChangelog(milestones); - }) - .catch(function (error) { - console.error(error); - - return false; - }); -} - /** * Create the CHANGELOG.md file * @@ -894,13 +815,8 @@ GithubReleaseNotes.prototype.changelog = function() { utils.printTask('\nChangelog'); var gren = this; - var changelogs = { - releaseDates: generateReleaseDatesChangelogBody, - milestones: generateMilestonesChangelogBody - }; - var changelogFunction = changelogs[this.options.timeMeter]; - return changelogFunction(this) + return generateReleaseDatesChangelogBody(this) .then(function(changelogBody) { return createChangelog(gren, changelogBody); }). From 08d015faad4e7d54fafa0169e306c9bac5ec386f Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 17 Jun 2016 21:21:48 +0100 Subject: [PATCH 17/35] Add a default for the includeMessages option --- src/gren.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gren.js b/src/gren.js index 84c90935..c3ab2ee4 100644 --- a/src/gren.js +++ b/src/gren.js @@ -15,6 +15,7 @@ var defaults = { draft: false, force: false, prefix: '', + includeMessages: 'commits', // || merges || all prerelease: false, dateZero: new Date(0), override: false From ceb05902d17b762f65d7eabb9ca750ee42de776e Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 17 Jun 2016 21:22:01 +0100 Subject: [PATCH 18/35] Update the package.json --- package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ab0883d4..b69cd36d 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "github-release-notes", - "version": "0.4.0", + "version": "0.5.0", "description": "Node module to publish release notes based on commits between the last two tags.", "main": "./github-release-notes.js", "scripts": { "start": "node github-release-notes.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "grunt test" }, "repository": { "type": "git", @@ -15,8 +15,11 @@ "Github", "Release", "notes", - "Release", - "Tag" + "Tag", + "Changelog", + "Changelog Generator", + "Issues", + "Commits" ], "author": "alexcanessa", "license": "ISC", From cf08171555c1337ce0f9b1202d143428a8f3c907 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 17 Jun 2016 21:22:17 +0100 Subject: [PATCH 19/35] Change the token expected reference --- src/github-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github-info.js b/src/github-info.js index 785e2888..fba20446 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -87,7 +87,7 @@ function repo(callback) { * @return {Promise} The promise that resolves token informations ({token: token}) */ function token(callback) { - return executeCommand('echo $CHANGELOG_GITHUB_TOKEN', function (token) { + return executeCommand('echo $GREN_GITHUB_TOKEN', function (token) { return { token: token }; From cbb8c7ec2f9437ceffb54d35cad94657b5fda888 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 17 Jun 2016 21:22:25 +0100 Subject: [PATCH 20/35] Update the README.md file --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 27ecfac4..fa8282ef 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# github-release-notes +# Github Release Notes [![npm version](https://badge.fury.io/js/github-release-notes.svg)](https://badge.fury.io/js/github-release-notes) +[![build](https://travis-ci.org/github-tools/github-release-notes.svg)](https://travis-ci.org/github-tools/github-release-notes) -> Node module which generates a release from the latest tag and compiles release notes based on commit messages between the last tag and the latest release. +> Node module that generates a release from the latest tag and compiles release notes based on commit messages or closed issues between the last tag and the latest release. It also can create a full changelog or add the latest release notes to the existing changelog file. ## Installation -The plugin requires Node `^0.12.`, since is using Promises. +The plugin requires `^0.12.` Install `github-release-notes` via npm: @@ -16,19 +17,52 @@ npm install github-release-notes --save-dev ## Usage -You can run the command via the terminal (the three arguments are all required): +**gren** can be ran through the terminal, but before you can use it, you need to set up a couple of things. + +### Github Informations + +**gren** by default looks for your local git configuration to get the repo informations. This means you need to run the command from the git repo folder. + +If you want, you can run it from wherever and specify a different repo as target, with: + +```shell +node github-release-notes --username=[username] --repo[repo name] +``` + +#### Token + +To work, **gren** needs a `github token` (that can be easily generated following [this link](https://help.github.com/articles/creating-an-access-token-for-command-line-use/)). _You only need "repo" scope for private repositories._ + +Once generated, you can run the gren command with the token as variable: + +```shell +node github-release-notes --token=your_token_here +``` + +Or you can add it to your `~/.bash_profile` or `~/.zshrc`) as follows: + +```shell +export GREN_GITHUB_TOKEN=your_token_here +``` + +And you're ready to use it! Just run this command in your terminal: ```shell -node github-release-notes --token=[token] --username=[username] --repo=[repo name] +node github-release-notes ``` -To generate a github token, follow [this link](https://help.github.com/articles/creating-an-access-token-for-command-line-use/); +The module will look for the last tag, get all the issues closed in the time between that tag and the latest release, and it wiil build release notes and draft the new release! -### Optionals +## Options -There are optional arguments such as: +Following the options for the module: -- `--draft=true` To set the release as a draft. Default: `false` -- `--prerelease=true` To set the release as a prerelease. Default: `false` -- `--prefix=v` Add a prefix to the tag version `e.g. v1.0.1` -- `--include-messages=merges/commits/all` used to filter the messages added to the release notes. Default: `commits` \ No newline at end of file +- `--action=release|changelog` The **gren** action to run. Default: `release` _(see details below for changelog generator)_ +- `--timeWrap=latest|history` The release notes you want to include in the changelog. Default: `latest` _Only applicable to the `changelog` action_ +- `--changelogFilename=CHANGELOG.md` The name of the changelog file. Default: `CHANGELOG.md` +- `--dataSource=issues|commits` The informations you want to use to build release notes. Default: `issues` +- `--draft=true|false` To set the release as a draft. Default: `false` +- `--prerelease=true|false` To set the release as a prerelease. Default: `false` +- `--prefix=v` Add a prefix to the tag version `e.g. v1.0.1`. Default: `null` +- `--include-messages=merges|commits|all` used to filter the messages added to the release notes. Default: `commits` +- `--override=true|false` Override the release notes if existing. Default: `false` From 3b59b96ba7e19000b14ca193480d84b91e26d71e Mon Sep 17 00:00:00 2001 From: Alex Canessa Date: Fri, 17 Jun 2016 21:25:24 +0100 Subject: [PATCH 21/35] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa8282ef..52ac5223 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Github Release Notes [![npm version](https://badge.fury.io/js/github-release-notes.svg)](https://badge.fury.io/js/github-release-notes) -[![build](https://travis-ci.org/github-tools/github-release-notes.svg)](https://travis-ci.org/github-tools/github-release-notes) +[![Build Status](https://travis-ci.org/github-tools/github-release-notes.svg?branch=release-0.5.0)](https://travis-ci.org/github-tools/github-release-notes) > Node module that generates a release from the latest tag and compiles release notes based on commit messages or closed issues between the last tag and the latest release. It also can create a full changelog or add the latest release notes to the existing changelog file. From a46d6ec18dae65f1bb1a4a76a0db0fef55befd58 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 16:30:26 +0100 Subject: [PATCH 22/35] Make the component global, use Object.assign --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b69cd36d..061b261e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "type": "git", "url": "git+https://github.com/alexcanessa/github-release-notes.git" }, + "preferGlobal": "true", + "bin": { + "gren": "bin/gren.js" + }, "keywords": [ "Github", "Release", @@ -31,7 +35,8 @@ "chalk": "^1.1.3", "es6-promise": "^3.2.1", "github-api": "^2.1.0", - "is-online": "^5.1.1" + "is-online": "^5.1.1", + "object-assign": "^4.1.0" }, "devDependencies": { "grunt": "^1.0.1", From 8eab41efa222753f740cc88665ea316f613598ba Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 16:31:00 +0100 Subject: [PATCH 23/35] Create a bin/gren.js for the global usage --- bin/gren.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 bin/gren.js diff --git a/bin/gren.js b/bin/gren.js new file mode 100644 index 00000000..6e1612f0 --- /dev/null +++ b/bin/gren.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +'use strict'; + +var GithubReleaseNotes = require('../src/gren'); +var gren = new GithubReleaseNotes(); +var utils = require('../src/utils'); + +var action = utils.getBashOptions(process.argv)['action']; + +gren.init() + .then(function (success) { + if(success) { + return gren[action || 'release'](); + } + }); From 488b5ff4f4ab8a4d9e970cdcd507d77b5c71ef7e Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 20:01:11 +0100 Subject: [PATCH 24/35] Throw error from github if the configuration is not set up --- src/github-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github-info.js b/src/github-info.js index fba20446..e889f13d 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -27,7 +27,7 @@ function executeCommand(command, callback) { }) .then(callback) .catch(function (error) { - console.log(chalk.red(error)); + throw chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder'); }); } From 1b7506502af1f19d18f793642c1d291d0dd4cf67 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 20:02:07 +0100 Subject: [PATCH 25/35] Update the CHANGELOG.md using issues --- CHANGELOG.md | 69 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c84318..235a1ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,70 +1,5 @@ # Changelog -## 0.4.0 (3/2/2016) +## 0.4.0 (03/03/2016) -- Changed option to --include-message -- Fixed default option -- Fixed typo -- Modifications to the options for messages to use lower case. - -Changed the casing of the include messages option -- Updated README.md -- Fixes after comments from Alex Canessa: - -1 - Since it is a new feature, the version should be 0.4.0 - -2 - I prefer to keep the default to Commits, to avoid a breaking change. -- Updated package.json version -- Added options for filtering messages - -Updated README.md -- NPM v0.3..3 -- Fix the release maker, when there are no previous releases - - --- - -## v0.3.2 (7/11/2015) - -- Version 0.3.1 -- Clean the code. Add the avoid message feature -- Fix the [ReferenceError: tags is not defined] error -- Package.json to reflect the real version -- Force option -- Release function returns a boolean. Stable version -- Refactoring of the promises, to return a promise from the .release() function -- Update README.md -- Divide the source code from the exec, creating the release() function -- Update README.md -- Release 0.2.0 -- Bit of refactoring -- [fix]: Change the prototype constructor -- [fix]: exporting the module -- src => lib -- Init the function and exports the constructor -- Install to start -- Move the lib into ./lib and require to allow the install from npm -- Update readme.md to show the npm version - - --- - -## v0.1.0 (12/10/2015) - -- [fix]: Avoid the double deleting if the first message is a merge -- Update the readme.md -- Reorder the functions, Release notes made between the latest tag and the latest release -- Change the default settings, fix the prepareRelease function binding the tags -- Fix the package.json file -- Remove global option -- Prefer Global option -- Change path to the bin -- Make the node global -- Update README.md -- update readme file -- Update the readme file -- Prefix option -- Documentation and prepareRelease function -- Error catching -- Update the readme.md file -- Release notes main js -- Package.json -- Add gitignore \ No newline at end of file +- [**enhancement**] Include Various Types Of Commit Messages [#5](https://github.com/github-tools/github-release-notes/issues/5) \ No newline at end of file From 70512abee1cf4905784835e0cf12b59207d02074 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 20:02:19 +0100 Subject: [PATCH 26/35] Update README.md --- README.md | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 52ac5223..8eb48c6d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ # Github Release Notes [![npm version](https://badge.fury.io/js/github-release-notes.svg)](https://badge.fury.io/js/github-release-notes) -[![Build Status](https://travis-ci.org/github-tools/github-release-notes.svg?branch=release-0.5.0)](https://travis-ci.org/github-tools/github-release-notes) +[![Build Status](https://travis-ci.org/github-tools/github-release-notes.svg)](https://travis-ci.org/github-tools/github-release-notes) > Node module that generates a release from the latest tag and compiles release notes based on commit messages or closed issues between the last tag and the latest release. It also can create a full changelog or add the latest release notes to the existing changelog file. ## Installation -The plugin requires `^0.12.` - Install `github-release-notes` via npm: ```shell -npm install github-release-notes --save-dev +npm install github-release-notes -g ``` ## Usage @@ -21,22 +19,22 @@ npm install github-release-notes --save-dev ### Github Informations -**gren** by default looks for your local git configuration to get the repo informations. This means you need to run the command from the git repo folder. +**gren** by default looks for your local git configuration to get the repo informations. This means you can run the command directly from the git repo folder. -If you want, you can run it from wherever and specify a different repo as target, with: +Otherwise, you can run it from wherever and specify a different repo as target, with: ```shell -node github-release-notes --username=[username] --repo[repo name] +gren --username=[username] --repo[repo name] ``` #### Token -To work, **gren** needs a `github token` (that can be easily generated following [this link](https://help.github.com/articles/creating-an-access-token-for-command-line-use/)). _You only need "repo" scope for private repositories._ +To work, **gren** needs a `github token` (that can be easily generated following [this link](https://help.github.com/articles/creating-an-access-token-for-command-line-use/)). _You only need "repo" scope._ Once generated, you can run the gren command with the token as variable: ```shell -node github-release-notes --token=your_token_here +gren --token=your_token_here ``` Or you can add it to your `~/.bash_profile` or `~/.zshrc`) as follows: @@ -48,7 +46,7 @@ export GREN_GITHUB_TOKEN=your_token_here And you're ready to use it! Just run this command in your terminal: ```shell -node github-release-notes +gren ``` The module will look for the last tag, get all the issues closed in the time between that tag and the latest release, and it wiil build release notes and draft the new release! @@ -58,11 +56,29 @@ The module will look for the last tag, get all the issues closed in the time bet Following the options for the module: - `--action=release|changelog` The **gren** action to run. Default: `release` _(see details below for changelog generator)_ -- `--timeWrap=latest|history` The release notes you want to include in the changelog. Default: `latest` _Only applicable to the `changelog` action_ -- `--changelogFilename=CHANGELOG.md` The name of the changelog file. Default: `CHANGELOG.md` -- `--dataSource=issues|commits` The informations you want to use to build release notes. Default: `issues` +- `--time-wrap=latest|history` The release notes you want to include in the changelog. Default: `latest` _Only applicable to the `changelog` action_ +- `--changelog-filename=CHANGELOG.md` The name of the changelog file. Default: `CHANGELOG.md` +- `--data-source=issues|commits` The informations you want to use to build release notes. Default: `issues` - `--draft=true|false` To set the release as a draft. Default: `false` - `--prerelease=true|false` To set the release as a prerelease. Default: `false` - `--prefix=v` Add a prefix to the tag version `e.g. v1.0.1`. Default: `null` - `--include-messages=merges|commits|all` used to filter the messages added to the release notes. Default: `commits` - `--override=true|false` Override the release notes if existing. Default: `false` + +## Changelog Generator + +**gren** can also update generate the changelog. + +The following command, will generate the release notes for the latest release, and add it to an existing file or create it in the same directory where you run the command. + +```shell +gren --action=changelog +``` + +The generated release notes will be added at the top of the file, and will look like this: + +> # Changelog +## v0.4.3 (02/03/2016) +[**bug**] This is a issue name [#123](https://github.com/github-tools/github-tools) + +To see a full example of the changelog here [CHANGELOG.md](#) From d31b1a85af685b6198daf74065aee5b33094d1e8 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 20:02:39 +0100 Subject: [PATCH 27/35] Clean the code for utils.js --- src/utils.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils.js b/src/utils.js index b4d77e3c..13fb0bd8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -88,8 +88,10 @@ function getBashOptions(args) { for(var i=2;i Date: Mon, 26 Sep 2016 20:03:12 +0100 Subject: [PATCH 28/35] Apply several of the new issues --- src/gren.js | 173 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 54 deletions(-) diff --git a/src/gren.js b/src/gren.js index c3ab2ee4..cf9c32f0 100644 --- a/src/gren.js +++ b/src/gren.js @@ -8,7 +8,10 @@ var chalk = require('chalk'); var Promise = Promise || require('es6-promise').Promise; var isOnline = require('is-online'); +Object.prototype.assign ? Object.prototype.assign : require('object-assign'); + var defaults = { + tags: false, timeWrap: 'latest', // || history changelogFilename: 'CHANGELOG.md', dataSource: 'issues', // || commits @@ -53,7 +56,7 @@ function editRelease(gren, releaseId, releaseOptions) { if(err) { reject(chalk.red(err)); } else { - console.log(chalk.green('\n\n' + release.name + ' has successfully updated!')); + console.log(chalk.green('\n\n' + release.name + ' has been successfully updated!')); resolve(true); } @@ -123,13 +126,42 @@ function prepareRelease(gren, block) { prerelease: gren.options.prerelease }; - if(gren.isEditingLatestRelease) { + if(block.id) { return editRelease(gren, block.id, releaseOptions); } else { return createRelease(gren, releaseOptions); } } +/** + * Get the tags information from the given ones, and adds + * the next one in case only one is given + * + * @since 0.5.0 + * @private + * + * @param {Boolean || Array} selectedTags + * @param {[Object]} tags + * + * @return {Boolean || Array} + */ +function getSelectedTags(optionTags, tags) { + if (!optionTags) { + return false; + } + + var selectedTags = [].concat(optionTags); + + return tags.filter(function (tag, index) { + var isSelectedTag = selectedTags.indexOf(tag.name) !== -1; + + if (isSelectedTag && selectedTags.length === 1 && tags[index + 1]) { + selectedTags.push(tags[index + 1].name); + } + return isSelectedTag; + }).slice(0, 2); +} + /** * Get all the tags of the repo * @@ -141,7 +173,7 @@ function prepareRelease(gren, block) { * @return {Promise} */ function getLastTags(gren, releases) { - var loaded = utils.task('Getting latest tag'); + var loaded = utils.task('Getting tags'); return new Promise(function (resolve, reject) { gren.repo.listTags(function (err, tags) { @@ -150,35 +182,25 @@ function getLastTags(gren, releases) { if(err) { reject(err); } else { - var filteredTags = tags.filter(function(tag, index) { - var previousTag = releases[0].tag_name ? tag.name === releases[0].tag_name : index === tags.length-1; - - return index === 0 || previousTag; - }) - .map(function (tag) { - return { - tag: tag, - releaseId: releases.filter(function (release) { - return release.tag_name === tag.name; - })[0].id || null - }; - }); - + var filteredTags = + (getSelectedTags(gren.options.tags, tags) || [tags[0], tags[1]]) + .map(function (tag) { + var tagRelease = releases && releases.filter(function (release) { + return release.tag_name === tag.name; + })[0] || false; + var releaseId = tagRelease ? tagRelease.id : null; - if(filteredTags.length === 1 && gren.options.override) { - gren.isEditingLatestRelease = true; + return { + tag: tag, + releaseId: releaseId + }; + }); - var secondTag = { - tag: tags.filter(function (tag) { - return tag.name === releases[1].tag_name; - })[0], - releaseId: releases[1].id - }; - - resolve(filteredTags.concat(secondTag)); - } else { - resolve(filteredTags); + if(filteredTags[0].releaseId && !gren.options.override) { + reject(chalk.red(filteredTags[0].tag.name + ' is a release, use --override flag to override an existing release!')); } + + resolve(filteredTags); } }); }); @@ -198,7 +220,7 @@ function getLastTags(gren, releases) { function getTagDates(gren, tags) { return tags.map(function (tag) { return new Promise(function (resolve, reject) { - gren.repo.getCommit(tag.tag.commit.sha, function (err, commit) { + gren.repo.getCommit(tag.tag.commit.sha, function (err, commit) { if(err) { reject(err); } else { @@ -232,7 +254,7 @@ function getReleaseDates(gren, releases) { date: release.created_at, body: release.body || null }; - }).concat(releases.length === 1 || gren.options.timeWrap === 'history' && {id: 0, date: gren.options.dateZero} || []); + }); } /** @@ -267,7 +289,7 @@ function getListReleases(gren) { } /** - * Get the latest two releases + * Get the latest releases * * @since 0.5.0 * @private @@ -276,7 +298,7 @@ function getListReleases(gren) { * * @return {Promise} The promise which resolves the tag name of the release */ -function getLatestTwoRelease(gren) { +function getLastTwoReleases(gren) { return getListReleases(gren) .then(function(releases) { return releases.slice(0, 2); @@ -362,6 +384,20 @@ function templateChangelog(blocks) { .join('\n\n --- \n\n'); } +/** + * Generate the Changelog issues body template + * + * @since 0.5.0 + * @private + * + * @param {Object[]} blocks + * + * @return {string} + */ +function templateIssueBody(body) { + return body.length ? body.join('\n') || range[0].body + '\n' : '*No changelog for this release.*'; +} + /** * Return a commit messages generated body * @@ -468,7 +504,7 @@ function getCommitBlocks(gren, releaseRanges) { id: range[0].id, release: range[0].name, date: range[0].date, - body: generateCommitsBody(gren, commits) + body: generateCommitsBody(gren, commits) + '\n' }; }); }) @@ -535,34 +571,59 @@ function getIssueBlocks(gren, releaseRanges) { Date.parse(range[0].date) ); }) - .map(templateIssue) - .join('\n') || range[0].body + '\n'; + .map(templateIssue); + return { id: range[0].id, release: range[0].name, date: range[0].date, - body: body + body: templateIssueBody(body) }; }); }); } +/** + * Sort releases by dates + * + * @since 0.5.0 + * @private + * + * @param {Array} releaseDates + * + * @return {Array} + */ +function sortReleasesByDate(releaseDates) { + return releaseDates.sort(function (release1, release2) { + return new Date(release1.date) < new Date(release2.date) ? 1 : -1; + }); +} + /** * Create the ranges of release dates * * @since 0.5.0 * @private - * + * + * @param {GithubReleaseNotes} gren * @param {Array} releaseDates The release dates * * @return {Array} */ -function createReleaseRanges(releaseDates) { +function createReleaseRanges(gren, releaseDates) { var ranges = []; var range = 2; + var sortedReleaseDates = sortReleasesByDate(releaseDates); + + if (sortedReleaseDates.length === 1 || gren.options.timeWrap === 'history') { + sortedReleaseDates.push({ + id: 0, + date: new Date(0) + }); + } - for(var i = 0; i Date: Mon, 26 Sep 2016 20:07:50 +0100 Subject: [PATCH 29/35] Lint js code --- src/gren.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/gren.js b/src/gren.js index cf9c32f0..4bc09452 100644 --- a/src/gren.js +++ b/src/gren.js @@ -7,8 +7,7 @@ var fs = require('fs'); var chalk = require('chalk'); var Promise = Promise || require('es6-promise').Promise; var isOnline = require('is-online'); - -Object.prototype.assign ? Object.prototype.assign : require('object-assign'); +var ObjectAssign = require('object-assign'); var defaults = { tags: false, @@ -394,8 +393,8 @@ function templateChangelog(blocks) { * * @return {string} */ -function templateIssueBody(body) { - return body.length ? body.join('\n') || range[0].body + '\n' : '*No changelog for this release.*'; +function templateIssueBody(body, rangeBody) { + return body.length ? body.join('\n') || rangeBody + '\n' : '*No changelog for this release.*'; } /** @@ -577,7 +576,7 @@ function getIssueBlocks(gren, releaseRanges) { id: range[0].id, release: range[0].name, date: range[0].date, - body: templateIssueBody(body) + body: templateIssueBody(body, range[0].body) }; }); }); @@ -770,7 +769,7 @@ function hasNetwork() { * @constructor */ function GithubReleaseNotes(options) { - this.options = Object.assign({}, defaults, options || utils.getBashOptions(process.argv)); + this.options = ObjectAssign({}, defaults, options || utils.getBashOptions(process.argv)); this.options.tags = this.options.tags && this.options.tags.split(','); this.repo = null; this.issues = null; @@ -800,7 +799,7 @@ GithubReleaseNotes.prototype.init = function() { } }) .then(function (optionData) { - gren.options = Object.assign(...optionData, gren.options); + gren.options = ObjectAssign(...optionData, gren.options); if(!gren.options.token) { throw chalk.red('You need to provide the token'); From 5a8283a5ba7de8dc93adc47abf6e266d96240c52 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 20:11:29 +0100 Subject: [PATCH 30/35] Update documentation --- CHANGELOG.md | 14 +++++++++++++- src/gren.js | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 235a1ac1..78341a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,16 @@ ## 0.4.0 (03/03/2016) -- [**enhancement**] Include Various Types Of Commit Messages [#5](https://github.com/github-tools/github-release-notes/issues/5) \ No newline at end of file +- [**enhancement**] Include Various Types Of Commit Messages [#5](https://github.com/github-tools/github-release-notes/issues/5) + + --- + +## v0.3.2 (07/12/2015) + +- [**enhancement**] Cleanse option [#3](https://github.com/github-tools/github-release-notes/issues/3) + + --- + +## v0.1.0 (12/11/2015) + +*No changelog for this release.* \ No newline at end of file diff --git a/src/gren.js b/src/gren.js index 4bc09452..eebf10b7 100644 --- a/src/gren.js +++ b/src/gren.js @@ -139,10 +139,10 @@ function prepareRelease(gren, block) { * @since 0.5.0 * @private * - * @param {Boolean || Array} selectedTags - * @param {[Object]} tags + * @param {Boolean|Array} selectedTags + * @param {Object[]} tags * - * @return {Boolean || Array} + * @return {Boolean|Array} */ function getSelectedTags(optionTags, tags) { if (!optionTags) { From bbc634b9cbbd9db1e7278773eb3d8e5a884edd63 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 21:52:28 +0100 Subject: [PATCH 31/35] Update the README.md and use the flags over the git configuration --- Gruntfile.js | 2 +- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++--- src/github-info.js | 2 +- src/gren.js | 10 ++++---- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index adc072ee..d63c5c1f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,7 +20,7 @@ module.exports = function(grunt) { }, jsdoc : { dist : { - src: ['CHANGELOG.md', 'src/*.js'], + src: ['README.md', 'src/*.js'], readme: 'README.md', version: true, options: { diff --git a/README.md b/README.md index 8eb48c6d..51fe693b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![npm version](https://badge.fury.io/js/github-release-notes.svg)](https://badge.fury.io/js/github-release-notes) [![Build Status](https://travis-ci.org/github-tools/github-release-notes.svg)](https://travis-ci.org/github-tools/github-release-notes) -> Node module that generates a release from the latest tag and compiles release notes based on commit messages or closed issues between the last tag and the latest release. It also can create a full changelog or add the latest release notes to the existing changelog file. +> Node module that generates release notes based on commit messages or closed issues between tags. It also can create a full changelog or add the latest release notes to the existing changelog file. ## Installation @@ -24,7 +24,7 @@ npm install github-release-notes -g Otherwise, you can run it from wherever and specify a different repo as target, with: ```shell -gren --username=[username] --repo[repo name] +gren --username=[username] --repo=[repo name] ``` #### Token @@ -49,13 +49,14 @@ And you're ready to use it! Just run this command in your terminal: gren ``` -The module will look for the last tag, get all the issues closed in the time between that tag and the latest release, and it wiil build release notes and draft the new release! +The module will look for the last tag, get all the issues closed in the time between that tag and the one before, and it will build release notes and draft the new release! ## Options Following the options for the module: - `--action=release|changelog` The **gren** action to run. Default: `release` _(see details below for changelog generator)_ +- `--tags=0.1.0|0.2.0,0.1.0` A specific tag or the range of tags to build the release notes from. - `--time-wrap=latest|history` The release notes you want to include in the changelog. Default: `latest` _Only applicable to the `changelog` action_ - `--changelog-filename=CHANGELOG.md` The name of the changelog file. Default: `CHANGELOG.md` - `--data-source=issues|commits` The informations you want to use to build release notes. Default: `issues` @@ -65,6 +66,46 @@ Following the options for the module: - `--include-messages=merges|commits|all` used to filter the messages added to the release notes. Default: `commits` - `--override=true|false` Override the release notes if existing. Default: `false` +## Examples + +The ways to use **gren** are various. + +### Simple + +The simple way, just looks for the last tag, gets all the issues closed between that tag and the one before and creates the new release with the generated body. + +``` +gren +``` + +### Commit messages + +Adding the flag `--data-source=commits` will change the source of the release notes to be the commit messages. + +``` +gren --data-source=commits +``` + +### Release specific tags + +The flag `--tags` accepts one or two tags. +If you only give one tag, it will get the issues (or commit messages) between that tag and the one before. +If you give two tags it will generate the release notes with the issues (or commit messages) between those two tag dates. + +``` +gren --tags=2.0.0,1.0.0 +``` + +### Override an existing release + +If you trying to create an existing release, **gren** will throw an error *"0.3.0 is a release, use --override flag to override an existing release!* +If you want then to override, simple use: + +``` +gren --override --tags=0.3.0 +``` + + ## Changelog Generator **gren** can also update generate the changelog. @@ -81,4 +122,16 @@ The generated release notes will be added at the top of the file, and will look ## v0.4.3 (02/03/2016) [**bug**] This is a issue name [#123](https://github.com/github-tools/github-tools) -To see a full example of the changelog here [CHANGELOG.md](#) +### Generate a full changelog + +If tou want to generate the whole changelog, you need to use the `--time-wrap=history`. This will generate a changelog based on issues (or on commit messages if the `--data-source=commits` is present). + +If you want to override the existing changelog, use `--override`. + +The usage would then be: + +``` +gren --time-wrap=history --override +``` + +To see a full example of the changelog here [CHANGELOG.md](#) \ No newline at end of file diff --git a/src/github-info.js b/src/github-info.js index e889f13d..4c0c0230 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -27,7 +27,7 @@ function executeCommand(command, callback) { }) .then(callback) .catch(function (error) { - throw chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder'); + throw chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder, or you using the --username and --repo flags.'); }); } diff --git a/src/gren.js b/src/gren.js index eebf10b7..012b11c9 100644 --- a/src/gren.js +++ b/src/gren.js @@ -732,11 +732,11 @@ function createChangelog(gren, body) { * * @return {Promise[]} */ -function generateOptions() { +function generateOptions(options) { return Promise.all([ - githubInfo.user(), - githubInfo.repo(), - githubInfo.token() + options.user ? Promise.resolve(options.user) : githubInfo.user(), + options.repo ? Promise.resolve(options.repo) : githubInfo.repo(), + options.token ? Promise.resolve(options.token) : githubInfo.token() ]); } @@ -793,7 +793,7 @@ GithubReleaseNotes.prototype.init = function() { return hasNetwork() .then(function (success) { if(success) { - return generateOptions(gren, gren.options); + return generateOptions(gren.options); } else { throw chalk.red('You need to have network connectivity'); } From 0b71007e13bb625d25c9b10f38c12a433619bfce Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 22:10:32 +0100 Subject: [PATCH 32/35] Change error text --- src/gren.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gren.js b/src/gren.js index 012b11c9..b21c8b81 100644 --- a/src/gren.js +++ b/src/gren.js @@ -97,7 +97,7 @@ function createRelease(gren, releaseOptions) { )); reject(false); } else { - console.log(chalk.green('\n\n' + release.name + ' has successfully created!')); + console.log(chalk.green('\n\n' + release.name + ' has been successfully created!')); resolve(true); } @@ -869,7 +869,7 @@ GithubReleaseNotes.prototype.release = function() { }; /** - * Generate the Changelog based on milestones + * Generate the Changelog * * @since 0.5.0 * @public From d8d40b4a104a48f449ba25090fdb5a1ff4a7734e Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 22:17:50 +0100 Subject: [PATCH 33/35] Clean gren.js --- src/gren.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gren.js b/src/gren.js index b21c8b81..da456e44 100644 --- a/src/gren.js +++ b/src/gren.js @@ -96,7 +96,7 @@ function createRelease(gren, releaseOptions) { responseText.errors[0].code )); reject(false); - } else { + } else { console.log(chalk.green('\n\n' + release.name + ' has been successfully created!')); resolve(true); From 3bc4cef414a87256f135c8bd09349bbdadc39320 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Mon, 26 Sep 2016 22:38:39 +0100 Subject: [PATCH 34/35] Replace jshint with eslint and add .editorconfig to the repo --- .editorconfig | 14 + .eslintrc | 26 ++ .jshintrc | 31 -- Gruntfile.js | 64 +-- README.md | 1 + package.json | 6 +- src/github-info.js | 90 ++--- src/gren.js | 940 ++++++++++++++++++++++----------------------- src/utils.js | 88 ++--- 9 files changed, 633 insertions(+), 627 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc delete mode 100644 .jshintrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..201241fe --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Editor code style configuration +# http://editorconfig.org/ + +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..6df7020c --- /dev/null +++ b/.eslintrc @@ -0,0 +1,26 @@ +{ + "env": { + "node": true + }, + "parserOptions": { + "ecmaVersion": 5, + "sourceType": "module", + "ecmaFeatures": { + "impliedStrict": true + }, + "allowImportExportEverywhere": false + }, + + "extends": [ + "standard" + ], + + "rules": { + "semi": [2, "always"], + "no-empty": 2, + "array-callback-return": 2, + "indent": [2, 4, { "SwitchCase": 1 }], + "space-before-function-paren": [2, "never"], + "no-debugger": 0 + } +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 55893226..00000000 --- a/.jshintrc +++ /dev/null @@ -1,31 +0,0 @@ -{ - "curly": true, - "eqeqeq": true, - "forin": false, - "freeze": true, - "funcscope": false, - "futurehostile": true, - "globals": { - "define": false - }, - "globalstrict": true, - "iterator": false, - "latedef": true, - "maxcomplexity": 6, - "maxdepth": 3, - "maxparams": 4, - "maxstatements": 15, - "noarg": true, - "nocomma": true, - "nonbsp": true, - "nonew": true, - "notypeof": true, - "shadow": true, - "singleGroups": true, - "unused": true, - - "strict": true, - - "node": true, - "esversion": 6 -} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index d63c5c1f..c6c8247f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,37 +1,37 @@ 'use strict'; module.exports = function(grunt) { - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-jsdoc'); - grunt.loadNpmTasks('grunt-contrib-nodeunit'); + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-jsdoc'); + grunt.loadNpmTasks('grunt-contrib-nodeunit'); - grunt.initConfig({ - nodeunit: { - all: ['test/**/*.js'] - }, - jshint: { - options: { - jshintrc: '.jshintrc', - }, - src: [ - 'Gruntfile.js', - 'src/**/*.js' - ] - }, - jsdoc : { - dist : { - src: ['README.md', 'src/*.js'], - readme: 'README.md', - version: true, - options: { - destination: 'docs', - template : "node_modules/ink-docstrap/template", - configure : "node_modules/ink-docstrap/template/jsdoc.conf.json" - } - } - } - }); + grunt.initConfig({ + nodeunit: { + all: ['test/**/*.js'] + }, + eslint: { + options: { + fix: true + }, + target: [ + 'Gruntfile.js', + 'src/**/*.js' + ] + }, + jsdoc: { + dist: { + src: ['README.md', 'src/*.js'], + readme: 'README.md', + version: true, + options: { + destination: 'docs', + template: 'node_modules/ink-docstrap/template', + configure: 'node_modules/ink-docstrap/template/jsdoc.conf.json' + } + } + } + }); - grunt.registerTask('ship', ['jshint', 'jsdoc']); - grunt.registerTask('test', ['jshint', 'nodeunit']); -}; \ No newline at end of file + grunt.registerTask('ship', ['eslint', 'jsdoc']); + grunt.registerTask('test', ['eslint', 'nodeunit']); +}; diff --git a/README.md b/README.md index 51fe693b..7e1b0f6f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Github Release Notes [![npm version](https://badge.fury.io/js/github-release-notes.svg)](https://badge.fury.io/js/github-release-notes) +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com) [![Build Status](https://travis-ci.org/github-tools/github-release-notes.svg)](https://travis-ci.org/github-tools/github-release-notes) > Node module that generates release notes based on commit messages or closed issues between tags. It also can create a full changelog or add the latest release notes to the existing changelog file. diff --git a/package.json b/package.json index 061b261e..719b8561 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,13 @@ "object-assign": "^4.1.0" }, "devDependencies": { + "eslint": "^3.6.0", + "eslint-config-standard": "^6.0.1", + "eslint-plugin-promise": "^2.0.1", + "eslint-plugin-standard": "^2.0.0", "grunt": "^1.0.1", - "grunt-contrib-jshint": "^1.0.0", "grunt-contrib-nodeunit": "^1.0.0", + "grunt-eslint": "^19.0.0", "grunt-jsdoc": "^2.1.0", "ink-docstrap": "^1.2.1" } diff --git a/src/github-info.js b/src/github-info.js index 4c0c0230..1ea5f33e 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -1,33 +1,33 @@ 'use strict'; -var exec = require('child_process').exec; -var chalk = require('chalk'); -var Promise = Promise || require('es6-promise').Promise; +let exec = require('child_process').exec; +let chalk = require('chalk'); +let Promise = Promise || require('es6-promise').Promise; /** * Execute a command in the bash and run a callback * * @since 0.5.0 * @private - * + * * @param {string} command The command to execute * @param {Function} callback The callback which returns the stdout - * + * * @return {Promise} */ function executeCommand(command, callback) { - return new Promise(function (resolve, reject) { - exec(command, function(err, stdout, stderr){ - if(err || stderr) { - reject(err || stderr); - } else { - resolve(stdout.replace('\n', '')); - } - }); - }) + return new Promise(function(resolve, reject) { + exec(command, function(err, stdout, stderr) { + if (err || stderr) { + reject(err || stderr); + } else { + resolve(stdout.replace('\n', '')); + } + }); + }) .then(callback) - .catch(function (error) { - throw chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder, or you using the --username and --repo flags.'); + .catch(function(error) { + throw new Error(chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder, or you using the --username and --repo flags.')); }); } @@ -36,17 +36,17 @@ function executeCommand(command, callback) { * * @since 0.5.0 * @public - * + * * @param {Function} callback - * + * * @return {Promise} The promise that resolves user informations ({ user: username}) */ function user(callback) { - return executeCommand('git config user.name', function (user) { - return { - user: user - }; - }) + return executeCommand('git config user.name', function(user) { + return { + user: user + }; + }) .then(callback); } @@ -55,24 +55,24 @@ function user(callback) { * * @since 0.5.0 * @public - * + * * @param {Function} callback - * + * * @return {Promise} The promise that resolves repo informations ({user: user, name: name}) */ function repo(callback) { - return executeCommand('git config remote.origin.url', function (repo) { - var repoPath = repo + return executeCommand('git config remote.origin.url', function(repo) { + let repoPath = repo .replace(/([^:]*:)|\.[^.]+$/g, '') .split('/'); - var user = repoPath[0]; - var name = repoPath[1]; + let user = repoPath[0]; + let name = repoPath[1]; - return { - username: user, - repo: name - }; - }) + return { + username: user, + repo: name + }; + }) .then(callback); } @@ -81,22 +81,22 @@ function repo(callback) { * * @since 0.5.0 * @public - * + * * @param {Function} callback - * + * * @return {Promise} The promise that resolves token informations ({token: token}) */ function token(callback) { - return executeCommand('echo $GREN_GITHUB_TOKEN', function (token) { - return { - token: token - }; - }) + return executeCommand('echo $GREN_GITHUB_TOKEN', function(token) { + return { + token: token + }; + }) .then(callback); } module.exports = { - user: user, - repo: repo, - token: token -}; \ No newline at end of file + user: user, + repo: repo, + token: token +}; diff --git a/src/gren.js b/src/gren.js index da456e44..e2fd8cf8 100644 --- a/src/gren.js +++ b/src/gren.js @@ -10,26 +10,25 @@ var isOnline = require('is-online'); var ObjectAssign = require('object-assign'); var defaults = { - tags: false, - timeWrap: 'latest', // || history - changelogFilename: 'CHANGELOG.md', - dataSource: 'issues', // || commits - draft: false, - force: false, - prefix: '', - includeMessages: 'commits', // || merges || all - prerelease: false, - dateZero: new Date(0), - override: false + tags: false, + timeWrap: 'latest', // || history + changelogFilename: 'CHANGELOG.md', + dataSource: 'issues', // || commits + draft: false, + force: false, + prefix: '', + includeMessages: 'commits', // || merges || all + prerelease: false, + dateZero: new Date(0), + override: false }; - /** * Edit arelease from a given tag (in the options) * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * @param {number} releaseId The id of the release to edit * @param {Object} releaseOptions The options to build the release: @@ -42,25 +41,25 @@ var defaults = { * "draft": false, * "prerelease": false * } - * + * * @return {Promise} */ function editRelease(gren, releaseId, releaseOptions) { - var loaded = utils.task('Updating latest release'); + var loaded = utils.task('Updating latest release'); - return new Promise(function (resolve, reject) { - gren.repo.updateRelease(releaseId, releaseOptions, function (err, release) { - loaded(); + return new Promise(function(resolve, reject) { + gren.repo.updateRelease(releaseId, releaseOptions, function(err, release) { + loaded(); - if(err) { - reject(chalk.red(err)); - } else { - console.log(chalk.green('\n\n' + release.name + ' has been successfully updated!')); + if (err) { + reject(chalk.red(err)); + } else { + console.log(chalk.green('\n\n' + release.name + ' has been successfully updated!')); - resolve(true); - } - }); - }); + resolve(true); + } + }); + }); } /** @@ -68,7 +67,7 @@ function editRelease(gren, releaseId, releaseOptions) { * * @since 0.1.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * @param {Object} releaseOptions The options to build the release: * @example { @@ -83,26 +82,26 @@ function editRelease(gren, releaseId, releaseOptions) { * @return {Promise} */ function createRelease(gren, releaseOptions) { - var loaded = utils.task('Preparing the release'); - - return new Promise(function (resolve, reject) { - gren.repo.createRelease(releaseOptions, function (err, release) { - loaded(); - - if(err) { - var responseText = JSON.parse(err.request.responseText); - console.log(chalk.red( - responseText.message + '\n' + - responseText.errors[0].code - )); - reject(false); - } else { - console.log(chalk.green('\n\n' + release.name + ' has been successfully created!')); - - resolve(true); - } - }); - }); + var loaded = utils.task('Preparing the release'); + + return new Promise(function(resolve, reject) { + gren.repo.createRelease(releaseOptions, function(err, release) { + loaded(); + + if (err) { + var responseText = JSON.parse(err.request.responseText); + console.log(chalk.red( + responseText.message + '\n' + + responseText.errors[0].code + )); + reject(false); + } else { + console.log(chalk.green('\n\n' + release.name + ' has been successfully created!')); + + resolve(true); + } + }); + }); } /** @@ -110,26 +109,26 @@ function createRelease(gren, releaseOptions) { * * @since 0.2.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * @param {Object[]} tags The collection of tags - * + * * @return {Promise} */ function prepareRelease(gren, block) { - var releaseOptions = { - tag_name: block.release, - name: gren.options.prefix + block.release, - body: block.body, - draft: gren.options.draft, - prerelease: gren.options.prerelease - }; - - if(block.id) { - return editRelease(gren, block.id, releaseOptions); - } else { - return createRelease(gren, releaseOptions); - } + var releaseOptions = { + tag_name: block.release, + name: gren.options.prefix + block.release, + body: block.body, + draft: gren.options.draft, + prerelease: gren.options.prerelease + }; + + if (block.id) { + return editRelease(gren, block.id, releaseOptions); + } else { + return createRelease(gren, releaseOptions); + } } /** @@ -138,27 +137,27 @@ function prepareRelease(gren, block) { * * @since 0.5.0 * @private - * + * * @param {Boolean|Array} selectedTags * @param {Object[]} tags - * + * * @return {Boolean|Array} */ -function getSelectedTags(optionTags, tags) { - if (!optionTags) { - return false; - } +function getSelectedTags(optionTags, tags) { + if (!optionTags) { + return false; + } - var selectedTags = [].concat(optionTags); + var selectedTags = [].concat(optionTags); - return tags.filter(function (tag, index) { - var isSelectedTag = selectedTags.indexOf(tag.name) !== -1; + return tags.filter(function(tag, index) { + var isSelectedTag = selectedTags.indexOf(tag.name) !== -1; - if (isSelectedTag && selectedTags.length === 1 && tags[index + 1]) { - selectedTags.push(tags[index + 1].name); - } - return isSelectedTag; - }).slice(0, 2); + if (isSelectedTag && selectedTags.length === 1 && tags[index + 1]) { + selectedTags.push(tags[index + 1].name); + } + return isSelectedTag; + }).slice(0, 2); } /** @@ -166,43 +165,43 @@ function getSelectedTags(optionTags, tags) { * * @since 0.1.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * * @return {Promise} */ function getLastTags(gren, releases) { - var loaded = utils.task('Getting tags'); - - return new Promise(function (resolve, reject) { - gren.repo.listTags(function (err, tags) { - loaded(); - - if(err) { - reject(err); - } else { - var filteredTags = - (getSelectedTags(gren.options.tags, tags) || [tags[0], tags[1]]) - .map(function (tag) { - var tagRelease = releases && releases.filter(function (release) { - return release.tag_name === tag.name; - })[0] || false; - var releaseId = tagRelease ? tagRelease.id : null; - - return { - tag: tag, - releaseId: releaseId - }; - }); - - if(filteredTags[0].releaseId && !gren.options.override) { - reject(chalk.red(filteredTags[0].tag.name + ' is a release, use --override flag to override an existing release!')); - } + var loaded = utils.task('Getting tags'); + + return new Promise(function(resolve, reject) { + gren.repo.listTags(function(err, tags) { + loaded(); - resolve(filteredTags); - } - }); - }); + if (err) { + reject(err); + } else { + var filteredTags = + (getSelectedTags(gren.options.tags, tags) || [tags[0], tags[1]]) + .map(function(tag) { + var tagRelease = releases && releases.filter(function(release) { + return release.tag_name === tag.name; + })[0] || false; + var releaseId = tagRelease ? tagRelease.id : null; + + return { + tag: tag, + releaseId: releaseId + }; + }); + + if (filteredTags[0].releaseId && !gren.options.override) { + reject(chalk.red(filteredTags[0].tag.name + ' is a release, use --override flag to override an existing release!')); + } + + resolve(filteredTags); + } + }); + }); } /** @@ -210,28 +209,28 @@ function getLastTags(gren, releases) { * * @since 0.1.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * @param {Object[]} tags List of all the tags in the repo * * @return {Promise[]} The promises which returns the dates */ function getTagDates(gren, tags) { - return tags.map(function (tag) { - return new Promise(function (resolve, reject) { - gren.repo.getCommit(tag.tag.commit.sha, function (err, commit) { - if(err) { - reject(err); - } else { - resolve({ - id: tag.releaseId, - name: tag.tag.name, - date: commit.committer.date - }); - } - }); - }); - }); + return tags.map(function(tag) { + return new Promise(function(resolve, reject) { + gren.repo.getCommit(tag.tag.commit.sha, function(err, commit) { + if (err) { + reject(err); + } else { + resolve({ + id: tag.releaseId, + name: tag.tag.name, + date: commit.committer.date + }); + } + }); + }); + }); } /** @@ -239,21 +238,21 @@ function getTagDates(gren, tags) { * * @since 0.5.0 * @private - * + * * @param {Object[]} releases A list of release Objects * * @return {Array} The list of the dates */ function getReleaseDates(gren, releases) { - return [].concat(releases).map(function (release) { - return { - id: release.id, - name: release.name, - tag_name: release.tag_name, - date: release.created_at, - body: release.body || null - }; - }); + return [].concat(releases).map(function(release) { + return { + id: release.id, + name: release.name, + tag_name: release.tag_name, + date: release.created_at, + body: release.body || null + }; + }); } /** @@ -261,30 +260,30 @@ function getReleaseDates(gren, releases) { * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * * @return {Promise} The promise which resolves an array of releases */ function getListReleases(gren) { - var loaded = utils.task('Getting the list of releases'); + var loaded = utils.task('Getting the list of releases'); - return new Promise(function (resolve, reject) { - gren.repo.listReleases(function (err, releases) { - loaded(); + return new Promise(function(resolve, reject) { + gren.repo.listReleases(function(err, releases) { + loaded(); - if(err && err.request.status !== 404) { - reject(err); - } else { - if(err && err.request.status === 404) { - resolve(false); + if (err && err.request.status !== 404) { + reject(err); } else { - process.stdout.write(releases.length + ' releases found\n'); - resolve(releases); + if (err && err.request.status === 404) { + resolve(false); + } else { + process.stdout.write(releases.length + ' releases found\n'); + resolve(releases); + } } - } - }); - }); + }); + }); } /** @@ -292,30 +291,30 @@ function getListReleases(gren) { * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * * @return {Promise} The promise which resolves the tag name of the release */ function getLastTwoReleases(gren) { - return getListReleases(gren) - .then(function(releases) { - return releases.slice(0, 2); - }); + return getListReleases(gren) + .then(function(releases) { + return releases.slice(0, 2); + }); } /** - * Return a string with a - to be a bullet list (used for a mapping) + * Return a string with a - to be a bulvar list (used for a mapping) * * @since 0.1.0 * @private - * + * * @param {string} message * * @return {string} */ function templateCommits(message) { - return '- ' + message; + return '- ' + message; } /** @@ -323,16 +322,16 @@ function templateCommits(message) { * * @since 0.5.0 * @private - * + * * @param {Object} issue - * + * * @return {string} */ function templateLabels(issue) { - return issue.labels ? issue.labels.map(function (label) { - return '[**' + label.name + '**] '; - }) - .join('') : '[closed]'; + return issue.labels ? issue.labels.map(function(label) { + return '[**' + label.name + '**] '; + }) + .join('') : '[closed]'; } /** @@ -340,16 +339,16 @@ function templateLabels(issue) { * * @since 0.5.0 * @private - * + * * @param {Object} block ({name: 'v1.2.3', body: []}) - * + * * @return {string} */ function templateBlock(block) { - var date = new Date(block.date); + var date = new Date(block.date); - return '## ' + block.release + ' (' + utils.formatDate(date) + ')' + '\n\n' + - block.body; + return '## ' + block.release + ' (' + utils.formatDate(date) + ')' + '\n\n' + + block.body; } /** @@ -357,13 +356,13 @@ function templateBlock(block) { * * @since 0.5.0 * @private - * + * * @param {Object} issue - * + * * @return {string} */ function templateIssue(issue) { - return '- ' + templateLabels(issue) + issue.title + ' [#' + issue.number + '](' + issue.html_url + ')'; + return '- ' + templateLabels(issue) + issue.title + ' [#' + issue.number + '](' + issue.html_url + ')'; } /** @@ -371,16 +370,16 @@ function templateIssue(issue) { * * @since 0.5.0 * @private - * + * * @param {Object[]} blocks - * + * * @return {string} */ function templateChangelog(blocks) { - return '# Changelog\n\n' + - blocks - .map(templateBlock) - .join('\n\n --- \n\n'); + return '# Changelog\n\n' + + blocks + .map(templateBlock) + .join('\n\n --- \n\n'); } /** @@ -388,48 +387,50 @@ function templateChangelog(blocks) { * * @since 0.5.0 * @private - * + * * @param {Object[]} blocks - * + * * @return {string} */ function templateIssueBody(body, rangeBody) { - return body.length ? body.join('\n') || rangeBody + '\n' : '*No changelog for this release.*'; + return body.length ? body.join('\n') || rangeBody + '\n' : '*No changelog for this release.*'; } /** - * Return a commit messages generated body + * Return a commit messages generated body * * @since 0.1.0 * @private - * + * * @param {string} message * * @return {string} */ function generateCommitsBody(gren, messages) { - return messages - .slice(0, -1) - .filter(function (message) { - var messageType = gren.options.includeMessages; - var filterMap = { - merges: function(message) { - return message.match(/^merge/i); - }, - commits: function(message) { - return !message.match(/^merge/i); - }, - all: function() { return true; } - }; - - if(filterMap[messageType]) { - return filterMap[messageType](message); - } - - return filterMap.commits(message); - }) - .map(templateCommits) - .join('\n'); + return messages + .slice(0, -1) + .filter(function(message) { + var messageType = gren.options.includeMessages; + var filterMap = { + merges: function(message) { + return message.match(/^merge/i); + }, + commits: function(message) { + return !message.match(/^merge/i); + }, + all: function() { + return true; + } + }; + + if (filterMap[messageType]) { + return filterMap[messageType](message); + } + + return filterMap.commits(message); + }) + .map(templateCommits) + .join('\n'); } /** @@ -437,15 +438,15 @@ function generateCommitsBody(gren, messages) { * * @since 0.1.0 * @private - * + * * @param {Object[]} commits The array of object containing the commits * * @return {String[]} */ function commitMessages(commits) { - return commits.map(function (commitObject) { - return commitObject.commit.message; - }); + return commits.map(function(commitObject) { + return commitObject.commit.message; + }); } /** @@ -453,7 +454,7 @@ function commitMessages(commits) { * * @since 0.1.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object * @param {string} since The since date in ISO * @param {string} until The until date in ISO @@ -461,23 +462,23 @@ function commitMessages(commits) { * @return {Promise} The promise which resolves the [Array] commit messages */ function getCommitsBetweenTwo(gren, since, until) { - process.stdout.write(chalk.green('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until)) + '\n')); - - var options = { - since: since, - until: until, - per_page: 100 - }; - - return new Promise(function (resolve, reject) { - gren.repo.listCommits(options, function (err, commits) { - if(err) { - reject(err); - } else { - resolve(commitMessages(commits)); - } - }); - }); + process.stdout.write(chalk.green('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until)) + '\n')); + + var options = { + since: since, + until: until, + per_page: 100 + }; + + return new Promise(function(resolve, reject) { + gren.repo.listCommits(options, function(err, commits) { + if (err) { + reject(err); + } else { + resolve(commitMessages(commits)); + } + }); + }); } /** @@ -485,29 +486,29 @@ function getCommitsBetweenTwo(gren, since, until) { * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren * @param {Array} releaseRanges The array of date ranges - * + * * @return {Promise[]} */ function getCommitBlocks(gren, releaseRanges) { - console.log(chalk.blue('\nCreating the body blocks from commits:')); + console.log(chalk.blue('\nCreating the body blocks from commits:')); - return Promise.all( - releaseRanges - .map(function (range) { + return Promise.all( + releaseRanges + .map(function(range) { return getCommitsBetweenTwo(gren, range[1].date, range[0].date) - .then(function (commits) { - return { - id: range[0].id, - release: range[0].name, - date: range[0].date, - body: generateCommitsBody(gren, commits) + '\n' - }; - }); - }) - ); + .then(function(commits) { + return { + id: range[0].id, + release: range[0].name, + date: range[0].date, + body: generateCommitsBody(gren, commits) + '\n' + }; + }); + }) + ); } /** @@ -515,71 +516,71 @@ function getCommitBlocks(gren, releaseRanges) { * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren The gren object - * + * * @return {Promise} The promise which resolves the list of the issues */ function getClosedIssues(gren) { - var loaded = utils.task('Getting all closed issues'); - - return new Promise(function (resolve, reject) { - gren.issues.listIssues({ - state: 'closed' - }, function (err, issues) { - loaded(); - - if(err) { - reject(err); - } else { - var filteredIssues = issues.filter(function (issue) { - return !issue.pull_request; - }); + var loaded = utils.task('Getting all closed issues'); + + return new Promise(function(resolve, reject) { + gren.issues.listIssues({ + state: 'closed' + }, function(err, issues) { + loaded(); - process.stdout.write(filteredIssues.length + ' issues found\n'); + if (err) { + reject(err); + } else { + var filteredIssues = issues.filter(function(issue) { + return !issue.pull_request; + }); - resolve(filteredIssues); - } - }); - }); + process.stdout.write(filteredIssues.length + ' issues found\n'); + + resolve(filteredIssues); + } + }); + }); } /** * Get the blocks of issues based on release dates - * + * * @since 0.5.0 * @private - * + * * @param {GithubReleaseNotes} gren * @param {Array} releaseRanges The array of date ranges - * + * * @return {Promise[]} */ function getIssueBlocks(gren, releaseRanges) { - console.log('\nCreating the body blocks from issues:'); - - return getClosedIssues(gren) - .then(function (issues) { - return releaseRanges - .map(function (range) { - var body = (!range[0].body || gren.options.override) && - issues.filter(function (issue) { - return utils.isInRange( - Date.parse(issue.closed_at), - Date.parse(range[1].date), - Date.parse(range[0].date) - ); + console.log('\nCreating the body blocks from issues:'); + + return getClosedIssues(gren) + .then(function(issues) { + return releaseRanges + .map(function(range) { + var body = (!range[0].body || gren.options.override) && + issues.filter(function(issue) { + return utils.isInRange( + Date.parse(issue.closed_at), + Date.parse(range[1].date), + Date.parse(range[0].date) + ); }) .map(templateIssue); - return { + return { id: range[0].id, release: range[0].name, date: range[0].date, body: templateIssueBody(body, range[0].body) - }; - }); - }); + }; + }); + }); } /** @@ -593,9 +594,9 @@ function getIssueBlocks(gren, releaseRanges) { * @return {Array} */ function sortReleasesByDate(releaseDates) { - return releaseDates.sort(function (release1, release2) { - return new Date(release1.date) < new Date(release2.date) ? 1 : -1; - }); + return releaseDates.sort(function(release1, release2) { + return new Date(release1.date) < new Date(release2.date) ? 1 : -1; + }); } /** @@ -606,26 +607,26 @@ function sortReleasesByDate(releaseDates) { * * @param {GithubReleaseNotes} gren * @param {Array} releaseDates The release dates - * + * * @return {Array} */ function createReleaseRanges(gren, releaseDates) { - var ranges = []; - var range = 2; - var sortedReleaseDates = sortReleasesByDate(releaseDates); - - if (sortedReleaseDates.length === 1 || gren.options.timeWrap === 'history') { - sortedReleaseDates.push({ - id: 0, - date: new Date(0) - }); - } - - for(var i = 0; i Date: Mon, 26 Sep 2016 22:42:56 +0100 Subject: [PATCH 35/35] Lint js code --- bin/gren.js | 12 ++--- github-release-notes.js | 10 ++-- src/github-info.js | 92 ++++++++++++++++---------------- src/utils.js | 114 ++++++++++++++++++++-------------------- 4 files changed, 114 insertions(+), 114 deletions(-) diff --git a/bin/gren.js b/bin/gren.js index 6e1612f0..1c59adef 100644 --- a/bin/gren.js +++ b/bin/gren.js @@ -1,5 +1,5 @@ #!/usr/bin/env node - + 'use strict'; var GithubReleaseNotes = require('../src/gren'); @@ -9,8 +9,8 @@ var utils = require('../src/utils'); var action = utils.getBashOptions(process.argv)['action']; gren.init() - .then(function (success) { - if(success) { - return gren[action || 'release'](); - } - }); +.then(function (success) { + if(success) { + return gren[action || 'release'](); + } +}); diff --git a/github-release-notes.js b/github-release-notes.js index dce81c37..af5efc7f 100644 --- a/github-release-notes.js +++ b/github-release-notes.js @@ -7,8 +7,8 @@ var utils = require('./src/utils'); var action = utils.getBashOptions(process.argv)['action']; gren.init() - .then(function (success) { - if(success) { - return gren[action || 'release'](); - } - }); +.then(function (success) { + if(success) { + return gren[action || 'release'](); + } +}); diff --git a/src/github-info.js b/src/github-info.js index 1ea5f33e..de3721ca 100644 --- a/src/github-info.js +++ b/src/github-info.js @@ -5,16 +5,16 @@ let chalk = require('chalk'); let Promise = Promise || require('es6-promise').Promise; /** - * Execute a command in the bash and run a callback - * - * @since 0.5.0 - * @private - * - * @param {string} command The command to execute - * @param {Function} callback The callback which returns the stdout - * - * @return {Promise} - */ +* Execute a command in the bash and run a callback +* +* @since 0.5.0 +* @private +* +* @param {string} command The command to execute +* @param {Function} callback The callback which returns the stdout +* +* @return {Promise} +*/ function executeCommand(command, callback) { return new Promise(function(resolve, reject) { exec(command, function(err, stdout, stderr) { @@ -25,46 +25,46 @@ function executeCommand(command, callback) { } }); }) - .then(callback) - .catch(function(error) { - throw new Error(chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder, or you using the --username and --repo flags.')); - }); + .then(callback) + .catch(function(error) { + throw new Error(chalk.red(error) + chalk.yellow('Make sure you\'re running the command from the repo folder, or you using the --username and --repo flags.')); + }); } /** - * Get user informations - * - * @since 0.5.0 - * @public - * - * @param {Function} callback - * - * @return {Promise} The promise that resolves user informations ({ user: username}) - */ +* Get user informations +* +* @since 0.5.0 +* @public +* +* @param {Function} callback +* +* @return {Promise} The promise that resolves user informations ({ user: username}) +*/ function user(callback) { return executeCommand('git config user.name', function(user) { return { user: user }; }) - .then(callback); + .then(callback); } /** - * Get repo informations - * - * @since 0.5.0 - * @public - * - * @param {Function} callback - * - * @return {Promise} The promise that resolves repo informations ({user: user, name: name}) - */ +* Get repo informations +* +* @since 0.5.0 +* @public +* +* @param {Function} callback +* +* @return {Promise} The promise that resolves repo informations ({user: user, name: name}) +*/ function repo(callback) { return executeCommand('git config remote.origin.url', function(repo) { let repoPath = repo - .replace(/([^:]*:)|\.[^.]+$/g, '') - .split('/'); + .replace(/([^:]*:)|\.[^.]+$/g, '') + .split('/'); let user = repoPath[0]; let name = repoPath[1]; @@ -73,26 +73,26 @@ function repo(callback) { repo: name }; }) - .then(callback); + .then(callback); } /** - * Get token informations - * - * @since 0.5.0 - * @public - * - * @param {Function} callback - * - * @return {Promise} The promise that resolves token informations ({token: token}) - */ +* Get token informations +* +* @since 0.5.0 +* @public +* +* @param {Function} callback +* +* @return {Promise} The promise that resolves token informations ({token: token}) +*/ function token(callback) { return executeCommand('echo $GREN_GITHUB_TOKEN', function(token) { return { token: token }; }) - .then(callback); + .then(callback); } module.exports = { diff --git a/src/utils.js b/src/utils.js index 59e4608f..50f68c4d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,26 +3,26 @@ let chalk = require('chalk'); /** - * Print a task name in a custom format - * - * @since 0.5.0 - * @public - * - * @param {string} name The name of the task - */ +* Print a task name in a custom format +* +* @since 0.5.0 +* @public +* +* @param {string} name The name of the task +*/ function printTask(name) { process.stdout.write(chalk.blue(name + ' task:\n===================================\n')); } /** - * Outputs the task status - * - * @since 0.5.0 - * @public - * - * @param {string} taskName The task name - * - * @return {Function} The function to be fired when is loaded - */ +* Outputs the task status +* +* @since 0.5.0 +* @public +* +* @param {string} taskName The task name +* +* @return {Function} The function to be fired when is loaded +*/ function task(taskName) { let time = process.hrtime(); process.stdout.write(chalk.green(taskName) + ': .'); @@ -40,49 +40,49 @@ function task(taskName) { } /** - * Check if e value is between a min and a max - * - * @since 0.5.0 - * @public - * - * @param {number} value - * @param {number} min - * @param {number} max - * - * @return {Boolean} - */ +* Check if e value is between a min and a max +* +* @since 0.5.0 +* @public +* +* @param {number} value +* @param {number} min +* @param {number} max +* +* @return {Boolean} +*/ function isInRange(value, min, max) { return !Math.floor((value - min) / (max - min)); } /** - * Transforms a dasherize string into a camel case one. - * - * @since 0.3.2 - * @public - * - * @param {string} value The dasherize string - * - * @return {string} The camel case string - */ +* Transforms a dasherize string into a camel case one. +* +* @since 0.3.2 +* @public +* +* @param {string} value The dasherize string +* +* @return {string} The camel case string +*/ function dashToCamelCase(value) { return value - .toLowerCase() - .replace(/-([a-z])/g, function(match) { - return match[1].toUpperCase(); - }); + .toLowerCase() + .replace(/-([a-z])/g, function(match) { + return match[1].toUpperCase(); + }); } /** - * Create a literal object of the node module options - * - * @since 0.1.0 - * @public - * - * @param {Array} args The array of arguments (the module arguments start from index 2) - * - * @return {Object} The object containg the key/value options - */ +* Create a literal object of the node module options +* +* @since 0.1.0 +* @public +* +* @param {Array} args The array of arguments (the module arguments start from index 2) +* +* @return {Object} The object containg the key/value options +*/ function getBashOptions(args) { let settings = {}; @@ -98,14 +98,14 @@ function getBashOptions(args) { } /** - * Format a date into a string - * - * @since 0.5.0 - * @public - * - * @param {Date} date - * @return {string} - */ +* Format a date into a string +* +* @since 0.5.0 +* @public +* +* @param {Date} date +* @return {string} +*/ function formatDate(date) { return ('0' + date.getDate()).slice(-2) + '/' + ('0' + (date.getMonth() + 1)).slice(-2) + '/' + date.getFullYear(); }