Merge pull request #828 from mikepenz/develop

dev -> main
This commit is contained in:
Mike Penz
2022-07-26 17:39:08 +02:00
committed by GitHub
18 changed files with 1104 additions and 241 deletions
+75 -3
View File
@@ -28,6 +28,7 @@
<a href="#full-sample-%EF%B8%8F">Sample 🖥️</a> &bull;
<a href="#customization-%EF%B8%8F">Customization 🖍️</a> &bull;
<a href="#contribute-">Contribute 🧬</a> &bull;
<a href="#local-testing-">Local Testing 🧪</a> &bull;
<a href="#license">License 📓</a>
</p>
@@ -86,6 +87,11 @@ A full set list of possible output values for this 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. |
| `outputs.changed_files` | Count of changed files in this release. |
| `outputs.additions` | Count of code additions in this release (lines). |
| `outputs.deletions` | Count of code deletions in this release (lines). |
| `outputs.changes` | Total count of changes in this release (lines). |
| `outputs.commits` | Count of commits which have been added in this release. |
## Full Sample 🖥️
@@ -291,8 +297,9 @@ Table of supported placeholders allowed to be used in the `pr_template` configur
| `${{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 `,` |
| `${{REVIEWERS}}` | GitHub Login names of specified reviewers, joined by `,`. Requires `fetchReviewers` to be enabled. |
| `${{APPROVERS}}` | GitHub Login names of users who approved the PR, joined by `,` |
| `${{DAYS_SINCE}}` | Days between the 2 releases. Requires `fetchReleaseInformation` to be enabled. |
### Template placeholders
@@ -307,14 +314,20 @@ Table of supported placeholders allowed to be used in the `template` and `empty_
| `${{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 |
| `${{FROM_TAG}}` | Defines the 'start' from where the changelog did consider merged pull requests | x |
| `${{FROM_TAG_DATE}}` | Defines the date at which the 'start' tag was created | x |
| `${{TO_TAG}}` | Defines until which tag the changelog did consider merged pull requests | x |
| `${{TO_TAG_DATE}}` | Defines the date at which the 'until' tag was created | x |
| `${{RELEASE_DIFF}}` | Introduces a link to the full diff between from tag and to tag releases | x |
| `${{CHANGED_FILES}}` | The count of changed files. | |
| `${{ADDITIONS}}` | The count of code additions (lines). | |
| `${{DELETIONS}}` | The count of code deletions (lines). | |
| `${{CHANGES}}` | The count of total changes (lines). | |
| `${{COMMITS}}` | The count of commits in this release. | |
| `${{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. | |
### Configuration Specification
Table of descriptions for the `configuration.json` options to configure the resulting release notes / changelog.
@@ -377,6 +390,65 @@ It's suggested to export the token to your path before running the tests so that
export GITHUB_TOKEN=your_personal_github_pat
```
## Local Testing 🧪
This GitHub action is fully developed in Typescript and can be run locally via npm. Doing so is a great way to test the action and/or your custom configurations locally, without the need to push and re-run GitHub actions over and over again.
To run this action locally, first make sure you provide a `GITHUB_TOKEN` with enough permissions to access the repository.
```
# GitHub token for the action
export GITHUB_TOKEN=your_read_only_github_token
```
Afterwards run the testcases with:
```bash
npm test -- custom.test.ts
```
<details><summary><b>custom.test.ts</b></summary>
<p>
```typescript
import {resolveConfiguration} from '../src/utils'
import {ReleaseNotesBuilder} from '../src/releaseNotesBuilder'
jest.setTimeout(180000)
it('Test custom changelog builder', 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(``)
})
```
</p>
</details>
Additionally it is possible to do full debugging including the option of breakpoints via (for example) Visual Code.
Open the project in Visual code -> open the terminal -> use the `+` and start a new `JavaScript Debug Terminal`. Afterwards run the tests as described above.
## Developed By
* Mike Penz
@@ -404,7 +476,7 @@ export GITHUB_TOKEN=your_personal_github_pat
All patches and changes applied to the original source are licensed under the Apache 2.0 license.
Copyright 2021 Mike Penz
Copyright 2022 Mike Penz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+31 -21
View File
@@ -14,18 +14,19 @@ it('Should have empty changelog (tags)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.0.1',
toTag: 'v0.0.2',
fromTag: { name: 'v0.0.1' },
toTag: { name: 'v0.0.2' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
const changeLog = await releaseNotes.pull()
console.log(changeLog)
expect(changeLog).toStrictEqual(null)
expect(changeLog).toStrictEqual("- no changes")
})
it('Should match generated changelog (tags)', async () => {
@@ -33,11 +34,12 @@ it('Should match generated changelog (tags)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.0.1',
toTag: 'v0.0.3',
fromTag: { name: 'v0.0.1'},
toTag: { name: 'v0.0.3' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -60,11 +62,12 @@ it('Should match generated changelog (refs)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
fromTag: { name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3' },
toTag: { name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -95,11 +98,12 @@ it('Should match generated changelog and replace all occurrences (refs)', async
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
fromTag: { name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3' },
toTag: { name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -132,11 +136,12 @@ it('Should match ordered ASC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -156,11 +161,12 @@ it('Should match ordered DESC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -180,11 +186,12 @@ it('Should match ordered by title ASC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -204,11 +211,12 @@ it('Should match ordered by title DESC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -228,11 +236,12 @@ it('Should ignore PRs not merged into develop branch', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v1.3.1',
toTag: 'v1.4.0',
fromTag: { name: 'v1.3.1' },
toTag: { name: 'v1.4.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -250,11 +259,12 @@ it('Should ignore PRs not merged into main branch', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v1.3.1',
toTag: 'v1.4.0',
fromTag: { name: 'v1.3.1' },
toTag: { name: 'v1.4.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
+76 -5
View File
@@ -17,6 +17,7 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -45,6 +46,7 @@ it('Should match generated changelog (unspecified tags)', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -70,6 +72,7 @@ it('Should use empty placeholder', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -96,6 +99,7 @@ it('Should fill empty placeholders', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -103,7 +107,7 @@ it('Should fill empty placeholders', async () => {
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`mikepenz\nrelease-changelog-builder-action\nv0.0.2\nv0.0.3`
`mikepenz\nrelease-changelog-builder-action\nv0.0.2\nv0.0.3\nhttps://github.com/mikepenz/release-changelog-builder-action/compare/v0.0.2...v0.0.3`
)
})
@@ -124,6 +128,7 @@ it('Should fill `template` placeholders', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -131,7 +136,7 @@ it('Should fill `template` placeholders', async () => {
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🧪 Tests\n\n- [CI] Specify Test Case\n - PR: #10\n\n\n\n\nmikepenz\nrelease-changelog-builder-action\nv0.0.1\nv0.0.3\n1\n0\n0`
`## 🧪 Tests\n\n- [CI] Specify Test Case\n - PR: #10\n\n\n\n\nmikepenz\nrelease-changelog-builder-action\nv0.0.1\nv0.0.3\nhttps://github.com/mikepenz/release-changelog-builder-action/compare/v0.0.1...v0.0.3\n1\n0\n0\n19\n14827\n444\n15271\n3`
)
})
@@ -152,6 +157,7 @@ it('Should fill `template` placeholders, ignore', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -159,7 +165,7 @@ it('Should fill `template` placeholders, ignore', async () => {
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features\n\n- Enhance sorting by using proper semver\n - PR: #51\n\n## 🧪 Tests\n\n- Improve test cases\n - PR: #49\n\n\n- Bump @types/node from 14.11.8 to 14.11.10\n - PR: #47\n- Adjust code to move fromTag resolving to main.ts\n - PR: #48\n- dev -> main\n - PR: #52\n- Update package.json to updated description\n - PR: #53\n- dev -> main\n - PR: #54\n\n- New additional placeholders for \`template\` and \`empty_template\`\n - PR: #50\n\nmikepenz\nrelease-changelog-builder-action\nv0.9.1\nv0.9.5\n2\n5\n1`
`## 🚀 Features\n\n- Enhance sorting by using proper semver\n - PR: #51\n\n## 🧪 Tests\n\n- Improve test cases\n - PR: #49\n\n\n- Bump @types/node from 14.11.8 to 14.11.10\n - PR: #47\n- Adjust code to move fromTag resolving to main.ts\n - PR: #48\n- dev -> main\n - PR: #52\n- Update package.json to updated description\n - PR: #53\n- dev -> main\n - PR: #54\n\n- New additional placeholders for \`template\` and \`empty_template\`\n - PR: #50\n\nmikepenz\nrelease-changelog-builder-action\nv0.9.1\nv0.9.5\nhttps://github.com/mikepenz/release-changelog-builder-action/compare/v0.9.1...v0.9.5\n2\n5\n1\n16\n2931\n450\n3381\n26`
)
})
@@ -180,6 +186,7 @@ it('Uncategorized category', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -208,6 +215,7 @@ it('Verify commit based changelog', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
true,
configuration
)
@@ -236,6 +244,7 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
true,
configuration
)
@@ -264,6 +273,7 @@ it('Verify default inclusion of open PRs', async () => {
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
@@ -292,6 +302,7 @@ it('Verify custom categorisation of open PRs', async () => {
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
@@ -303,7 +314,7 @@ it('Verify custom categorisation of open PRs', async () => {
)
})
it('Verify reviewers who approved are fetched', async () => {
it('Verify reviewers who approved are fetched and also release information', async () => {
const configuration = resolveConfiguration(
'',
'configs_test/configuration_approvers.json'
@@ -320,6 +331,7 @@ it('Verify reviewers who approved are fetched', async () => {
false, // failOnError
false, // ignorePrePrelease
true, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
@@ -327,6 +339,65 @@ it('Verify reviewers who approved are fetched', async () => {
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`
`## 🚀 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\n\n0`
)
})
it('Fetch release information', async () => {
const configuration = resolveConfiguration(
'',
'configs_test/configuration_approvers.json'
)
configuration.template = "${{FROM_TAG}}-${{FROM_TAG_DATE}}\n${{TO_TAG}}-${{TO_TAG_DATE}}\n${{DAYS_SINCE}}"
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
null, // token
'.', // repoPath
'mikepenz', // user
'release-changelog-builder-action-playground', // repo
'2.0.0', // fromTag
'3.0.0-a01', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`2.0.0-2022-04-08T07:52:40.000Z\n3.0.0-a01-2022-07-26T14:28:36.000Z\n109`
)
})
it('Fetch release information for non existing tag / release', async () => {
const configuration = resolveConfiguration(
'',
'configs_test/configuration_approvers.json'
)
configuration.template = "${{FROM_TAG}}-${{FROM_TAG_DATE}}\n${{TO_TAG}}-${{TO_TAG_DATE}}\n${{DAYS_SINCE}}"
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
null, // token
'.', // repoPath
'mikepenz', // user
'release-changelog-builder-action-playground', // repo
'2.0.0', // fromTag
'3.0.1', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`- no changes`)
})
+55 -41
View File
@@ -2,6 +2,7 @@ import {buildChangelog} from '../src/transform'
import {PullRequestInfo} from '../src/pullRequests'
import moment from 'moment'
import { DefaultConfiguration, Configuration } from '../src/configuration';
import { DefaultDiffInfo } from '../src/commits';
jest.setTimeout(180000)
@@ -130,14 +131,15 @@ it('Extract label from title, combined regex', async () => {
}
]
const resultChangelog = buildChangelog(mergedPullRequests, {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -159,14 +161,15 @@ it('Extract label from title and body, combined regex', async () => {
let prs = Array.from(mergedPullRequests)
prs.push(pullRequestWithLabelInBody)
const resultChangelog = buildChangelog(prs, {
const resultChangelog = buildChangelog(DefaultDiffInfo, prs, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -190,14 +193,15 @@ it('Extract label from title, split regex', async () => {
}
]
const resultChangelog = buildChangelog(mergedPullRequests, {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -221,14 +225,15 @@ it('Extract label from title, match', async () => {
}
]
const resultChangelog = buildChangelog(mergedPullRequests, {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -247,14 +252,15 @@ it('Extract label from title, match multiple', async () => {
}
]
const resultChangelog = buildChangelog(mergedPullRequests, {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -274,14 +280,15 @@ it('Extract label from title, match multiple, custon non matching label', async
}
]
const resultChangelog = buildChangelog(mergedPullRequests, {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -388,14 +395,15 @@ it('Match multiple labels exhaustive for category', async () => {
}
]
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -413,14 +421,15 @@ it('Deduplicate duplicated PRs', async () => {
method: 'match'
}
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -439,14 +448,15 @@ it('Deduplicate duplicated PRs DESC', async () => {
method: 'match'
}
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -471,14 +481,15 @@ it('Use empty_content for empty category', async () => {
}
]
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -493,14 +504,15 @@ it('Commit SHA-1 in commitMode', async () => {
customConfig.sort = "DESC"
customConfig.pr_template = "${{MERGE_SHA}}"
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: true,
configuration: customConfig
})
@@ -512,22 +524,23 @@ it('Commit SHA-1 in commitMode', async () => {
it('Release Diff', async () => {
const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.template = "${{RELEASE_DIFF}}"
customConfig.template = "${{RELEASE_DIFF}}\n${{DAYS_SINCE}}"
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v2.8.0',
toTag: 'v2.8.1',
fromTag: { name: 'v2.8.0' },
toTag: { name: 'v2.8.1' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: true,
commitMode: true,
configuration: customConfig
})
expect(resultChangelog).toStrictEqual(
`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1`
`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`
)
})
@@ -553,14 +566,15 @@ it('Use exclude labels to not include a PR within a category.', async () => {
}
]
const resultChangelog = buildChangelog(pullRequestsWithLabels, {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
+3
View File
@@ -29,6 +29,9 @@ inputs:
fetchReviewers:
description: 'Will enable fetching the users/reviewers who approved the PR'
default: "false"
fetchReleaseInformation:
description: 'Will enable fetching release information for the tags. E.g. creation date'
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"
+1 -1
View File
@@ -1,4 +1,4 @@
{
"template": "${{CHANGELOG}}",
"template": "${{CHANGELOG}}\n\n${{DAYS_SINCE}}",
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] ${{REVIEWERS}} --- ${{APPROVERS}}"
}
@@ -1,6 +1,6 @@
{
"template": "${{CHANGELOG}}\n${{UNCATEGORIZED}}\n${{IGNORED}}\n${{OWNER}}\n${{REPO}}\n${{FROM_TAG}}\n${{TO_TAG}}\n${{CATEGORIZED_COUNT}}\n${{UNCATEGORIZED_COUNT}}\n${{IGNORED_COUNT}}",
"empty_template": "${{OWNER}}\n${{REPO}}\n${{FROM_TAG}}\n${{TO_TAG}}",
"template": "${{CHANGELOG}}\n${{UNCATEGORIZED}}\n${{IGNORED}}\n${{OWNER}}\n${{REPO}}\n${{FROM_TAG}}\n${{TO_TAG}}\n${{RELEASE_DIFF}}\n${{CATEGORIZED_COUNT}}\n${{UNCATEGORIZED_COUNT}}\n${{IGNORED_COUNT}}\n${{CHANGED_FILES}}\n${{ADDITIONS}}\n${{DELETIONS}}\n${{CHANGES}}\n${{COMMITS}}",
"empty_template": "${{OWNER}}\n${{REPO}}\n${{FROM_TAG}}\n${{TO_TAG}}\n${{RELEASE_DIFF}}",
"max_pull_requests": 1000,
"max_back_track_time_days": 1000
}
Generated Vendored
+171 -55
View File
@@ -42,21 +42,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.filterCommits = exports.Commits = void 0;
exports.filterCommits = exports.Commits = exports.DefaultDiffInfo = void 0;
const core = __importStar(__nccwpck_require__(2186));
const moment_1 = __importDefault(__nccwpck_require__(9623));
exports.DefaultDiffInfo = {
changedFiles: 0,
additions: 0,
deletions: 0,
changes: 0,
commits: 0,
commitInfo: []
};
class Commits {
constructor(octokit) {
this.octokit = octokit;
}
getDiff(owner, repo, base, head) {
return __awaiter(this, void 0, void 0, function* () {
const commits = yield this.getDiffRemote(owner, repo, base, head);
return this.sortCommits(commits);
const diff = yield this.getDiffRemote(owner, repo, base, head);
diff.commitInfo = this.sortCommits(diff.commitInfo);
return diff;
});
}
getDiffRemote(owner, repo, base, head) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let changedFilesCount = 0;
let additionCount = 0;
let deletionCount = 0;
let changeCount = 0;
let commitCount = 0;
// Fetch comparisons recursively until we don't find any commits
// This is because the GitHub API limits the number of commits returned in a single response.
let commits = [];
@@ -72,23 +87,40 @@ class Commits {
if (compareResult.data.total_commits === 0) {
break;
}
changedFilesCount += (_b = (_a = compareResult.data.files) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
const files = compareResult.data.files;
if (files !== undefined) {
for (const file of files) {
additionCount += file.additions;
deletionCount += file.deletions;
changeCount += file.changes;
}
}
commitCount += compareResult.data.commits.length;
commits = compareResult.data.commits.concat(commits);
compareHead = `${commits[0].sha}^`;
}
core.info(`️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`);
return commits
.filter(commit => commit.sha)
.map(commit => {
var _a, _b;
return ({
sha: commit.sha || '',
summary: commit.commit.message.split('\n')[0],
message: commit.commit.message,
date: (0, moment_1.default)((_a = commit.commit.committer) === null || _a === void 0 ? void 0 : _a.date),
author: ((_b = commit.commit.author) === null || _b === void 0 ? void 0 : _b.name) || '',
prNumber: undefined
});
});
return {
changedFiles: changedFilesCount,
additions: additionCount,
deletions: deletionCount,
changes: changeCount,
commits: commitCount,
commitInfo: commits
.filter(commit => commit.sha)
.map(commit => {
var _a, _b;
return ({
sha: commit.sha || '',
summary: commit.commit.message.split('\n')[0],
message: commit.commit.message,
date: (0, moment_1.default)((_a = commit.commit.committer) === null || _a === void 0 ? void 0 : _a.date),
author: ((_b = commit.commit.author) === null || _b === void 0 ? void 0 : _b.name) || '',
prNumber: undefined
});
})
};
});
}
sortCommits(commits) {
@@ -275,6 +307,16 @@ class GitCommandManager {
return revListOutput.stdout.trim();
});
}
tagCreation(tagName) {
return __awaiter(this, void 0, void 0, function* () {
const creationDate = yield this.execGit([
'for-each-ref',
'--format="%(creatordate:rfc)"',
`refs/tags/${tagName}`
]);
return creationDate.stdout.trim().replace(/"/g, '');
});
}
static createCommandManager(workingDirectory) {
return __awaiter(this, void 0, void 0, function* () {
const result = new GitCommandManager();
@@ -385,8 +427,9 @@ function run() {
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true';
const failOnError = core.getInput('failOnError') === 'true';
const fetchReviewers = core.getInput('fetchReviewers') === 'true';
const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true';
const commitMode = core.getInput('commitMode') === 'true';
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, commitMode, configuration).build();
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, fetchReleaseInformation, commitMode, configuration).build();
core.setOutput('changelog', result);
// write the result in changelog to file if possible
const outputFile = core.getInput('outputFile');
@@ -718,9 +761,12 @@ class ReleaseNotes {
pull() {
return __awaiter(this, void 0, void 0, function* () {
let mergedPullRequests;
let diffInfo;
if (!this.options.commitMode) {
core.startGroup(`🚀 Load pull requests`);
mergedPullRequests = yield this.getMergedPullRequests(this.octokit);
const [info, prs] = yield this.getMergedPullRequests(this.octokit);
mergedPullRequests = prs;
diffInfo = info;
// define the included PRs within this release as output
core.setOutput('pull_requests', mergedPullRequests
.map(pr => {
@@ -732,15 +778,23 @@ class ReleaseNotes {
else {
core.startGroup(`🚀 Load commit history`);
core.info(`⚠️ Executing experimental commit mode`);
mergedPullRequests = yield this.generateCommitPRs(this.octokit);
const [info, prs] = yield this.generateCommitPRs(this.octokit);
mergedPullRequests = prs;
diffInfo = info;
core.endGroup();
}
core.setOutput('changed_files', diffInfo.changedFiles);
core.setOutput('additions', diffInfo.additions);
core.setOutput('deletions', diffInfo.deletions);
core.setOutput('changes', diffInfo.changes);
core.setOutput('commits', diffInfo.commits);
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`);
return null;
return (0, transform_1.fillAdditionalPlaceholders)(this.options.configuration.empty_template ||
configuration_1.DefaultConfiguration.empty_template, this.options);
}
core.startGroup('📦 Build changelog');
const resultChangelog = (0, transform_1.buildChangelog)(mergedPullRequests, this.options);
const resultChangelog = (0, transform_1.buildChangelog)(diffInfo, mergedPullRequests, this.options);
core.endGroup();
return resultChangelog;
});
@@ -748,29 +802,30 @@ class ReleaseNotes {
getCommitHistory(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, fromTag, toTag, failOnError } = this.options;
core.info(`️ Comparing ${owner}/${repo} - '${fromTag}...${toTag}'`);
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`);
const commitsApi = new commits_1.Commits(octokit);
let commits;
let diffInfo;
try {
commits = yield commitsApi.getDiff(owner, repo, fromTag, toTag);
diffInfo = yield commitsApi.getDiff(owner, repo, fromTag.name, toTag.name);
}
catch (error) {
(0, utils_1.failOrError)(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError);
return [];
return commits_1.DefaultDiffInfo;
}
if (commits.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag}...${toTag}`);
return [];
if (diffInfo.commitInfo.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`);
return commits_1.DefaultDiffInfo;
}
return commits;
return diffInfo;
});
}
getMergedPullRequests(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, includeOpen, fetchReviewers, configuration } = this.options;
const commits = yield this.getCommitHistory(octokit);
const diffInfo = yield this.getCommitHistory(octokit);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
return [];
return [diffInfo, []];
}
const firstCommit = commits[0];
const lastCommit = commits[commits.length - 1];
@@ -832,20 +887,24 @@ class ReleaseNotes {
}
}
}
return finalPrs;
else {
core.debug(`️ Fetching reviewers was disabled`);
}
return [diffInfo, finalPrs];
});
}
generateCommitPRs(octokit) {
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, configuration } = this.options;
const commits = yield this.getCommitHistory(octokit);
const diffInfo = yield this.getCommitHistory(octokit);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
return [];
return [diffInfo, []];
}
const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches ||
configuration_1.DefaultConfiguration.exclude_merge_branches);
core.info(`️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`);
return prCommits.map(function (commit) {
const prs = prCommits.map(function (commit) {
return {
number: 0,
title: commit.summary,
@@ -865,6 +924,7 @@ class ReleaseNotes {
status: 'merged'
};
});
return [diffInfo, prs];
});
}
}
@@ -918,9 +978,8 @@ const rest_1 = __nccwpck_require__(5375);
const releaseNotes_1 = __nccwpck_require__(5882);
const tags_1 = __nccwpck_require__(7532);
const utils_1 = __nccwpck_require__(918);
const transform_1 = __nccwpck_require__(1644);
class ReleaseNotesBuilder {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, commitMode, configuration) {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, fetchReleaseInformation = false, commitMode, configuration) {
this.baseUrl = baseUrl;
this.token = token;
this.repositoryPath = repositoryPath;
@@ -932,11 +991,11 @@ class ReleaseNotesBuilder {
this.failOnError = failOnError;
this.ignorePreReleases = ignorePreReleases;
this.fetchReviewers = fetchReviewers;
this.fetchReleaseInformation = fetchReleaseInformation;
this.commitMode = commitMode;
this.configuration = configuration;
}
build() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (!this.owner) {
(0, utils_1.failOrError)(`💥 Missing or couldn't resolve 'owner'`, this.failOnError);
@@ -965,40 +1024,46 @@ class ReleaseNotesBuilder {
const tagsApi = new tags_1.Tags(octokit);
const tagRange = yield tagsApi.retrieveRange(this.repositoryPath, this.owner, this.repo, this.fromTag, this.toTag, this.ignorePreReleases, this.configuration.max_tags_to_fetch ||
configuration_1.DefaultConfiguration.max_tags_to_fetch, this.configuration.tag_resolver || configuration_1.DefaultConfiguration.tag_resolver);
const thisTag = (_a = tagRange.to) === null || _a === void 0 ? void 0 : _a.name;
let thisTag = tagRange.to;
if (!thisTag) {
(0, utils_1.failOrError)(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError);
return null;
}
else {
this.toTag = thisTag;
core.setOutput('toTag', thisTag);
core.debug(`Resolved 'toTag' as ${thisTag}`);
core.setOutput('toTag', thisTag.name);
core.debug(`Resolved 'toTag' as ${thisTag.name}`);
}
const previousTag = (_b = tagRange.from) === null || _b === void 0 ? void 0 : _b.name;
let previousTag = tagRange.from;
if (previousTag == null) {
(0, utils_1.failOrError)(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError);
return null;
}
this.fromTag = previousTag;
core.setOutput('fromTag', previousTag);
core.debug(`fromTag resolved via previousTag as: ${previousTag}`);
core.setOutput('fromTag', previousTag.name);
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`);
if (this.fetchReleaseInformation) {
// load release information from the GitHub API
core.info(`️ Fetching release information was enabled`);
thisTag = yield tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, thisTag);
previousTag = yield tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, previousTag);
}
else {
core.debug(`️ Fetching release information was disabled`);
}
core.endGroup();
const options = {
owner: this.owner,
repo: this.repo,
fromTag: this.fromTag,
toTag: this.toTag,
fromTag: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
commitMode: this.commitMode,
configuration: this.configuration
};
const releaseNotes = new releaseNotes_1.ReleaseNotes(octokit, options);
return ((yield releaseNotes.pull()) ||
(0, transform_1.fillAdditionalPlaceholders)(this.configuration.empty_template ||
configuration_1.DefaultConfiguration.empty_template, options));
return yield releaseNotes.pull();
});
}
}
@@ -1051,6 +1116,9 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.sortTags = exports.filterTags = exports.Tags = void 0;
const core = __importStar(__nccwpck_require__(2186));
@@ -1059,6 +1127,7 @@ const semver = __importStar(__nccwpck_require__(1383));
const semver_1 = __nccwpck_require__(1383);
const gitHelper_1 = __nccwpck_require__(353);
const transform_1 = __nccwpck_require__(1644);
const moment_1 = __importDefault(__nccwpck_require__(9623));
class Tags {
constructor(octokit) {
this.octokit = octokit;
@@ -1100,6 +1169,35 @@ class Tags {
return tagsInfo;
});
}
fillTagInformation(repositoryPath, owner, repo, tagInfo) {
return __awaiter(this, void 0, void 0, function* () {
const options = this.octokit.repos.getReleaseByTag.endpoint.merge({
owner,
repo,
tag: tagInfo.name
});
try {
const response = yield this.octokit.request(options);
const release = response.data;
tagInfo.date = (0, moment_1.default)(release.created_at);
core.info(`️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API`);
}
catch (error) {
core.info(`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`);
const gitHelper = yield (0, gitHelper_1.createCommandManager)(repositoryPath);
const creationTimeString = yield gitHelper.tagCreation(tagInfo.name);
const creationTime = (0, moment_1.default)(creationTimeString);
if (creationTimeString !== null && creationTime.isValid()) {
tagInfo.date = creationTime;
core.info(`️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}`);
}
else {
core.info(`⚠️ Could not retrieve tag creation time via git cli 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}'`);
}
}
return tagInfo;
});
}
findPredecessorTag(sortedTags, repositoryPath, tag, ignorePreReleases) {
return __awaiter(this, void 0, void 0, function* () {
const tags = sortedTags;
@@ -1364,7 +1462,7 @@ exports.validateTransformer = exports.fillAdditionalPlaceholders = exports.build
const core = __importStar(__nccwpck_require__(2186));
const configuration_1 = __nccwpck_require__(5527);
const pullRequests_1 = __nccwpck_require__(4217);
function buildChangelog(prs, options) {
function buildChangelog(diffInfo, prs, options) {
// sort to target order
const config = options.configuration;
const sort = config.sort || configuration_1.DefaultConfiguration.sort;
@@ -1546,18 +1644,36 @@ function buildChangelog(prs, options) {
transformedChangelog = transformedChangelog.replace(/\${{UNCATEGORIZED_COUNT}}/g, uncategorizedPrs.length.toString());
transformedChangelog = transformedChangelog.replace(/\${{OPEN_COUNT}}/g, openPrs.length.toString());
transformedChangelog = transformedChangelog.replace(/\${{IGNORED_COUNT}}/g, ignoredPrs.length.toString());
// code change placeholders
transformedChangelog = transformedChangelog.replace(/\${{CHANGED_FILES}}/g, diffInfo.changedFiles.toString());
transformedChangelog = transformedChangelog.replace(/\${{ADDITIONS}}/g, diffInfo.additions.toString());
transformedChangelog = transformedChangelog.replace(/\${{DELETIONS}}/g, diffInfo.deletions.toString());
transformedChangelog = transformedChangelog.replace(/\${{CHANGES}}/g, diffInfo.changes.toString());
transformedChangelog = transformedChangelog.replace(/\${{COMMITS}}/g, diffInfo.commits.toString());
transformedChangelog = fillAdditionalPlaceholders(transformedChangelog, options);
core.info(`️ Filled template`);
return transformedChangelog;
}
exports.buildChangelog = buildChangelog;
function fillAdditionalPlaceholders(text, options) {
var _a, _b;
let transformed = text;
// repository placeholders
transformed = transformed.replace(/\${{OWNER}}/g, options.owner);
transformed = transformed.replace(/\${{REPO}}/g, options.repo);
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag);
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag);
transformed = transformed.replace(/\${{RELEASE_DIFF}}/g, `https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag}...${options.toTag}`);
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag.name);
transformed = transformed.replace(/\${{FROM_TAG_DATE}}/g, ((_a = options.fromTag.date) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag.name);
transformed = transformed.replace(/\${{TO_TAG_DATE}}/g, ((_b = options.toTag.date) === null || _b === void 0 ? void 0 : _b.toISOString()) || '');
const fromDate = options.fromTag.date;
const toDate = options.toTag.date;
if (fromDate !== undefined && toDate !== undefined) {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, toDate.diff(fromDate, 'days').toString() || '');
}
else {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, '');
}
transformed = transformed.replace(/\${{RELEASE_DIFF}}/g, `https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`);
return transformed;
}
exports.fillAdditionalPlaceholders = fillAdditionalPlaceholders;
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+438 -40
View File
@@ -17,15 +17,15 @@
"moment": "^2.29.4",
"semver": "^7.3.7",
"tunnel": "^0.0.6",
"webpack": "^5.73.0"
"webpack": "^5.74.0"
},
"devDependencies": {
"@types/jest": "^28.1.6",
"@types/node": "^18.0.6",
"@typescript-eslint/parser": "^5.30.7",
"@types/node": "^18.6.1",
"@typescript-eslint/parser": "^5.31.0",
"@vercel/ncc": "^0.34.0",
"eslint": "^8.20.0",
"eslint-plugin-github": "^4.3.6",
"eslint-plugin-github": "^4.3.7",
"eslint-plugin-jest": "^26.6.0",
"jest": "^28.1.3",
"jest-circus": "^28.1.3",
@@ -592,6 +592,31 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime-corejs3": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz",
"integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==",
"dev": true,
"dependencies": {
"core-js-pure": "^3.20.2",
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz",
@@ -1569,9 +1594,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "18.0.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
"integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
"version": "18.6.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.1.tgz",
"integrity": "sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg=="
},
"node_modules/@types/prettier": {
"version": "2.6.3",
@@ -1639,14 +1664,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.30.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.7.tgz",
"integrity": "sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==",
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.31.0.tgz",
"integrity": "sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.30.7",
"@typescript-eslint/types": "5.30.7",
"@typescript-eslint/typescript-estree": "5.30.7",
"@typescript-eslint/scope-manager": "5.31.0",
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/typescript-estree": "5.31.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1665,6 +1690,80 @@
}
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz",
"integrity": "sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/visitor-keys": "5.31.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.31.0.tgz",
"integrity": "sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz",
"integrity": "sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/visitor-keys": "5.31.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz",
"integrity": "sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.31.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.30.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz",
@@ -2082,6 +2181,19 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
},
"engines": {
"node": ">=6.0"
}
},
"node_modules/array-includes": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
@@ -2128,6 +2240,27 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ast-types-flow": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true
},
"node_modules/axe-core": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz",
"integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
"integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
"dev": true
},
"node_modules/babel-jest": {
"version": "28.1.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz",
@@ -2461,6 +2594,17 @@
"safe-buffer": "~5.1.1"
}
},
"node_modules/core-js-pure": {
"version": "3.24.0",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.0.tgz",
"integrity": "sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA==",
"dev": true,
"hasInstallScript": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -2475,6 +2619,12 @@
"node": ">= 8"
}
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -2869,9 +3019,9 @@
}
},
"node_modules/eslint-plugin-github": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.3.6.tgz",
"integrity": "sha512-W4l9CV1DdSaWYZXWiuH/v0bh3c55iFyyhw2EgIwdAeRxogn7svsI5MLBRA9YZ0cdQyekWMZUfAHEeFhDpOyzWg==",
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.3.7.tgz",
"integrity": "sha512-tYZdXvAEz4JCMrC4NHIUoJTsLUvydCxff5OqB5hgU0vQbLmMkw6VOipN2KNe+T06pEhAWs1KBEwyq9cmMWRe7A==",
"dev": true,
"dependencies": {
"@typescript-eslint/eslint-plugin": "^5.1.0",
@@ -2882,9 +3032,11 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-i18n-text": "^1.0.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jsx-a11y": "^6.6.0",
"eslint-plugin-no-only-tests": "^2.6.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-rule-documentation": ">=1.0.0",
"jsx-ast-utils": "^3.3.2",
"prettier": "^2.2.1",
"svg-element-attributes": "^1.3.1"
},
@@ -2982,6 +3134,48 @@
}
}
},
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz",
"integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.18.9",
"aria-query": "^4.2.2",
"array-includes": "^3.1.5",
"ast-types-flow": "^0.0.7",
"axe-core": "^4.4.3",
"axobject-query": "^2.2.0",
"damerau-levenshtein": "^1.0.8",
"emoji-regex": "^9.2.2",
"has": "^1.0.3",
"jsx-ast-utils": "^3.3.2",
"language-tags": "^1.0.5",
"minimatch": "^3.1.2",
"semver": "^6.3.0"
},
"engines": {
"node": ">=4.0"
},
"peerDependencies": {
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
"node_modules/eslint-plugin-jsx-a11y/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-no-only-tests": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-2.6.0.tgz",
@@ -4619,6 +4813,19 @@
"node": ">=6"
}
},
"node_modules/jsx-ast-utils": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
"integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==",
"dev": true,
"dependencies": {
"array-includes": "^3.1.5",
"object.assign": "^4.1.2"
},
"engines": {
"node": ">=4.0"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -4628,6 +4835,21 @@
"node": ">=6"
}
},
"node_modules/language-subtag-registry": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
"integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
"dev": true
},
"node_modules/language-tags": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
"integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
"dev": true,
"dependencies": {
"language-subtag-registry": "~0.3.2"
}
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -5351,6 +5573,12 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
@@ -6195,20 +6423,20 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/webpack": {
"version": "5.73.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
"integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
"version": "5.74.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
"acorn": "^8.4.1",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.9.3",
"enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -6221,7 +6449,7 @@
"schema-utils": "^3.1.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3",
"watchpack": "^2.3.1",
"watchpack": "^2.4.0",
"webpack-sources": "^3.2.3"
},
"bin": {
@@ -6836,6 +7064,25 @@
"@babel/helper-plugin-utils": "^7.18.6"
}
},
"@babel/runtime": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime-corejs3": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz",
"integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==",
"dev": true,
"requires": {
"core-js-pure": "^3.20.2",
"regenerator-runtime": "^0.13.4"
}
},
"@babel/template": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz",
@@ -7653,9 +7900,9 @@
"dev": true
},
"@types/node": {
"version": "18.0.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
"integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
"version": "18.6.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.1.tgz",
"integrity": "sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg=="
},
"@types/prettier": {
"version": "2.6.3",
@@ -7707,15 +7954,58 @@
}
},
"@typescript-eslint/parser": {
"version": "5.30.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.7.tgz",
"integrity": "sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==",
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.31.0.tgz",
"integrity": "sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.30.7",
"@typescript-eslint/types": "5.30.7",
"@typescript-eslint/typescript-estree": "5.30.7",
"@typescript-eslint/scope-manager": "5.31.0",
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/typescript-estree": "5.31.0",
"debug": "^4.3.4"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz",
"integrity": "sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/visitor-keys": "5.31.0"
}
},
"@typescript-eslint/types": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.31.0.tgz",
"integrity": "sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz",
"integrity": "sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.31.0",
"@typescript-eslint/visitor-keys": "5.31.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz",
"integrity": "sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.31.0",
"eslint-visitor-keys": "^3.3.0"
}
}
}
},
"@typescript-eslint/scope-manager": {
@@ -8032,6 +8322,16 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
}
},
"array-includes": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
@@ -8063,6 +8363,24 @@
"es-shim-unscopables": "^1.0.0"
}
},
"ast-types-flow": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true
},
"axe-core": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz",
"integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==",
"dev": true
},
"axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
"integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
"dev": true
},
"babel-jest": {
"version": "28.1.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz",
@@ -8315,6 +8633,12 @@
"safe-buffer": "~5.1.1"
}
},
"core-js-pure": {
"version": "3.24.0",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.0.tgz",
"integrity": "sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA==",
"dev": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -8326,6 +8650,12 @@
"which": "^2.0.1"
}
},
"damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -8632,9 +8962,9 @@
}
},
"eslint-plugin-github": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.3.6.tgz",
"integrity": "sha512-W4l9CV1DdSaWYZXWiuH/v0bh3c55iFyyhw2EgIwdAeRxogn7svsI5MLBRA9YZ0cdQyekWMZUfAHEeFhDpOyzWg==",
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.3.7.tgz",
"integrity": "sha512-tYZdXvAEz4JCMrC4NHIUoJTsLUvydCxff5OqB5hgU0vQbLmMkw6VOipN2KNe+T06pEhAWs1KBEwyq9cmMWRe7A==",
"dev": true,
"requires": {
"@typescript-eslint/eslint-plugin": "^5.1.0",
@@ -8645,9 +8975,11 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-i18n-text": "^1.0.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jsx-a11y": "^6.6.0",
"eslint-plugin-no-only-tests": "^2.6.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-rule-documentation": ">=1.0.0",
"jsx-ast-utils": "^3.3.2",
"prettier": "^2.2.1",
"svg-element-attributes": "^1.3.1"
}
@@ -8715,6 +9047,41 @@
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-jsx-a11y": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz",
"integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==",
"dev": true,
"requires": {
"@babel/runtime": "^7.18.9",
"aria-query": "^4.2.2",
"array-includes": "^3.1.5",
"ast-types-flow": "^0.0.7",
"axe-core": "^4.4.3",
"axobject-query": "^2.2.0",
"damerau-levenshtein": "^1.0.8",
"emoji-regex": "^9.2.2",
"has": "^1.0.3",
"jsx-ast-utils": "^3.3.2",
"language-tags": "^1.0.5",
"minimatch": "^3.1.2",
"semver": "^6.3.0"
},
"dependencies": {
"emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"eslint-plugin-no-only-tests": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-2.6.0.tgz",
@@ -9909,12 +10276,37 @@
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true
},
"jsx-ast-utils": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
"integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==",
"dev": true,
"requires": {
"array-includes": "^3.1.5",
"object.assign": "^4.1.2"
}
},
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true
},
"language-subtag-registry": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
"integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
"dev": true
},
"language-tags": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
"integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
"dev": true,
"requires": {
"language-subtag-registry": "~0.3.2"
}
},
"leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -10444,6 +10836,12 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
},
"regexp.prototype.flags": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
@@ -11024,20 +11422,20 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"webpack": {
"version": "5.73.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
"integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
"version": "5.74.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
"acorn": "^8.4.1",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.9.3",
"enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -11050,7 +11448,7 @@
"schema-utils": "^3.1.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3",
"watchpack": "^2.3.1",
"watchpack": "^2.4.0",
"webpack-sources": "^3.2.3"
},
"dependencies": {
+4 -4
View File
@@ -40,15 +40,15 @@
"moment": "^2.29.4",
"semver": "^7.3.7",
"tunnel": "^0.0.6",
"webpack": "^5.73.0"
"webpack": "^5.74.0"
},
"devDependencies": {
"@types/jest": "^28.1.6",
"@types/node": "^18.0.6",
"@typescript-eslint/parser": "^5.30.7",
"@types/node": "^18.6.1",
"@typescript-eslint/parser": "^5.31.0",
"@vercel/ncc": "^0.34.0",
"eslint": "^8.20.0",
"eslint-plugin-github": "^4.3.6",
"eslint-plugin-github": "^4.3.7",
"eslint-plugin-jest": "^26.6.0",
"jest": "^28.1.3",
"jest-circus": "^28.1.3",
+56 -19
View File
@@ -2,6 +2,24 @@ import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import moment from 'moment'
export interface DiffInfo {
changedFiles: number
additions: number
deletions: number
changes: number
commits: number
commitInfo: CommitInfo[]
}
export const DefaultDiffInfo: DiffInfo = {
changedFiles: 0,
additions: 0,
deletions: 0,
changes: 0,
commits: 0,
commitInfo: []
}
export interface CommitInfo {
sha: string
summary: string
@@ -18,14 +36,10 @@ export class Commits {
repo: string,
base: string,
head: string
): Promise<CommitInfo[]> {
const commits: CommitInfo[] = await this.getDiffRemote(
owner,
repo,
base,
head
)
return this.sortCommits(commits)
): Promise<DiffInfo> {
const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head)
diff.commitInfo = this.sortCommits(diff.commitInfo)
return diff
}
private async getDiffRemote(
@@ -33,7 +47,13 @@ export class Commits {
repo: string,
base: string,
head: string
): Promise<CommitInfo[]> {
): Promise<DiffInfo> {
let changedFilesCount = 0
let additionCount = 0
let deletionCount = 0
let changeCount = 0
let commitCount = 0
// Fetch comparisons recursively until we don't find any commits
// This is because the GitHub API limits the number of commits returned in a single response.
let commits: RestEndpointMethodTypes['repos']['compareCommits']['response']['data']['commits'] =
@@ -50,6 +70,16 @@ export class Commits {
if (compareResult.data.total_commits === 0) {
break
}
changedFilesCount += compareResult.data.files?.length ?? 0
const files = compareResult.data.files
if (files !== undefined) {
for (const file of files) {
additionCount += file.additions
deletionCount += file.deletions
changeCount += file.changes
}
}
commitCount += compareResult.data.commits.length
commits = compareResult.data.commits.concat(commits)
compareHead = `${commits[0].sha}^`
}
@@ -58,16 +88,23 @@ export class Commits {
`️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`
)
return commits
.filter(commit => commit.sha)
.map(commit => ({
sha: commit.sha || '',
summary: commit.commit.message.split('\n')[0],
message: commit.commit.message,
date: moment(commit.commit.committer?.date),
author: commit.commit.author?.name || '',
prNumber: undefined
}))
return {
changedFiles: changedFilesCount,
additions: additionCount,
deletions: deletionCount,
changes: changeCount,
commits: commitCount,
commitInfo: commits
.filter(commit => commit.sha)
.map(commit => ({
sha: commit.sha || '',
summary: commit.commit.message.split('\n')[0],
message: commit.commit.message,
date: moment(commit.commit.committer?.date),
author: commit.commit.author?.name || '',
prNumber: undefined
}))
}
}
private sortCommits(commits: CommitInfo[]): CommitInfo[] {
+9
View File
@@ -44,6 +44,15 @@ class GitCommandManager {
return revListOutput.stdout.trim()
}
async tagCreation(tagName: string): Promise<string> {
const creationDate = await this.execGit([
'for-each-ref',
'--format="%(creatordate:rfc)"',
`refs/tags/${tagName}`
])
return creationDate.stdout.trim().replace(/"/g, '')
}
static async createCommandManager(
workingDirectory: string
): Promise<GitCommandManager> {
+3
View File
@@ -36,6 +36,8 @@ async function run(): Promise<void> {
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
const failOnError = core.getInput('failOnError') === 'true'
const fetchReviewers = core.getInput('fetchReviewers') === 'true'
const fetchReleaseInformation =
core.getInput('fetchReleaseInformation') === 'true'
const commitMode = core.getInput('commitMode') === 'true'
const result = await new ReleaseNotesBuilder(
@@ -50,6 +52,7 @@ async function run(): Promise<void> {
failOnError,
ignorePreReleases,
fetchReviewers,
fetchReleaseInformation,
commitMode,
configuration
).build()
+57 -26
View File
@@ -1,19 +1,21 @@
import * as core from '@actions/core'
import {CommitInfo, Commits, filterCommits} from './commits'
import {Commits, filterCommits, DiffInfo, DefaultDiffInfo} from './commits'
import {Configuration, DefaultConfiguration} from './configuration'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Octokit} from '@octokit/rest'
import {buildChangelog} from './transform'
import {buildChangelog, fillAdditionalPlaceholders} from './transform'
import {failOrError} from './utils'
import {TagInfo} from './tags'
export interface ReleaseNotesOptions {
owner: string // the owner of the repository
repo: string // the repository
fromTag: string // the tag/ref to start from
toTag: string // the tag/ref up to
fromTag: TagInfo // the tag/ref to start from
toTag: TagInfo // 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
fetchReleaseInformation: boolean // defines if the action should fetch the release information for the from and to tag - e.g. the creation date for the associated release
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`
}
@@ -21,11 +23,15 @@ export interface ReleaseNotesOptions {
export class ReleaseNotes {
constructor(private octokit: Octokit, private options: ReleaseNotesOptions) {}
async pull(): Promise<string | null> {
async pull(): Promise<string> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
if (!this.options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
mergedPullRequests = await this.getMergedPullRequests(this.octokit)
const [info, prs] = await this.getMergedPullRequests(this.octokit)
mergedPullRequests = prs
diffInfo = info
// define the included PRs within this release as output
core.setOutput(
@@ -41,53 +47,74 @@ export class ReleaseNotes {
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
mergedPullRequests = await this.generateCommitPRs(this.octokit)
const [info, prs] = await this.generateCommitPRs(this.octokit)
mergedPullRequests = prs
diffInfo = info
core.endGroup()
}
core.setOutput('changed_files', diffInfo.changedFiles)
core.setOutput('additions', diffInfo.additions)
core.setOutput('deletions', diffInfo.deletions)
core.setOutput('changes', diffInfo.changes)
core.setOutput('commits', diffInfo.commits)
if (mergedPullRequests.length === 0) {
core.warning(`⚠️ No pull requests found`)
return null
return fillAdditionalPlaceholders(
this.options.configuration.empty_template ||
DefaultConfiguration.empty_template,
this.options
)
}
core.startGroup('📦 Build changelog')
const resultChangelog = buildChangelog(mergedPullRequests, this.options)
const resultChangelog = buildChangelog(
diffInfo,
mergedPullRequests,
this.options
)
core.endGroup()
return resultChangelog
}
private async getCommitHistory(octokit: Octokit): Promise<CommitInfo[]> {
private async getCommitHistory(octokit: Octokit): Promise<DiffInfo> {
const {owner, repo, fromTag, toTag, failOnError} = this.options
core.info(`️ Comparing ${owner}/${repo} - '${fromTag}...${toTag}'`)
core.info(
`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`
)
const commitsApi = new Commits(octokit)
let commits: CommitInfo[]
let diffInfo: DiffInfo
try {
commits = await commitsApi.getDiff(owner, repo, fromTag, toTag)
diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name)
} catch (error) {
failOrError(
`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`,
failOnError
)
return []
return DefaultDiffInfo
}
if (commits.length === 0) {
core.warning(`⚠️ No commits found between - ${fromTag}...${toTag}`)
return []
if (diffInfo.commitInfo.length === 0) {
core.warning(
`⚠️ No commits found between - ${fromTag.name}...${toTag.name}`
)
return DefaultDiffInfo
}
return commits
return diffInfo
}
private async getMergedPullRequests(
octokit: Octokit
): Promise<PullRequestInfo[]> {
): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, configuration} =
this.options
const commits = await this.getCommitHistory(octokit)
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return []
return [diffInfo, []]
}
const firstCommit = commits[0]
@@ -191,19 +218,22 @@ export class ReleaseNotes {
)
}
}
} else {
core.debug(`️ Fetching reviewers was disabled`)
}
return finalPrs
return [diffInfo, finalPrs]
}
private async generateCommitPRs(
octokit: Octokit
): Promise<PullRequestInfo[]> {
): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, configuration} = this.options
const commits = await this.getCommitHistory(octokit)
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
if (commits.length === 0) {
return []
return [diffInfo, []]
}
const prCommits = filterCommits(
@@ -214,7 +244,7 @@ export class ReleaseNotes {
core.info(`️ Retrieved ${prCommits.length} commits for ${owner}/${repo}`)
return prCommits.map(function (commit): PullRequestInfo {
const prs = prCommits.map(function (commit): PullRequestInfo {
return {
number: 0,
title: commit.summary,
@@ -234,5 +264,6 @@ export class ReleaseNotes {
status: 'merged'
}
})
return [diffInfo, prs]
}
}
+31 -19
View File
@@ -4,7 +4,6 @@ import {Octokit} from '@octokit/rest'
import {ReleaseNotes} from './releaseNotes'
import {Tags} from './tags'
import {failOrError} from './utils'
import {fillAdditionalPlaceholders} from './transform'
export class ReleaseNotesBuilder {
constructor(
@@ -19,6 +18,7 @@ export class ReleaseNotesBuilder {
private failOnError: boolean,
private ignorePreReleases: boolean,
private fetchReviewers: boolean = false,
private fetchReleaseInformation: boolean = false,
private commitMode: boolean,
private configuration: Configuration
) {}
@@ -62,17 +62,16 @@ export class ReleaseNotesBuilder {
this.configuration.tag_resolver || DefaultConfiguration.tag_resolver
)
const thisTag = tagRange.to?.name
let thisTag = tagRange.to
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
} else {
this.toTag = thisTag
core.setOutput('toTag', thisTag)
core.debug(`Resolved 'toTag' as ${thisTag}`)
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
const previousTag = tagRange.from?.name
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(
`💥 Unable to retrieve previous tag given ${this.toTag}`,
@@ -80,31 +79,44 @@ export class ReleaseNotesBuilder {
)
return null
}
this.fromTag = previousTag
core.setOutput('fromTag', previousTag)
core.debug(`fromTag resolved via previousTag as: ${previousTag}`)
core.setOutput('fromTag', previousTag.name)
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
if (this.fetchReleaseInformation) {
// load release information from the GitHub API
core.info(`️ Fetching release information was enabled`)
thisTag = await tagsApi.fillTagInformation(
this.repositoryPath,
this.owner,
this.repo,
thisTag
)
previousTag = await tagsApi.fillTagInformation(
this.repositoryPath,
this.owner,
this.repo,
previousTag
)
} else {
core.debug(`️ Fetching release information was disabled`)
}
core.endGroup()
const options = {
owner: this.owner,
repo: this.repo,
fromTag: this.fromTag,
toTag: this.toTag,
fromTag: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
commitMode: this.commitMode,
configuration: this.configuration
}
const releaseNotes = new ReleaseNotes(octokit, options)
return (
(await releaseNotes.pull()) ||
fillAdditionalPlaceholders(
this.configuration.empty_template ||
DefaultConfiguration.empty_template,
options
)
)
return await releaseNotes.pull()
}
}
+46 -1
View File
@@ -6,6 +6,7 @@ import {SemVer} from 'semver'
import {TagResolver} from './configuration'
import {createCommandManager} from './gitHelper'
import {RegexTransformer, validateTransformer} from './transform'
import moment from 'moment'
export interface TagResult {
from: TagInfo | null
@@ -14,7 +15,8 @@ export interface TagResult {
export interface TagInfo {
name: string
commit: string
commit?: string
date?: moment.Moment
}
export interface SortableTagInfo extends TagInfo {
@@ -61,6 +63,49 @@ export class Tags {
return tagsInfo
}
async fillTagInformation(
repositoryPath: string,
owner: string,
repo: string,
tagInfo: TagInfo
): Promise<TagInfo> {
const options = this.octokit.repos.getReleaseByTag.endpoint.merge({
owner,
repo,
tag: tagInfo.name
})
try {
const response = await this.octokit.request(options)
type ReleaseInformation =
RestEndpointMethodTypes['repos']['getReleaseByTag']['response']['data']
const release: ReleaseInformation = response.data as ReleaseInformation
tagInfo.date = moment(release.created_at)
core.info(
`️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API`
)
} catch (error) {
core.info(
`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`
)
const gitHelper = await createCommandManager(repositoryPath)
const creationTimeString = await gitHelper.tagCreation(tagInfo.name)
const creationTime = moment(creationTimeString)
if (creationTimeString !== null && creationTime.isValid()) {
tagInfo.date = creationTime
core.info(
`️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}`
)
} else {
core.info(
`⚠️ Could not retrieve tag creation time via git cli 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}'`
)
}
}
return tagInfo
}
async findPredecessorTag(
sortedTags: TagInfo[],
repositoryPath: string,
+45 -3
View File
@@ -7,8 +7,10 @@ import {
} from './configuration'
import {PullRequestInfo, sortPullRequests} from './pullRequests'
import {ReleaseNotesOptions} from './releaseNotes'
import {DiffInfo} from './commits'
export function buildChangelog(
diffInfo: DiffInfo,
prs: PullRequestInfo[],
options: ReleaseNotesOptions
): string {
@@ -272,6 +274,27 @@ export function buildChangelog(
/\${{IGNORED_COUNT}}/g,
ignoredPrs.length.toString()
)
// code change placeholders
transformedChangelog = transformedChangelog.replace(
/\${{CHANGED_FILES}}/g,
diffInfo.changedFiles.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{ADDITIONS}}/g,
diffInfo.additions.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{DELETIONS}}/g,
diffInfo.deletions.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{CHANGES}}/g,
diffInfo.changes.toString()
)
transformedChangelog = transformedChangelog.replace(
/\${{COMMITS}}/g,
diffInfo.commits.toString()
)
transformedChangelog = fillAdditionalPlaceholders(
transformedChangelog,
options
@@ -286,13 +309,32 @@ export function fillAdditionalPlaceholders(
options: ReleaseNotesOptions
): string {
let transformed = text
// repository placeholders
transformed = transformed.replace(/\${{OWNER}}/g, options.owner)
transformed = transformed.replace(/\${{REPO}}/g, options.repo)
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag)
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag)
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag.name)
transformed = transformed.replace(
/\${{FROM_TAG_DATE}}/g,
options.fromTag.date?.toISOString() || ''
)
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag.name)
transformed = transformed.replace(
/\${{TO_TAG_DATE}}/g,
options.toTag.date?.toISOString() || ''
)
const fromDate = options.fromTag.date
const toDate = options.toTag.date
if (fromDate !== undefined && toDate !== undefined) {
transformed = transformed.replace(
/\${{DAYS_SINCE}}/g,
toDate.diff(fromDate, 'days').toString() || ''
)
} else {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, '')
}
transformed = transformed.replace(
/\${{RELEASE_DIFF}}/g,
`https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag}...${options.toTag}`
`https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`
)
return transformed
}