Skip to content

Commit db2e9c8

Browse files
ChumperNils Plaschke
and
Nils Plaschke
authored
feat: add additional release links to the release (#282)
A new option `addReleases` has been added. Setting this option will add links to other releases to the Github release body. The option can be one of `false|"top"|"bottom"`. The default is `false` to be backward compatible. Closes #281 Co-authored-by: Nils Plaschke <plaschke@adobe.com>
1 parent 32654fb commit db2e9c8

10 files changed

+573
-4
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,13 @@ Each label name is generated with [Lodash template](https://lodash.com/docs#temp
204204
The `releasedLabels` ```['released<%= nextRelease.channel ? ` on @\${nextRelease.channel}` : "" %> from <%= branch.name %>']``` will generate the label:
205205

206206
> released on @next from branch next
207+
208+
#### addReleases
209+
210+
Add links to other releases to the GitHub release body.
211+
212+
Valid values for this option are `false`, `"top"` or `"bottom"`.
213+
214+
##### addReleases example
215+
216+
See [The introducing PR](https://github.com/semantic-release/github/pull/282) for an example on how it will look.

lib/definitions/errors.js

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ Your configuration for the \`assignees\` option is \`${stringify(assignees)}\`.`
5757
)}) if defined, must be an \`Array\` of non empty \`String\`.
5858
5959
Your configuration for the \`releasedLabels\` option is \`${stringify(releasedLabels)}\`.`,
60+
}),
61+
EINVALIDADDRELEASES: ({addReleases}) => ({
62+
message: 'Invalid `addReleases` option.',
63+
details: `The [addReleases option](${linkify('README.md#options')}) if defined, must be one of \`false|top|bottom\`.
64+
65+
Your configuration for the \`addReleases\` option is \`${stringify(addReleases)}\`.`,
6066
}),
6167
EINVALIDGITHUBURL: () => ({
6268
message: 'The git repository URL is not a valid GitHub URL.',

lib/get-release-links.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const {RELEASE_NAME} = require('./definitions/constants');
2+
3+
const linkify = (releaseInfo) =>
4+
`${
5+
releaseInfo.url
6+
? releaseInfo.url.startsWith('http')
7+
? `[${releaseInfo.name}](${releaseInfo.url})`
8+
: `${releaseInfo.name}: \`${releaseInfo.url}\``
9+
: `\`${releaseInfo.name}\``
10+
}`;
11+
12+
const filterReleases = (releaseInfos) =>
13+
releaseInfos.filter((releaseInfo) => releaseInfo.name && releaseInfo.name !== RELEASE_NAME);
14+
15+
module.exports = (releaseInfos) =>
16+
`${
17+
filterReleases(releaseInfos).length > 0
18+
? `This release is also available on:\n${filterReleases(releaseInfos)
19+
.map((releaseInfo) => `- ${linkify(releaseInfo)}`)
20+
.join('\n')}`
21+
: ''
22+
}`;

lib/publish.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ module.exports = async (pluginConfig, context) => {
2828
// When there are no assets, we publish a release directly
2929
if (!assets || assets.length === 0) {
3030
const {
31-
data: {html_url: url},
31+
data: {html_url: url, id: releaseId},
3232
} = await github.repos.createRelease(release);
3333

3434
logger.log('Published GitHub release: %s', url);
35-
return {url, name: RELEASE_NAME};
35+
return {url, name: RELEASE_NAME, id: releaseId};
3636
}
3737

3838
// We'll create a draft release, append the assets to it, and then publish it.
@@ -94,5 +94,5 @@ module.exports = async (pluginConfig, context) => {
9494
} = await github.repos.updateRelease({owner, repo, release_id: releaseId, draft: false});
9595

9696
logger.log('Published GitHub release: %s', url);
97-
return {url, name: RELEASE_NAME};
97+
return {url, name: RELEASE_NAME, id: releaseId};
9898
};

lib/resolve-config.js

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = (
1212
labels,
1313
assignees,
1414
releasedLabels,
15+
addReleases,
1516
},
1617
{env}
1718
) => ({
@@ -30,4 +31,5 @@ module.exports = (
3031
: releasedLabels === false
3132
? false
3233
: castArray(releasedLabels),
34+
addReleases: isNil(addReleases) ? false : addReleases,
3335
});

lib/success.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const {isNil, uniqBy, template, flatten} = require('lodash');
1+
const {isNil, uniqBy, template, flatten, isEmpty} = require('lodash');
22
const pFilter = require('p-filter');
33
const AggregateError = require('aggregate-error');
44
const issueParser = require('issue-parser');
@@ -9,6 +9,8 @@ const getClient = require('./get-client');
99
const getSearchQueries = require('./get-search-queries');
1010
const getSuccessComment = require('./get-success-comment');
1111
const findSRIssues = require('./find-sr-issues');
12+
const {RELEASE_NAME} = require('./definitions/constants');
13+
const getReleaseLinks = require('./get-release-links');
1214

1315
module.exports = async (pluginConfig, context) => {
1416
const {
@@ -17,6 +19,7 @@ module.exports = async (pluginConfig, context) => {
1719
nextRelease,
1820
releases,
1921
logger,
22+
notes,
2023
} = context;
2124
const {
2225
githubToken,
@@ -27,6 +30,7 @@ module.exports = async (pluginConfig, context) => {
2730
failComment,
2831
failTitle,
2932
releasedLabels,
33+
addReleases,
3034
} = resolveConfig(pluginConfig, context);
3135

3236
const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy});
@@ -140,6 +144,21 @@ module.exports = async (pluginConfig, context) => {
140144
);
141145
}
142146

147+
if (addReleases !== false && errors.length === 0) {
148+
const ghRelease = releases.find((release) => release.name && release.name === RELEASE_NAME);
149+
if (!isNil(ghRelease)) {
150+
const ghRelaseId = ghRelease.id;
151+
const additionalReleases = getReleaseLinks(releases);
152+
if (!isEmpty(additionalReleases) && !isNil(ghRelaseId)) {
153+
const newBody =
154+
addReleases === 'top'
155+
? additionalReleases.concat('\n---\n', notes)
156+
: notes.concat('\n---\n', additionalReleases);
157+
await github.repos.updateRelease({owner, repo, release_id: ghRelaseId, body: newBody});
158+
}
159+
}
160+
}
161+
143162
if (errors.length > 0) {
144163
throw new AggregateError(errors);
145164
}

lib/verify.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const getClient = require('./get-client');
77
const getError = require('./get-error');
88

99
const isNonEmptyString = (value) => isString(value) && value.trim();
10+
const oneOf = (enumArray) => (value) => enumArray.some((element) => element === value);
1011
const isStringOrStringArray = (value) =>
1112
isNonEmptyString(value) || (isArray(value) && value.every((string) => isNonEmptyString(string)));
1213
const isArrayOf = (validator) => (array) => isArray(array) && array.every((value) => validator(value));
@@ -24,6 +25,7 @@ const VALIDATORS = {
2425
labels: canBeDisabled(isArrayOf(isNonEmptyString)),
2526
assignees: isArrayOf(isNonEmptyString),
2627
releasedLabels: canBeDisabled(isArrayOf(isNonEmptyString)),
28+
addReleases: canBeDisabled(oneOf(['bottom', 'top'])),
2729
};
2830

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

test/get-release-links.test.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const test = require('ava');
2+
const getReleaseLinks = require('../lib/get-release-links');
3+
const {RELEASE_NAME} = require('../lib/definitions/constants');
4+
5+
test('Comment for release with multiple releases', (t) => {
6+
const releaseInfos = [
7+
{name: RELEASE_NAME, url: 'https://github.com/release'},
8+
{name: 'Http release', url: 'https://release.com/release'},
9+
{name: 'npm release', url: 'https://npm.com/release'},
10+
];
11+
const comment = getReleaseLinks(releaseInfos);
12+
13+
t.is(
14+
comment,
15+
`This release is also available on:
16+
- [Http release](https://release.com/release)
17+
- [npm release](https://npm.com/release)`
18+
);
19+
});
20+
21+
test('Release with missing release URL', (t) => {
22+
const releaseInfos = [
23+
{name: RELEASE_NAME, url: 'https://github.com/release'},
24+
{name: 'Http release', url: 'https://release.com/release'},
25+
{name: 'npm release'},
26+
];
27+
const comment = getReleaseLinks(releaseInfos);
28+
29+
t.is(
30+
comment,
31+
`This release is also available on:
32+
- [Http release](https://release.com/release)
33+
- \`npm release\``
34+
);
35+
});
36+
37+
test('Release with one release', (t) => {
38+
const releaseInfos = [
39+
{name: RELEASE_NAME, url: 'https://github.com/release'},
40+
{name: 'Http release', url: 'https://release.com/release'},
41+
];
42+
const comment = getReleaseLinks(releaseInfos);
43+
44+
t.is(
45+
comment,
46+
`This release is also available on:
47+
- [Http release](https://release.com/release)`
48+
);
49+
});
50+
51+
test('Release with non http releases', (t) => {
52+
const releaseInfos = [{name: 'S3', url: 's3://my-bucket/release-asset'}];
53+
const comment = getReleaseLinks(releaseInfos);
54+
55+
t.is(
56+
comment,
57+
`This release is also available on:
58+
- S3: \`s3://my-bucket/release-asset\``
59+
);
60+
});
61+
62+
test('Release with only github release', (t) => {
63+
const releaseInfos = [{name: RELEASE_NAME, url: 'https://github.com/release'}];
64+
const comment = getReleaseLinks(releaseInfos);
65+
66+
t.is(comment, '');
67+
});
68+
69+
test('Comment with no release object', (t) => {
70+
const releaseInfos = [];
71+
const comment = getReleaseLinks(releaseInfos);
72+
73+
t.is(comment, '');
74+
});

0 commit comments

Comments
 (0)