Skip to content

Commit 1d8110e

Browse files
feat: Add a config option includeCommitBody to render the body text of the commit (#15)
The default value is false Closes #14
1 parent 0eecf8a commit 1d8110e

File tree

5 files changed

+118
-42
lines changed

5 files changed

+118
-42
lines changed

README.md

+28-15
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
</p>
44

55
# tag-changelog
6+
67
A GitHub Action triggered by a new (valid semver) tag getting pushed. It then fetches all the commits since the previous tag and creates a changelog text using the [Conventional Commits](https://www.conventionalcommits.org) format. It will also turn PR numbers into clickable links, and mentions the author.
78

89
This action returns the generated changelog text, but doesn't do anything more; you need to for example prepend it to a `CHANGELOG.md` file, create a GitHub Release with this text, etc.
910

1011
## Example workflow
11-
``` yml
12+
13+
```yml
1214
name: Create Release
1315

1416
on:
1517
push:
1618
tags:
17-
- '*'
19+
- "*"
1820

1921
jobs:
2022
create-release:
@@ -42,28 +44,31 @@ jobs:
4244
```
4345
4446
## Inputs
45-
* `token`: Your GitHub token, `${{ secrets.GITHUB_TOKEN }}`. Required.
46-
* `exclude_types`: A comma separated list of commit types you want to exclude from the changelog, for example: "other,chore". Optional (defaults to nothing). Can also be configured in the config file.
47-
* `config_file`: Location of the config file. Optional.
47+
48+
- `token`: Your GitHub token, `${{ secrets.GITHUB_TOKEN }}`. Required.
49+
- `exclude_types`: A comma separated list of commit types you want to exclude from the changelog, for example: "other,chore". Optional (defaults to nothing). Can also be configured in the config file.
50+
- `config_file`: Location of the config file. Optional.
4851

4952
## Outputs
50-
* `changelog`: Generated changelog for the latest tag, including the version/date header (suitable for prepending to a CHANGELOG.md file).
51-
* `changes`: Generated changelog for the latest tag, without the version/date header (suitable for GitHub Releases).
53+
54+
- `changelog`: Generated changelog for the latest tag, including the version/date header (suitable for prepending to a CHANGELOG.md file).
55+
- `changes`: Generated changelog for the latest tag, without the version/date header (suitable for GitHub Releases).
5256

5357
## Custom config
54-
``` yml
58+
59+
```yml
5560
- name: Create changelog text
5661
uses: loopwerk/tag-changelog@v1
5762
with:
5863
token: ${{ secrets.GITHUB_TOKEN }}
5964
config_file: .github/tag-changelog-config.js
6065
```
6166

62-
The config file can be used to map commit types to changelog labels, to override the rendering of changelog sections, and the rendering of the overall changelog. You only need to override the things you want to override. For example, you can leave out `renderTypeSection` and `renderChangelog` and only include the `types` config; the default config will be used for whatever is not overriden.
67+
The config file can be used to map commit types to changelog labels, to override the rendering of changelog sections, and the rendering of the overall changelog. You only need to override the things you want to override. For example, you can leave out `renderTypeSection` and `renderChangelog` and only include the `types` config; the [default config](https://github.com/loopwerk/tag-changelog/blob/main/src/defaultConfig.js) will be used for whatever is not overriden.
6368

6469
### Example config file:
6570

66-
``` javascript
71+
```javascript
6772
module.exports = {
6873
types: [
6974
{ types: ["feat", "feature"], label: "🎉 New Features" },
@@ -84,8 +89,11 @@ module.exports = {
8489
renderTypeSection: function (label, commits) {
8590
let text = `\n## ${label}\n`;
8691

87-
commits.forEach((commit) => {
92+
commits.forEach(commit => {
8893
text += `- ${commit.subject}\n`;
94+
if (commit.body) {
95+
text += `${commit.body}\n`;
96+
}
8997
});
9098

9199
return text;
@@ -103,24 +111,29 @@ The order in which the `types` appear also determines the order of the generated
103111
## Example output
104112

105113
> # v0.14.0 - 2021-02-22
106-
>
114+
>
107115
> ## New Features
116+
>
108117
> - merge the default config with the user config so that the user config only has to override values it wants, and use the defaults for the others
109118
> - the custom config file is now JS instead of JSON, allow the override of the changelog text templates ([#2](https://github.com/loopwerk/tag-changelog/pull/2) by [kevinrenskers](https://github.com/kevinrenskers))
110119
> - commit types to exclude can now also be configured via the config file
111-
>
120+
>
112121
> ## Documentation Changes
122+
>
113123
> - simplified readme
114-
>
124+
>
115125
> ## Chores
126+
>
116127
> - added project logo
117-
>
128+
>
118129
> ## BREAKING CHANGES
130+
>
119131
> - due to [bcb876](https://github.com/loopwerk/tag-changelog/commit/bcb8767bc22bc7d4ab47a4fffd4ef435de581054): commit types to exclude can now also be configured via the config file
120132
>
121133
> The `exclude` input parameter has been renamed to `exclude_types`.
122134
123135
You can also check out the [Releases page](https://github.com/loopwerk/tag-changelog/releases) for tag-changelog.
124136

125137
## Thanks
138+
126139
Thanks to [Helmisek/conventional-changelog-generator](https://github.com/Helmisek/conventional-changelog-generator) and [ardalanamini/auto-changelog](https://github.com/ardalanamini/auto-changelog) for inspiration. Thanks to [nektos/act](https://github.com/nektos/act) for making it possible to run GitHub Actions locally, making development and testing a whole lot easier.

dist/index.js

+24-19
Original file line numberDiff line numberDiff line change
@@ -10680,12 +10680,17 @@ const DEFAULT_CONFIG = {
1068010680

1068110681
excludeTypes: [],
1068210682

10683-
renderTypeSection: function (label, commits) {
10683+
includeCommitBody: false,
10684+
10685+
renderTypeSection: function (label, commits, includeCommitBody) {
1068410686
let text = `\n## ${label}\n`;
1068510687

10686-
commits.forEach((commit) => {
10688+
commits.forEach(commit => {
1068710689
const scope = commit.scope ? `**${commit.scope}:** ` : "";
1068810690
text += `- ${scope}${commit.subject}\n`;
10691+
if (commit.body && includeCommitBody) {
10692+
text += `${commit.body}\n`;
10693+
}
1068910694
});
1069010695

1069110696
return text;
@@ -10694,7 +10699,7 @@ const DEFAULT_CONFIG = {
1069410699
renderNotes: function (notes) {
1069510700
let text = `\n## BREAKING CHANGES\n`;
1069610701

10697-
notes.forEach((note) => {
10702+
notes.forEach(note => {
1069810703
text += `- due to [${note.commit.sha.substr(0, 6)}](${note.commit.url}): ${note.commit.subject}\n\n`;
1069910704
text += `${note.text}\n\n`;
1070010705
});
@@ -10724,30 +10729,30 @@ function generateChangelog(releaseName, commitObjects, config) {
1072410729
let changes = "";
1072510730

1072610731
commitsByType
10727-
.filter((obj) => {
10732+
.filter(obj => {
1072810733
return !config.excludeTypes.includes(obj.type);
1072910734
})
10730-
.forEach((obj) => {
10735+
.forEach(obj => {
1073110736
const niceType = translateType(obj.type, config.types);
10732-
changes += config.renderTypeSection(niceType, obj.commits);
10737+
changes += config.renderTypeSection(niceType, obj.commits, config.includeCommitBody);
1073310738
});
1073410739

1073510740
// Find all the notes of all the commits of all the types
1073610741
const notes = commitsByType
10737-
.flatMap((obj) => {
10742+
.flatMap(obj => {
1073810743
return obj.commits
10739-
.map((commit) => {
10744+
.map(commit => {
1074010745
if (commit.notes && commit.notes.length) {
10741-
return commit.notes.map((note) => {
10746+
return commit.notes.map(note => {
1074210747
const noteObj = note;
1074310748
noteObj.commit = commit;
1074410749
return noteObj;
1074510750
});
1074610751
}
1074710752
})
10748-
.filter((o) => o);
10753+
.filter(o => o);
1074910754
})
10750-
.flatMap((o) => o);
10755+
.flatMap(o => o);
1075110756

1075210757
if (notes.length) {
1075310758
changes += config.renderNotes(notes);
@@ -10775,7 +10780,7 @@ function groupByType(commits, typeConfig) {
1077510780
// First, group all the commits by their types.
1077610781
// We end up with a dictionary where the key is the type, and the values is an array of commits.
1077710782
const byType = {};
10778-
commits.forEach((commit) => {
10783+
commits.forEach(commit => {
1077910784
if (!byType[commit.type]) {
1078010785
byType[commit.type] = [];
1078110786
}
@@ -10785,7 +10790,7 @@ function groupByType(commits, typeConfig) {
1078510790
// Turn that dictionary into an array of objects,
1078610791
// where the key is the type, and the values is an array of commits.
1078710792
const byTypeArray = [];
10788-
Object.keys(byType).forEach((key) => {
10793+
Object.keys(byType).forEach(key => {
1078910794
byTypeArray.push({
1079010795
type: key,
1079110796
commits: byType[key],
@@ -10794,9 +10799,9 @@ function groupByType(commits, typeConfig) {
1079410799

1079510800
// And now we sort that array using the TYPES object.
1079610801
byTypeArray.sort((a, b) => {
10797-
let aOrder = typeConfig.findIndex((t) => t.types.includes(a.type));
10802+
let aOrder = typeConfig.findIndex(t => t.types.includes(a.type));
1079810803
if (aOrder === -1) aOrder = 999;
10799-
let bOrder = typeConfig.findIndex((t) => t.types.includes(b.type));
10804+
let bOrder = typeConfig.findIndex(t => t.types.includes(b.type));
1080010805
if (bOrder === -1) bOrder = 999;
1080110806
return aOrder - bOrder;
1080210807
});
@@ -10854,7 +10859,7 @@ module.exports = parseCommitMessage;
1085410859
/***/ ((module) => {
1085510860

1085610861
function translateType(type, typeConfig) {
10857-
const foundType = typeConfig.find((t) => t.types.includes(type));
10862+
const foundType = typeConfig.find(t => t.types.includes(type));
1085810863
if (foundType) {
1085910864
return foundType.label;
1086010865
}
@@ -11089,7 +11094,7 @@ async function run() {
1108911094
const tags = await octokit.paginate("GET /repos/{owner}/{repo}/tags", { owner, repo });
1109011095

1109111096
const validSortedTags = tags
11092-
.filter((t) => validate(t.name))
11097+
.filter(t => validate(t.name))
1109311098
.sort((a, b) => {
1109411099
return compareVersions(a.name, b.name);
1109511100
})
@@ -11139,14 +11144,14 @@ async function run() {
1113911144
// Parse every commit, getting the type, turning PR numbers into links, etc
1114011145
const commitObjects = await Promise.all(
1114111146
result.data.commits
11142-
.map(async (commit) => {
11147+
.map(async commit => {
1114311148
const commitObj = await parseCommitMessage(commit.commit.message, `https://github.com/${owner}/${repo}`, fetchUserFunc);
1114411149
commitObj.sha = commit.sha;
1114511150
commitObj.url = commit.html_url;
1114611151
commitObj.author = commit.author;
1114711152
return commitObj;
1114811153
})
11149-
.filter((m) => m !== false)
11154+
.filter(m => m !== false)
1115011155
);
1115111156

1115211157
// And generate the changelog

src/defaultConfig.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ const DEFAULT_CONFIG = {
1515

1616
excludeTypes: [],
1717

18-
renderTypeSection: function (label, commits) {
18+
includeCommitBody: false,
19+
20+
renderTypeSection: function (label, commits, includeCommitBody) {
1921
let text = `\n## ${label}\n`;
2022

2123
commits.forEach(commit => {
2224
const scope = commit.scope ? `**${commit.scope}:** ` : "";
2325
text += `- ${scope}${commit.subject}\n`;
26+
if (commit.body && includeCommitBody) {
27+
text += `${commit.body}\n`;
28+
}
2429
});
2530

2631
return text;

src/generateChangelog.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function generateChangelog(releaseName, commitObjects, config) {
1111
})
1212
.forEach(obj => {
1313
const niceType = translateType(obj.type, config.types);
14-
changes += config.renderTypeSection(niceType, obj.commits);
14+
changes += config.renderTypeSection(niceType, obj.commits, config.includeCommitBody);
1515
});
1616

1717
// Find all the notes of all the commits of all the types

test/generateChangelog.spec.js

+59-6
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ const DEFAULT_CONFIG = require("../src/defaultConfig");
77
describe("generateChangelog", () => {
88
it("should create a changelog", () => {
99
const commitObjects = [
10-
{ subject: "Subject 1", type: "fix", notes: [] },
11-
{ subject: "Subject 2", type: "feat", notes: [] },
12-
{ subject: "Subject 3", type: "feat", notes: [] },
13-
{ subject: "Subject 4", type: "fix", notes: [] },
14-
{ subject: "Subject 5", type: "feat", notes: [] },
15-
{ subject: "Subject 6", type: "other", notes: [] },
10+
{ subject: "Subject 1", body: "Body 1", type: "fix", notes: [] },
11+
{ subject: "Subject 2", body: "Body 2", type: "feat", notes: [] },
12+
{ subject: "Subject 3", body: "Body 3", type: "feat", notes: [] },
13+
{ subject: "Subject 4", body: "Body 4", type: "fix", notes: [] },
14+
{ subject: "Subject 5", body: "Body 5", type: "feat", notes: [] },
15+
{ subject: "Subject 6", body: "Body 6", type: "other", notes: [] },
1616
];
1717

1818
const dateString = new Date().toISOString().substr(0, 10);
@@ -47,6 +47,59 @@ describe("generateChangelog", () => {
4747
assert.strictEqual(result.changelog, expectedChangelog);
4848
});
4949

50+
it("should create a changelog with body text", () => {
51+
const commitObjects = [
52+
{ subject: "Subject 1", body: "Body 1", type: "fix", notes: [] },
53+
{ subject: "Subject 2", body: "Body 2", type: "feat", notes: [] },
54+
{ subject: "Subject 3", body: "Body 3", type: "feat", notes: [] },
55+
{ subject: "Subject 4", body: "Body 4", type: "fix", notes: [] },
56+
{ subject: "Subject 5", body: "Body 5", type: "feat", notes: [] },
57+
{ subject: "Subject 6", body: "Body 6", type: "other", notes: [] },
58+
];
59+
60+
const dateString = new Date().toISOString().substr(0, 10);
61+
62+
const expectedChanges = `## New Features
63+
- Subject 2
64+
Body 2
65+
- Subject 3
66+
Body 3
67+
- Subject 5
68+
Body 5
69+
70+
## Bugfixes
71+
- Subject 1
72+
Body 1
73+
- Subject 4
74+
Body 4`;
75+
76+
const expectedChangelog = `# 0.0.1 - ${dateString}
77+
78+
## New Features
79+
- Subject 2
80+
Body 2
81+
- Subject 3
82+
Body 3
83+
- Subject 5
84+
Body 5
85+
86+
## Bugfixes
87+
- Subject 1
88+
Body 1
89+
- Subject 4
90+
Body 4
91+
92+
`;
93+
94+
const config = DEFAULT_CONFIG;
95+
config.excludeTypes = ["other"];
96+
config.includeCommitBody = true;
97+
98+
const result = generateChangelog("0.0.1", commitObjects, config);
99+
assert.strictEqual(result.changes, expectedChanges);
100+
assert.strictEqual(result.changelog, expectedChangelog);
101+
});
102+
50103
it("should create a changelog with breaking changes", () => {
51104
const commitObjects = [
52105
{

0 commit comments

Comments
 (0)