From 5c1f0660f02bbfd13b7504b94099fbeb835ad42b Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 21 Sep 2017 18:02:41 +0100 Subject: [PATCH 01/19] Add all the defaults to Release.js --- lib/src/Program.js | 19 ++++++++++++++++-- lib/src/Release.js | 46 +++++++++++++++++--------------------------- lib/src/_template.js | 8 ++++---- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/lib/src/Program.js b/lib/src/Program.js index 526fa8aa..fe13c58f 100644 --- a/lib/src/Program.js +++ b/lib/src/Program.js @@ -14,7 +14,7 @@ export default class Program { .description(this.description) .parse(props.argv); - this.options = this._camelCaseObjectKeys( + this.options = this._filterObject(this._camelCaseObjectKeys( ObjectAssign( {}, defaults, @@ -22,7 +22,22 @@ export default class Program { this._getConfigFromFile(props.cwd), this._getOptionsFromProgram(defaults) ) - ); + )); + } + + /** + * Remove all the properties that have undefined values from an object + * + * @param {Object} object [description] + * @return {[type]} [description] + */ + _filterObject(object) { + return Object.entries(object) + .filter(([key, value]) => value !== undefined) + .reduce((carry, [key, value]) => { + carry[key] = value; + return carry; + }, {}); } /** diff --git a/lib/src/Release.js b/lib/src/Release.js index bace1114..b4a21de4 100644 --- a/lib/src/Release.js +++ b/lib/src/Release.js @@ -7,13 +7,30 @@ import templateConfig from './templates.json'; import ObjectAssign from 'object-assign-deep'; const defaults = { - template: templateConfig + tags: [], + changelogFilename: 'CHANGELOG.md', + dataSource: 'issues', + onlyMilestones: false, + milestoneMatch: 'Release {{tag_name}}', + draft: false, + force: false, + prefix: '', + includeMessages: 'commits', + prerelease: false, + generate: false, + override: false, + ignoreLabels: false, + ignoreIssuesWith: false, + template: templateConfig, + groupBy: false }; export default class Release { constructor(props) { this.options = ObjectAssign({}, defaults, props); + console.log(this.options); + if (!this.options.token) { throw chalk.red('You need to provide the token'); } @@ -41,7 +58,6 @@ export default class Release { * Edit a release from a given tag (in the options) * * @since 0.5.0 - * @private * * @param {number} releaseId The id of the release to edit * @param {Object} releaseOptions The options to build the release: @@ -76,7 +92,6 @@ export default class Release { * Create a release from a given tag (in the options) * * @since 0.1.0 - * @private * * @param {Object} releaseOptions The options to build the release: * @example { @@ -108,7 +123,6 @@ export default class Release { * Creates the options to make the release * * @since 0.2.0 - * @private * * @param {Object[]} tags The collection of tags * @@ -141,7 +155,6 @@ export default class Release { * the next one in case only one is given * * @since 0.5.0 - * @private * * @param {Array|string} selectedTags * @param {Object[]} tags @@ -173,7 +186,6 @@ export default class Release { * Get all the tags of the repo * * @since 0.1.0 - * @private * * @return {Promise} */ @@ -207,7 +219,6 @@ export default class Release { * Get the dates of the last two tags * * @since 0.1.0 - * @private * * @param {Object[]} tags List of all the tags in the repo * @@ -226,7 +237,6 @@ export default class Release { * Get all releases * * @since 0.5.0 - * @private * * @return {Promise} The promise which resolves an array of releases */ @@ -249,7 +259,6 @@ export default class Release { * Return the templated commit message * * @since 0.1.0 - * @private * * @param {string} message * @@ -265,7 +274,6 @@ export default class Release { * Generate the MD template from all the labels of a specific issue * * @since 0.5.0 - * @private * * @param {Object} issue * @@ -287,7 +295,6 @@ export default class Release { * Generate the releases bodies from a release Objects Array * * @since 0.8.0 - * @private * * @param {Array} releases The release Objects Array coming from GitHub * @@ -305,7 +312,6 @@ export default class Release { * Generate the MD template for each issue * * @since 0.5.0 - * @private * * @param {Object} issue * @@ -324,7 +330,6 @@ export default class Release { * Generate the Changelog issues body template * * @since 0.5.0 - * @private * * @param {Object[]} blocks * @@ -338,7 +343,6 @@ export default class Release { * Generates the template for the groups * * @since 0.8.0 - * @private * * @param {Object} groups The groups to template e.g. * { @@ -362,7 +366,6 @@ export default class Release { * Return a commit messages generated body * * @since 0.1.0 - * @private * * @param {string} message * @@ -400,7 +403,6 @@ export default class Release { * Transforms the commits to commit messages * * @since 0.1.0 - * @private * * @param {Object[]} commits The array of object containing the commits * @@ -414,7 +416,6 @@ export default class Release { * Gets all the commits between two dates * * @since 0.1.0 - * @private * * @param {string} since The since date in ISO * @param {string} until The until date in ISO @@ -438,7 +439,6 @@ export default class Release { * Get the blocks of commits based on release dates * * @since 0.5.0 - * @private * * @param {Array} releaseRanges The array of date ranges * @@ -464,7 +464,6 @@ export default class Release { * Compare the ignored labels with the passed ones * * @since 0.6.0 - * @private * * @param {Array} ignoreLabels The labels to ignore * @param {Array} labels The labels to check @@ -480,7 +479,6 @@ export default class Release { * Filter the issue based on gren options and labels * * @since 0.9.0 - * @private * * @param {Object} issue * @@ -495,7 +493,6 @@ export default class Release { * Get all the closed issues from the current repo * * @since 0.5.0 - * @private * * @param {Array} releaseRanges The array of date ranges * @@ -523,7 +520,6 @@ export default class Release { * Group the issues based on their first label * * @since 0.8.0 - * @private * * @param {Array} issues * @@ -553,7 +549,6 @@ export default class Release { * Create groups of issues based on labels * * @since 0.8.0 - * @private * * @param {Array} issues The array of all the issues. * @@ -604,7 +599,6 @@ export default class Release { * milestone. * * @since 0.9.0 - * @private * * @param {Array} range The release ranges * @param {Object} issue GitHub issue @@ -627,7 +621,6 @@ export default class Release { * Get the blocks of issues based on release dates * * @since 0.5.0 - * @private * * @param {Array} releaseRanges The array of date ranges * @@ -656,7 +649,6 @@ export default class Release { * Sort releases by dates * * @since 0.5.0 - * @private * * @param {Array} releaseDates * @@ -670,7 +662,6 @@ export default class Release { * Create the ranges of release dates * * @since 0.5.0 - * @private * * @param {Array} releaseDates The release dates * @@ -729,7 +720,6 @@ export default class Release { * Check if there is connectivity * * @since 0.5.0 - * @private * * @return {Promise} */ diff --git a/lib/src/_template.js b/lib/src/_template.js index c787d791..226deee1 100644 --- a/lib/src/_template.js +++ b/lib/src/_template.js @@ -15,11 +15,11 @@ function generate(placeholders, string) { return string(placeholders); } - return Object.keys(placeholders) - .reduce((carry, placeholder) => { - const placeholderRegExp = new RegExp(`{{${placeholder}}}`, 'g'); + return Object.entries(placeholders) + .reduce((carry, [key, placeholder]) => { + const placeholderRegExp = new RegExp(`{{${key}}}`, 'g'); - return carry.replace(placeholderRegExp, placeholders[placeholder]); + return carry.replace(placeholderRegExp, placeholder); }, string); } From bf0fcfd0bf04a0b90d4cb66b58bcfa8793c1a23e Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Thu, 21 Sep 2017 22:53:10 +0100 Subject: [PATCH 02/19] First change for README.md file --- README.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 152 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 310aef08..f3ddfa14 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,107 @@ -# Github Release Notes +# `gren` šŸ¤– +> Github release notes and changelog generator [![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?branch=master)](https://travis-ci.org/github-tools/github-release-notes) [![Join the chat at https://gitter.im/github-release-notes/Lobby](https://badges.gitter.im/github-release-notes/Lobby.svg)](https://gitter.im/github-release-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -> 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. +## Motivation -If you want to include it in a task, you can use [grunt-github-release-notes](https://github.com/github-tools/grunt-github-release-notes) +Everyone loves neat, transparent, informative release notes. + +Everyone would also rather avoid maintaining them. What a hassle to have to evaluate what issues have been solved between two points in project's timeline, what types of problems they were, are they important to inform the users about, what issues solved them etc. + +Wouldn't it be great to get fantastic release notes compiled for you automaticaly based on all the hard work you put into your GitHub issues and pull requests. + +## OK, what can `gren` do for me? + +`gren` is a small helpful robot that will do for you just that! To put simply, it can create a release from a tag and compile the release notes using issues or commits. + +It also can generate a `CHANGELOG.md` file based on the release notes (or generate a brand new). + +- [The Concept](#the-concept) +- [Feed šŸ¤–](#feed-gren-) +- [Installation](#installation) +- [Basic Usage](#basic-usage) +- [Configuration file](#configuration-file) +- [Full Documentation](https://github-tools.github.io/github-release-notes) + +## The Concept + +The main motivation for bringing `gren` to life was the need for auto generating release notes for every tag in a project. +The process, [as explained here](https://help.github.com/articles/creating-releases/), requires to go to your project's releases page in GitHub, draft that tag as a new release and manually add what I remembered has changed. + +Let `gren` take care of that for you. It automates this process and also writes release notes for you, creating something like this: + +> ## v0.6.0 (14/03/2017) +> +> #### Framework Enhancements: +> +> - [#32](https://github.com/github-tools/github-release-notes/issues/32) Unwrap github-api promises +> - [#26](https://github.com/github-tools/github-release-notes/issues/26) Use external config file +> - [#23](https://github.com/github-tools/github-release-notes/issues/23) Introduce templates for the issues +> - [#19](https://github.com/github-tools/github-release-notes/issues/19) Add an "ignore label" flag +> - [#12](https://github.com/github-tools/github-release-notes/issues/12) Add the chance to rebuild the history of release notes +> +> #### Bug Fixes: +> +> - [#29](https://github.com/github-tools/github-release-notes/issues/29) Remove escaping character on regex +> - [#24](https://github.com/github-tools/github-release-notes/issues/24) The changelog action doesn't compile latest release + +_(yes, this is one of_ šŸ¤– _'s actual releases)_ + +## Feed `gren` šŸ¤– + +Where is the data coming from? There are two options: + +### `issues` (ā­) + +If you manage your project with issues, that's where all the information about a change are. +Issue labels increase the level of depth of what the release notes should show, helping `gren` to group the notes. + +_e.g. if you see the example above, the issues are grouped by the two labels `enhancement` and `bug`, then customised via a config file._ + +`gren` generates those notes by collecting all the issues closed between a tag (defaults to latest) and the tag before it (or a tag that you specify). +If you want to be more accurate on the issues that belong to a release, you can group them in [milestones](https://github-tools.github.io/github-release-notes/examples.html#milestones) and use only the issues that belong to that Milestone. + +> The output above is a result of release notes built from issues. + +#### Help šŸ¤– to write wondeful stuff (issues) + +In order to have spliendid generated release notes, we reccomend to follow these convensions: + +1. Start the title with a verb (e.g. Change header styles) +2. Use the imperative mood in the title (e.g. Fix, not Fixed or Fixes header styles) +3. Use labels wisely and assign one label per issue. `gren` has the [option to ignore issues](#) _(alex: put link to options)_ that have a specified issues. + +### `commits` + +The simplest way of getting data is from the commits you write. +Even though it doesn't require a machine-readable commit, still would be better to have them in a nice format. + +The output would then use commit messages (title + description) to look something like: + +> ## v0.9.0 (17/05/2017) +> +> - Filter milestones (#75) +> * Create milestones data-source option +> * Add documentation for the milestones option +> - Support GitHub enterprise (#73) +> * Support GitHub enterprise +> * Add api-url to options documentation +> - Update CHANGELOG.md + +#### Help šŸ¤– to write wondeful stuff (commits) + +In order to have spliendid generated release notes, we reccomend to follow these convensions: + +1. Start the subject line with a verb (e.g. Change header styles) +2. Use the imperative mood in the subject line (e.g. Fix, not Fixed or Fixes header styles) +3. Limit the subject line to about 50 characters +4. Do not end the subject line with a period +5. Separate subject from body with a blank line +6. Wrap the body at 72 characters +7. Use the body to explain _what_ and _why_ not _how_ ## Installation @@ -17,10 +111,10 @@ Install `github-release-notes` via npm: npm install github-release-notes -g ``` -## Setup +### Setup -First generate a `github token` at [this link](https://help.github.com/articles/creating-an-access-token-for-command-line-use/). _You only need "repo" scope._ -Then add this to `~/.bash_profile` or `~/.zshrc`): +First, generate a `GitHub token`, _with **repo** scope_, at [this link](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/). +Then add this line to `~/.bash_profile` (or `~/.zshrc`): ```shell export GREN_GITHUB_TOKEN=your_token_here @@ -28,32 +122,71 @@ export GREN_GITHUB_TOKEN=your_token_here ## Basic Usage +`gren` gets the repo information directly from the folder where `git` is initialised. + ```shell # Navigate to your project directory cd ~/Path/to/repo -# Run the task -gren +# Run the task (see below) +gren release ``` -Otherwise, you can run it anywhere passing the repo information: +Otherwise, you can run it _anywhere_ passing the repo information: ```shell -gren --username=[username] --repo=[repo name] +gren release --username=[username] --repo=[repo name] ``` -To use a specific token you can specify it as option: +If you don't want to save the token, you can specify one as an option: ```shell -gren --token=[your token] +gren release --token=[your token] ``` -### Demo +### Commands + +There are two main commands that can be ran with šŸ¤–: + +#### `gren release` + +`gren` will look for the latest tag, draft a new release using the issues closed between when that tag and the one before were created and publish that release in your **release** panel in your GitHub repo. ([@see how to feed šŸ¤–](#feed-gren-)). + +#### `gren changelog` + +Create a `CHANGELOG.md` file using all the release notes of the repo _(like the ones generated by_ šŸ¤– _)._ +If the file exists already, use the `--override` option to proceed. + +```shell +gren changelog --override +``` + +To generate a brand new release notes, using the same approach as per the releases, you have to run the command with the `--generate` option. + +```shell +gren changelog --generate +``` + +### Help! šŸ†˜ + +`gren` is using [Commander.js](https://github.com/tj/commander.js) which generates the `--help` section. +To trigger the help of a command, run: + +```shell +# General usage +gren --help +# Command usage +gren help release # or gren release --help +``` -![gren demo](./docs/images/examples/exec_gren.gif) +## Configuration file -### Actions +You can create a configuration file where the task will be ran, where to specify your options. [See how to set up the config file](https://github-tools.github.io/github-release-notes/options.html#configuration-file) +The accepted file extensions are the following: -**Gren** has two main usages: `release` and `changelog`. -You can select the action with the `--action` option. +- `.grenrc` +- `.grenrc.json` +- `.grenrc.yml` +- `.grenrc.yaml` +- `.grenrc.js` -[See full documentation here](https://github-tools.github.io/github-release-notes) +### [See full documentation here](https://github-tools.github.io/github-release-notes) From 9ea257afd6e3f036937e338d5abf263da8acb2f1 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 22 Sep 2017 00:57:04 +0100 Subject: [PATCH 03/19] Create Gren task and add functionality for the changelog command --- .gitignore | 1 + .npmignore | 1 + lib/_options.js | 120 ++++++++++++++ lib/_release-options.js | 102 ------------ lib/gren-changelog.js | 36 ++++- lib/gren-release.js | 12 +- lib/src/{Release.js => Gren.js} | 279 +++++++++++++++++++------------- lib/src/_utils.js | 4 +- test/options.js | 15 ++ test/options.js.xml | 15 ++ test/utils.js | 2 +- 11 files changed, 367 insertions(+), 220 deletions(-) create mode 100644 lib/_options.js delete mode 100644 lib/_release-options.js rename lib/src/{Release.js => Gren.js} (73%) create mode 100644 test/options.js create mode 100644 test/options.js.xml diff --git a/.gitignore b/.gitignore index eaa33871..80814166 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ docs/_site docs/.sass-cache dist bin +test/**.js.xml diff --git a/.npmignore b/.npmignore index dc382c5b..f110c3e1 100644 --- a/.npmignore +++ b/.npmignore @@ -1,5 +1,6 @@ lib/ docs/ +test/ node_modules/ gulpfile.js .grenrc diff --git a/lib/_options.js b/lib/_options.js new file mode 100644 index 00000000..ad53115b --- /dev/null +++ b/lib/_options.js @@ -0,0 +1,120 @@ +const chalk = require('chalk'); + +module.exports = { + changelogOptions: [ + { + short: '-G', + name: 'generate', + description: `${chalk.blue('changelog only')} - Generate the changelog with GithubReleaseNotes rather then using the repo releases` + }, + { + short: '-f', + name: 'changelog-filename', + valueType: '', + description: `${chalk.blue('changelog only')} - The name of the changelog file. [CHANGELOG.md]`, + defaultValue: 'CHANGELOG.md' + } + ], + releaseOptions: [ + { + short: '-u', + name: 'username', + valueType: '', + description: 'The username of the repo e.g. github-tools' + }, + { + short: '-r', + name: 'repo', + valueType: '', + description: 'The repository name e.g. github-release-notes' + }, + { + short: '-T', + name: 'token', + valueType: '', + description: 'The token generated with repo access' + }, + { + short: '-au', + name: 'api-url', + valueType: '', + description: 'Override the GitHub API URL, allows gren to connect to a private GHE installation' + }, + { + short: '-o', + name: 'override', + description: 'Override the release notes if exist.' + }, + { + short: '-t', + name: 'tags', + valueType: '..', + description: 'Write release notes for a range of tags, a specific tag or all. e.g. 0.2.0..0.1.0 or 0.2.0.. or ..0.1.0 or 0.2.0 or *', // needs to be documented better, + action: value => value.split('..') + }, + { + short: '-D', + name: 'data-source', + valueType: '', + description: 'The informations you want to use to build release notes. [issues]', + action: /^(issues|commits|milestones)$/i, + defaultValue: 'issues' + }, + { + short: '-im', + name: 'include-messages', + valueType: '', + description: 'Filter the messages added to the release notes. Only used when --data-source used is commits [commits]', + action: /^(merge|commits|all)$/i, + defaultValue: 'commits' + }, + { + short: '-p', + name: 'prefix', + valueType: '', + description: 'Add a prefix to the tag version. e.g. \'v\'' + }, + { + short: '-d', + name: 'draft', + description: 'Set the release as a draft.' + }, + { + short: '-pr', + name: 'prerelease', + description: 'Set the release as a prerelease.' + }, + { + short: '-g', + name: 'group-by', + valueType: '<"label"|Object>', + description: 'Group the issues using the labels as group headings. You can set custom headings for groups of labels from a configuration file.' + }, + { + short: '-L', + name: 'ignore-labels', + valueType: ',', + description: 'Ignore the specified labels.', + action: value => value.split(',') + }, + { + short: '-I', + name: 'ignore-issues-with', + valueType: ',', + description: 'Ignore issues that contains one of the specified labels.', + action: value => value.split(',') + }, + { + short: '-mm', + name: 'milestone-match', + valueType: '', + description: 'The title that the script needs to match to link the release to the milestone. e.g. v will match v0.1.0 [Release {{tag_name}}]', + defaultValue: 'Release {{tag_name}}' + }, + { + short: '-M', + name: 'only-milestones', + description: 'Add to the release bodies only the issues that have a milestone' + } + ] +}; diff --git a/lib/_release-options.js b/lib/_release-options.js deleted file mode 100644 index d2d03873..00000000 --- a/lib/_release-options.js +++ /dev/null @@ -1,102 +0,0 @@ -// TODO: write test to check whether there are any duplicates (short or name) -export const options = [ - { - short: '-u', - name: 'username', - valueType: '', - description: 'The username of the repo e.g. github-tools' - }, - { - short: '-r', - name: 'repo', - valueType: '', - description: 'The repository name e.g. github-release-notes' - }, - { - short: '-T', - name: 'token', - valueType: '', - description: 'The token generated with repo access' - }, - { - short: '-au', - name: 'api-url', - valueType: '', - description: 'Override the GitHub API URL, allows gren to connect to a private GHE installation' - }, - { - short: '-o', - name: 'override', - description: 'Override the release notes if exist.' - }, - { - short: '-t', - name: 'tags', - valueType: '..', - description: 'Write release notes for a range of tags, a specific tag or all. e.g. 0.2.0..0.1.0 or 0.2.0.. or ..0.1.0 or 0.2.0 or *', // needs to be documented better, - action: value => value.split('..') - }, - { - short: '-D', - name: 'data-source', - valueType: '', - description: 'The informations you want to use to build release notes.', - action: /^(issues|commits|milestones)$/i, - defaultValue: 'issues' - }, - { - short: '-im', - name: 'include-messages', - valueType: '', - description: 'Filter the messages added to the release notes. Only used when --data-source used is commits', - action: /^(merge|commits|all)$/i, - defaultValue: 'commits' - }, - { - short: '-p', - name: 'prefix', - valueType: '', - description: 'Add a prefix to the tag version. e.g. \'v\'' - }, - { - short: '-d', - name: 'draft', - description: 'Set the release as a draft.' - }, - { - short: '-pr', - name: 'prerelease', - description: 'Set the release as a prerelease.' - }, - { - short: '-g', - name: 'group-by', - valueType: '<"label"|Object>', - description: 'Group the issues using the labels as group headings. You can set custom headings for groups of labels from a configuration file.' - }, - { - short: '-L', - name: 'ignore-labels', - valueType: ',,', - description: 'Ignore the specified labels.', - action: value => value.split(',') - }, - { - short: '-I', - name: 'ignore-issues-with', - valueType: ',,', - description: 'Ignore issues that contains one of the specified labels.', - action: value => value.split(',') - }, - { - short: '-mm', - name: 'milestone-match', - valueType: '', - description: 'The title that the script needs to match to link the release to the milestone. e.g. v will match v0.1.0' - }, - { - short: '-M', - name: 'only-milestones', - description: 'Add to the release bodies only the issues that have a milestone' - } -]; diff --git a/lib/gren-changelog.js b/lib/gren-changelog.js index b442dd2e..21ac005b 100644 --- a/lib/gren-changelog.js +++ b/lib/gren-changelog.js @@ -1,3 +1,37 @@ #!/usr/bin/env node -console.log('changelog'); +import Program from '../dist/Program'; +import GitHubInfo from '../dist/GitHubInfo'; +import Gren from '../dist/Gren'; +import { releaseOptions, changelogOptions } from './_options'; +import { green } from 'chalk'; + +const githubInfo = new GitHubInfo(); + +githubInfo.options.then(githubOptions => Promise.resolve(new Program({ + name: `${green('gren')} changelog`, + description: 'Create a CHANGELOG.md file, based on release notes', + argv: process.argv, + cwd: process.cwd(), + bashOptions: Object.assign({}, ...githubOptions), + options: releaseOptions.concat(changelogOptions), + events: { + '--help': () => { + console.log(''); + console.log(' Basic Examples:'); + console.log(''); + console.log(' $ gren changelog'); + console.log(''); + console.log(' $ gren changelog --generate'); + console.log(''); + } + } +}))) + .then(({ options }) => { + const changelogAction = new Gren(options); + + return changelogAction.changelog(); + }) + .catch(error => { + console.error(error); + }); diff --git a/lib/gren-release.js b/lib/gren-release.js index a6efa4e3..589b1092 100644 --- a/lib/gren-release.js +++ b/lib/gren-release.js @@ -2,18 +2,19 @@ import Program from '../dist/Program'; import GitHubInfo from '../dist/GitHubInfo'; -import Release from '../dist/Release'; -import { options } from './_release-options'; +import Gren from '../dist/Gren'; +import { releaseOptions } from './_options'; +import { green } from 'chalk'; const githubInfo = new GitHubInfo(); githubInfo.options.then(githubOptions => Promise.resolve(new Program({ - name: 'gren release', + name: `${green('gren')} release`, description: 'Generate release notes and attach them to a tag', argv: process.argv, cwd: process.cwd(), bashOptions: Object.assign({}, ...githubOptions), - options, + options: releaseOptions, events: { '--help': () => { console.log(''); @@ -27,7 +28,8 @@ githubInfo.options.then(githubOptions => Promise.resolve(new Program({ } }))) .then(({ options }) => { - const releaseAction = new Release(options); + const releaseAction = new Gren(options); + return releaseAction.release(); }) .catch(error => { diff --git a/lib/src/Release.js b/lib/src/Gren.js similarity index 73% rename from lib/src/Release.js rename to lib/src/Gren.js index b4a21de4..384de597 100644 --- a/lib/src/Release.js +++ b/lib/src/Gren.js @@ -5,32 +5,24 @@ import { generate } from './_template.js'; import connectivity from 'connectivity'; import templateConfig from './templates.json'; import ObjectAssign from 'object-assign-deep'; +import fs from 'fs'; const defaults = { tags: [], - changelogFilename: 'CHANGELOG.md', - dataSource: 'issues', - onlyMilestones: false, - milestoneMatch: 'Release {{tag_name}}', - draft: false, - force: false, prefix: '', - includeMessages: 'commits', + template: templateConfig, prerelease: false, generate: false, override: false, ignoreLabels: false, ignoreIssuesWith: false, - template: templateConfig, groupBy: false }; -export default class Release { +export default class Gren { constructor(props) { this.options = ObjectAssign({}, defaults, props); - console.log(this.options); - if (!this.options.token) { throw chalk.red('You need to provide the token'); } @@ -48,10 +40,76 @@ export default class Release { this.options.ignoreIssuesWith = utils.convertStringToArray(this.options.ignoreIssuesWith); } + /** + * Generate release notes and draft a new release + * + * @return {Promise} + */ release() { - // print task - return this.getReleaseBlocks() - .then((blocks) => blocks.reduce((carry, block) => carry.then(this.prepareRelease.bind(this, block)), Promise.resolve())); + utils.printTask('\nGenerate release notes'); + + return this._getReleaseBlocks() + .then(blocks => blocks.reduce((carry, block) => carry.then(this._prepareRelease.bind(this, block)), Promise.resolve())); + } + + /** + * Generate changelog file based on the release notes or generate new one + * + * @return {Promise} + */ + changelog() { + utils.printTask('\nGenerate changelog file'); + + return this._checkChangelogFile() + .then(() => { + if (this.options.generate) { + return this._getReleaseBlocks(); + } + + return this._getListReleases(); + }) + .then(releases => { + if (releases.length === 0) { + throw chalk.red('There are no releases, use --generate to create release notes, or run the release command.'); + } + + return Promise.resolve(releases); + }) + .then(releases => { + this._createChangelog(this._templateReleases(releases)); + }); + } + + /** + * Check if the changelog file exists + * + * @since 0.8.0 + * + * @return {Promise} + */ + _checkChangelogFile() { + const filePath = process.cwd() + '/' + this.options.changelogFilename; + + if (fs.existsSync(filePath) && !this.options.override) { + return Promise.reject(chalk.red('Looks like there is already a changelog, to override it use --override')); + } + + return Promise.resolve(); + } + + /** + * Create the changelog file + * + * @since 0.8.0 + * + * @param {string} body The body of the file + */ + _createChangelog(body) { + const filePath = process.cwd() + '/' + this.options.changelogFilename; + + fs.writeFileSync(filePath, this.options.template.changelogTitle + body); + + console.log(chalk.green('\nChangelog created!')); } /** @@ -73,11 +131,11 @@ export default class Release { * * @return {Promise} */ - editRelease(releaseId, releaseOptions) { + _editRelease(releaseId, releaseOptions) { const loaded = utils.task(this, 'Updating latest release'); return this.repo.updateRelease(releaseId, releaseOptions) - .then((response) => { + .then(response => { loaded(); const release = response.data; @@ -105,11 +163,11 @@ export default class Release { * * @return {Promise} */ - createRelease(releaseOptions) { + _createRelease(releaseOptions) { const loaded = utils.task(this, 'Preparing the release'); - return this.repo.createRelease(releaseOptions) - .then((response) => { + return this.repo._createRelease(releaseOptions) + .then(response => { loaded(); const release = response.data; @@ -128,7 +186,7 @@ export default class Release { * * @return {Promise} */ - prepareRelease(block) { + _prepareRelease(block) { const releaseOptions = { tag_name: block.release, name: block.name, @@ -144,10 +202,10 @@ export default class Release { return Promise.resolve(); } - return this.editRelease(block.id, releaseOptions); + return this._editRelease(block.id, releaseOptions); } - return this.createRelease(releaseOptions); + return this._createRelease(releaseOptions); } /** @@ -161,7 +219,7 @@ export default class Release { * * @return {Boolean|Array} */ - getSelectedTags(optionTags = [], tags) { + _getSelectedTags(optionTags = [], tags) { if (optionTags.indexOf('all') >= 0) { return tags; } @@ -189,18 +247,18 @@ export default class Release { * * @return {Promise} */ - getLastTags(releases) { + _getLastTags(releases) { const loaded = utils.task(this, 'Getting tags'); return this.repo.listTags() - .then((response) => { + .then(response => { loaded(); const tags = response.data; - const filteredTags = (this.getSelectedTags(this.options.tags, tags) || [tags[0], tags[1]]) + const filteredTags = (this._getSelectedTags(this.options.tags, tags) || [tags[0], tags[1]]) .filter(Boolean) - .map((tag) => { - const tagRelease = releases ? releases.filter((release) => release.tag_name === tag.name)[0] : false; + .map(tag => { + const tagRelease = releases ? releases.filter(release => release.tag_name === tag.name)[0] : false; const releaseId = tagRelease ? tagRelease.id : null; return { @@ -209,7 +267,7 @@ export default class Release { }; }); - console.log('Tags found: ' + filteredTags.map((tag) => tag.tag.name).join(', ')); + console.log('Tags found: ' + filteredTags.map(tag => tag.tag.name).join(', ')); return filteredTags; }); @@ -224,9 +282,9 @@ export default class Release { * * @return {Promise[]} The promises which returns the dates */ - getTagDates(tags) { - return tags.map((tag) => this.repo.getCommit(tag.tag.commit.sha) - .then((response) => ({ + _getTagDates(tags) { + return tags.map(tag => this.repo.getCommit(tag.tag.commit.sha) + .then(response => ({ id: tag.releaseId, name: tag.tag.name, date: response.data.committer.date @@ -240,11 +298,11 @@ export default class Release { * * @return {Promise} The promise which resolves an array of releases */ - getListReleases() { + _getListReleases() { const loaded = utils.task(this, 'Getting the list of releases'); return this.repo.listReleases() - .then((response) => { + .then(response => { loaded(); const releases = response.data; @@ -255,6 +313,26 @@ export default class Release { }); } + /** + * Generate the releases bodies from a release Objects Array + * + * @since 0.8.0 + * @private + * + * @param {Array} releases The release Objects Array coming from GitHub + * + * @return {string} + */ + _templateReleases(releases) { + const { template } = this.options; + + return releases.map(release => generate({ + release: release.name, + date: utils.formatDate(new Date(release.published_at)), + body: release.body + }, template.release)).join(template.releaseSeparator); + } + /** * Return the templated commit message * @@ -264,7 +342,7 @@ export default class Release { * * @return {string} */ - templateCommits(message) { + _templateCommits(message) { return generate({ message: message }, this.options.template.commit); @@ -279,35 +357,18 @@ export default class Release { * * @return {string} */ - templateLabels(issue) { + _templateLabels(issue) { if (!issue.labels.length && this.options.template.noLabel) { issue.labels.push({name: this.options.template.noLabel}); } return issue.labels - .filter((label) => this.options.ignoreLabels.indexOf(label.name) === -1) - .map((label) => generate({ + .filter(label => this.options.ignoreLabels.indexOf(label.name) === -1) + .map(label => generate({ label: label.name }, this.options.template.label)).join(''); } - /** - * Generate the releases bodies from a release Objects Array - * - * @since 0.8.0 - * - * @param {Array} releases The release Objects Array coming from GitHub - * - * @return {string} - */ - templateReleases(releases) { - return releases.map((release) => generate({ - release: release.name, - date: utils.formatDate(new Date(release.published_at)), - body: release.body - }, this.options.template.release)).join(this.options.template.releaseSeparator); - } - /** * Generate the MD template for each issue * @@ -317,9 +378,9 @@ export default class Release { * * @return {string} */ - templateIssue(issue) { + _templateIssue(issue) { return generate({ - labels: this.templateLabels(issue), + labels: this._templateLabels(issue), name: issue.title, text: '#' + issue.number, url: issue.html_url @@ -335,7 +396,7 @@ export default class Release { * * @return {string} */ - templateIssueBody(body, rangeBody) { + _templateIssueBody(body, rangeBody) { return (body.length ? body.join('\n') : rangeBody || '*No changelog for this release.*') + '\n'; } @@ -351,7 +412,7 @@ export default class Release { * * @return {string} */ - templateGroups(groups) { + _templateGroups(groups) { return Object.entries(groups).map(([key, value]) => { const heading = generate({ heading: key @@ -371,11 +432,11 @@ export default class Release { * * @return {string} */ - generateCommitsBody(messages) { + _generateCommitsBody(messages) { messages.length === 1 && messages.push(null); return messages .slice(0, -1) - .filter((message) => { + .filter(message => { const messageType = this.options.includeMessages; const filterMap = { merges: function(message) { @@ -395,7 +456,7 @@ export default class Release { return filterMap.commits(message); }) - .map(this.templateCommits.bind(this)) + .map(this._templateCommits.bind(this)) .join('\n'); } @@ -408,8 +469,8 @@ export default class Release { * * @return {String[]} */ - commitMessages(commits) { - return commits.map((commitObject) => commitObject.commit.message); + _commitMessages(commits) { + return commits.map(commitObject => commitObject.commit.message); } /** @@ -422,7 +483,7 @@ export default class Release { * * @return {Promise} The promise which resolves the [Array] commit messages */ - getCommitsBetweenTwo(since, until) { + _getCommitsBetweenTwo(since, until) { process.stdout.write(chalk.green('Get commits between ' + utils.formatDate(new Date(since)) + ' and ' + utils.formatDate(new Date(until)) + '\n')); const options = { @@ -432,7 +493,7 @@ export default class Release { }; return this.repo.listCommits(options) - .then((response) => this.commitMessages(response.data)); + .then(response => this._commitMessages(response.data)); } /** @@ -444,18 +505,18 @@ export default class Release { * * @return {Promise[]} */ - getCommitBlocks(releaseRanges) { + _getCommitBlocks(releaseRanges) { console.log(chalk.blue('\nCreating the body blocks from commits:')); return Promise.all( releaseRanges - .map((range) => this.getCommitsBetweenTwo(range[1].date, range[0].date) - .then((commits) => ({ + .map(range => this._getCommitsBetweenTwo(range[1].date, range[0].date) + .then(commits => ({ id: range[0].id, name: this.options.prefix + range[0].name, release: range[0].name, published_at: range[0].date, - body: this.generateCommitsBody(commits) + '\n' + body: this._generateCommitsBody(commits) + '\n' }))) ); } @@ -470,9 +531,9 @@ export default class Release { * * @return {boolean} If the labels array contain any of the ignore ones */ - compareIssueLabels(ignoreLabels, labels) { + _compareIssueLabels(ignoreLabels, labels) { return ignoreLabels - .reduce((carry, ignoredLabel) => carry && labels.map((label) => label.name).indexOf(ignoredLabel) === -1, true); + .reduce((carry, ignoredLabel) => carry && labels.map(label => label.name).indexOf(ignoredLabel) === -1, true); } /** @@ -484,8 +545,8 @@ export default class Release { * * @return {Boolean} */ - filterIssue(issue) { - return !issue.pull_request && this.compareIssueLabels(this.options.ignoreIssuesWith, issue.labels) && + _filterIssue(issue) { + return !issue.pull_request && this._compareIssueLabels(this.options.ignoreIssuesWith, issue.labels) && !((this.options.onlyMilestones || this.options.dataSource === 'milestones') && !issue.milestone); } @@ -498,17 +559,17 @@ export default class Release { * * @return {Promise} The promise which resolves the list of the issues */ - getClosedIssues(releaseRanges) { + _getClosedIssues(releaseRanges) { const loaded = utils.task(this, 'Getting all closed issues'); return this.issues.listIssues({ state: 'closed', since: releaseRanges[releaseRanges.length - 1][1].date }) - .then((response) => { + .then(response => { loaded(); - const filteredIssues = response.data.filter(this.filterIssue.bind(this)); + const filteredIssues = response.data.filter(this._filterIssue.bind(this)); process.stdout.write(filteredIssues.length + ' issues found\n'); @@ -525,10 +586,10 @@ export default class Release { * * @return {string} */ - groupByLabel(issues) { + _groupByLabel(issues) { const groups = []; - issues.forEach((issue) => { + issues.forEach(issue => { if (!issue.labels.length && this.options.template.noLabel) { issue.labels.push({name: this.options.template.noLabel}); } @@ -539,10 +600,10 @@ export default class Release { groups[labelName] = []; } - groups[labelName].push(this.templateIssue(issue)); + groups[labelName].push(this._templateIssue(issue)); }); - return this.templateGroups(utils.sortObject(groups)); + return this._templateGroups(utils.sortObject(groups)); } /** @@ -554,15 +615,15 @@ export default class Release { * * @return {Array} */ - groupBy(issues) { + _groupBy(issues) { const { groupBy } = this.options; if (!groupBy) { - return issues.map(this.templateIssue.bind(this)); + return issues.map(this._templateIssue.bind(this)); } if (groupBy === 'label') { - return this.groupByLabel(issues); + return this._groupByLabel(issues); } if (typeof groupBy !== 'object') { @@ -572,17 +633,17 @@ export default class Release { const allLabels = Object.keys(groupBy).reduce((carry, group) => carry.concat(groupBy[group]), []); const groups = Object.keys(groupBy).reduce((carry, group) => { - const groupIssues = issues.filter((issue) => { + const groupIssues = issues.filter(issue => { if (!issue.labels.length && this.options.template.noLabel) { issue.labels.push({name: this.options.template.noLabel}); } - return issue.labels.some((label) => { + return issue.labels.some(label => { const isOtherLabel = groupBy[group].indexOf('...') !== -1 && allLabels.indexOf(label.name) === -1; return groupBy[group].indexOf(label.name) !== -1 || isOtherLabel; }); - }).map(this.templateIssue.bind(this)); + }).map(this._templateIssue.bind(this)); if (groupIssues.length) { carry[group] = groupIssues; @@ -591,7 +652,7 @@ export default class Release { return carry; }, {}); - return this.templateGroups(groups); + return this._templateGroups(groups); } /** @@ -605,7 +666,7 @@ export default class Release { * * @return {Boolean} */ - filterBlockIssue(range, issue) { + _filterBlockIssue(range, issue) { if (this.options.dataSource === 'milestones') { return this.options.milestoneMatch.replace('{{tag_name}}', range[0].name) === issue.milestone.title; } @@ -626,21 +687,21 @@ export default class Release { * * @return {Promise[]} */ - getIssueBlocks(releaseRanges) { + _getIssueBlocks(releaseRanges) { console.log('Creating the body blocks from releases:'); - return this.getClosedIssues(releaseRanges) - .then((issues) => releaseRanges - .map((range) => { - const filteredIssues = issues.filter(this.filterBlockIssue.bind(this, range)); - const body = (!range[0].body || this.options.override) && this.groupBy(filteredIssues); + return this._getClosedIssues(releaseRanges) + .then(issues => releaseRanges + .map(range => { + const filteredIssues = issues.filter(this._filterBlockIssue.bind(this, range)); + const body = (!range[0].body || this.options.override) && this._groupBy(filteredIssues); return { id: range[0].id, release: range[0].name, name: this.options.prefix + range[0].name, published_at: range[0].date, - body: this.templateIssueBody(body, range[0].body) + body: this._templateIssueBody(body, range[0].body) }; })); } @@ -654,7 +715,7 @@ export default class Release { * * @return {Array} */ - sortReleasesByDate(releaseDates) { + _sortReleasesByDate(releaseDates) { return releaseDates.sort((release1, release2) => new Date(release2.date) - new Date(release1.date)); } @@ -667,10 +728,10 @@ export default class Release { * * @return {Array} */ - createReleaseRanges(releaseDates) { + _createReleaseRanges(releaseDates) { const ranges = []; const range = 2; - const sortedReleaseDates = this.sortReleasesByDate(releaseDates); + const sortedReleaseDates = this._sortReleasesByDate(releaseDates); if (sortedReleaseDates.length === 1) { sortedReleaseDates.push({ @@ -692,26 +753,26 @@ export default class Release { * * @return {Promise} Resolving the release blocks */ - getReleaseBlocks() { + _getReleaseBlocks() { let loaded; const dataSource = { - issues: this.getIssueBlocks.bind(this), - commits: this.getCommitBlocks.bind(this), - milestones: this.getIssueBlocks.bind(this) + issues: this._getIssueBlocks.bind(this), + commits: this._getCommitBlocks.bind(this), + milestones: this._getIssueBlocks.bind(this) }; - return this.getListReleases() - .then((releases) => this.getLastTags(releases.length ? releases : false)) - .then((tags) => { + return this._getListReleases() + .then(releases => this._getLastTags(releases.length ? releases : false)) + .then(tags => { loaded = utils.task(this, 'Getting the tag dates ranges'); - return Promise.all(this.getTagDates(tags)); + return Promise.all(this._getTagDates(tags)); }) - .then((releaseDates) => { + .then(releaseDates => { loaded(); return dataSource[this.options.dataSource]( - this.createReleaseRanges(releaseDates) + this._createReleaseRanges(releaseDates) ); }); } diff --git a/lib/src/_utils.js b/lib/src/_utils.js index 26f47e70..0e9d544c 100644 --- a/lib/src/_utils.js +++ b/lib/src/_utils.js @@ -31,7 +31,7 @@ function sortObject(object) { * @param {string} name The name of the task */ function printTask(name) { - process.stdout.write(chalk.blue(name + ' task:\n===================================\n')); + process.stdout.write(chalk.blue(`${name} :\n===================================\n`)); } /** * Outputs the task status @@ -55,7 +55,7 @@ function task(gren, taskName) { const diff = process.hrtime(time); const seconds = ((diff[0] * 1e9 + diff[1]) * 1e-9).toFixed(2); - process.stdout.write(message || '' + chalk.yellow(' (' + seconds + ' secs)\n')); + process.stdout.write(message || '' + chalk.yellow(` (${seconds} secs)\n`)); clearInterval(gren.tasks[taskName]); gren.tasks[taskName] = seconds; diff --git a/test/options.js b/test/options.js new file mode 100644 index 00000000..8b889075 --- /dev/null +++ b/test/options.js @@ -0,0 +1,15 @@ +const { releaseOptions, changelogOptions } = require('../lib/_options'); + +exports['options'] = { + 'There should be no duplicates': function (test) { + test.expect(1); + + const duplicates = releaseOptions.concat(changelogOptions) + .map(option => option.short) + .filter((short, index, array) => array.indexOf(short) !== index) + .length; + + test.ok(duplicates === 0, 'There are no duplicates'); + test.done(); + } +}; diff --git a/test/options.js.xml b/test/options.js.xml new file mode 100644 index 00000000..62284ca6 --- /dev/null +++ b/test/options.js.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/test/utils.js b/test/utils.js index 5d425076..278e517a 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('../lib/src/_utils'); +const utils = require('../lib/src/_utils'); exports['utils'] = { 'Should return the string of the formatted date': function (test) { From f39b5ea02ef2636f4e899add5db271e5baf1ffa7 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 22 Sep 2017 00:59:46 +0100 Subject: [PATCH 04/19] Update node version in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 85a61b8b..f8789ec8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: - node_js node_js: - - "5.1" + - "7.0" cache: directories: From d890d87d3f921fcefcd0a2ce37cc34207466033c Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 22 Sep 2017 01:16:21 +0100 Subject: [PATCH 05/19] Change the task for the Gren --- github-release-notes.js | 15 +-------------- lib/_options.js | 14 ++++++++++++-- lib/gren-changelog.js | 2 +- lib/gren.js | 2 +- lib/src/Gren.js | 12 +++++++----- lib/src/Program.js | 2 +- lib/src/_utils.js | 4 ++-- package.json | 2 +- test/options.js | 1 + 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/github-release-notes.js b/github-release-notes.js index 1010a590..b543314f 100644 --- a/github-release-notes.js +++ b/github-release-notes.js @@ -1,16 +1,3 @@ '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() { - return gren[action || 'release'](); - }) - .catch(function(error) { - utils.clearTasks(gren); - - console.error(error); - }); +const gren = require('./bin/gren.js'); diff --git a/lib/_options.js b/lib/_options.js index ad53115b..e77e7671 100644 --- a/lib/_options.js +++ b/lib/_options.js @@ -2,17 +2,27 @@ const chalk = require('chalk'); module.exports = { changelogOptions: [ + { + short: false, + name: false, + description: `\n${chalk.yellow('Changelog options')}\n\n` + }, { short: '-G', name: 'generate', - description: `${chalk.blue('changelog only')} - Generate the changelog with GithubReleaseNotes rather then using the repo releases` + description: 'Generate the changelog with GithubReleaseNotes rather then using the repo releases' }, { short: '-f', name: 'changelog-filename', valueType: '', - description: `${chalk.blue('changelog only')} - The name of the changelog file. [CHANGELOG.md]`, + description: 'The name of the changelog file. [CHANGELOG.md]', defaultValue: 'CHANGELOG.md' + }, + { + short: false, + name: false, + description: `\n\n${chalk.yellow('Release options')} \n${chalk.blue('(only applicable with the --generate option).')}\n\n` } ], releaseOptions: [ diff --git a/lib/gren-changelog.js b/lib/gren-changelog.js index 21ac005b..45d6ef8b 100644 --- a/lib/gren-changelog.js +++ b/lib/gren-changelog.js @@ -14,7 +14,7 @@ githubInfo.options.then(githubOptions => Promise.resolve(new Program({ argv: process.argv, cwd: process.cwd(), bashOptions: Object.assign({}, ...githubOptions), - options: releaseOptions.concat(changelogOptions), + options: changelogOptions.concat(releaseOptions), events: { '--help': () => { console.log(''); diff --git a/lib/gren.js b/lib/gren.js index 760f6edc..41be0d63 100644 --- a/lib/gren.js +++ b/lib/gren.js @@ -15,7 +15,7 @@ const argvWithVersion = argvs => { gren .version(version) - .description(description) + .description(`gren (šŸ¤– ) ${description}`) .usage('[command] [options]') .command('release', 'Release into chunk').alias('r') .command('changelog', 'Write a motherfucking changelog').alias('c') diff --git a/lib/src/Gren.js b/lib/src/Gren.js index 384de597..6735d0f1 100644 --- a/lib/src/Gren.js +++ b/lib/src/Gren.js @@ -46,7 +46,7 @@ export default class Gren { * @return {Promise} */ release() { - utils.printTask('\nGenerate release notes'); + utils.printTask('Generate release notes'); return this._getReleaseBlocks() .then(blocks => blocks.reduce((carry, block) => carry.then(this._prepareRelease.bind(this, block)), Promise.resolve())); @@ -58,7 +58,7 @@ export default class Gren { * @return {Promise} */ changelog() { - utils.printTask('\nGenerate changelog file'); + utils.printTask('Generate changelog file'); return this._checkChangelogFile() .then(() => { @@ -109,7 +109,7 @@ export default class Gren { fs.writeFileSync(filePath, this.options.template.changelogTitle + body); - console.log(chalk.green('\nChangelog created!')); + console.log(chalk.green(`\nChangelog created in ${filePath} ā¤ļø`)); } /** @@ -140,7 +140,8 @@ export default class Gren { const release = response.data; - console.log(chalk.green(release.name + ' has been successfully updated!')); + console.log(chalk.green(`\n${release.name} has been successfully updated!`)); + console.log(chalk.blue(`See the results here: ${release.html_url}`)); return release; }); @@ -171,7 +172,8 @@ export default class Gren { loaded(); const release = response.data; - console.log(chalk.green(release.name + ' has been successfully created!')); + console.log(chalk.green(`\n${release.name} has been successfully created!`)); + console.log(chalk.blue(`See the results here: ${release.html_url}`)); return release; }); diff --git a/lib/src/Program.js b/lib/src/Program.js index fe13c58f..5b93270c 100644 --- a/lib/src/Program.js +++ b/lib/src/Program.js @@ -115,7 +115,7 @@ export default class Program { */ _consumeOptions(opts) { const programOptions = opts.map(({ short, name, valueType, description, defaultValue, action }) => ({ - name: `${short}, --${name} ${valueType || ''}`, + name: short && name ? `${short}, --${name} ${valueType || ''}` : ' ', description, defaultValue, action diff --git a/lib/src/_utils.js b/lib/src/_utils.js index 0e9d544c..16c2b484 100644 --- a/lib/src/_utils.js +++ b/lib/src/_utils.js @@ -31,7 +31,7 @@ function sortObject(object) { * @param {string} name The name of the task */ function printTask(name) { - process.stdout.write(chalk.blue(`${name} :\n===================================\n`)); + process.stdout.write(chalk.blue(`\nšŸ¤– - ${name} :\n===================================\n`)); } /** * Outputs the task status @@ -45,7 +45,7 @@ function printTask(name) { */ function task(gren, taskName) { const time = process.hrtime(); - process.stdout.write(chalk.green(taskName) + ': .'); + process.stdout.write(`\n${chalk.green(taskName)} : .`); gren.tasks[taskName] = setInterval(() => { process.stdout.write('.'); diff --git a/package.json b/package.json index 8367ef7d..a2ea558c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "github-release-notes", "version": "0.9.0", - "description": "Node module to publish release notes based on commits between the last two tags.", + "description": "Creates a release from a tag and uses issues or commits to creating the release notes. It also can generate a CHANGELOG.md file based on the release notes (or generate a brand new).", "main": "./github-release-notes.js", "scripts": { "start": "node github-release-notes.js", diff --git a/test/options.js b/test/options.js index 8b889075..7aec6ce6 100644 --- a/test/options.js +++ b/test/options.js @@ -6,6 +6,7 @@ exports['options'] = { const duplicates = releaseOptions.concat(changelogOptions) .map(option => option.short) + .filter(Boolean) .filter((short, index, array) => array.indexOf(short) !== index) .length; From 0efd7b1aeba5e4424efee26d14857aa05a7c09b0 Mon Sep 17 00:00:00 2001 From: alexcanessa Date: Fri, 22 Sep 2017 08:28:19 +0100 Subject: [PATCH 06/19] Update options description --- lib/_options.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/_options.js b/lib/_options.js index e77e7671..e0efd240 100644 --- a/lib/_options.js +++ b/lib/_options.js @@ -59,7 +59,7 @@ module.exports = { short: '-t', name: 'tags', valueType: '..', - description: 'Write release notes for a range of tags, a specific tag or all. e.g. 0.2.0..0.1.0 or 0.2.0.. or ..0.1.0 or 0.2.0 or *', // needs to be documented better, + description: 'Write release notes for using data collected until . If only one tag is specified, will use data until the previous tag. To run gren for all the tags, use --tags=*', action: value => value.split('..') }, { @@ -97,8 +97,9 @@ module.exports = { { short: '-g', name: 'group-by', - valueType: '<"label"|Object>', - description: 'Group the issues using the labels as group headings. You can set custom headings for groups of labels from a configuration file.' + valueType: '