Skip to content

Commit 5f7c3f1

Browse files
authored
Group by labels (#62)
* Release 0.7.2 * Fix issues option in the docs * Add the group by label option * Change the configuration file for gren * Update documentation for the group by function
1 parent 802bbfc commit 5f7c3f1

File tree

7 files changed

+193
-24
lines changed

7 files changed

+193
-24
lines changed

.grenrc.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ module.exports = {
66
"help wanted"
77
],
88
"template": {
9-
issue: ({ text, name, url, labels }) => {
10-
labels = labels.slice(0, -1);
11-
12-
return `- [${text}](${url}) ${name} - ${labels}`;
13-
},
14-
"label": "_{{label}}_,"
9+
"issue": "- [{{text}}]({{url}}) {{name}}"
10+
},
11+
"groupBy": {
12+
"Framework Enhancements:": ["enhancement"],
13+
"Bug Fixes:": ["bug"]
1514
}
16-
}
15+
};

docs/examples.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,41 @@ gren --tags=all
6262

6363
![exec gren all tags]({{ "images/examples/exec_gren_all_tags.gif" | relative_rul }})
6464

65+
### Group by
66+
67+
You can group the issues in the release notes. So far the only way to group them is by label and you can do it in two different ways:
68+
69+
1. Just using the first label of an issue to group it.
70+
71+
```json
72+
{
73+
"groupBy": "label"
74+
}
75+
```
76+
77+
2. Using an object where the keys are the group names, and the values are the arrays of labels included in that group.
78+
79+
```json
80+
{
81+
"groupBy": {
82+
"Bug fixes:": ["bug"],
83+
"Framework Enhancements:": ["enhancement"]
84+
}
85+
}
86+
```
87+
88+
An issue with no label will be included using the template option `no-label` (default "_closed_") as group name.
89+
To list all the issues that have any other label, use `"..."` as label name.
90+
91+
```json
92+
{
93+
"groupBy": {
94+
"Bug fixes:": ["bug"],
95+
"Other stuff:": ["..."] // This will include the issues that have any label but not "bug"
96+
}
97+
}
98+
```
99+
65100
### Changelog
66101

67102
There different ways to generate the changelog:

docs/options.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ To pass it to the `GithubReleaseNotes` class, in the [configuration file](#confi
1919
| `username` | **Required** | The username of the repo _e.g. `github-tools`_ | `null` |
2020
| `repo` | **Required** | The repository name _e.g. `github-release-notes`_ | `null` |
2121
| `action`| `release` `changelog` | The **gren** action to run. _(see details below for changelog generator)_ | `release` |
22-
| `ignore-labels` | `wont_fix` `wont_fix,duplicate` | Ignore issues that contains one of the specified labels. | `false` |
22+
| `ignore-labels` | `wont_fix` `wont_fix,duplicate` | Ignore the specified labels. | `false` |
2323
| `ignore-issues-with` | `wont_fix` `wont_fix,duplicate` | Ignore issues that contains one of the specified labels. | `false` |
2424
| `data-source` | `issues` `commits` | The informations you want to use to build release notes. | `issues` |
2525
| `prefix` | **String** `e.g. v` | Add a prefix to the tag version. | `null` |
@@ -33,6 +33,7 @@ To pass it to the `GithubReleaseNotes` class, in the [configuration file](#confi
3333
| `draft` | **Flag** | Set the release as a draft. | `false` |
3434
| `prerelease` | **Flag** | To set the release as a prerelease. | `false` |
3535
| `tags` | `0.1.0` `0.2.0,0.1.0` `all` | A specific tag or the range of tags to build the release notes from. You can also specify `all` to write all releases. _(To override existing releases use the --override flag)_ | `false` |
36+
| `group-by` | `label` `{...}` | Group the issues using the labels as group headings. You can set custom headings for groups of labels. [See example]({{ "example#group-by" | relative_url }}) | `false` |
3637

3738
### Changelog options
3839

@@ -79,7 +80,9 @@ You can configure the output of **gren** using templates. Set your own configura
7980
"commit": "- {{message}}",
8081
"issue": "- {{labels}} {{name}} [{{text}}]({{url}})",
8182
"label": "[**{{label}}**]",
82-
"release": "## {{release}} {{date}}"
83+
"noLabel": "closed",
84+
"release": "## {{release}} {{date}}",
85+
"group": "#### {{heading}}\n"
8386
}
8487
}
8588
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "github-release-notes",
3-
"version": "0.7.1",
3+
"version": "0.7.2",
44
"description": "Node module to publish release notes based on commits between the last two tags.",
55
"main": "./github-release-notes.js",
66
"scripts": {

src/gren.js

Lines changed: 123 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ var defaults = {
2626
override: false,
2727
ignoreLabels: false, // || array of labels
2828
ignoreIssuesWith: false, // || array of labels
29-
template: templateConfig
29+
template: templateConfig,
30+
groupBy: false
3031
};
3132

3233
/**
@@ -293,7 +294,7 @@ function getLastTwoReleases(gren) {
293294
}
294295

295296
/**
296-
* Return a string with a - to be a bulvar list (used for a mapping)
297+
* Return the templated commit message
297298
*
298299
* @since 0.1.0
299300
* @private
@@ -319,8 +320,8 @@ function templateCommits(gren, message) {
319320
* @return {string}
320321
*/
321322
function templateLabels(gren, issue) {
322-
if (!issue.labels.length) {
323-
issue.labels.push({name: 'closed'});
323+
if (!issue.labels.length && gren.options.template.noLabel) {
324+
issue.labels.push({name: gren.options.template.noLabel});
324325
}
325326

326327
return issue.labels
@@ -404,6 +405,31 @@ function templateIssueBody(body, rangeBody) {
404405
return (body.length ? body.join('\n') : rangeBody || '*No changelog for this release.*') + '\n';
405406
}
406407

408+
/**
409+
* Generates the template for the groups
410+
*
411+
* @since 0.8.0
412+
* @private
413+
*
414+
* @param {GithubReleaseNotes} gren
415+
* @param {Object} groups The groups to template e.g.
416+
* {
417+
* 'bugs': [{...}, {...}, {...}]
418+
* }
419+
*
420+
* @return {string}
421+
*/
422+
function templateGroups(gren, groups) {
423+
return Object.keys(groups).map(function(group) {
424+
var heading = template.generate({
425+
heading: group
426+
}, gren.options.template.group);
427+
var body = groups[group].join('\n');
428+
429+
return heading + '\n' + body;
430+
});
431+
}
432+
407433
/**
408434
* Return a commit messages generated body
409435
*
@@ -567,6 +593,90 @@ function getClosedIssues(gren, releaseRanges) {
567593
});
568594
}
569595

596+
/**
597+
* Group the issues based on their first label
598+
*
599+
* @since 0.8.0
600+
* @private
601+
*
602+
* @param {GithubReleaseNotes} gren
603+
* @param {Array} issues
604+
*
605+
* @return {string}
606+
*/
607+
function groupByLabel(gren, issues) {
608+
var groups = [];
609+
610+
issues.forEach(function(issue) {
611+
if (!issue.labels.length && gren.options.template.noLabel) {
612+
issue.labels.push({name: gren.options.template.noLabel});
613+
}
614+
615+
var labelName = issue.labels[0].name;
616+
617+
if (!groups[labelName]) {
618+
groups[labelName] = [];
619+
}
620+
621+
groups[labelName].push(templateIssue(gren, issue));
622+
});
623+
624+
return templateGroups(gren, utils.sortObject(groups));
625+
}
626+
627+
/**
628+
* Create groups of issues based on labels
629+
*
630+
* @since 0.8.0
631+
* @private
632+
*
633+
* @param {GithubReleaseNotes} gren
634+
* @param {Array} issues The array of all the issues.
635+
*
636+
* @return {Array}
637+
*/
638+
function groupBy(gren, issues) {
639+
var groupBy = gren.options.groupBy;
640+
641+
if (!groupBy) {
642+
return issues.map(templateIssue.bind(null, gren));
643+
}
644+
645+
if (groupBy === 'label') {
646+
return groupByLabel(gren, issues);
647+
}
648+
649+
if (typeof groupBy !== 'object') {
650+
throw chalk.red('The option for groupBy is invalid, please check the documentation');
651+
}
652+
653+
var allLabels = Object.keys(groupBy).reduce(function(carry, group) {
654+
return carry.concat(groupBy[group]);
655+
}, []);
656+
657+
var groups = Object.keys(groupBy).reduce(function(carry, group) {
658+
var groupIssues = issues.filter(function(issue) {
659+
if (!issue.labels.length && gren.options.template.noLabel) {
660+
issue.labels.push({name: gren.options.template.noLabel});
661+
}
662+
663+
return issue.labels.some(function(label) {
664+
var isOtherLabel = groupBy[group].indexOf('...') !== -1 && allLabels.indexOf(label.name) === -1;
665+
666+
return groupBy[group].indexOf(label.name) !== -1 || isOtherLabel;
667+
});
668+
}).map(templateIssue.bind(null, gren));
669+
670+
if (groupIssues.length) {
671+
carry[group] = groupIssues;
672+
}
673+
674+
return carry;
675+
}, {});
676+
677+
return templateGroups(gren, groups);
678+
}
679+
570680
/**
571681
* Get the blocks of issues based on release dates
572682
*
@@ -585,15 +695,15 @@ function getIssueBlocks(gren, releaseRanges) {
585695
.then(function(issues) {
586696
return releaseRanges
587697
.map(function(range) {
588-
var body = (!range[0].body || gren.options.override) &&
589-
issues.filter(function(issue) {
590-
return utils.isInRange(
591-
Date.parse(issue.closed_at),
592-
Date.parse(range[1].date),
593-
Date.parse(range[0].date)
594-
);
595-
})
596-
.map(templateIssue.bind(null, gren));
698+
var filteredIssues = issues.filter(function(issue) {
699+
return utils.isInRange(
700+
Date.parse(issue.closed_at),
701+
Date.parse(range[1].date),
702+
Date.parse(range[0].date)
703+
);
704+
});
705+
706+
var body = (!range[0].body || gren.options.override) && groupBy(gren, filteredIssues);
597707

598708
return {
599709
id: range[0].id,

src/templates.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
"commit": "- {{message}}",
33
"issue": "- {{labels}} {{name}} [{{text}}]({{url}})",
44
"label": "[**{{label}}**]",
5-
"release": "## {{release}} {{date}}"
5+
"noLabel": "closed",
6+
"release": "## {{release}} {{date}}",
7+
"group": "#### {{heading}}\n"
68
}

src/utils.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ var chalk = require('chalk');
44
var fs = require('fs');
55
require('require-yaml');
66

7+
/**
8+
* Sort an object by its keys
9+
*
10+
* @since 0.8.0
11+
* @public
12+
*
13+
* @param {Object} object
14+
* @return {Object}
15+
*/
16+
function sortObject(object) {
17+
return Object.keys(object)
18+
.sort()
19+
.reduce(function(result, key) {
20+
result[key] = object[key];
21+
22+
return result;
23+
}, {});
24+
}
25+
726
/**
827
* Print a task name in a custom format
928
*
@@ -213,6 +232,7 @@ function getConfigFromFile(path) {
213232
}
214233

215234
module.exports = {
235+
sortObject: sortObject,
216236
printTask: printTask,
217237
task: task,
218238
clearTasks: clearTasks,

0 commit comments

Comments
 (0)