Skip to content

Commit 9e2678c

Browse files
authoredSep 25, 2023
feat: releaseNameTemplate and releaseBodyTemplate options for customizing release body and name (#704)
1 parent 2265f07 commit 9e2678c

File tree

7 files changed

+241
-3
lines changed

7 files changed

+241
-3
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ When using the _GITHUB_TOKEN_, the **minimum required permissions** are:
9393
| `releasedLabels` | The [labels](https://help.github.com/articles/about-labels) to add to each issue and pull request resolved by the release. Set to `false` to not add any label. See [releasedLabels](#releasedlabels). | `['released<%= nextRelease.channel ? \` on @\${nextRelease.channel}\` : "" %>']- |
9494
| `addReleases` | Will add release links to the GitHub Release. Can be `false`, `"bottom"` or `"top"`. See [addReleases](#addReleases). | `false` |
9595
| `draftRelease` | A boolean indicating if a GitHub Draft Release should be created instead of publishing an actual GitHub Release. | `false` |
96+
| `releaseNameTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's name | `<%= nextverison.name %>` |
97+
| `releaseBodyTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's body | `<%= nextverison.notes %>` |
9698

9799
#### proxy
98100

‎lib/definitions/errors.js

+26
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,29 @@ export function ENOGHTOKEN({ owner, repo }) {
195195
Please make sure to create a [GitHub personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) and to set it in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. The token must allow to push to the repository ${owner}/${repo}.`,
196196
};
197197
}
198+
199+
export function EINVALIDRELEASEBODYTEMPLATE({ releaseBodyTemplate }) {
200+
return {
201+
message: "Invalid `releaseBodyTemplate` option.",
202+
details: `The [releaseBodyTemplate option](${linkify(
203+
"README.md#releaseBodyTemplate",
204+
)}) must be a non empty \`String\`.
205+
206+
Your configuration for the \`releaseBodyTemplate\` option is \`${stringify(
207+
releaseBodyTemplate,
208+
)}\`.`,
209+
};
210+
}
211+
212+
export function EINVALIDRELEASENAMETEMPLATE({ releaseNameTemplate }) {
213+
return {
214+
message: "Invalid `releaseNameTemplate` option.",
215+
details: `The [releaseNameTemplate option](${linkify(
216+
"README.md#releaseNameTemplate",
217+
)}) must be a non empty \`String\`.
218+
219+
Your configuration for the \`releaseNameTemplate\` option is \`${stringify(
220+
releaseNameTemplate,
221+
)}\`.`,
222+
};
223+
}

‎lib/publish.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default async function publish(pluginConfig, context, { Octokit }) {
1919
cwd,
2020
options: { repositoryUrl },
2121
branch,
22-
nextRelease: { name, gitTag, notes },
22+
nextRelease: { gitTag },
2323
logger,
2424
} = context;
2525
const {
@@ -29,6 +29,8 @@ export default async function publish(pluginConfig, context, { Octokit }) {
2929
proxy,
3030
assets,
3131
draftRelease,
32+
releaseNameTemplate,
33+
releaseBodyTemplate,
3234
} = resolveConfig(pluginConfig, context);
3335
const { owner, repo } = parseGithubUrl(repositoryUrl);
3436
const octokit = new Octokit(
@@ -44,8 +46,8 @@ export default async function publish(pluginConfig, context, { Octokit }) {
4446
repo,
4547
tag_name: gitTag,
4648
target_commitish: branch.name,
47-
name,
48-
body: notes,
49+
name: template(releaseNameTemplate)(context),
50+
body: template(releaseBodyTemplate)(context),
4951
prerelease: isPrerelease(branch),
5052
};
5153

‎lib/resolve-config.js

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export default function resolveConfig(
1414
releasedLabels,
1515
addReleases,
1616
draftRelease,
17+
releaseNameTemplate,
18+
releaseBodyTemplate,
1719
},
1820
{ env },
1921
) {
@@ -44,5 +46,11 @@ export default function resolveConfig(
4446
: castArray(releasedLabels),
4547
addReleases: isNil(addReleases) ? false : addReleases,
4648
draftRelease: isNil(draftRelease) ? false : draftRelease,
49+
releaseBodyTemplate: !isNil(releaseBodyTemplate)
50+
? releaseBodyTemplate
51+
: "<%= nextRelease.notes %>",
52+
releaseNameTemplate: !isNil(releaseNameTemplate)
53+
? releaseNameTemplate
54+
: "<%= nextRelease.name %>",
4755
};
4856
}

‎lib/verify.js

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ const VALIDATORS = {
4545
releasedLabels: canBeDisabled(isArrayOf(isNonEmptyString)),
4646
addReleases: canBeDisabled(oneOf(["bottom", "top"])),
4747
draftRelease: isBoolean,
48+
releaseBodyTemplate: isNonEmptyString,
49+
releaseNameTemplate: isNonEmptyString,
4850
};
4951

5052
export default async function verify(pluginConfig, context, { Octokit }) {

‎test/publish.test.js

+126
Original file line numberDiff line numberDiff line change
@@ -720,3 +720,129 @@ test("Publish a release when env.GITHUB_URL is set to https://github.com (Defaul
720720
]);
721721
t.true(fetch.done());
722722
});
723+
724+
test("Publish a custom release body", async (t) => {
725+
const owner = "test_user";
726+
const repo = "test_repo";
727+
const env = { GITHUB_TOKEN: "github_token" };
728+
const pluginConfig = {
729+
releaseBodyTemplate:
730+
"To install this run npm install package@<%= nextRelease.name %>\n\n<%= nextRelease.notes %>",
731+
};
732+
const nextRelease = {
733+
gitTag: "v1.0.0",
734+
name: "v1.0.0",
735+
notes: "Test release note body",
736+
};
737+
const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` };
738+
const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`;
739+
const releaseId = 1;
740+
const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`;
741+
const uploadUrl = `https://github.com${uploadUri}{?name,label}`;
742+
const branch = "test_branch";
743+
744+
const fetch = fetchMock.sandbox().postOnce(
745+
`https://api.github.local/repos/${owner}/${repo}/releases`,
746+
{
747+
upload_url: uploadUrl,
748+
html_url: releaseUrl,
749+
},
750+
{
751+
body: {
752+
tag_name: nextRelease.gitTag,
753+
target_commitish: branch,
754+
name: nextRelease.name,
755+
body: `To install this run npm install package@${nextRelease.name}\n\n${nextRelease.notes}`,
756+
prerelease: false,
757+
},
758+
},
759+
);
760+
761+
const result = await publish(
762+
pluginConfig,
763+
{
764+
cwd,
765+
env,
766+
options,
767+
branch: { name: branch, type: "release", main: true },
768+
nextRelease,
769+
logger: t.context.logger,
770+
},
771+
{
772+
Octokit: TestOctokit.defaults((options) => ({
773+
...options,
774+
request: { ...options.request, fetch },
775+
})),
776+
},
777+
);
778+
779+
t.is(result.url, releaseUrl);
780+
t.deepEqual(t.context.log.args[0], [
781+
"Published GitHub release: %s",
782+
releaseUrl,
783+
]);
784+
t.true(fetch.done());
785+
});
786+
787+
test("Publish a custom release name", async (t) => {
788+
const owner = "test_user";
789+
const repo = "test_repo";
790+
const env = { GITHUB_TOKEN: "github_token" };
791+
const pluginConfig = {
792+
releaseNameTemplate:
793+
"omg its the best release: <%= nextRelease.name %> 🌈🌈",
794+
};
795+
const nextRelease = {
796+
gitTag: "v1.0.0",
797+
name: "v1.0.0",
798+
notes: "Test release note body",
799+
};
800+
const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` };
801+
const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`;
802+
const releaseId = 1;
803+
const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`;
804+
const uploadUrl = `https://github.com${uploadUri}{?name,label}`;
805+
const branch = "test_branch";
806+
807+
const fetch = fetchMock.sandbox().postOnce(
808+
`https://api.github.local/repos/${owner}/${repo}/releases`,
809+
{
810+
upload_url: uploadUrl,
811+
html_url: releaseUrl,
812+
},
813+
{
814+
body: {
815+
tag_name: nextRelease.gitTag,
816+
target_commitish: branch,
817+
name: `omg its the best release: ${nextRelease.name} 🌈🌈`,
818+
body: nextRelease.notes,
819+
prerelease: false,
820+
},
821+
},
822+
);
823+
824+
const result = await publish(
825+
pluginConfig,
826+
{
827+
cwd,
828+
env,
829+
options,
830+
branch: { name: branch, type: "release", main: true },
831+
nextRelease,
832+
logger: t.context.logger,
833+
},
834+
{
835+
Octokit: TestOctokit.defaults((options) => ({
836+
...options,
837+
request: { ...options.request, fetch },
838+
})),
839+
},
840+
);
841+
842+
t.is(result.url, releaseUrl);
843+
t.deepEqual(t.context.log.args[0], [
844+
"Published GitHub release: %s",
845+
releaseUrl,
846+
]);
847+
t.true(fetch.done());
848+
});

‎test/verify.test.js

+72
Original file line numberDiff line numberDiff line change
@@ -2050,3 +2050,75 @@ test('Throw SemanticReleaseError if "draftRelease" option is not a valid boolean
20502050
t.is(error.code, "EINVALIDDRAFTRELEASE");
20512051
t.true(fetch.done());
20522052
});
2053+
2054+
test('Throw SemanticReleaseError if "releaseBodyTemplate" option is an empty string', async (t) => {
2055+
const owner = "test_user";
2056+
const repo = "test_repo";
2057+
const env = { GH_TOKEN: "github_token" };
2058+
2059+
const fetch = fetchMock
2060+
.sandbox()
2061+
.getOnce(`https://api.github.local/repos/${owner}/${repo}`, {
2062+
permissions: { push: true },
2063+
});
2064+
2065+
const {
2066+
errors: [error, ...errors],
2067+
} = await t.throwsAsync(
2068+
verify(
2069+
{ releaseBodyTemplate: "" },
2070+
{
2071+
env,
2072+
options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` },
2073+
logger: t.context.logger,
2074+
},
2075+
{
2076+
Octokit: TestOctokit.defaults((options) => ({
2077+
...options,
2078+
request: { ...options.request, fetch },
2079+
})),
2080+
},
2081+
),
2082+
);
2083+
2084+
t.is(errors.length, 0);
2085+
t.is(error.name, "SemanticReleaseError");
2086+
t.is(error.code, "EINVALIDRELEASEBODYTEMPLATE");
2087+
t.true(fetch.done());
2088+
});
2089+
2090+
test('Throw SemanticReleaseError if "releaseNameTemplate" option is an empty string', async (t) => {
2091+
const owner = "test_user";
2092+
const repo = "test_repo";
2093+
const env = { GH_TOKEN: "github_token" };
2094+
2095+
const fetch = fetchMock
2096+
.sandbox()
2097+
.getOnce(`https://api.github.local/repos/${owner}/${repo}`, {
2098+
permissions: { push: true },
2099+
});
2100+
2101+
const {
2102+
errors: [error, ...errors],
2103+
} = await t.throwsAsync(
2104+
verify(
2105+
{ releaseNameTemplate: "" },
2106+
{
2107+
env,
2108+
options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` },
2109+
logger: t.context.logger,
2110+
},
2111+
{
2112+
Octokit: TestOctokit.defaults((options) => ({
2113+
...options,
2114+
request: { ...options.request, fetch },
2115+
})),
2116+
},
2117+
),
2118+
);
2119+
2120+
t.is(errors.length, 0);
2121+
t.is(error.name, "SemanticReleaseError");
2122+
t.is(error.code, "EINVALIDRELEASENAMETEMPLATE");
2123+
t.true(fetch.done());
2124+
});

0 commit comments

Comments
 (0)
Please sign in to comment.