Skip to content

Commit 949d5c8

Browse files
committed
feat: remove title from the release notes
This information is redundant in the GitHub Release. BREAKING CHANGE: The title is now removed from the release notes by default. You can disable the removal of title/restore the old behavior with [`removeTitleFromReleaseNotes: false`](https://github.com/semantic-release/github#removetitlefromreleasenotes). Fixes: semantic-release#316
1 parent 8d3df0e commit 949d5c8

File tree

7 files changed

+213
-28
lines changed

7 files changed

+213
-28
lines changed

.vscode/extensions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"recommendations": [
3+
"samverschueren.linter-xo"
4+
]
5+
}

.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"xo.enable": true,
3+
"xo.format.enable": true,
4+
"[javascript]": {
5+
"editor.defaultFormatter": "samverschueren.linter-xo",
6+
"editor.formatOnSave": true
7+
}
8+
}

README.md

Lines changed: 50 additions & 25 deletions
Large diffs are not rendered by default.

lib/publish.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@ module.exports = async (pluginConfig, context) => {
1515
cwd,
1616
options: {repositoryUrl},
1717
branch,
18-
nextRelease: {name, gitTag, notes},
18+
nextRelease: {name, gitTag, notes, version},
1919
logger,
2020
} = context;
21-
const {githubToken, githubUrl, githubApiPathPrefix, proxy, assets} = resolveConfig(pluginConfig, context);
21+
const {
22+
githubToken,
23+
githubUrl,
24+
githubApiPathPrefix,
25+
proxy,
26+
assets,
27+
removeTitleFromReleaseNotes: shouldRemoveTitleFromReleaseNotes,
28+
} = resolveConfig(pluginConfig, context);
2229
const {owner, repo} = parseGithubUrl(repositoryUrl);
2330
const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy});
2431
const release = {
@@ -27,7 +34,7 @@ module.exports = async (pluginConfig, context) => {
2734
tag_name: gitTag,
2835
target_commitish: branch.name,
2936
name,
30-
body: notes,
37+
body: shouldRemoveTitleFromReleaseNotes ? removeTitleFromReleaseNotes(notes, version) : notes,
3138
prerelease: isPrerelease(branch),
3239
};
3340

@@ -104,3 +111,12 @@ module.exports = async (pluginConfig, context) => {
104111
logger.log('Published GitHub release: %s', url);
105112
return {url, name: RELEASE_NAME, id: releaseId};
106113
};
114+
115+
const removeTitleFromReleaseNotes = (notes, version) => {
116+
const titlePrefix = `### [${version}]`;
117+
if (notes.startsWith(titlePrefix)) {
118+
return notes.split('\n').slice(2).join('\n');
119+
}
120+
121+
return notes;
122+
};

lib/resolve-config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = (
1313
assignees,
1414
releasedLabels,
1515
addReleases,
16+
removeTitleFromReleaseNotes,
1617
},
1718
{env}
1819
) => ({
@@ -32,4 +33,5 @@ module.exports = (
3233
? false
3334
: castArray(releasedLabels),
3435
addReleases: isNil(addReleases) ? false : addReleases,
36+
removeTitleFromReleaseNotes: isNil(removeTitleFromReleaseNotes) ? true : removeTitleFromReleaseNotes,
3537
});

lib/verify.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const VALIDATORS = {
2626
assignees: isArrayOf(isNonEmptyString),
2727
releasedLabels: canBeDisabled(isArrayOf(isNonEmptyString)),
2828
addReleases: canBeDisabled(oneOf(['bottom', 'top'])),
29+
removeTitleFromReleaseNotes: canBeDisabled(oneOf([false, true])),
2930
};
3031

3132
module.exports = async (pluginConfig, context) => {

test/publish.test.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,134 @@ test.serial('Publish a release', async (t) => {
6565
t.true(github.isDone());
6666
});
6767

68+
test.serial('Publish a release removing the title from the release notes by default', async (t) => {
69+
const owner = 'test_user';
70+
const repo = 'test_repo';
71+
const env = {GITHUB_TOKEN: 'github_token'};
72+
const pluginConfig = {};
73+
const expectedBody = 'Test release note body';
74+
const nextRelease = {
75+
version: '1.0.1',
76+
gitTag: 'v1.0.1',
77+
name: 'v1.0.1',
78+
notes: `### [1.0.1](https://github.com/${owner}/${repo}/compare/v1.0.0...v1.0.1) (2020-12-12)\n\n${expectedBody}`,
79+
};
80+
const options = {repositoryUrl: `https://github.com/${owner}/${repo}.git`};
81+
const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`;
82+
const releaseId = 1;
83+
const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`;
84+
const uploadUrl = `https://github.com${uploadUri}{?name,label}`;
85+
const branch = 'test_branch';
86+
87+
const github = authenticate(env)
88+
.post(`/repos/${owner}/${repo}/releases`, {
89+
tag_name: nextRelease.gitTag,
90+
target_commitish: branch,
91+
name: nextRelease.name,
92+
body: expectedBody,
93+
prerelease: false,
94+
})
95+
.reply(200, {upload_url: uploadUrl, html_url: releaseUrl});
96+
97+
const result = await publish(pluginConfig, {
98+
cwd,
99+
env,
100+
options,
101+
branch: {name: branch, type: 'release', main: true},
102+
nextRelease,
103+
logger: t.context.logger,
104+
});
105+
106+
t.is(result.url, releaseUrl);
107+
t.deepEqual(t.context.log.args[0], ['Published GitHub release: %s', releaseUrl]);
108+
t.true(github.isDone());
109+
});
110+
111+
test.serial('Publish a release removing the title from the release notes', async (t) => {
112+
const owner = 'test_user';
113+
const repo = 'test_repo';
114+
const env = {GITHUB_TOKEN: 'github_token'};
115+
const pluginConfig = {removeTitleFromReleaseNotes: true};
116+
const expectedBody = 'Test release note body';
117+
const nextRelease = {
118+
version: '1.0.1',
119+
gitTag: 'v1.0.1',
120+
name: 'v1.0.1',
121+
notes: `### [1.0.1](https://github.com/${owner}/${repo}/compare/v1.0.0...v1.0.1) (2020-12-12)\n\n${expectedBody}`,
122+
};
123+
const options = {repositoryUrl: `https://github.com/${owner}/${repo}.git`};
124+
const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`;
125+
const releaseId = 1;
126+
const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`;
127+
const uploadUrl = `https://github.com${uploadUri}{?name,label}`;
128+
const branch = 'test_branch';
129+
130+
const github = authenticate(env)
131+
.post(`/repos/${owner}/${repo}/releases`, {
132+
tag_name: nextRelease.gitTag,
133+
target_commitish: branch,
134+
name: nextRelease.name,
135+
body: expectedBody,
136+
prerelease: false,
137+
})
138+
.reply(200, {upload_url: uploadUrl, html_url: releaseUrl});
139+
140+
const result = await publish(pluginConfig, {
141+
cwd,
142+
env,
143+
options,
144+
branch: {name: branch, type: 'release', main: true},
145+
nextRelease,
146+
logger: t.context.logger,
147+
});
148+
149+
t.is(result.url, releaseUrl);
150+
t.deepEqual(t.context.log.args[0], ['Published GitHub release: %s', releaseUrl]);
151+
t.true(github.isDone());
152+
});
153+
154+
test.serial('Publish a release without removing the title from the release notes', async (t) => {
155+
const owner = 'test_user';
156+
const repo = 'test_repo';
157+
const env = {GITHUB_TOKEN: 'github_token'};
158+
const pluginConfig = {removeTitleFromReleaseNotes: false};
159+
const nextRelease = {
160+
version: '1.0.1',
161+
gitTag: 'v1.0.1',
162+
name: 'v1.0.1',
163+
notes: `### [1.0.1](https://github.com/${owner}/${repo}/compare/v1.0.0...v1.0.1) (2020-12-12)\n\nTest release note body`,
164+
};
165+
const options = {repositoryUrl: `https://github.com/${owner}/${repo}.git`};
166+
const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`;
167+
const releaseId = 1;
168+
const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`;
169+
const uploadUrl = `https://github.com${uploadUri}{?name,label}`;
170+
const branch = 'test_branch';
171+
172+
const github = authenticate(env)
173+
.post(`/repos/${owner}/${repo}/releases`, {
174+
tag_name: nextRelease.gitTag,
175+
target_commitish: branch,
176+
name: nextRelease.name,
177+
body: nextRelease.notes,
178+
prerelease: false,
179+
})
180+
.reply(200, {upload_url: uploadUrl, html_url: releaseUrl});
181+
182+
const result = await publish(pluginConfig, {
183+
cwd,
184+
env,
185+
options,
186+
branch: {name: branch, type: 'release', main: true},
187+
nextRelease,
188+
logger: t.context.logger,
189+
});
190+
191+
t.is(result.url, releaseUrl);
192+
t.deepEqual(t.context.log.args[0], ['Published GitHub release: %s', releaseUrl]);
193+
t.true(github.isDone());
194+
});
195+
68196
test.serial('Publish a release on a channel', async (t) => {
69197
const owner = 'test_user';
70198
const repo = 'test_repo';

0 commit comments

Comments
 (0)