@@ -83,8 +83,9 @@ A full set list of possible output values for this action.
|
||||
| `outputs.toTag` | Defines the `toTag` which describes the upper bound to process pull request for |
|
||||
| `outputs.failed` | Defines if there was an issue with the action run, and the changelog may not have been generated correctly. [true, false] |
|
||||
| `outputs.pull_requests` | Defines a `,` joined array with all PR IDs associated with the generated changelog. |
|
||||
| `outputs.categorized_prs` | Count of PRs which were successfully categorized as part of the action. |
|
||||
| `outputs.uncategorized_prs` | Count of PRs which were not categorized as part of the action. |
|
||||
| `outputs.categorized_prs` | Count of PRs which were successfully categorized as part of the action. |
|
||||
| `outputs.open_prs` | Count of open PRs. Only fetched if `includeOpen` is enabled. |
|
||||
| `outputs.uncategorized_prs` | Count of PRs which were not categorized as part of the action. |
|
||||
|
||||
|
||||
## Full Sample 🖥️
|
||||
@@ -263,9 +264,11 @@ For advanced use cases additional settings can be provided to the action
|
||||
| `toTag` | Defines until which tag the changelog will consider merged pull requests |
|
||||
| `path` | Allows to specify an alternative sub directory, to use as base |
|
||||
| `token` | Alternative config to specify token. You should prefer `env.GITHUB_TOKEN` instead though |
|
||||
| `baseUrl` | Alternative config to specify base url for GitHub Enterprise authentication. Default value set to `https://api.github.com` |
|
||||
| `baseUrl` | Alternative config to specify base url for GitHub Enterprise authentication. Default value set to `https://api.github.com` |
|
||||
| `includeOpen` | Enables to also fetch currently open PRs. Default: false |
|
||||
| `ignorePreReleases` | Allows to ignore pre-releases for changelog generation (E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick). Only used if `fromTag` was not specified. Default: false |
|
||||
| `failOnError` | Defines if the action will result in a build failure if problems occurred. Default: false |
|
||||
| `fetchReviewers` | Will enable fetching the users/reviewers who approved the PR. Default: false |
|
||||
| `commitMode` | Special configuration for projects which work without PRs. Uses commit messages as changelog. This mode looses access to information only available for PRs. Default: false |
|
||||
|
||||
💡 `${{ secrets.GITHUB_TOKEN }}` only grants rights to the current repository, for other repositories please use a PAT (Personal Access Token).
|
||||
@@ -274,19 +277,22 @@ For advanced use cases additional settings can be provided to the action
|
||||
|
||||
Table of supported placeholders allowed to be used in the `pr_template` configuration, which will be included in the release notes / changelog.
|
||||
|
||||
| **Placeholder** | **Description** |
|
||||
|------------------|-------------------------------------------------------------|
|
||||
| `${{NUMBER}}` | The number referencing this pull request. E.g. 13 |
|
||||
| `${{TITLE}}` | Specified title of the merged pull request |
|
||||
| `${{URL}}` | Url linking to the pull request on GitHub |
|
||||
| `${{MERGED_AT}}` | The ISO time, the pull request was merged at |
|
||||
| `${{MERGE_SHA}}` | The commit SHA, the pull request was merged with |
|
||||
| `${{AUTHOR}}` | Author creating and opening the pull request |
|
||||
| `${{LABELS}}` | The labels associated with this pull request, joined by `,` |
|
||||
| `${{MILESTONE}}` | Milestone this PR was part of, as assigned on GitHub |
|
||||
| `${{BODY}}` | Description/Body of the pull request as specified on GitHub |
|
||||
| `${{ASSIGNEES}}` | Login names of assigned GitHub users, joined by `,` |
|
||||
| `${{REVIEWERS}}` | GitHub Login names of specified reviewers, joined by `,` |
|
||||
| **Placeholder** | **Description** |
|
||||
|-------------------|-------------------------------------------------------------------|
|
||||
| `${{NUMBER}}` | The number referencing this pull request. E.g. 13 |
|
||||
| `${{TITLE}}` | Specified title of the merged pull request |
|
||||
| `${{URL}}` | Url linking to the pull request on GitHub |
|
||||
| `${{STATUS}}` | Status of the PR. Usually always `merged`. Possibly `Open` if `includeOpen` is configured. |
|
||||
| `${{CREATED_AT}}` | The ISO time, the pull request was created at |
|
||||
| `${{MERGED_AT}}` | The ISO time, the pull request was merged at |
|
||||
| `${{MERGE_SHA}}` | The commit SHA, the pull request was merged with |
|
||||
| `${{AUTHOR}}` | Author creating and opening the pull request |
|
||||
| `${{LABELS}}` | The labels associated with this pull request, joined by `,` |
|
||||
| `${{MILESTONE}}` | Milestone this PR was part of, as assigned on GitHub |
|
||||
| `${{BODY}}` | Description/Body of the pull request as specified on GitHub |
|
||||
| `${{ASSIGNEES}}` | Login names of assigned GitHub users, joined by `,` |
|
||||
| `${{REVIEWERS}}` | GitHub Login names of specified reviewers, joined by `,` |
|
||||
| `${{APPROVERS}}` | GitHub Login names of users who approved the PR, joined by `,` |
|
||||
|
||||
### Template placeholders
|
||||
|
||||
@@ -296,6 +302,7 @@ Table of supported placeholders allowed to be used in the `template` and `empty_
|
||||
|----------------------------|----------------------------------------------------------------------------------------------------|:---------:|
|
||||
| `${{CHANGELOG}}` | The contents of the changelog, matching the labels as specified in the categories configuration | |
|
||||
| `${{UNCATEGORIZED}}` | All pull requests not matching a specified label in categories | |
|
||||
| `${{OPEN}}` | All open pull requests. Will only be fetched if `includeOpen` is enabled. | |
|
||||
| `${{IGNORED}}` | All pull requests defining labels matching the `ignore_labels` configuration | |
|
||||
| `${{OWNER}}` | Describes the owner of the repository the changelog was generated for | x |
|
||||
| `${{REPO}}` | The repository name of the repo the changelog was generated for | x |
|
||||
@@ -304,6 +311,7 @@ Table of supported placeholders allowed to be used in the `template` and `empty_
|
||||
| `${{RELEASE_DIFF}}` | Introduces a link to the full diff between from tag and to tag releases | x |
|
||||
| `${{CATEGORIZED_COUNT}}` | The count of PRs which were categorized | |
|
||||
| `${{UNCATEGORIZED_COUNT}}` | The count of PRs and changes which were not categorized. No label overlapping with category labels | |
|
||||
| `${{OPEN_COUNT}}` | The count of open PRs. Will only be fetched if `includeOpen` is configured. | |
|
||||
| `${{IGNORED_COUNT}}` | The count of PRs and changes which were specifically ignored from the changelog. | |
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ it('Should have empty changelog (tags)', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.0.1',
|
||||
toTag: 'v0.0.2',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -33,7 +35,9 @@ it('Should match generated changelog (tags)', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.0.1',
|
||||
toTag: 'v0.0.3',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -58,7 +62,9 @@ it('Should match generated changelog (refs)', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
|
||||
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -91,7 +97,9 @@ it('Should match generated changelog and replace all occurrences (refs)', async
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
|
||||
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -126,7 +134,9 @@ it('Should match ordered ASC', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.3.0',
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -148,7 +158,9 @@ it('Should match ordered DESC', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.3.0',
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -170,7 +182,9 @@ it('Should ignore PRs not merged into develop branch', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v1.3.1',
|
||||
toTag: 'v1.4.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -190,7 +204,9 @@ it('Should ignore PRs not merged into main branch', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v1.3.1',
|
||||
toTag: 'v1.4.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
|
||||
@@ -16,6 +16,8 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -42,6 +44,8 @@ it('Should match generated changelog (unspecified tags)', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -65,6 +69,8 @@ it('Should use empty placeholder', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -89,6 +95,8 @@ it('Should fill empty placeholders', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -115,6 +123,8 @@ it('Should fill `template` placeholders', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -141,6 +151,8 @@ it('Should fill `template` placeholders, ignore', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -167,6 +179,8 @@ it('Uncategorized category', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
|
||||
@@ -192,6 +206,8 @@ it('Verify commit based changelog', async () => {
|
||||
'v0.0.3',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
true,
|
||||
configuration
|
||||
)
|
||||
@@ -218,6 +234,8 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
|
||||
'17a9e4dfaedcabe6a6eff2754bebb715e1c58ec4',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
true,
|
||||
configuration
|
||||
)
|
||||
@@ -228,3 +246,87 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
|
||||
`## 🚀 Features\n\n- add dynamic merging\n- add auto-cleaning\n- add built-in adb support\n- add adb fallback (thanks to @mikepenz ;))\n- add install note\n- add @mikepenz to credits\n\n## 🐛 Fixes\n\n- fix dynamic lib replacement\n- fix apostrophe issue with app name\n- fix java.util.logger error\n\n## 💬 Other\n\n- update screenshot with truecaller stack\n\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Verify default inclusion of open PRs', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_including_open.json'
|
||||
)
|
||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||
null, // baseUrl
|
||||
null, // token
|
||||
'.', // repoPath
|
||||
'mikepenz', // user
|
||||
'release-changelog-builder-action-playground', // repo
|
||||
'1.5.0', // fromTag
|
||||
'2.0.0', // toTag
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
false, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
|
||||
const changeLog = await releaseNotesBuilder.build()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features\n\n- A feature to be going to v2 (nr3) (#3) merged\n- New feature to keep open (nr5) (#7) open\n\n\n\n\nUncategorized\n\n\n\nOpen\n- New feature to keep open (nr5) (#7) open\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Verify custom categorisation of open PRs', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_excluding_open.json'
|
||||
)
|
||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||
null, // baseUrl
|
||||
null, // token
|
||||
'.', // repoPath
|
||||
'mikepenz', // user
|
||||
'release-changelog-builder-action-playground', // repo
|
||||
'1.5.0', // fromTag
|
||||
'2.0.0', // toTag
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
false, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
|
||||
const changeLog = await releaseNotesBuilder.build()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features Merged\n\n- A feature to be going to v2 (nr3) -- (#3) [merged] {feature}\n\n## 🚀 Features Open\n\n- New feature to keep open (nr5) -- (#7) [open] {feature}\n\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Verify reviewers who approved are fetched', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_approvers.json'
|
||||
)
|
||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||
null, // baseUrl
|
||||
null, // token
|
||||
'.', // repoPath
|
||||
'mikepenz', // user
|
||||
'release-changelog-builder-action-playground', // repo
|
||||
'1.5.0', // fromTag
|
||||
'2.0.0', // toTag
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
true, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
|
||||
const changeLog = await releaseNotesBuilder.build()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features\n\n- A feature to be going to v2 (nr3) -- (#3) [merged] --- \n- New feature to keep open (nr5) -- (#7) [open] --- gabrielpopa\n\n`
|
||||
)
|
||||
})
|
||||
@@ -33,6 +33,7 @@ mergedPullRequests.push(
|
||||
title: '[Feature][AB-1234] - this is a PR 1 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1',
|
||||
author: 'Mike',
|
||||
@@ -41,13 +42,16 @@ mergedPullRequests.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: '[Issue][AB-4321] - this is a PR 2 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1',
|
||||
author: 'Mike',
|
||||
@@ -56,13 +60,16 @@ mergedPullRequests.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: '[Issue][Feature][AB-1234321] - this is a PR 3 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1',
|
||||
author: 'Mike',
|
||||
@@ -71,13 +78,16 @@ mergedPullRequests.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: '[AB-404] - not found label',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1',
|
||||
author: 'Mike',
|
||||
@@ -86,7 +96,9 @@ mergedPullRequests.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
)
|
||||
|
||||
@@ -95,6 +107,7 @@ const pullRequestWithLabelInBody: PullRequestInfo = {
|
||||
title: 'label in body',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1',
|
||||
author: 'Mike',
|
||||
@@ -103,7 +116,9 @@ const pullRequestWithLabelInBody: PullRequestInfo = {
|
||||
milestone: '',
|
||||
body: '[Issue][Feature][AB-1234321] - no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
|
||||
it('Extract label from title, combined regex', async () => {
|
||||
@@ -120,7 +135,9 @@ it('Extract label from title, combined regex', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -147,7 +164,9 @@ it('Extract label from title and body, combined regex', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -176,7 +195,9 @@ it('Extract label from title, split regex', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -205,7 +226,9 @@ it('Extract label from title, match', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -229,7 +252,9 @@ it('Extract label from title, match multiple', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -254,7 +279,9 @@ it('Extract label from title, match multiple, custon non matching label', async
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -272,6 +299,7 @@ pullRequestsWithLabels.push(
|
||||
title: '[ABC-1234] - this is a PR 1 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1-1',
|
||||
author: 'Mike',
|
||||
@@ -280,13 +308,16 @@ pullRequestsWithLabels.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: '[ABC-4321] - this is a PR 2 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1-2',
|
||||
author: 'Mike',
|
||||
@@ -295,13 +326,16 @@ pullRequestsWithLabels.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: '[ABC-1234] - this is a PR 3 title message',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment().add(1, 'days'),
|
||||
mergeCommitSha: 'sha1-3',
|
||||
author: 'Mike',
|
||||
@@ -310,13 +344,16 @@ pullRequestsWithLabels.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: '[AB-404] - not found label',
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: moment(),
|
||||
mergedAt: moment(),
|
||||
mergeCommitSha: 'sha1-4',
|
||||
author: 'Mike',
|
||||
@@ -325,7 +362,9 @@ pullRequestsWithLabels.push(
|
||||
milestone: '',
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
)
|
||||
|
||||
@@ -354,7 +393,9 @@ it('Match multiple labels exhaustive for category', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -377,7 +418,9 @@ it('Deduplicate duplicated PRs', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -401,7 +444,9 @@ it('Deduplicate duplicated PRs DESC', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -421,7 +466,9 @@ it('Commit SHA-1 in commitMode', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: true,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -440,7 +487,9 @@ it('Release Diff', async () => {
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v2.8.0',
|
||||
toTag: 'v2.8.1',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: true,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -477,7 +526,9 @@ it('Use exclude labels to not include a PR within a category.', async () => {
|
||||
repo: 'test-repo',
|
||||
fromTag: '1.0.0',
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
|
||||
+15
-1
@@ -17,12 +17,18 @@ inputs:
|
||||
description: 'Defines the previous tag to compare against'
|
||||
toTag:
|
||||
description: 'Defines the newly tag created'
|
||||
includeOpen:
|
||||
description: 'Defines if the changelog should include open PRs'
|
||||
default: "false"
|
||||
ignorePreReleases:
|
||||
description: 'Defines if the action will only use full releases to compare against (Only used if fromTag is not defined). E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick'
|
||||
default: "false"
|
||||
failOnError:
|
||||
description: 'Defines if the action should result in a build failure, if an error was discovered'
|
||||
default: "false"
|
||||
fetchReviewers:
|
||||
description: 'Will enable fetching the users/reviewers who approved the PR'
|
||||
default: "false"
|
||||
commitMode:
|
||||
description: 'Enables a `light` commit based mode. This mode generates changelogs based on the commits. Please note that this is not officially supported, and lacks a lot of features only possible with PRs.'
|
||||
default: "false"
|
||||
@@ -35,6 +41,8 @@ inputs:
|
||||
outputs:
|
||||
changelog:
|
||||
description: The built release changelog built from the merged pull requests
|
||||
pull_requests:
|
||||
description: A comma seperated list of PR numbers that were merged
|
||||
owner:
|
||||
description: Specifies the owner of the repository processed
|
||||
repo:
|
||||
@@ -45,6 +53,12 @@ outputs:
|
||||
description: Defines the `toTag` which describes the upper bound to process pull request for
|
||||
failed:
|
||||
description: Defines if there was an issue with the action run, and the changelog may not have been generated correctly. [true, false]
|
||||
categorized_prs:
|
||||
description: Count of categorized pull requests
|
||||
uncategorized_prs:
|
||||
description: Count of uncategorized pull requests
|
||||
open_prs:
|
||||
description: Count of open pull requests. Only fetched if `includeOpen` is enabled.
|
||||
runs:
|
||||
using: 'node12'
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"template": "${{CHANGELOG}}",
|
||||
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] ${{REVIEWERS}} --- ${{APPROVERS}}"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"title": "## 🚀 Features Merged",
|
||||
"labels": ["feature"],
|
||||
"exclude_labels": ["--rcba-open"]
|
||||
},
|
||||
{
|
||||
"title": "## 🚀 Features Open",
|
||||
"labels": ["feature", "--rcba-open"],
|
||||
"exhaustive": true
|
||||
}
|
||||
],
|
||||
"template": "${{CHANGELOG}}",
|
||||
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] {${{LABELS}}}${{APPROVERS}}"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"title": "## 🚀 Features",
|
||||
"labels": ["feature"]
|
||||
}
|
||||
],
|
||||
"template": "${{CHANGELOG}}\n\n\nUncategorized\n${{UNCATEGORIZED}}\n\n\nOpen\n${{OPEN}}",
|
||||
"pr_template": "- ${{TITLE}} (#${{NUMBER}}) ${{STATUS}}${{APPROVERS}}"
|
||||
}
|
||||
+446
-203
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1171
-1504
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "release-changelog-builder-action",
|
||||
"version": "v1.0.0",
|
||||
"version": "v3.0.0",
|
||||
"private": true,
|
||||
"description": "A GitHub action that builds your release notes / changelog fast, easy and exactly the way you want.",
|
||||
"main": "lib/main.js",
|
||||
@@ -33,27 +33,27 @@
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.6.0",
|
||||
"@actions/exec": "^1.1.0",
|
||||
"@actions/github": "^5.0.0",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^5.0.1",
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"@types/semver": "^7.3.9",
|
||||
"moment": "^2.29.1",
|
||||
"semver": "^7.3.5",
|
||||
"webpack": "^5.68.0"
|
||||
"moment": "^2.29.2",
|
||||
"semver": "^7.3.7",
|
||||
"webpack": "^5.72.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.4.0",
|
||||
"@typescript-eslint/parser": "^5.11.0",
|
||||
"@types/node": "^17.0.17",
|
||||
"@vercel/ncc": "^0.33.1",
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-plugin-github": "^4.3.5",
|
||||
"eslint-plugin-jest": "^26.1.0",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@types/node": "^17.0.24",
|
||||
"@vercel/ncc": "^0.33.4",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-plugin-github": "^4.3.6",
|
||||
"eslint-plugin-jest": "^26.1.4",
|
||||
"jest": "^27.5.1",
|
||||
"jest-circus": "^27.5.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "2.5.1",
|
||||
"ts-jest": "^27.1.3",
|
||||
"typescript": "^4.5.5"
|
||||
"prettier": "2.6.2",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,10 @@ async function run(): Promise<void> {
|
||||
const fromTag = core.getInput('fromTag')
|
||||
const toTag = core.getInput('toTag')
|
||||
// read in flags
|
||||
const includeOpen = core.getInput('includeOpen') === 'true'
|
||||
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
|
||||
const failOnError = core.getInput('failOnError') === 'true'
|
||||
const fetchReviewers = core.getInput('fetchReviewers') === 'true'
|
||||
const commitMode = core.getInput('commitMode') === 'true'
|
||||
|
||||
const result = await new ReleaseNotesBuilder(
|
||||
@@ -44,8 +46,10 @@ async function run(): Promise<void> {
|
||||
repo,
|
||||
fromTag,
|
||||
toTag,
|
||||
includeOpen,
|
||||
failOnError,
|
||||
ignorePreReleases,
|
||||
fetchReviewers,
|
||||
commitMode,
|
||||
configuration
|
||||
).build()
|
||||
|
||||
+95
-11
@@ -8,7 +8,8 @@ export interface PullRequestInfo {
|
||||
title: string
|
||||
htmlURL: string
|
||||
baseBranch: string
|
||||
mergedAt: moment.Moment
|
||||
createdAt: moment.Moment
|
||||
mergedAt: moment.Moment | null
|
||||
mergeCommitSha: string
|
||||
author: string
|
||||
repoName: string
|
||||
@@ -17,6 +18,8 @@ export interface PullRequestInfo {
|
||||
body: string
|
||||
assignees: string[]
|
||||
requestedReviewers: string[]
|
||||
approvedReviewers: string[]
|
||||
status: 'open' | 'merged'
|
||||
}
|
||||
|
||||
type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data']
|
||||
@@ -24,6 +27,9 @@ type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data']
|
||||
type PullsListData =
|
||||
RestEndpointMethodTypes['pulls']['list']['response']['data']
|
||||
|
||||
type PullReviewData =
|
||||
RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
|
||||
|
||||
export class PullRequests {
|
||||
constructor(private octokit: Octokit) {}
|
||||
|
||||
@@ -69,7 +75,7 @@ export class PullRequests {
|
||||
const prs: PullsListData = response.data as PullsListData
|
||||
|
||||
for (const pr of prs.filter(p => !!p.merged_at)) {
|
||||
mergedPRs.push(mapPullRequest(pr))
|
||||
mergedPRs.push(mapPullRequest(pr, 'merged'))
|
||||
}
|
||||
|
||||
const firstPR = prs[0]
|
||||
@@ -89,6 +95,64 @@ export class PullRequests {
|
||||
|
||||
return sortPullRequests(mergedPRs, true)
|
||||
}
|
||||
|
||||
async getOpen(
|
||||
owner: string,
|
||||
repo: string,
|
||||
maxPullRequests: number
|
||||
): Promise<PullRequestInfo[]> {
|
||||
const openPrs: PullRequestInfo[] = []
|
||||
const options = this.octokit.pulls.list.endpoint.merge({
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
sort: 'created',
|
||||
per_page: '100',
|
||||
direction: 'desc'
|
||||
})
|
||||
|
||||
for await (const response of this.octokit.paginate.iterator(options)) {
|
||||
const prs: PullsListData = response.data as PullsListData
|
||||
|
||||
for (const pr of prs) {
|
||||
openPrs.push(mapPullRequest(pr, 'open'))
|
||||
}
|
||||
|
||||
const firstPR = prs[0]
|
||||
if (firstPR === undefined || openPrs.length >= maxPullRequests) {
|
||||
if (openPrs.length >= maxPullRequests) {
|
||||
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests}`)
|
||||
}
|
||||
|
||||
// bail out early to not keep iterating on PRs super old
|
||||
return sortPullRequests(openPrs, true)
|
||||
}
|
||||
}
|
||||
|
||||
return sortPullRequests(openPrs, true)
|
||||
}
|
||||
|
||||
async getReviewers(
|
||||
owner: string,
|
||||
repo: string,
|
||||
pr: PullRequestInfo
|
||||
): Promise<PullReviewData[]> {
|
||||
const options = this.octokit.pulls.listReviews.endpoint.merge({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number
|
||||
})
|
||||
|
||||
for await (const response of this.octokit.paginate.iterator(options)) {
|
||||
const reviews: PullReviewData = response.data as PullReviewData
|
||||
pr.approvedReviewers = reviews
|
||||
.filter(r => r.state === 'APPROVED')
|
||||
.map(r => r.user?.login)
|
||||
.filter(r => !!r) as string[]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export function sortPullRequests(
|
||||
@@ -97,18 +161,22 @@ export function sortPullRequests(
|
||||
): PullRequestInfo[] {
|
||||
if (ascending) {
|
||||
pullRequests.sort((a, b) => {
|
||||
if (a.mergedAt.isBefore(b.mergedAt)) {
|
||||
const aa = a.mergedAt || a.createdAt
|
||||
const bb = b.mergedAt || b.createdAt
|
||||
if (aa.isBefore(bb)) {
|
||||
return -1
|
||||
} else if (b.mergedAt.isBefore(a.mergedAt)) {
|
||||
} else if (bb.isBefore(aa)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
} else {
|
||||
pullRequests.sort((b, a) => {
|
||||
if (a.mergedAt.isBefore(b.mergedAt)) {
|
||||
const aa = a.mergedAt || a.createdAt
|
||||
const bb = b.mergedAt || b.createdAt
|
||||
if (aa.isBefore(bb)) {
|
||||
return -1
|
||||
} else if (b.mergedAt.isBefore(a.mergedAt)) {
|
||||
} else if (bb.isBefore(aa)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
@@ -117,23 +185,39 @@ export function sortPullRequests(
|
||||
return pullRequests
|
||||
}
|
||||
|
||||
// helper function to add a special open label to prs not merged.
|
||||
function attachSpeciaLabels(
|
||||
status: 'open' | 'merged',
|
||||
labels: Set<string>
|
||||
): Set<string> {
|
||||
labels.add(`--rcba-${status}`)
|
||||
return labels
|
||||
}
|
||||
|
||||
const mapPullRequest = (
|
||||
pr: PullData | Unpacked<PullsListData>
|
||||
pr: PullData | Unpacked<PullsListData>,
|
||||
status: 'open' | 'merged' = 'open'
|
||||
): PullRequestInfo => ({
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
htmlURL: pr.html_url,
|
||||
baseBranch: pr.base.ref,
|
||||
mergedAt: moment(pr.merged_at),
|
||||
createdAt: moment(pr.created_at),
|
||||
mergedAt: pr.merged_at ? moment(pr.merged_at) : null,
|
||||
mergeCommitSha: pr.merge_commit_sha || '',
|
||||
author: pr.user?.login || '',
|
||||
repoName: pr.base.repo.full_name,
|
||||
labels: new Set(
|
||||
pr.labels?.map(lbl => lbl.name?.toLocaleLowerCase('en') || '') || []
|
||||
labels: attachSpeciaLabels(
|
||||
status,
|
||||
new Set(
|
||||
pr.labels?.map(lbl => lbl.name?.toLocaleLowerCase('en') || '') || []
|
||||
)
|
||||
),
|
||||
milestone: pr.milestone?.title || '',
|
||||
body: pr.body || '',
|
||||
assignees: pr.assignees?.map(asignee => asignee?.login || '') || [],
|
||||
requestedReviewers:
|
||||
pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || []
|
||||
pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || [],
|
||||
approvedReviewers: [],
|
||||
status
|
||||
})
|
||||
|
||||
+55
-9
@@ -11,7 +11,9 @@ export interface ReleaseNotesOptions {
|
||||
repo: string // the repository
|
||||
fromTag: string // the tag/ref to start from
|
||||
toTag: string // the tag/ref up to
|
||||
includeOpen: boolean // defines if we should also fetch open pull requests
|
||||
failOnError: boolean // defines if we should fail the action in case of an error
|
||||
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
|
||||
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported
|
||||
configuration: Configuration // the configuration as defined in `configuration.ts`
|
||||
}
|
||||
@@ -80,7 +82,8 @@ export class ReleaseNotes {
|
||||
private async getMergedPullRequests(
|
||||
octokit: Octokit
|
||||
): Promise<PullRequestInfo[]> {
|
||||
const {owner, repo, configuration} = this.options
|
||||
const {owner, repo, includeOpen, fetchReviewers, configuration} =
|
||||
this.options
|
||||
|
||||
const commits = await this.getCommitHistory(octokit)
|
||||
if (commits.length === 0) {
|
||||
@@ -133,6 +136,33 @@ export class ReleaseNotes {
|
||||
return commmit.sha
|
||||
})
|
||||
|
||||
// filter out pull requests not associated with this release
|
||||
const mergedPullRequests = pullRequests.filter(pr => {
|
||||
return releaseCommitHashes.includes(pr.mergeCommitSha)
|
||||
})
|
||||
|
||||
let allPullRequests = mergedPullRequests
|
||||
if (includeOpen) {
|
||||
// retrieve all open pull requests
|
||||
const openPullRequests = await pullRequestsApi.getOpen(
|
||||
owner,
|
||||
repo,
|
||||
configuration.max_pull_requests ||
|
||||
DefaultConfiguration.max_pull_requests
|
||||
)
|
||||
|
||||
core.info(
|
||||
`ℹ️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`
|
||||
)
|
||||
|
||||
// all pull requests
|
||||
allPullRequests = allPullRequests.concat(openPullRequests)
|
||||
|
||||
core.info(
|
||||
`ℹ️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`
|
||||
)
|
||||
}
|
||||
|
||||
// retrieve base branches we allow
|
||||
const baseBranches =
|
||||
configuration.base_branches || DefaultConfiguration.base_branches
|
||||
@@ -140,17 +170,30 @@ export class ReleaseNotes {
|
||||
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu')
|
||||
})
|
||||
|
||||
// return only the pull requests associated with this release
|
||||
// and if the baseBranch is matching the configuration
|
||||
return pullRequests.filter(pr => {
|
||||
let keep = releaseCommitHashes.includes(pr.mergeCommitSha)
|
||||
if (keep && baseBranches.length !== 0) {
|
||||
keep = baseBranchPatterns.some(pattern => {
|
||||
// return only prs if the baseBranch is matching the configuration
|
||||
const finalPrs = allPullRequests.filter(pr => {
|
||||
if (baseBranches.length !== 0) {
|
||||
return baseBranchPatterns.some(pattern => {
|
||||
return pr.baseBranch.match(pattern) !== null
|
||||
})
|
||||
}
|
||||
return keep
|
||||
return true
|
||||
})
|
||||
|
||||
if (fetchReviewers) {
|
||||
core.info(`ℹ️ Fetching reviewers was enabled`)
|
||||
// update PR information with reviewers who approved
|
||||
for (const pr of finalPrs) {
|
||||
await pullRequestsApi.getReviewers(owner, repo, pr)
|
||||
if (pr.approvedReviewers.length > 0) {
|
||||
core.info(
|
||||
`ℹ️ Retrieved ${pr.approvedReviewers.length} reviewer(s) for PR ${owner}/${repo}/#${pr.number}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalPrs
|
||||
}
|
||||
|
||||
private async generateCommitPRs(
|
||||
@@ -177,6 +220,7 @@ export class ReleaseNotes {
|
||||
title: commit.summary,
|
||||
htmlURL: '',
|
||||
baseBranch: '',
|
||||
createdAt: commit.date,
|
||||
mergedAt: commit.date,
|
||||
mergeCommitSha: commit.sha,
|
||||
author: commit.author || '',
|
||||
@@ -185,7 +229,9 @@ export class ReleaseNotes {
|
||||
milestone: '',
|
||||
body: commit.message || '',
|
||||
assignees: [],
|
||||
requestedReviewers: []
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: 'merged'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ export class ReleaseNotesBuilder {
|
||||
private repo: string | null,
|
||||
private fromTag: string | null,
|
||||
private toTag: string | null,
|
||||
private includeOpen: boolean = false,
|
||||
private failOnError: boolean,
|
||||
private ignorePreReleases: boolean,
|
||||
private fetchReviewers: boolean = false,
|
||||
private commitMode: boolean,
|
||||
private configuration: Configuration
|
||||
) {}
|
||||
@@ -88,7 +90,9 @@ export class ReleaseNotesBuilder {
|
||||
repo: this.repo,
|
||||
fromTag: this.fromTag,
|
||||
toTag: this.toTag,
|
||||
includeOpen: this.includeOpen,
|
||||
failOnError: this.failOnError,
|
||||
fetchReviewers: this.fetchReviewers,
|
||||
commitMode: this.commitMode,
|
||||
configuration: this.configuration
|
||||
}
|
||||
|
||||
+54
-3
@@ -95,6 +95,7 @@ export function buildChangelog(
|
||||
|
||||
const categorizedPrs: string[] = []
|
||||
const ignoredPrs: string[] = []
|
||||
const openPrs: string[] = []
|
||||
const uncategorizedPrs: string[] = []
|
||||
|
||||
// bring elements in order
|
||||
@@ -109,6 +110,10 @@ export function buildChangelog(
|
||||
continue
|
||||
}
|
||||
|
||||
if (pr.status === 'open') {
|
||||
openPrs.push(body)
|
||||
}
|
||||
|
||||
let matched = false
|
||||
for (const [category, pullRequests] of categorized) {
|
||||
// check if any exclude label matches
|
||||
@@ -185,6 +190,11 @@ export function buildChangelog(
|
||||
}
|
||||
}
|
||||
core.info(`✒️ Wrote ${categorizedPrs.length} categorized pull requests down`)
|
||||
if (core.isDebug()) {
|
||||
for (const pr of categorizedPrs) {
|
||||
core.debug(` ${pr}`)
|
||||
}
|
||||
}
|
||||
core.setOutput('categorized_prs', categorizedPrs.length)
|
||||
|
||||
let changelogUncategorized = ''
|
||||
@@ -194,12 +204,36 @@ export function buildChangelog(
|
||||
core.info(
|
||||
`✒️ Wrote ${uncategorizedPrs.length} non categorized pull requests down`
|
||||
)
|
||||
if (core.isDebug()) {
|
||||
for (const pr of uncategorizedPrs) {
|
||||
core.debug(` ${pr}`)
|
||||
}
|
||||
}
|
||||
core.setOutput('uncategorized_prs', uncategorizedPrs.length)
|
||||
|
||||
let changelogOpen = ''
|
||||
if (openPrs.length > 0) {
|
||||
for (const pr of openPrs) {
|
||||
changelogOpen = `${changelogOpen + pr}\n`
|
||||
}
|
||||
core.info(`✒️ Wrote ${openPrs.length} open pull requests down`)
|
||||
if (core.isDebug()) {
|
||||
for (const pr of openPrs) {
|
||||
core.debug(` ${pr}`)
|
||||
}
|
||||
}
|
||||
core.setOutput('open_prs', openPrs.length)
|
||||
}
|
||||
|
||||
let changelogIgnored = ''
|
||||
for (const pr of ignoredPrs) {
|
||||
changelogIgnored = `${changelogIgnored + pr}\n`
|
||||
}
|
||||
if (core.isDebug()) {
|
||||
for (const pr of ignoredPrs) {
|
||||
core.debug(` ${pr}`)
|
||||
}
|
||||
}
|
||||
core.info(`✒️ Wrote ${ignoredPrs.length} ignored pull requests down`)
|
||||
|
||||
// fill template
|
||||
@@ -212,6 +246,10 @@ export function buildChangelog(
|
||||
/\${{UNCATEGORIZED}}/g,
|
||||
changelogUncategorized
|
||||
)
|
||||
transformedChangelog = transformedChangelog.replace(
|
||||
/\${{OPEN}}/g,
|
||||
changelogOpen
|
||||
)
|
||||
transformedChangelog = transformedChangelog.replace(
|
||||
/\${{IGNORED}}/g,
|
||||
changelogIgnored
|
||||
@@ -226,6 +264,10 @@ export function buildChangelog(
|
||||
/\${{UNCATEGORIZED_COUNT}}/g,
|
||||
uncategorizedPrs.length.toString()
|
||||
)
|
||||
transformedChangelog = transformedChangelog.replace(
|
||||
/\${{OPEN_COUNT}}/g,
|
||||
openPrs.length.toString()
|
||||
)
|
||||
transformedChangelog = transformedChangelog.replace(
|
||||
/\${{IGNORED_COUNT}}/g,
|
||||
ignoredPrs.length.toString()
|
||||
@@ -268,15 +310,20 @@ function fillTemplate(pr: PullRequestInfo, template: string): string {
|
||||
transformed = transformed.replace(/\${{NUMBER}}/g, pr.number.toString())
|
||||
transformed = transformed.replace(/\${{TITLE}}/g, pr.title)
|
||||
transformed = transformed.replace(/\${{URL}}/g, pr.htmlURL)
|
||||
transformed = transformed.replace(/\${{STATUS}}/g, pr.status)
|
||||
transformed = transformed.replace(
|
||||
/\${{CREATED_AT}}/g,
|
||||
pr.createdAt.toISOString()
|
||||
)
|
||||
transformed = transformed.replace(
|
||||
/\${{MERGED_AT}}/g,
|
||||
pr.mergedAt.toISOString()
|
||||
pr.mergedAt?.toISOString() || ''
|
||||
)
|
||||
transformed = transformed.replace(/\${{MERGE_SHA}}/g, pr.mergeCommitSha)
|
||||
transformed = transformed.replace(/\${{AUTHOR}}/g, pr.author)
|
||||
transformed = transformed.replace(
|
||||
/\${{LABELS}}/g,
|
||||
[...pr.labels]?.join(', ') || ''
|
||||
[...pr.labels]?.filter(l => !l.startsWith('--rcba-'))?.join(', ') || ''
|
||||
)
|
||||
transformed = transformed.replace(/\${{MILESTONE}}/g, pr.milestone || '')
|
||||
transformed = transformed.replace(/\${{BODY}}/g, pr.body)
|
||||
@@ -288,6 +335,10 @@ function fillTemplate(pr: PullRequestInfo, template: string): string {
|
||||
/\${{REVIEWERS}}/g,
|
||||
pr.requestedReviewers?.join(', ') || ''
|
||||
)
|
||||
transformed = transformed.replace(
|
||||
/\${{APPROVERS}}/g,
|
||||
pr.approvedReviewers?.join(', ') || ''
|
||||
)
|
||||
return transformed
|
||||
}
|
||||
|
||||
@@ -369,7 +420,7 @@ function extractValues(
|
||||
|
||||
if (extractor.onProperty !== undefined) {
|
||||
let results: string[] = []
|
||||
const list: ('title' | 'author' | 'milestone' | 'body')[] =
|
||||
const list: ('title' | 'author' | 'milestone' | 'body' | 'status')[] =
|
||||
extractor.onProperty
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user