Merge pull request #1278 from mikepenz/develop

dev -> main
This commit is contained in:
Mike Penz
2023-11-05 12:21:11 +01:00
committed by GitHub
39 changed files with 10158 additions and 1115 deletions
+1 -1
View File
@@ -99,4 +99,4 @@ __tests__/runner/*
lib/**/* lib/**/*
lib lib
pr-collector/dist src/pr-collector/dist
+46 -15
View File
@@ -100,7 +100,8 @@ Below is a complete example showcasing how to define a build, which is executed
- Build changelog, given the tag - Build changelog, given the tag
- Create release on GitHub - specifying body with constructed changelog - Create release on GitHub - specifying body with constructed changelog
> Note: Pre v4 PRs will only show up in the changelog if assigned one of the default label categories "feature", "fix" or "test". Starting with v4 these PRs will be in the `Uncategorized` section. > [!NOTE]
> Pre v4 PRs will only show up in the changelog if assigned one of the default label categories "feature", "fix" or "test". Starting with v4 these PRs will be in the `Uncategorized` section.
<details><summary><b>Example</b></summary> <details><summary><b>Example</b></summary>
<p> <p>
@@ -169,15 +170,19 @@ jobs:
### Note ### Note
> **Warning**: When running this action for non tags trigger the `toTag` will be automatically resolved using the latest tag as retrieved by the git API. > [!IMPORTANT]
> When running this action for non tags trigger the `toTag` will be automatically resolved using the latest tag as retrieved by the git API.
> **Note**: The first release tag is a special case since there is no previous release the action can reference to. For this case, there are 2 options: > [!NOTE]
> The first release tag is a special case since there is no previous release the action can reference to. For this case, there are 2 options:
> 1. When checking out the source via `git` (E.g.: `actions/checkout`), the action will use the first commit. > 1. When checking out the source via `git` (E.g.: `actions/checkout`), the action will use the first commit.
> 2. Create a initial tag on the commit you want to begin a changelog from (for example `v0.0.1`). > 2. Create a initial tag on the commit you want to begin a changelog from (for example `v0.0.1`).
> **Note**: By default not specifying `fromTag` or `toTag` will resolve `toTag` from either the `ref` or alternatively fallback to the latest tag from the git API. `fromTag` is resolved by sorting tags using [semver](https://semver.org/). Check the [configuration](#configuration-specification) for alternatives. > [!NOTE]
> By default not specifying `fromTag` or `toTag` will resolve `toTag` from either the `ref` or alternatively fallback to the latest tag from the git API. `fromTag` is resolved by sorting tags using [semver](https://semver.org/). Check the [configuration](#configuration-specification) for alternatives.
> **Note**: If you are behind a corporate HTTP proxy, you can set the `https_proxy` environment variable to the proxy URL. For reference, please see the Octokit [documentation](https://github.com/octokit/octokit.js/#proxy-servers-nodejs-only). > [!NOTE]
> If you are behind a corporate HTTP proxy, you can set the `https_proxy` environment variable to the proxy URL. For reference, please see the Octokit [documentation](https://github.com/octokit/octokit.js/#proxy-servers-nodejs-only).
### Configuration ### Configuration
@@ -192,11 +197,14 @@ The action supports flexible configuration options to modify vast areas of its b
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
``` ```
> **Note** Defaults for the configuration can be found in the [configuration.ts](https://github.com/mikepenz/release-changelog-builder-action/blob/develop/src/configuration.ts) > [!NOTE]
> Defaults for the configuration can be found in the [configuration.ts](https://github.com/mikepenz/release-changelog-builder-action/blob/develop/src/configuration.ts)
> **Warning** It is required to have a `checkout` step prior to the changelog step if `configuration` is used, to allow the action to discover the configuration file. Use `configurationJson` as alternative. > [!WARNING]
> It is required to have a `checkout` step prior to the changelog step if `configuration` is used, to allow the action to discover the configuration file. Use `configurationJson` as alternative.
> **Note** It is possible to provide the configuration as file and as json via the yml file. The order of config values used: `configurationJson` > `configuration` > `DefaultConfiguration`. > [!NOTE]
> It is possible to provide the configuration as file and as json via the yml file. The order of config values used: `configurationJson` > `configuration` > `DefaultConfiguration`.
This configuration is a `JSON` in the following format. (The below showcases *example* configurations for all possible options. In most scenarios most of the settings will not be needed, and the defaults will be appropiate.) This configuration is a `JSON` in the following format. (The below showcases *example* configurations for all possible options. In most scenarios most of the settings will not be needed, and the defaults will be appropiate.)
@@ -293,7 +301,8 @@ This configuration is a `JSON` in the following format. (The below showcases *ex
Any section of the configuration can be omitted to have defaults apply. Any section of the configuration can be omitted to have defaults apply.
> **Warning**: `ignore_labels` take precedence over category labels, allowing to specifically exclude certain PRs. > [!WARNING]
> `ignore_labels` take precedence over category labels, allowing to specifically exclude certain PRs.
Please see the [Configuration Specification](#configuration-specification) for detailed descriptions on the offered configuration options. Please see the [Configuration Specification](#configuration-specification) for detailed descriptions on the offered configuration options.
@@ -316,7 +325,8 @@ For advanced use cases additional settings can be provided to the action
token: ${{ secrets.PAT }} token: ${{ secrets.PAT }}
``` ```
> **Note**: All input values are optional. It is only required to provide the `token` either via the input, or as `env` variable. > [!NOTE]
> All input values are optional. It is only required to provide the `token` either via the input, or as `env` variable.
| **Input** | **Description** | | **Input** | **Description** |
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -342,7 +352,8 @@ For advanced use cases additional settings can be provided to the action
| `exportOnly` | When enabled, will result in only exporting the cache, without genearting a changelog. Default: false (Requires `exportCache` to be enabled) | | `exportOnly` | When enabled, will result in only exporting the cache, without genearting a changelog. Default: false (Requires `exportCache` to be enabled) |
| `cache` | The file path to write/read the cache to/from. | | `cache` | The file path to write/read the cache to/from. |
> **Warning**: `${{ secrets.GITHUB_TOKEN }}` only grants rights to the current repository, for other repositories please use a PAT (Personal Access Token). > [!WARNING]
> `${{ secrets.GITHUB_TOKEN }}` only grants rights to the current repository, for other repositories please use a PAT (Personal Access Token).
### PR Template placeholders ### PR Template placeholders
@@ -365,7 +376,8 @@ Table of supported placeholders allowed to be used in the `pr_template` configur
| `#{{REVIEWERS}}` | GitHub Login names of specified reviewers, joined by `,`. Requires `fetchReviewers` to be enabled. | | `#{{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 `,`. | | `#{{APPROVERS}}` | GitHub Login names of users who approved the PR, joined by `,`. |
> **Note**: `v4` updates the default placeholders format to `#{{}}`. The old format `${{}}` will be supported until v5 for backwards compatiblity. > [!IMPORTANT]
> `v4` updates the default placeholders format to `#{{}}`. The old format `${{}}` will be supported until v5 for backwards compatiblity.
<details><summary><b>Array Placeholders</b></summary> <details><summary><b>Array Placeholders</b></summary>
<p> <p>
@@ -406,7 +418,6 @@ Similar to `REVIEWS`, `REFERENCED` PRs also offer special placeholders.
</details> </details>
### Template placeholders ### Template placeholders
Table of supported placeholders allowed to be used in the `template` and `empty_template` (only supports placeholder marked for empty) configuration, to give additional control on defining the contents of the release notes / changelog. Table of supported placeholders allowed to be used in the `template` and `empty_template` (only supports placeholder marked for empty) configuration, to give additional control on defining the contents of the release notes / changelog.
@@ -482,10 +493,10 @@ Table of descriptions for the `configuration.json` options to configure the resu
| tag_resolver.transformer | Defines a regex transformer used to optionally transform the tag after the filter was applied. Allows to adjust the format to e.g. semver. | | tag_resolver.transformer | Defines a regex transformer used to optionally transform the tag after the filter was applied. Allows to adjust the format to e.g. semver. |
| base_branches | The target branches for the merged PR, ingnores PRs with different target branch. Values can be a `regex`. Default: allow all base branches | | base_branches | The target branches for the merged PR, ingnores PRs with different target branch. Values can be a `regex`. Default: allow all base branches |
| trim_values | Defines if all values inserted in templates are `trimmed`. Default: false | | trim_values | Defines if all values inserted in templates are `trimmed`. Default: false |
### Custom placeholders 🧪
## Experimental 🧪 Starting with v3.2.0 the action provides a feature of defining `CUSTOM_PLACEHOLDERS`.
Starting with v3.2.0 the action provides an experimental feature of defining `CUSTOM_PLACEHOLDERS`.
Custom placeholders allow to extract values from any existing placeholder and insert them into the target template. Custom placeholders allow to extract values from any existing placeholder and insert them into the target template.
<details><summary><b>Example</b></summary> <details><summary><b>Example</b></summary>
@@ -530,6 +541,26 @@ Custom placeholders offer one new feature though. PR related placeholders can be
</p> </p>
</details> </details>
### Gitea support 🧪
Starting with v4.1.0 the action is also compatible with [gitea](https://github.com/go-gitea/gitea).
> [!WARNING]
> The API from gitea does not allow to retrieve the `diff` via its API, requiring the repo to be checked out
The API for gitea is equal to the one from GitHub, however it requires the `platform` to be specified.
```yml
- name: Build Changelog
uses: https://github.com/mikepenz/release-changelog-builder-action@v4.1.0
with:
platform: "gitea" # gitea or github, default is github
commitMode: true
configuration: "configuration.json"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Do not change this
```
## Contribute 🧬 ## Contribute 🧬
```bash ```bash
@@ -0,0 +1,793 @@
import {mergeConfiguration, resolveConfiguration} from '../../src/utils'
import {ReleaseNotesBuilder} from '../../src/releaseNotesBuilder'
import {GiteaRepository} from '../../src/repositories/GiteaRepository'
jest.setTimeout(180000)
/**
* Before starting testing, you should manually clone the repository
* cd /tmp && git clone https://gitea.com/mikepenz/sip
*
* (Forked from: https://gitea.com/jolheiser/sip)
*/
const token = process.env.GITEA_TOKEN || ''
const workingDirectory = '/tmp/sip/'
const owner = 'jolheiser'
const repo = 'sip'
const configurationFile = 'configs/configuration_gitea.json'
it('[Gitea] Verify reviewers who approved are fetched and also release information', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
giteaRepository, // token
workingDirectory, // repoPath
owner, // user
repo, // repo
'v0.5.0', // fromTag
'master', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch via commits
true, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 📦 Uncategorized
- Add attachment removal and change message -- (#36) [merged] ---
- Change to vanity URL -- (#37) [merged] ---
4`
)
})
it('[Gitea] Should match generated changelog (unspecified fromTag)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
null,
'v0.1.1',
false,
false,
false,
true, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🧪 Upgrade
- Clean up and polish
- PR: #1
`)
})
//
it('[Gitea] Should match generated changelog (unspecified tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
null,
null,
false,
false,
false,
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add release attachments
- PR: #26
`)
})
it('[Gitea] Should use empty placeholder', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.1.1',
'v0.3.0',
false,
false,
false,
true, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add Drone and releases
- PR: #2
- Add search filters
- PR: #8
- Add qualifier module
- PR: #10
- Add create repo command
- PR: #13
- Add release support and CSV output
- PR: #16
## 🐛 Fixes
- Fix PR head nil panic
- PR: #7
- Fix Drone release
- PR: #14
`)
})
it('[Gitea] Should fill empty placeholders', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.1.0',
'v0.4.0',
false,
false,
false,
true, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features
- Add create repo command
- PR: #13
- Add release support and CSV output
- PR: #16
- Add open functionality
- PR: #21
## 📦 Uncategorized
- Clean up and polish
- PR: #1
- Add Drone and releases
- PR: #2
- Update Beaver and fix bugs
- PR: #6
- Fix PR head nil panic
- PR: #7
- Add search filters
- PR: #8
- Changelog 0.2.0
- PR: #9
- Add qualifier module
- PR: #10
- Fix Drone release
- PR: #14
- Imp formatting
- PR: #17
- Update Gitea SDK and other modules
- PR: #19
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Clean up and polish
- PR: #1
- Add Drone and releases
- PR: #2
- Update Beaver and fix bugs
- PR: #6
- Fix PR head nil panic
- PR: #7
- Add search filters
- PR: #8
- Changelog 0.2.0
- PR: #9
- Add qualifier module
- PR: #10
- Fix Drone release
- PR: #14
- Imp formatting
- PR: #17
- Update Gitea SDK and other modules
- PR: #19
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
jolheiser
sip
v0.1.0
v0.4.0
https://gitea.com/jolheiser/sip/compare/v0.1.0...v0.4.0
3
12
0
36
1322
274
0
16`
)
})
it('[Gitea] Should fill `template` placeholders', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.1.0',
'v0.4.0',
false,
false,
false,
true, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features
- Add create repo command
- PR: #13
- Add release support and CSV output
- PR: #16
- Add open functionality
- PR: #21
## 📦 Uncategorized
- Clean up and polish
- PR: #1
- Add Drone and releases
- PR: #2
- Update Beaver and fix bugs
- PR: #6
- Fix PR head nil panic
- PR: #7
- Add search filters
- PR: #8
- Changelog 0.2.0
- PR: #9
- Add qualifier module
- PR: #10
- Fix Drone release
- PR: #14
- Imp formatting
- PR: #17
- Update Gitea SDK and other modules
- PR: #19
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Clean up and polish
- PR: #1
- Add Drone and releases
- PR: #2
- Update Beaver and fix bugs
- PR: #6
- Fix PR head nil panic
- PR: #7
- Add search filters
- PR: #8
- Changelog 0.2.0
- PR: #9
- Add qualifier module
- PR: #10
- Fix Drone release
- PR: #14
- Imp formatting
- PR: #17
- Update Gitea SDK and other modules
- PR: #19
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
jolheiser
sip
v0.1.0
v0.4.0
https://gitea.com/jolheiser/sip/compare/v0.1.0...v0.4.0
3
12
0
36
1322
274
0
16`
)
})
it('[Gitea] Should fill `template` placeholders, ignore', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
configuration.categories.pop() // drop `uncategorized` category
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.3.0',
'v0.5.0',
false,
false,
false,
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features
- Add open functionality
- PR: #21
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Update Gitea SDK
- PR: #23
- Update repo info
- PR: #24
- Add release attachments
- PR: #26
- Refactor
- PR: #29
jolheiser
sip
v0.3.0
v0.5.0
https://gitea.com/jolheiser/sip/compare/v0.3.0...v0.5.0
1
6
0
41
748
314
0
8`
)
})
it('[Gitea] Uncategorized category', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_uncategorized_category.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.3.0',
'v0.5.0',
false,
false,
false,
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features
- Add open functionality
- PR: #21
## 📦 Uncategorized
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Update Gitea SDK
- PR: #23
- Update repo info
- PR: #24
- Add release attachments
- PR: #26
- Refactor
- PR: #29
Uncategorized:
- Update modules
- PR: #20
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Update Gitea SDK
- PR: #23
- Update repo info
- PR: #24
- Add release attachments
- PR: #26
- Refactor
- PR: #29
Ignored:
6
0`
)
})
it('[Gitea] Verify commit based changelog', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'v0.3.0',
'v0.5.0',
false,
false,
false,
true, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
true, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 📦 Uncategorized
- Update modules (#20)
- Add open functionality (#21)
- Add changelog for 0.3.0 and 0.4.0 (#22)
- Update Gitea SDK (#23)
- Update repo info (#24)
- Add release attachments (#26)
- Refactor (#29)
- Changelog 0.5.0 (#30)
Uncategorized:
- Update modules (#20)
- Add open functionality (#21)
- Add changelog for 0.3.0 and 0.4.0 (#22)
- Update Gitea SDK (#23)
- Update repo info (#24)
- Add release attachments (#26)
- Refactor (#29)
- Changelog 0.5.0 (#30)
Ignored:
8
0`
)
})
it('[Gitea] Verify commit based changelog', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null,
giteaRepository,
workingDirectory,
owner,
repo,
'3e49adf6047960a03f53346bbececb3ce7e0809b',
'894a641ef86c444dccfa55eca457186b5e10da95',
false,
false,
false,
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
true, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(
`## 🚀 Features
- Add release attachments (#26)
- PR: #0
`
)
})
// no open prs
it('[Gitea] Verify default inclusion of open PRs', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_including_open.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
giteaRepository, // token
workingDirectory, // repoPath
owner, // user
repo, // repo
'v0.5.0', // fromTag
'master', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`
Uncategorized
- Add attachment removal and change message (#36) merged
- Change to vanity URL (#37) merged
Open
`)
})
it('[Gitea] Verify custom categorisation of open PRs', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_excluding_open.json'))
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
giteaRepository, // token
workingDirectory, // repoPath
owner, // user
repo, // repo
'v0.5.0', // fromTag
'master', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch via commits
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(``)
})
it('[Gitea] Fetch release information', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}'
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
giteaRepository, // token
workingDirectory, // repoPath
owner, // user
repo, // repo
'v0.5.0', // fromTag
'master', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch via commits
false, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`v0.5.0-2020-09-17T16:34:40.000Z
master-2020-09-21T19:30:21.000Z
4`)
})
it('[Gitea] Fetch release information for non existing tag / release', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}'
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl
giteaRepository, // token
workingDirectory, // repoPath
owner, // user
repo, // repo
'v0.5.0', // fromTag
'master', // toTag
true, // includeOpen
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch via commits
false, // enable to fetch reviewers
true, // enable to fetch tag release information
false, // enable to fetch reviews
false, // enable commitMode
false, // enable exportCache
false, // enable exportOnly
null, // path to the cache
configuration // configuration
)
const changeLog = await releaseNotesBuilder.build()
console.log(changeLog)
expect(changeLog).toStrictEqual(`v0.5.0-2020-09-17T16:34:40.000Z
master-2020-09-21T19:30:21.000Z
4`)
})
@@ -0,0 +1,349 @@
import {checkExportedData, mergeConfiguration, resolveConfiguration} from '../../src/utils'
import {buildChangelog} from '../../src/transform'
import {pullData} from '../../src/pr-collector/prCollector'
import {GiteaRepository} from '../../src/repositories/GiteaRepository'
jest.setTimeout(180000)
// load octokit instance
const enablePullData = false
/**
* if false -> use cache for data
* Use the below snippet to export the cache:
*
* writeCacheData({
* mergedPullRequests: data.mergedPullRequests,
* diffInfo: data.diffInfo,
* options
* }, 'caches/gitea_rcba_0.1.0-master_cache.json')
*/
/**
* Before starting testing, you should manually clone the repository
* cd /tmp && git clone https://gitea.com/mikepenz/sip
*
* (Forked from: https://gitea.com/jolheiser/sip)
*/
const token = process.env.GITEA_TOKEN || ''
const workingDirectory = '/tmp/sip/'
const owner = 'jolheiser'
const repo = 'sip'
const giteaRepository = new GiteaRepository(token, undefined, workingDirectory)
const configurationFile = 'configs/configuration_gitea.json'
it('[Gitea] Should have changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const options = {
owner: owner,
repo: repo,
fromTag: {name: 'v0.5.0'},
toTag: {name: 'master'},
includeOpen: false,
failOnError: false,
fetchViaCommits: true,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_0.5.0-master_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add attachment removal and change message
- PR: #36
## 🐛 Fixes
- Fix drone-gitea-main
- PR: #38
`)
})
it('[Gitea] Should match generated changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
const options = {
owner: owner,
repo: repo,
fromTag: {name: 'v0.5.0'},
toTag: {name: 'master'},
includeOpen: false,
failOnError: false,
fetchViaCommits: true,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_0.5.0-master_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add attachment removal and change message
- PR: #36
## 🐛 Fixes
- Fix drone-gitea-main
- PR: #38
`)
})
it('[Gitea] Should match generated changelog (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_all_placeholders.json'))
const options = {
owner: owner,
repo: repo,
fromTag: {name: '3e49adf6047960a03f53346bbececb3ce7e0809b'},
toTag: {name: '894a641ef86c444dccfa55eca457186b5e10da95'},
includeOpen: false,
failOnError: false,
fetchViaCommits: true,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_3e49adf-894a64_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 📦 Uncategorized
Add release attachments
26
https://gitea.com/jolheiser/sip/pulls/26
2020-09-16T18:07:32.000Z
jolheiser
enhancement
0.5.0
Resolves #25
Refactor
29
https://gitea.com/jolheiser/sip/pulls/29
2020-09-17T16:25:11.000Z
jolheiser
enhancement
0.5.0
Fixes #27
Fixes #28
This PR refactors and cleans up packages.
It also scans CLI flags into variables for fewer footguns.
`)
})
it('[Gitea] Should match generated changelog and replace all occurrences (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_replace_all_placeholders.json'))
const options = {
owner: owner,
repo: repo,
fromTag: {name: '3e49adf6047960a03f53346bbececb3ce7e0809b'},
toTag: {name: '894a641ef86c444dccfa55eca457186b5e10da95'},
includeOpen: false,
failOnError: false,
fetchViaCommits: true,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_3e49adf-894a64_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 📦 Uncategorized
Add release attachments
Add release attachments
26
https://gitea.com/jolheiser/sip/pulls/26
2020-09-16T18:07:32.000Z
jolheiser
jolheiser
enhancement
0.5.0
Resolves #25
Refactor
Refactor
29
https://gitea.com/jolheiser/sip/pulls/29
2020-09-17T16:25:11.000Z
jolheiser
jolheiser
enhancement
0.5.0
Fixes #27
Fixes #28
This PR refactors and cleans up packages.
It also scans CLI flags into variables for fewer footguns.
`)
})
it('[Gitea] Should match ordered ASC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', configurationFile))
configuration.categories.pop() // drop `uncategorized` category
const options = {
owner: owner,
repo: repo,
fromTag: {name: 'v0.1.0'},
toTag: {name: 'master'},
includeOpen: false,
failOnError: false,
fetchViaCommits: false,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_0.1.0-master_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add Drone and releases
- PR: #2
- Add search filters
- PR: #8
- Add qualifier module
- PR: #10
- Add create repo command
- PR: #13
- Add release support and CSV output
- PR: #16
- Add open functionality
- PR: #21
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Add release attachments
- PR: #26
- Add attachment removal and change message
- PR: #36
## 🐛 Fixes
- Fix PR head nil panic
- PR: #7
- Fix Drone release
- PR: #14
`)
})
it('[Gitea] Should match ordered DESC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_gitea_desc.json'))
configuration.categories.pop() // drop `uncategorized` category
const options = {
owner: owner,
repo: repo,
fromTag: {name: 'v0.1.0'},
toTag: {name: 'master'},
includeOpen: false,
failOnError: false,
fetchViaCommits: false,
fetchReviewers: false,
fetchReleaseInformation: false,
fetchReviews: false,
commitMode: false,
configuration,
repositoryUtils: giteaRepository
}
let data: any
if (enablePullData) {
data = await pullData(giteaRepository, options)
} else {
data = checkExportedData(false, 'caches/gitea_rcba_0.1.0-master_cache.json')
}
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, options)
console.log(changeLog)
expect(changeLog).toStrictEqual(`## 🚀 Features
- Add attachment removal and change message
- PR: #36
- Add release attachments
- PR: #26
- Add changelog for 0.3.0 and 0.4.0
- PR: #22
- Add open functionality
- PR: #21
- Add release support and CSV output
- PR: #16
- Add create repo command
- PR: #13
- Add qualifier module
- PR: #10
- Add search filters
- PR: #8
- Add Drone and releases
- PR: #2
## 🐛 Fixes
- Fix Drone release
- PR: #14
- Fix PR head nil panic
- PR: #7
`)
})
/*
* I delete these test cases about gitea
* Should match ordered by title ASC
* Should match ordered by title DESC
* Should ignore PRs not merged into develop branch
*/
+8 -4
View File
@@ -6,17 +6,19 @@ import * as fs from 'fs'
jest.setTimeout(180000) jest.setTimeout(180000)
test('missing values should result in failure', () => { test('missing values should result in failure', () => {
expect.assertions(1)
process.env['GITHUB_WORKSPACE'] = '.' process.env['GITHUB_WORKSPACE'] = '.'
process.env['INPUT_CONFIGURATION'] = 'configuration.json' process.env['INPUT_OWNER'] = undefined
process.env['INPUT_CONFIGURATION'] = 'configs/configuration.json'
const ip = path.join(__dirname, '..', 'lib', 'main.js') const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecSyncOptions = { const options: cp.ExecSyncOptions = {
env: process.env env: process.env
} }
try { try {
cp.execSync(`node ${ip}`, options).toString() cp.execSync(`node ${ip}`, options).toString()
fail('Should not succeed, because values miss') } catch (error: any) {
} catch (error) { expect(true).toBe(true)
console.log(`correctly failed due to: ${error}`)
} }
}) })
@@ -27,6 +29,7 @@ test('complete input should succeed', () => {
process.env['INPUT_REPO'] = 'release-changelog-builder-action' process.env['INPUT_REPO'] = 'release-changelog-builder-action'
process.env['INPUT_FROMTAG'] = 'v0.3.0' process.env['INPUT_FROMTAG'] = 'v0.3.0'
process.env['INPUT_TOTAG'] = 'v0.5.0' process.env['INPUT_TOTAG'] = 'v0.5.0'
process.env['INPUT_CACHE'] = 'caches/rcba_0.3.0-0.5.0_cache.json'
const ip = path.join(__dirname, '..', 'lib', 'main.js') const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecSyncOptions = { const options: cp.ExecSyncOptions = {
@@ -45,6 +48,7 @@ test('should write result to file', () => {
process.env['INPUT_FROMTAG'] = 'v0.3.0' process.env['INPUT_FROMTAG'] = 'v0.3.0'
process.env['INPUT_TOTAG'] = 'v0.5.0' process.env['INPUT_TOTAG'] = 'v0.5.0'
process.env['INPUT_OUTPUTFILE'] = 'test.md' process.env['INPUT_OUTPUTFILE'] = 'test.md'
process.env['INPUT_CACHE'] = 'caches/rcba_0.3.0-0.5.0_cache.json'
const ip = path.join(__dirname, '..', 'lib', 'main.js') const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecSyncOptions = { const options: cp.ExecSyncOptions = {
+31 -28
View File
@@ -1,13 +1,16 @@
import {mergeConfiguration, resolveConfiguration} from '../src/utils' import {mergeConfiguration, resolveConfiguration} from '../src/utils'
import {ReleaseNotesBuilder} from '../src/releaseNotesBuilder' import {ReleaseNotesBuilder} from '../src/releaseNotesBuilder'
import {GithubRepository} from '../src/repositories/GithubRepository'
jest.setTimeout(180000) jest.setTimeout(180000)
it('Should match generated changelog (unspecified fromTag)', async () => { const token = process.env.GITHUB_TOKEN || ''
const githubRepository = new GithubRepository(token, undefined, '.')
it('[Github] Should match generated changelog (unspecified fromTag)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -37,11 +40,11 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
`) `)
}) })
it('Should match generated changelog (unspecified tags)', async () => { it('[Github] Should match generated changelog (unspecified tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'action-junit-report-legacy', 'action-junit-report-legacy',
@@ -66,11 +69,11 @@ it('Should match generated changelog (unspecified tags)', async () => {
expect(changeLog).toStrictEqual(`## 🐛 Fixes\n\n- Stacktrace Data can be an array\n - PR: #39\n\n`) expect(changeLog).toStrictEqual(`## 🐛 Fixes\n\n- Stacktrace Data can be an array\n - PR: #39\n\n`)
}) })
it('Should use empty placeholder', async () => { it('[Github] Should use empty placeholder', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -95,11 +98,11 @@ it('Should use empty placeholder', async () => {
expect(changeLog).toStrictEqual(`- no changes`) expect(changeLog).toStrictEqual(`- no changes`)
}) })
it('Should fill empty placeholders', async () => { it('[Github] Should fill empty placeholders', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -126,11 +129,11 @@ it('Should fill empty placeholders', async () => {
) )
}) })
it('Should fill `template` placeholders', async () => { it('[Github] Should fill `template` placeholders', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -157,12 +160,12 @@ it('Should fill `template` placeholders', async () => {
) )
}) })
it('Should fill `template` placeholders, ignore', async () => { it('[Github] Should fill `template` placeholders, ignore', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_empty_all_placeholders.json'))
configuration.categories.pop() // drop `uncategorized` category configuration.categories.pop() // drop `uncategorized` category
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -189,11 +192,11 @@ it('Should fill `template` placeholders, ignore', async () => {
) )
}) })
it('Uncategorized category', async () => { it('[Github] Uncategorized category', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_uncategorized_category.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_uncategorized_category.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -220,11 +223,11 @@ it('Uncategorized category', async () => {
) )
}) })
it('Verify commit based changelog', async () => { it('[Github] Verify commit based changelog', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'mikepenz', 'mikepenz',
'release-changelog-builder-action', 'release-changelog-builder-action',
@@ -251,11 +254,11 @@ it('Verify commit based changelog', async () => {
) )
}) })
it('Verify commit based changelog, with emoji categorisation', async () => { it('[Github] Verify commit based changelog, with emoji categorisation', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits_emoji.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_commits_emoji.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, null,
null, githubRepository,
'.', '.',
'theapache64', 'theapache64',
'stackzy', 'stackzy',
@@ -282,11 +285,11 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
) )
}) })
it('Verify default inclusion of open PRs', async () => { it('[Github] Verify default inclusion of open PRs', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_including_open.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_including_open.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl null, // baseUrl
null, // token githubRepository, // token
'.', // repoPath '.', // repoPath
'mikepenz', // user 'mikepenz', // user
'release-changelog-builder-action-playground', // repo 'release-changelog-builder-action-playground', // repo
@@ -313,11 +316,11 @@ it('Verify default inclusion of open PRs', async () => {
) )
}) })
it('Verify custom categorisation of open PRs', async () => { it('[Github] Verify custom categorisation of open PRs', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_excluding_open.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_excluding_open.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl null, // baseUrl
null, // token githubRepository, // token
'.', // repoPath '.', // repoPath
'mikepenz', // user 'mikepenz', // user
'release-changelog-builder-action-playground', // repo 'release-changelog-builder-action-playground', // repo
@@ -344,11 +347,11 @@ it('Verify custom categorisation of open PRs', async () => {
) )
}) })
it('Verify reviewers who approved are fetched and also release information', async () => { it('[Github] Verify reviewers who approved are fetched and also release information', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl null, // baseUrl
null, // token githubRepository, // token
'.', // repoPath '.', // repoPath
'mikepenz', // user 'mikepenz', // user
'release-changelog-builder-action-playground', // repo 'release-changelog-builder-action-playground', // repo
@@ -375,12 +378,12 @@ it('Verify reviewers who approved are fetched and also release information', asy
) )
}) })
it('Fetch release information', async () => { it('[Github] Fetch release information', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}' configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}'
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl null, // baseUrl
null, // token githubRepository, // token
'.', // repoPath '.', // repoPath
'mikepenz', // user 'mikepenz', // user
'release-changelog-builder-action-playground', // repo 'release-changelog-builder-action-playground', // repo
@@ -405,12 +408,12 @@ it('Fetch release information', async () => {
expect(changeLog).toStrictEqual(`2.0.0-2022-04-08T07:52:40.000Z\n3.0.0-a01-2022-07-26T14:28:36.000Z\n109`) 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 () => { it('[Github] Fetch release information for non existing tag / release', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_approvers.json'))
configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}' configuration.template = '#{{FROM_TAG}}-#{{FROM_TAG_DATE}}\n#{{TO_TAG}}-#{{TO_TAG_DATE}}\n#{{DAYS_SINCE}}'
const releaseNotesBuilder = new ReleaseNotesBuilder( const releaseNotesBuilder = new ReleaseNotesBuilder(
null, // baseUrl null, // baseUrl
null, // token githubRepository, // token
'.', // repoPath '.', // repoPath
'mikepenz', // user 'mikepenz', // user
'release-changelog-builder-action-playground', // repo 'release-changelog-builder-action-playground', // repo
+33 -25
View File
@@ -1,17 +1,15 @@
import {checkExportedData, mergeConfiguration, resolveConfiguration} from '../src/utils' import {checkExportedData, mergeConfiguration, resolveConfiguration} from '../src/utils'
import {Octokit} from '@octokit/rest'
import {buildChangelog} from '../src/transform' import {buildChangelog} from '../src/transform'
import {pullData} from '../src/pr-collector/prCollector' import {pullData} from '../src/pr-collector/prCollector'
import {Data} from '../src/releaseNotesBuilder' import {GithubRepository} from '../src/repositories/GithubRepository'
jest.setTimeout(180000) jest.setTimeout(180000)
// load octokit instance // load octokit instance
const enablePullData = false // if false -> use cache for data const enablePullData = false // if false -> use cache for data
const octokit = new Octokit({
auth: `token ${process.env.GITHUB_TOKEN}`
})
const token = process.env.GITHUB_TOKEN || ''
const githubRepository = new GithubRepository(token, undefined, '.')
it('Should have empty changelog (tags)', async () => { it('Should have empty changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
@@ -27,11 +25,12 @@ it('Should have empty changelog (tags)', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.0.2-0.0.3_cache.json') data = checkExportedData(false, 'caches/rcba_0.0.2-0.0.3_cache.json')
} }
@@ -54,11 +53,12 @@ it('Should match generated changelog (tags)', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.0.1-0.0.3_cache.json') data = checkExportedData(false, 'caches/rcba_0.0.1-0.0.3_cache.json')
} }
@@ -87,11 +87,12 @@ it('Should match generated changelog (refs)', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_5ec7a2-fa3788_cache.json') data = checkExportedData(false, 'caches/rcba_5ec7a2-fa3788_cache.json')
} }
@@ -127,11 +128,12 @@ it('Should match generated changelog and replace all occurrences (refs)', async
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_5ec7a2-fa3788_cache.json') data = checkExportedData(false, 'caches/rcba_5ec7a2-fa3788_cache.json')
} }
@@ -170,11 +172,12 @@ it('Should match ordered ASC', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json') data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json')
} }
@@ -198,11 +201,12 @@ it('Should match ordered DESC', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json') data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json')
} }
@@ -225,11 +229,12 @@ it('Should match ordered by title ASC', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json') data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json')
} }
@@ -254,11 +259,12 @@ it('Should match ordered by title DESC', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json') data = checkExportedData(false, 'caches/rcba_0.3.0-0.5.0_cache.json')
} }
@@ -283,11 +289,12 @@ it('Should ignore PRs not merged into develop branch', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_1.3.1-1.4.0_base_develop_cache.json') data = checkExportedData(false, 'caches/rcba_1.3.1-1.4.0_base_develop_cache.json')
} }
@@ -310,11 +317,12 @@ it('Should ignore PRs not merged into main branch', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration configuration,
repositoryUtils: githubRepository
} }
let data: any let data: any
if (enablePullData) { if (enablePullData) {
data = await pullData(octokit, options) data = await pullData(githubRepository, options)
} else { } else {
data = checkExportedData(false, 'caches/rcba_1.3.1-1.4.0_base_main_cache.json') data = checkExportedData(false, 'caches/rcba_1.3.1-1.4.0_base_main_cache.json')
} }
+1 -1
View File
@@ -1,4 +1,4 @@
import {TagInfo, filterTags, prepareAndSortTags} from '../src/pr-collector/tags' import {filterTags, prepareAndSortTags, TagInfo} from '../src/pr-collector/tags'
jest.setTimeout(180000) jest.setTimeout(180000)
+8 -3
View File
@@ -3,6 +3,7 @@ import moment from 'moment'
import {Configuration, DefaultConfiguration} from '../src/configuration' import {Configuration, DefaultConfiguration} from '../src/configuration'
import {PullRequestInfo} from '../src/pr-collector/pullRequests' import {PullRequestInfo} from '../src/pr-collector/pullRequests'
import {DefaultDiffInfo} from '../src/pr-collector/commits' import {DefaultDiffInfo} from '../src/pr-collector/commits'
import {GithubRepository} from '../src/repositories/GithubRepository'
jest.setTimeout(180000) jest.setTimeout(180000)
@@ -437,6 +438,7 @@ it('Use empty_content for empty category', async () => {
) )
}) })
const repositoryUtils = new GithubRepository(process.env.GITEA_TOKEN || '', undefined, '.')
it('Commit SHA-1 in commitMode', async () => { it('Commit SHA-1 in commitMode', async () => {
const customConfig = Object.assign({}, DefaultConfiguration) const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.sort = 'DESC' customConfig.sort = 'DESC'
@@ -453,7 +455,8 @@ it('Commit SHA-1 in commitMode', async () => {
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: true, commitMode: true,
configuration: customConfig configuration: customConfig,
repositoryUtils: repositoryUtils
}) })
expect(resultChangelog).toStrictEqual(`## 🚀 Features\n\nsha1-3\nsha1-1\n\n## 🐛 Fixes\n\nsha1-3\nsha1-2\n\n`) expect(resultChangelog).toStrictEqual(`## 🚀 Features\n\nsha1-3\nsha1-1\n\n## 🐛 Fixes\n\nsha1-3\nsha1-2\n\n`)
@@ -474,7 +477,8 @@ it('Release Diff', async () => {
fetchReleaseInformation: true, fetchReleaseInformation: true,
fetchReviews: false, fetchReviews: false,
commitMode: true, commitMode: true,
configuration: customConfig configuration: customConfig,
repositoryUtils: repositoryUtils
}) })
expect(resultChangelog).toStrictEqual(`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`) expect(resultChangelog).toStrictEqual(`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`)
@@ -678,6 +682,7 @@ function buildChangelogTest(config: Configuration, prs: PullRequestInfo[]): stri
fetchReleaseInformation: false, fetchReleaseInformation: false,
fetchReviews: false, fetchReviews: false,
commitMode: false, commitMode: false,
configuration: config configuration: config,
repositoryUtils: repositoryUtils
}) })
} }
+3
View File
@@ -58,6 +58,9 @@ inputs:
default: "false" default: "false"
cache: cache:
description: 'Provide the cache of a previous run. Allows to re-use collected information multiple times to generate different release notes. Requires `exportCache` to be enabled for the previous run.' description: 'Provide the cache of a previous run. Allows to re-use collected information multiple times to generate different release notes. Requires `exportCache` to be enabled for the previous run.'
platform:
description: 'Defines the platform the action is run on. Available options: [`github`, `gitea`]. Defaults to `github`.'
default: "github"
outputs: outputs:
changelog: changelog:
description: The built release changelog built from the merged pull requests description: The built release changelog built from the merged pull requests
+708
View File
@@ -0,0 +1,708 @@
{
"mergedPullRequests": [
{
"number": 1,
"title": "Clean up and polish",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/1",
"baseBranch": "master",
"branch": "20df4b7f69f8dc609e98e3274739b49dc756884c",
"mergedAt": "2020-02-18T05:27:54.000Z",
"mergeCommitSha": "427ecdb7f1f4e289cb820ce2fbafe158b9b30012",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.1.1",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-02-18T05:27:13.000Z",
"status": "merged"
},
{
"number": 2,
"title": "Add Drone and releases",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/2",
"baseBranch": "master",
"branch": "427ecdb7f1f4e289cb820ce2fbafe158b9b30012",
"mergedAt": "2020-02-18T15:51:14.000Z",
"mergeCommitSha": "5929bebe01e7b8c7c57b3c8fdfd41d4038bb85a1",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "0.2.0",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-02-18T15:41:58.000Z",
"status": "merged"
},
{
"number": 6,
"title": "Update Beaver and fix bugs",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/6",
"baseBranch": "master",
"branch": "5929bebe01e7b8c7c57b3c8fdfd41d4038bb85a1",
"mergedAt": "2020-02-27T02:53:14.000Z",
"mergeCommitSha": "bb773780df3488204df93228bdd72eb59418029b",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"bug"
],
"milestone": "0.2.0",
"body": "Fixes #3 \nFixes #4 \nFixes #5",
"approvedReviewers": [],
"createdAt": "2020-02-27T02:45:46.000Z",
"status": "merged"
},
{
"number": 7,
"title": "Fix PR head nil panic",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/7",
"baseBranch": "master",
"branch": "bb773780df3488204df93228bdd72eb59418029b",
"mergedAt": "2020-02-27T03:03:34.000Z",
"mergeCommitSha": "1956062791dae3e66c0c11d6fa86c59b031e51ee",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"bug"
],
"milestone": "0.2.0",
"body": "When checking PR status, retrieved PRs may have a nil head if the branch has been deleted.",
"approvedReviewers": [],
"createdAt": "2020-02-27T03:01:09.000Z",
"status": "merged"
},
{
"number": 8,
"title": "Add search filters",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/8",
"baseBranch": "master",
"branch": "1956062791dae3e66c0c11d6fa86c59b031e51ee",
"mergedAt": "2020-02-27T04:58:04.000Z",
"mergeCommitSha": "df58f223f25edd30ce8d15328d9d99fc9f98954b",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.2.0",
"body": "Adds search filters for state, author, labels, and milestone\n\nCheck the updated README for more details.",
"approvedReviewers": [],
"createdAt": "2020-02-27T04:39:37.000Z",
"status": "merged"
},
{
"number": 9,
"title": "Changelog 0.2.0",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/9",
"baseBranch": "master",
"branch": "df58f223f25edd30ce8d15328d9d99fc9f98954b",
"mergedAt": "2020-02-27T05:20:31.000Z",
"mergeCommitSha": "222dcf402b1cc0b57dbd4e2e9bc34196410c8a6b",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"docs"
],
"milestone": "0.2.0",
"body": "As title",
"approvedReviewers": [],
"createdAt": "2020-02-27T05:17:31.000Z",
"status": "merged"
},
{
"number": 10,
"title": "Add qualifier module",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/10",
"baseBranch": "master",
"branch": "222dcf402b1cc0b57dbd4e2e9bc34196410c8a6b",
"mergedAt": "2020-03-06T04:18:38.000Z",
"mergeCommitSha": "4490cc9888b4e39e14ce1c8fe9bdf64de348de71",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.2.1",
"body": "Better support for search query qualifiers",
"approvedReviewers": [],
"createdAt": "2020-02-28T03:33:43.000Z",
"status": "merged"
},
{
"number": 13,
"title": "Add create repo command",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/13",
"baseBranch": "master",
"branch": "4490cc9888b4e39e14ce1c8fe9bdf64de348de71",
"mergedAt": "2020-03-06T04:21:58.000Z",
"mergeCommitSha": "03fb319d3989c68a39c0652d8ff972e0957c5e6c",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"feature"
],
"milestone": "0.3.0",
"body": "Resolves #12",
"approvedReviewers": [],
"createdAt": "2020-03-06T04:17:46.000Z",
"status": "merged"
},
{
"number": 14,
"title": "Fix Drone release",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/14",
"baseBranch": "master",
"branch": "03fb319d3989c68a39c0652d8ff972e0957c5e6c",
"mergedAt": "2020-03-31T20:17:02.000Z",
"mergeCommitSha": "5ea204fb61cdb30501f8a6686b7132473f743b69",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "0.2.1",
"body": "\r\nSigned-off-by: jolheiser <john.olheiser@gmail.com>",
"approvedReviewers": [],
"createdAt": "2020-03-31T20:11:46.000Z",
"status": "merged"
},
{
"number": 16,
"title": "Add release support and CSV output",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/16",
"baseBranch": "master",
"branch": "5ea204fb61cdb30501f8a6686b7132473f743b69",
"mergedAt": "2020-04-17T03:59:15.000Z",
"mergeCommitSha": "0a6162ad05900394d381eef9ec05704b3941c5c4",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"feature"
],
"milestone": "0.3.0",
"body": "Resolves #15\r\n\r\nThis PR adds support for listing and creating releases.\r\n\r\nIt also adds a `--csv <file>` flag to issue/PR/release searches, which will instead dump CSV-formatting information to the specified file.",
"approvedReviewers": [],
"createdAt": "2020-04-17T03:54:27.000Z",
"status": "merged"
},
{
"number": 17,
"title": "Imp formatting",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/17",
"baseBranch": "master",
"branch": "0a6162ad05900394d381eef9ec05704b3941c5c4",
"mergedAt": "2020-05-07T01:09:33.000Z",
"mergeCommitSha": "448e12ce38128909c32f30b29dc560ac44d1d7a5",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"docs"
],
"milestone": "0.3.0",
"body": "This PR is the result of running [imp](https://gitea.com/jolheiser/imp) against the repo.",
"approvedReviewers": [],
"createdAt": "2020-05-07T01:07:19.000Z",
"status": "merged"
},
{
"number": 19,
"title": "Update Gitea SDK and other modules",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/19",
"baseBranch": "master",
"branch": "448e12ce38128909c32f30b29dc560ac44d1d7a5",
"mergedAt": "2020-07-15T18:15:49.000Z",
"mergeCommitSha": "e4303d76389f45d9d7a5da29b504200038c46eb4",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.3.0",
"body": "As title",
"approvedReviewers": [],
"createdAt": "2020-05-28T03:21:56.000Z",
"status": "merged"
},
{
"number": 20,
"title": "Update modules",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/20",
"baseBranch": "master",
"branch": "e4303d76389f45d9d7a5da29b504200038c46eb4",
"mergedAt": "2020-09-13T03:59:45.000Z",
"mergeCommitSha": "dac69ffbd020f83a39b8fadcf3dcb928ed0826f1",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "0.4.0",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-09-13T03:36:59.000Z",
"status": "merged"
},
{
"number": 21,
"title": "Add open functionality",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/21",
"baseBranch": "master",
"branch": "dac69ffbd020f83a39b8fadcf3dcb928ed0826f1",
"mergedAt": "2020-09-13T04:34:13.000Z",
"mergeCommitSha": "b4d7ff5775e913e3681c6c2f1009e37591a060cf",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"feature"
],
"milestone": "0.4.0",
"body": "Closes #11\n\nThis PR adds functionality to open a repo/issue/PR\n\nTo open the current repo `sip open`\n\nTo open an issue `sip open 1234`\n\nTo open a different repo `sip open jolheiser/vanity`",
"approvedReviewers": [],
"createdAt": "2020-09-13T04:23:10.000Z",
"status": "merged"
},
{
"number": 22,
"title": "Add changelog for 0.3.0 and 0.4.0",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/22",
"baseBranch": "master",
"branch": "b4d7ff5775e913e3681c6c2f1009e37591a060cf",
"mergedAt": "2020-09-13T04:42:13.000Z",
"mergeCommitSha": "40e256e8b9da14692f348ec10db17f773569c66f",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"docs",
"skip-changelog"
],
"milestone": "0.4.0",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-09-13T04:41:50.000Z",
"status": "merged"
},
{
"number": 23,
"title": "Update Gitea SDK",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/23",
"baseBranch": "master",
"branch": "40e256e8b9da14692f348ec10db17f773569c66f",
"mergedAt": "2020-09-15T18:02:46.000Z",
"mergeCommitSha": "8db0c08253f7e30ef86a8ba67169ea4d3e4cf72b",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "0.5.0",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-09-15T17:57:59.000Z",
"status": "merged"
},
{
"number": 24,
"title": "Update repo info",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/24",
"baseBranch": "master",
"branch": "8db0c08253f7e30ef86a8ba67169ea4d3e4cf72b",
"mergedAt": "2020-09-16T03:54:50.000Z",
"mergeCommitSha": "3e49adf6047960a03f53346bbececb3ce7e0809b",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.0",
"body": "Now that the SDK supports forks, watchers, stars, etc.\n\nThis PR updates the `sip repo` information",
"approvedReviewers": [],
"createdAt": "2020-09-16T03:48:01.000Z",
"status": "merged"
},
{
"number": 26,
"title": "Add release attachments",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/26",
"baseBranch": "master",
"branch": "3e49adf6047960a03f53346bbececb3ce7e0809b",
"mergedAt": "2020-09-16T18:07:32.000Z",
"mergeCommitSha": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.0",
"body": "Resolves #25",
"approvedReviewers": [],
"createdAt": "2020-09-16T17:57:57.000Z",
"status": "merged"
},
{
"number": 29,
"title": "Refactor",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/29",
"baseBranch": "master",
"branch": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"mergedAt": "2020-09-17T16:25:11.000Z",
"mergeCommitSha": "894a641ef86c444dccfa55eca457186b5e10da95",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.0",
"body": "Fixes #27\n\nFixes #28\n\nThis PR refactors and cleans up packages.\n\nIt also scans CLI flags into variables for fewer footguns.",
"approvedReviewers": [],
"createdAt": "2020-09-17T04:11:50.000Z",
"status": "merged"
},
{
"number": 30,
"title": "Changelog 0.5.0",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/30",
"baseBranch": "master",
"branch": "894a641ef86c444dccfa55eca457186b5e10da95",
"mergedAt": "2020-09-17T16:34:42.000Z",
"mergeCommitSha": "cce2360f0fcec4f63c66e5b05bac25a7ba42f942",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"docs"
],
"milestone": "0.5.0",
"body": "## [0.5.0](https://gitea.com/jolheiser/sip/pulls?q=&type=all&state=closed&milestone=1311) - 2020-09-17\r\n\r\n<details><summary>ENHANCEMENTS</summary>\r\n\r\n* Refactor (#29)\r\n* Add release attachments (#26)\r\n* Update repo info (#24)\r\n</details>\r\n<details><summary>BUILD</summary>\r\n\r\n* Update Gitea SDK (#23)\r\n</details>\r\n",
"approvedReviewers": [],
"createdAt": "2020-09-17T16:34:05.000Z",
"status": "merged"
},
{
"number": 36,
"title": "Add attachment removal and change message",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/36",
"baseBranch": "master",
"branch": "cce2360f0fcec4f63c66e5b05bac25a7ba42f942",
"mergedAt": "2020-09-21T14:19:38.000Z",
"mergeCommitSha": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.1",
"body": "Fixes #35\r\n\r\nChanges the message to instead tell the user how many files were added/removed from the release.\r\n\r\nThis PR also adds a way to multi-select attachments to be removed from a release via `sip release attach remove` since `sip release remove` will be reserved for removing a release in the future.",
"approvedReviewers": [],
"createdAt": "2020-09-21T03:59:49.000Z",
"status": "merged"
},
{
"number": 37,
"title": "Change to vanity URL",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/37",
"baseBranch": "master",
"branch": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"mergedAt": "2020-09-21T19:21:09.000Z",
"mergeCommitSha": "5ecbcde5180e4a3b12bdc35416c2f050cd0808f1",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-09-21T04:07:33.000Z",
"status": "merged"
}
],
"diffInfo": {
"changedFiles": 43,
"additions": 1942,
"deletions": 418,
"changes": 0,
"commits": 24,
"commitInfo": [
{
"sha": "427ecdb7f1f4e289cb820ce2fbafe158b9b30012",
"summary": "Clean up and polish (#1)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-18T05:27:52.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-18T05:27:52.000Z"
},
{
"sha": "5929bebe01e7b8c7c57b3c8fdfd41d4038bb85a1",
"summary": "Add Drone and releases (#2)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-18T15:51:10.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-18T15:51:10.000Z"
},
{
"sha": "bb773780df3488204df93228bdd72eb59418029b",
"summary": "Update Beaver and fix bugs (#6)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-27T02:53:11.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-27T02:53:11.000Z"
},
{
"sha": "1956062791dae3e66c0c11d6fa86c59b031e51ee",
"summary": "Fix PR head nil panic (#7)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-27T03:03:32.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-27T03:03:32.000Z"
},
{
"sha": "df58f223f25edd30ce8d15328d9d99fc9f98954b",
"summary": "Add search filters (#8)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-27T04:58:02.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-27T04:58:02.000Z"
},
{
"sha": "222dcf402b1cc0b57dbd4e2e9bc34196410c8a6b",
"summary": "Changelog 0.2.0 (#9)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-02-27T05:20:29.000Z",
"committer": "John Olheiser",
"commitDate": "2020-02-27T05:20:29.000Z"
},
{
"sha": "4490cc9888b4e39e14ce1c8fe9bdf64de348de71",
"summary": "Add qualifier module (#10)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-03-06T04:18:36.000Z",
"committer": "John Olheiser",
"commitDate": "2020-03-06T04:18:36.000Z"
},
{
"sha": "03fb319d3989c68a39c0652d8ff972e0957c5e6c",
"summary": "Add create repo command (#13)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-03-06T04:21:56.000Z",
"committer": "John Olheiser",
"commitDate": "2020-03-06T04:21:56.000Z"
},
{
"sha": "64b0335e0ae45a4bd264bbab008551b7820f8811",
"summary": "Fix Drone release",
"message": "",
"author": "jolheiser",
"authorDate": "2020-03-31T20:10:18.000Z",
"committer": "jolheiser",
"commitDate": "2020-03-31T20:10:18.000Z"
},
{
"sha": "5ea204fb61cdb30501f8a6686b7132473f743b69",
"summary": "Merge pull request 'Fix Drone release' (#14) from drone-release into master",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-03-31T20:17:01.000Z",
"committer": "John Olheiser",
"commitDate": "2020-03-31T20:17:01.000Z"
},
{
"sha": "0a6162ad05900394d381eef9ec05704b3941c5c4",
"summary": "Add release support and CSV output (#16)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-04-17T03:59:12.000Z",
"committer": "John Olheiser",
"commitDate": "2020-04-17T03:59:12.000Z"
},
{
"sha": "448e12ce38128909c32f30b29dc560ac44d1d7a5",
"summary": "Imp formatting (#17)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-05-07T01:09:31.000Z",
"committer": "John Olheiser",
"commitDate": "2020-05-07T01:09:31.000Z"
},
{
"sha": "e4303d76389f45d9d7a5da29b504200038c46eb4",
"summary": "Update Gitea SDK and other modules (#19)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-07-15T18:15:46.000Z",
"committer": "John Olheiser",
"commitDate": "2020-07-15T18:15:46.000Z"
},
{
"sha": "dac69ffbd020f83a39b8fadcf3dcb928ed0826f1",
"summary": "Update modules (#20)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-13T03:59:43.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-13T03:59:43.000Z"
},
{
"sha": "b4d7ff5775e913e3681c6c2f1009e37591a060cf",
"summary": "Add open functionality (#21)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-13T04:34:11.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-13T04:34:11.000Z"
},
{
"sha": "40e256e8b9da14692f348ec10db17f773569c66f",
"summary": "Add changelog for 0.3.0 and 0.4.0 (#22)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-13T04:42:11.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-13T04:42:11.000Z"
},
{
"sha": "8db0c08253f7e30ef86a8ba67169ea4d3e4cf72b",
"summary": "Update Gitea SDK (#23)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-15T18:02:45.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-15T18:02:45.000Z"
},
{
"sha": "3e49adf6047960a03f53346bbececb3ce7e0809b",
"summary": "Update repo info (#24)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-16T03:54:48.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-16T03:54:48.000Z"
},
{
"sha": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"summary": "Add release attachments (#26)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-16T18:07:30.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-16T18:07:30.000Z"
},
{
"sha": "894a641ef86c444dccfa55eca457186b5e10da95",
"summary": "Refactor (#29)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-17T16:25:09.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-17T16:25:09.000Z"
},
{
"sha": "cce2360f0fcec4f63c66e5b05bac25a7ba42f942",
"summary": "Changelog 0.5.0 (#30)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-17T16:34:40.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-17T16:34:40.000Z"
},
{
"sha": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"summary": "Add attachment removal and change message (#36)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T14:19:36.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T14:19:36.000Z"
},
{
"sha": "5ecbcde5180e4a3b12bdc35416c2f050cd0808f1",
"summary": "Change to vanity URL (#37)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T19:21:07.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T19:21:07.000Z"
},
{
"sha": "1742fcc37c35faca189eae173cd803e9623a8b80",
"summary": "Fix drone-gitea-main (#38)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T19:30:21.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T19:30:21.000Z"
}
]
},
"options": {
"owner": "jolheiser",
"repo": "sip",
"fromTag": {
"name": "v0.1.0"
},
"toTag": {
"name": "master"
},
"includeOpen": false,
"failOnError": false,
"fetchViaCommits": false,
"fetchReviewers": false,
"fetchReleaseInformation": false,
"fetchReviews": false,
"commitMode": false,
"configuration": {
"max_tags_to_fetch": 200,
"max_pull_requests": 1000,
"max_back_track_time_days": 1000,
"exclude_merge_branches": [],
"sort": "ASC",
"template": "#{{CHANGELOG}}",
"pr_template": "- #{{TITLE}}\n - PR: ##{{NUMBER}}",
"empty_template": "- no changes",
"categories": [
{
"title": "## 🚀 Features",
"labels": [
"add",
"Add"
]
},
{
"title": "## 🐛 Fixes",
"labels": [
"fix",
"Fix"
]
}
],
"ignore_labels": [
"ignore"
],
"label_extractor": [
{
"pattern": "(\\w+) (.+)",
"target": "$1",
"on_property": "title"
}
],
"transformers": [],
"tag_resolver": {
"method": "semver"
},
"base_branches": [],
"custom_placeholders": [],
"trim_values": false
}
}
}
+167
View File
@@ -0,0 +1,167 @@
{
"mergedPullRequests": [
{
"number": 36,
"title": "Add attachment removal and change message",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/36",
"baseBranch": "master",
"branch": "cce2360f0fcec4f63c66e5b05bac25a7ba42f942",
"mergedAt": "2020-09-21T14:19:38.000Z",
"mergeCommitSha": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.1",
"body": "Fixes #35\r\n\r\nChanges the message to instead tell the user how many files were added/removed from the release.\r\n\r\nThis PR also adds a way to multi-select attachments to be removed from a release via `sip release attach remove` since `sip release remove` will be reserved for removing a release in the future.",
"approvedReviewers": [],
"createdAt": "2020-09-21T03:59:49.000Z",
"status": "merged"
},
{
"number": 37,
"title": "Change to vanity URL",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/37",
"baseBranch": "master",
"branch": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"mergedAt": "2020-09-21T19:21:09.000Z",
"mergeCommitSha": "5ecbcde5180e4a3b12bdc35416c2f050cd0808f1",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"build"
],
"milestone": "",
"body": "",
"approvedReviewers": [],
"createdAt": "2020-09-21T04:07:33.000Z",
"status": "merged"
},
{
"number": 38,
"title": "Fix drone-gitea-main",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/38",
"baseBranch": "master",
"branch": "5ecbcde5180e4a3b12bdc35416c2f050cd0808f1",
"mergedAt": "2020-09-21T19:30:23.000Z",
"mergeCommitSha": "1742fcc37c35faca189eae173cd803e9623a8b80",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"bug",
"build",
"skip-changelog"
],
"milestone": "0.5.1",
"body": "No `:1` tag exists for the image, only `latest`.",
"approvedReviewers": [],
"createdAt": "2020-09-21T19:30:06.000Z",
"status": "merged"
}
],
"diffInfo": {
"changedFiles": 24,
"additions": 160,
"deletions": 41,
"changes": 0,
"commits": 3,
"commitInfo": [
{
"sha": "0bbe2e35ff5b482bae10ecf7abcd89815274c217",
"summary": "Add attachment removal and change message (#36)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T14:19:36.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T14:19:36.000Z"
},
{
"sha": "5ecbcde5180e4a3b12bdc35416c2f050cd0808f1",
"summary": "Change to vanity URL (#37)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T19:21:07.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T19:21:07.000Z"
},
{
"sha": "1742fcc37c35faca189eae173cd803e9623a8b80",
"summary": "Fix drone-gitea-main (#38)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-21T19:30:21.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-21T19:30:21.000Z"
}
]
},
"options": {
"owner": "jolheiser",
"repo": "sip",
"fromTag": {
"name": "v0.5.0"
},
"toTag": {
"name": "master"
},
"includeOpen": false,
"failOnError": false,
"fetchViaCommits": true,
"fetchReviewers": false,
"fetchReleaseInformation": false,
"fetchReviews": false,
"commitMode": false,
"configuration": {
"max_tags_to_fetch": 200,
"max_pull_requests": 1000,
"max_back_track_time_days": 1000,
"exclude_merge_branches": [],
"sort": "ASC",
"template": "#{{CHANGELOG}}",
"pr_template": "- #{{TITLE}}\n - PR: ##{{NUMBER}}",
"empty_template": "- no changes",
"categories": [
{
"title": "## 🚀 Features",
"labels": [
"add",
"Add"
]
},
{
"title": "## 🐛 Fixes",
"labels": [
"fix",
"Fix"
]
},
{
"title": "## 🧪 Upgrade",
"labels": [
"upgrade",
"Upgrade",
"Clean"
]
}
],
"ignore_labels": [
"ignore"
],
"label_extractor": [
{
"pattern": "(\\w+) (.+)",
"target": "$1",
"on_property": "title"
}
],
"transformers": [],
"tag_resolver": {
"method": "semver"
},
"base_branches": [],
"custom_placeholders": [],
"trim_values": false
}
}
}
+134
View File
@@ -0,0 +1,134 @@
{
"mergedPullRequests": [
{
"number": 26,
"title": "Add release attachments",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/26",
"baseBranch": "master",
"branch": "3e49adf6047960a03f53346bbececb3ce7e0809b",
"mergedAt": "2020-09-16T18:07:32.000Z",
"mergeCommitSha": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.0",
"body": "Resolves #25",
"approvedReviewers": [],
"createdAt": "2020-09-16T17:57:57.000Z",
"status": "merged"
},
{
"number": 29,
"title": "Refactor",
"htmlURL": "https://gitea.com/jolheiser/sip/pulls/29",
"baseBranch": "master",
"branch": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"mergedAt": "2020-09-17T16:25:11.000Z",
"mergeCommitSha": "894a641ef86c444dccfa55eca457186b5e10da95",
"author": "jolheiser",
"repoName": "jolheiser/sip",
"labels": [
"enhancement"
],
"milestone": "0.5.0",
"body": "Fixes #27\n\nFixes #28\n\nThis PR refactors and cleans up packages.\n\nIt also scans CLI flags into variables for fewer footguns.",
"approvedReviewers": [],
"createdAt": "2020-09-17T04:11:50.000Z",
"status": "merged"
}
],
"diffInfo": {
"changedFiles": 36,
"additions": 576,
"deletions": 205,
"changes": 0,
"commits": 2,
"commitInfo": [
{
"sha": "a51305f8162b0a5ec80387db0b5ecc8a89e209be",
"summary": "Add release attachments (#26)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-16T18:07:30.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-16T18:07:30.000Z"
},
{
"sha": "894a641ef86c444dccfa55eca457186b5e10da95",
"summary": "Refactor (#29)",
"message": "",
"author": "John Olheiser",
"authorDate": "2020-09-17T16:25:09.000Z",
"committer": "John Olheiser",
"commitDate": "2020-09-17T16:25:09.000Z"
}
]
},
"options": {
"owner": "jolheiser",
"repo": "sip",
"fromTag": {
"name": "3e49adf6047960a03f53346bbececb3ce7e0809b"
},
"toTag": {
"name": "894a641ef86c444dccfa55eca457186b5e10da95"
},
"includeOpen": false,
"failOnError": false,
"fetchViaCommits": true,
"fetchReviewers": false,
"fetchReleaseInformation": false,
"fetchReviews": false,
"commitMode": false,
"configuration": {
"max_tags_to_fetch": 200,
"max_pull_requests": 1000,
"max_back_track_time_days": 1000,
"exclude_merge_branches": [],
"sort": {
"order": "ASC",
"on_property": "mergedAt"
},
"template": "#{{CHANGELOG}}",
"pr_template": "#{{TITLE}}\n#{{NUMBER}}\n#{{URL}}\n#{{MERGED_AT}}\n#{{AUTHOR}}\n#{{LABELS}}\n#{{MILESTONE}}\n#{{BODY}}\n#{{ASSIGNEES}}\n#{{REVIEWERS}}",
"empty_template": "- no changes",
"categories": [
{
"title": "## 🚀 Features",
"labels": [
"feature"
]
},
{
"title": "## 🐛 Fixes",
"labels": [
"fix"
]
},
{
"title": "## 🧪 Tests",
"labels": [
"test"
]
},
{
"title": "## 📦 Uncategorized",
"labels": []
}
],
"ignore_labels": [
"ignore"
],
"label_extractor": [],
"transformers": [],
"tag_resolver": {
"method": "semver"
},
"base_branches": [],
"custom_placeholders": [],
"trim_values": false
}
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"categories": [
{
"title": "## 🚀 Features",
"labels": ["add","Add"]
},
{
"title": "## 🐛 Fixes",
"labels": ["fix","Fix"]
},
{
"title": "## 🧪 Upgrade",
"labels": ["upgrade","Upgrade","Clean"]
}
],
"label_extractor": [
{
"pattern": "(\\w+) (.+)",
"target": "$1",
"on_property": "title"
}
],
"sort": "ASC",
"template": "${{CHANGELOG}}",
"pr_template": "- ${{TITLE}}\n - PR: #${{NUMBER}}",
"empty_template": "- no changes",
"max_pull_requests": 1000,
"max_back_track_time_days": 1000
}
@@ -0,0 +1,29 @@
{
"categories": [
{
"title": "## 🚀 Features",
"labels": ["add","Add"]
},
{
"title": "## 🐛 Fixes",
"labels": ["fix","Fix"]
},
{
"title": "## 🧪 Upgrade",
"labels": ["upgrade","Upgrade","Clean"]
}
],
"label_extractor": [
{
"pattern": "(\\w+) (.+)",
"target": "$1",
"on_property": "title"
}
],
"sort": "DESC",
"template": "${{CHANGELOG}}",
"pr_template": "- ${{TITLE}}\n - PR: #${{NUMBER}}",
"empty_template": "- no changes",
"max_pull_requests": 1000,
"max_back_track_time_days": 1000
}
Generated Vendored
+6672 -481
View File
File diff suppressed because it is too large Load Diff
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
Generated Vendored
+25
View File
@@ -553,6 +553,31 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
gitea-js
MIT
MIT License
Copyright (c) 2022 Anbraten
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
has-flag has-flag
MIT MIT
MIT License MIT License
+413 -216
View File
File diff suppressed because it is too large Load Diff
+13 -9
View File
@@ -1,17 +1,20 @@
{ {
"name": "release-changelog-builder-action", "name": "release-changelog-builder-action",
"version": "v4.0.0", "version": "v4.1.0",
"private": true, "private": true,
"description": "A GitHub action that builds your release notes / changelog fast, easy and exactly the way you want.", "description": "A GitHub action that builds your release notes / changelog fast, easy and exactly the way you want.",
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"format": "prettier --write **/*.ts", "format": "prettier --write **/*.ts **/**/*.ts",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --check **/*.ts",
"format-fix": "eslint --fix src/**/*.ts",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"package": "ncc build --source-map --license licenses.txt", "package": "ncc build --source-map --license licenses.txt",
"test": "jest", "test": "jest",
"all": "npm run build && npm run format && npm run lint && npm run package && npm test" "test-github": "jest __tests__/*.test.ts",
"test-gitea": "jest __tests__/gitea/*.test.ts",
"all": "npm run build && npm run format && npm run lint && npm run package && npm run test-github"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -36,20 +39,21 @@
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0", "@actions/github": "^6.0.0",
"@octokit/rest": "^20.0.2", "@octokit/rest": "^20.0.2",
"gitea-js": "^1.20.1",
"https-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.2",
"moment": "^2.29.4", "moment": "^2.29.4",
"semver": "^7.5.4" "semver": "^7.5.4"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.6", "@types/jest": "^29.5.7",
"@types/node": "^20.8.7", "@types/node": "^20.8.10",
"@types/semver": "^7.5.4", "@types/semver": "^7.5.4",
"@typescript-eslint/eslint-plugin": "^6.8.0", "@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.8.0", "@typescript-eslint/parser": "^6.9.1",
"@vercel/ncc": "^0.38.1", "@vercel/ncc": "^0.38.1",
"eslint": "^8.51.0", "eslint": "^8.53.0",
"eslint-plugin-github": "^4.10.1", "eslint-plugin-github": "^4.10.1",
"eslint-plugin-jest": "^27.4.2", "eslint-plugin-jest": "^27.6.0",
"eslint-plugin-prettier": "^5.0.1", "eslint-plugin-prettier": "^5.0.1",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-circus": "^29.7.0", "jest-circus": "^29.7.0",
+1 -1
View File
@@ -1,4 +1,4 @@
import {Rule, Extractor, Regex, Transformer, Sort, PullConfiguration} from './pr-collector/types' import {Extractor, PullConfiguration, Regex, Rule, Sort, Transformer} from './pr-collector/types'
export interface Configuration extends PullConfiguration { export interface Configuration extends PullConfiguration {
max_tags_to_fetch: number max_tags_to_fetch: number
+20 -3
View File
@@ -3,13 +3,29 @@ import * as github from '@actions/github'
import {mergeConfiguration, parseConfiguration, resolveConfiguration, retrieveRepositoryPath, writeOutput} from './utils' import {mergeConfiguration, parseConfiguration, resolveConfiguration, retrieveRepositoryPath, writeOutput} from './utils'
import {ReleaseNotesBuilder} from './releaseNotesBuilder' import {ReleaseNotesBuilder} from './releaseNotesBuilder'
import {Configuration} from './configuration' import {Configuration} from './configuration'
import {GithubRepository} from './repositories/GithubRepository'
import {GiteaRepository} from './repositories/GiteaRepository'
async function run(): Promise<void> { async function run(): Promise<void> {
core.setOutput('failed', false) // mark the action not failed by default const supportedPlatform = {
github: GithubRepository,
gitea: GiteaRepository
}
function isSupportedPlatform(type: string): type is keyof typeof supportedPlatform {
return type in supportedPlatform
}
core.setOutput('failed', false) // mark the action not failed by default
core.startGroup(`📘 Reading input values`) core.startGroup(`📘 Reading input values`)
try { try {
// read in path specification, resolve github workspace, and repo path // read in path specification, resolve github workspace, and repo path
const platform = core.getInput('platform') || 'github'
if (!isSupportedPlatform(platform)) {
core.setFailed(`The ${platform} platform is not supported. `)
return
}
const inputPath = core.getInput('path') const inputPath = core.getInput('path')
const repositoryPath = retrieveRepositoryPath(inputPath) const repositoryPath = retrieveRepositoryPath(inputPath)
@@ -40,7 +56,7 @@ async function run(): Promise<void> {
// read in repository inputs // read in repository inputs
const baseUrl = core.getInput('baseUrl') const baseUrl = core.getInput('baseUrl')
const token = core.getInput('token') const token = core.getInput('token') || process.env.GITHUB_TOKEN || ''
const owner = core.getInput('owner') || github.context.repo.owner const owner = core.getInput('owner') || github.context.repo.owner
const repo = core.getInput('repo') || github.context.repo.repo const repo = core.getInput('repo') || github.context.repo.repo
// read in from, to tag inputs // read in from, to tag inputs
@@ -59,9 +75,10 @@ async function run(): Promise<void> {
const exportOnly = core.getInput('exportOnly') === 'true' const exportOnly = core.getInput('exportOnly') === 'true'
const cache = core.getInput('cache') const cache = core.getInput('cache')
const repositoryUtils = new supportedPlatform[platform](token, baseUrl, repositoryPath)
const result = await new ReleaseNotesBuilder( const result = await new ReleaseNotesBuilder(
baseUrl, baseUrl,
token, repositoryUtils,
repositoryPath, repositoryPath,
owner, owner,
repo, repo,
-1
View File
@@ -1 +0,0 @@
../pr-collector/src/
+4 -59
View File
@@ -1,9 +1,9 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import moment from 'moment' import moment from 'moment'
import {failOrError} from './utils' import {failOrError} from './utils'
import {PullRequestInfo} from './pullRequests' import {PullRequestInfo} from './pullRequests'
import {Options} from './prCollector' import {Options} from './prCollector'
import {BaseRepository} from '../repositories/BaseRepository'
export interface DiffInfo { export interface DiffInfo {
changedFiles: number changedFiles: number
@@ -34,7 +34,7 @@ export interface CommitInfo {
} }
export class Commits { export class Commits {
constructor(private octokit: Octokit) {} constructor(private repositoryUtils: BaseRepository) {}
async getDiff(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> { async getDiff(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> {
const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head) const diff: DiffInfo = await this.getDiffRemote(owner, repo, base, head)
@@ -43,62 +43,7 @@ export class Commits {
} }
private async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> { private async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> {
let changedFilesCount = 0 return this.repositoryUtils.getDiffRemote(owner, repo, base, head)
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'] = []
let compareHead = head
// eslint-disable-next-line no-constant-condition
while (true) {
const compareResult = await this.octokit.repos.compareCommits({
owner,
repo,
base,
head: compareHead
})
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}^`
}
core.info(`️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`)
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,
author: commit.author?.login || '',
authorDate: moment(commit.commit.author?.date),
committer: commit.committer?.login || '',
commitDate: moment(commit.commit.committer?.date),
prNumber: undefined
}))
}
} }
private sortCommits(commits: CommitInfo[]): CommitInfo[] { private sortCommits(commits: CommitInfo[]): CommitInfo[] {
@@ -129,7 +74,7 @@ export class Commits {
const {owner, repo, fromTag, toTag, failOnError} = options const {owner, repo, fromTag, toTag, failOnError} = options
core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`) core.info(`️ Comparing ${owner}/${repo} - '${fromTag.name}...${toTag.name}'`)
const commitsApi = new Commits(this.octokit) const commitsApi = new Commits(this.repositoryUtils)
let diffInfo: DiffInfo let diffInfo: DiffInfo
try { try {
diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name) diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name)
@@ -39,7 +39,7 @@ class GitCommandManager {
return result return result
} }
private async execGit(args: string[], allowAllExitCodes = false, silent = false): Promise<GitOutput> { async execGit(args: string[], allowAllExitCodes = false, silent = false): Promise<GitOutput> {
directoryExistsSync(this.workingDirectory, true) directoryExistsSync(this.workingDirectory, true)
const result = new GitOutput() const result = new GitOutput()
@@ -1,11 +1,10 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {PullConfiguration} from './types' import {PullConfiguration} from './types'
import {Octokit} from '@octokit/rest'
import {TagInfo, Tags} from './tags' import {TagInfo, Tags} from './tags'
import {failOrError} from './utils' import {failOrError} from './utils'
import {HttpsProxyAgent} from 'https-proxy-agent'
import {PullRequestInfo, PullRequests} from './pullRequests' import {PullRequestInfo, PullRequests} from './pullRequests'
import {Commits, DiffInfo} from './commits' import {Commits, DiffInfo} from './commits'
import {BaseRepository} from '../repositories/BaseRepository'
export interface Options { export interface Options {
owner: string // the owner of the repository owner: string // the owner of the repository
@@ -32,7 +31,7 @@ export interface Data {
export class PullRequestCollector { export class PullRequestCollector {
constructor( constructor(
private baseUrl: string | null, private baseUrl: string | null,
private token: string | null, private repositoryUtils: BaseRepository,
private repositoryPath: string, private repositoryPath: string,
private owner: string, private owner: string,
private repo: string, private repo: string,
@@ -51,32 +50,10 @@ export class PullRequestCollector {
async build(): Promise<Data | null> { async build(): Promise<Data | null> {
// check proxy setup for GHES environments // check proxy setup for GHES environments
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY
const noProxy = process.env.no_proxy || process.env.NO_PROXY
let noProxyArray: string[] = []
if (noProxy) {
noProxyArray = noProxy.split(',')
}
// load octokit instance
const octokit = new Octokit({
auth: `token ${this.token || process.env.GITHUB_TOKEN}`,
baseUrl: `${this.baseUrl || 'https://api.github.com'}`
})
if (proxy) {
const agent = new HttpsProxyAgent(proxy)
octokit.hook.before('request', options => {
if (noProxyArray.includes(options.request.hostname)) {
return
}
options.request.agent = agent
})
}
// ensure proper from <-> to tag range // ensure proper from <-> to tag range
core.startGroup(`🔖 Resolve tags`) core.startGroup(`🔖 Resolve tags`)
const tagsApi = new Tags(octokit) const tagsApi = new Tags(this.repositoryUtils)
const tagRange = await tagsApi.retrieveRange( const tagRange = await tagsApi.retrieveRange(
this.repositoryPath, this.repositoryPath,
this.owner, this.owner,
@@ -114,7 +91,7 @@ export class PullRequestCollector {
core.endGroup() core.endGroup()
return await pullData(octokit, { return await pullData(this.repositoryUtils, {
owner: this.owner, owner: this.owner,
repo: this.repo, repo: this.repo,
fromTag: previousTag, fromTag: previousTag,
@@ -131,14 +108,14 @@ export class PullRequestCollector {
} }
} }
export async function pullData(octokit: Octokit, options: Options): Promise<Data | null> { export async function pullData(repositoryUtils: BaseRepository, options: Options): Promise<Data | null> {
let mergedPullRequests: PullRequestInfo[] let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo let diffInfo: DiffInfo
const commitsApi = new Commits(octokit) const commitsApi = new Commits(repositoryUtils)
if (!options.commitMode) { if (!options.commitMode) {
core.startGroup(`🚀 Load pull requests`) core.startGroup(`🚀 Load pull requests`)
const pullRequestsApi = new PullRequests(octokit, commitsApi) const pullRequestsApi = new PullRequests(repositoryUtils, commitsApi)
const [info, prs] = await pullRequestsApi.getMergedPullRequests(options) const [info, prs] = await pullRequestsApi.getMergedPullRequests(options)
mergedPullRequests = prs mergedPullRequests = prs
diffInfo = info diffInfo = info
+10 -164
View File
@@ -1,10 +1,10 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest' import {RestEndpointMethodTypes} from '@octokit/rest'
import {Unpacked} from './utils'
import moment from 'moment' import moment from 'moment'
import {Property, Sort} from './types' import {Property, Sort} from './types'
import {Commits, DiffInfo, filterCommits} from './commits' import {Commits, DiffInfo, filterCommits} from './commits'
import {Options} from './prCollector' import {Options} from './prCollector'
import {BaseRepository} from '../repositories/BaseRepository'
export interface PullRequestInfo { export interface PullRequestInfo {
number: number number: number
@@ -64,53 +64,20 @@ export const EMPTY_COMMENT_INFO: CommentInfo = {
state: undefined state: undefined
} }
type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data'] export type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data']
type PullsListData = RestEndpointMethodTypes['pulls']['list']['response']['data'] export type PullsListData = RestEndpointMethodTypes['pulls']['list']['response']['data']
type PullReviewsData = RestEndpointMethodTypes['pulls']['listReviews']['response']['data'] export type PullReviewsData = RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
export class PullRequests { export class PullRequests {
constructor( constructor(
private octokit: Octokit, private repositoryUtils: BaseRepository,
private commits: Commits private commits: Commits
) {} ) {}
async getSingle(owner: string, repo: string, prNumber: number): Promise<PullRequestInfo | null> {
try {
const {data} = await this.octokit.pulls.get({
owner,
repo,
pull_number: prNumber
})
return mapPullRequest(data)
} catch (e: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) {
core.warning(`⚠️ Cannot find PR ${owner}/${repo}#${prNumber} - ${e.message}`)
return null
}
}
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> { async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = [] return sortPrs(await this.repositoryUtils.getForCommitHash(owner, repo, commit_sha, maxPullRequests))
const options = this.octokit.repos.listPullRequestsAssociatedWithCommit.endpoint.merge({
owner,
repo,
commit_sha,
per_page: `${Math.min(10, maxPullRequests)}`,
direction: 'desc'
})
for await (const response of this.octokit.paginate.iterator(options)) {
const prs: PullsListData = response.data as PullsListData
for (const pr of prs) {
mergedPRs.push(mapPullRequest(pr, pr.merged_at ? 'merged' : 'open'))
}
}
return sortPrs(mergedPRs)
} }
async getBetweenDates( async getBetweenDates(
@@ -120,86 +87,15 @@ export class PullRequests {
toDate: moment.Moment, toDate: moment.Moment,
maxPullRequests: number maxPullRequests: number
): Promise<PullRequestInfo[]> { ): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = [] return sortPrs(await this.repositoryUtils.getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests))
const options = this.octokit.pulls.list.endpoint.merge({
owner,
repo,
state: 'closed',
sort: 'merged',
per_page: `${Math.min(100, maxPullRequests)}`,
direction: 'desc'
})
for await (const response of this.octokit.paginate.iterator(options)) {
const prs: PullsListData = response.data as PullsListData
for (const pr of prs.filter(p => !!p.merged_at)) {
mergedPRs.push(mapPullRequest(pr, 'merged'))
}
if (mergedPRs.length >= maxPullRequests) {
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (1)`)
break // bail out early to not keep iterating forever
} else if (prs.length > 0) {
if (fetchedEnough(prs, fromDate)) {
return sortPrs(mergedPRs) // bail out early to not keep iterating on PRs super old
}
} else {
core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`)
break
}
}
return sortPrs(mergedPRs)
} }
async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> { async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
const openPrs: PullRequestInfo[] = [] return sortPrs(await this.repositoryUtils.getOpen(owner, repo, maxPullRequests))
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} (2)`)
}
break // bail out early to not keep iterating forever
}
}
return sortPrs(openPrs)
} }
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> { async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
const options = this.octokit.pulls.listReviews.endpoint.merge({ await this.repositoryUtils.getReviews(owner, repo, pr)
owner,
repo,
pull_number: pr.number,
sort: 'created',
direction: 'desc'
})
const prReviews: CommentInfo[] = []
for await (const response of this.octokit.paginate.iterator(options)) {
const comments: PullReviewsData = response.data as PullReviewsData
for (const comment of comments) {
prReviews.push(mapComment(comment))
}
}
pr.reviews = prReviews
} }
async getMergedPullRequests(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> { async getMergedPullRequests(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> {
@@ -323,20 +219,6 @@ export class PullRequests {
} }
} }
function fetchedEnough(pullRequests: PullsListData, fromDate: moment.Moment): boolean {
for (let i = 0; i < Math.min(pullRequests.length, 3); i++) {
const firstPR = pullRequests[i]
if (!firstPR.merged_at) {
continue // no merged_at timestamp -> look for the next
} else if (fromDate.isAfter(moment(firstPR.merged_at))) {
return true
} else {
break // not enough PRs yet, go further
}
}
return false
}
function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] { function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] {
return sortPullRequests(pullRequests, { return sortPullRequests(pullRequests, {
order: 'ASC', order: 'ASC',
@@ -401,39 +283,3 @@ export function retrieveProperty(pr: PullRequestInfo, property: Property, useCas
} }
return value return value
} }
// helper function to add a special open label to prs not merged.
function attachSpeciaLabels(status: 'open' | 'merged', labels: string[]): string[] {
labels.push(`--rcba-${status}`)
return labels
}
const mapPullRequest = (pr: PullData | Unpacked<PullsListData>, status: 'open' | 'merged' = 'open'): PullRequestInfo => ({
number: pr.number,
title: pr.title,
htmlURL: pr.html_url,
baseBranch: pr.base.ref,
branch: pr.head.ref,
createdAt: moment(pr.created_at),
mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined,
mergeCommitSha: pr.merge_commit_sha || '',
author: pr.user?.login || '',
repoName: pr.base.repo.full_name,
labels: attachSpeciaLabels(status, 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 || '') || [],
approvedReviewers: [],
reviews: undefined,
status
})
const mapComment = (comment: Unpacked<PullReviewsData>): CommentInfo => ({
id: comment.id,
htmlURL: comment.html_url,
submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined,
author: comment.user?.login || '',
body: comment.body,
state: comment.state
})
+4 -59
View File
@@ -1,12 +1,12 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as semver from 'semver' import * as semver from 'semver'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import {SemVer} from 'semver' import {SemVer} from 'semver'
import {RegexTransformer, TagResolver, Transformer} from './types' import {RegexTransformer, TagResolver, Transformer} from './types'
import {createCommandManager} from './gitHelper' import {createCommandManager} from './gitHelper'
import moment from 'moment' import moment from 'moment'
import {validateTransformer} from './regexUtils' import {validateTransformer} from './regexUtils'
import {BaseRepository} from '../repositories/BaseRepository'
export interface TagResult { export interface TagResult {
from: TagInfo | null from: TagInfo | null
@@ -25,69 +25,14 @@ export interface SortableTagInfo extends TagInfo {
} }
export class Tags { export class Tags {
constructor(private octokit: Octokit) {} constructor(private repositoryUtils: BaseRepository) {}
async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]> { async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]> {
const tagsInfo: TagInfo[] = [] return this.repositoryUtils.getTags(owner, repo, maxTagsToFetch)
const options = this.octokit.repos.listTags.endpoint.merge({
owner,
repo,
direction: 'desc',
per_page: 100
})
for await (const response of this.octokit.paginate.iterator(options)) {
type TagsListData = RestEndpointMethodTypes['repos']['listTags']['response']['data']
const tags: TagsListData = response.data as TagsListData
for (const tag of tags) {
tagsInfo.push({
name: tag.name,
commit: tag.commit.sha
})
}
// for performance only fetch newest maxTagsToFetch tags!!
if (tagsInfo.length >= maxTagsToFetch) {
break
}
}
core.info(`️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`)
return tagsInfo
} }
async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise<TagInfo> { async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise<TagInfo> {
const options = this.octokit.repos.getReleaseByTag.endpoint.merge({ return this.repositoryUtils.fillTagInformation(repositoryPath, owner, repo, tagInfo)
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( async findPredecessorTag(
+1 -1
View File
@@ -1,5 +1,5 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Rule, RegexTransformer} from './pr-collector/types' import {RegexTransformer, Rule} from './pr-collector/types'
import {PullRequestInfo, retrieveProperty} from './pr-collector/pullRequests' import {PullRequestInfo, retrieveProperty} from './pr-collector/pullRequests'
import {validateTransformer} from './pr-collector/regexUtils' import {validateTransformer} from './pr-collector/regexUtils'
+9 -6
View File
@@ -1,13 +1,13 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Configuration} from './configuration' import {Configuration} from './configuration'
import {checkExportedData, writeCacheData} from './utils' import {checkExportedData, writeCacheData} from './utils'
import {PullRequestData, buildChangelog} from './transform' import {buildChangelog, PullRequestData} from './transform'
import {PullRequestCollector} from './pr-collector/prCollector' import {PullRequestCollector} from './pr-collector/prCollector'
import {failOrError} from './pr-collector/utils' import {failOrError} from './pr-collector/utils'
import {TagInfo} from './pr-collector/tags' import {TagInfo} from './pr-collector/tags'
import {DiffInfo} from './pr-collector/commits' import {DiffInfo} from './pr-collector/commits'
import {PullRequestInfo} from './pr-collector/pullRequests' import {PullRequestInfo} from './pr-collector/pullRequests'
import * as fs from 'fs' import {BaseRepository} from './repositories/BaseRepository'
export interface ReleaseNotesOptions { export interface ReleaseNotesOptions {
owner: string // the owner of the repository owner: string // the owner of the repository
@@ -21,6 +21,7 @@ export interface ReleaseNotesOptions {
fetchReviews: boolean // defines if the action should fetch the reviews for the PR. fetchReviews: boolean // defines if the action should fetch the reviews for the PR.
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported 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` configuration: Configuration // the configuration as defined in `configuration.ts`
repositoryUtils: BaseRepository // the repository implementation used to generate the changelog
} }
export interface Data { export interface Data {
@@ -32,7 +33,7 @@ export interface Data {
export class ReleaseNotesBuilder { export class ReleaseNotesBuilder {
constructor( constructor(
private baseUrl: string | null, private baseUrl: string | null,
private token: string | null, private repositoryUtils: BaseRepository,
private repositoryPath: string, private repositoryPath: string,
private owner: string | null, private owner: string | null,
private repo: string | null, private repo: string | null,
@@ -78,7 +79,7 @@ export class ReleaseNotesBuilder {
const prData = await new PullRequestCollector( const prData = await new PullRequestCollector(
this.baseUrl, this.baseUrl,
this.token, this.repositoryUtils,
this.repositoryPath, this.repositoryPath,
this.owner, this.owner,
this.repo, this.repo,
@@ -110,7 +111,8 @@ export class ReleaseNotesBuilder {
fetchReleaseInformation: this.fetchReleaseInformation, fetchReleaseInformation: this.fetchReleaseInformation,
fetchReviews: this.fetchReviews, fetchReviews: this.fetchReviews,
commitMode: this.commitMode, commitMode: this.commitMode,
configuration: this.configuration configuration: this.configuration,
repositoryUtils: this.repositoryUtils
} }
const mergedPullRequests = prData.mergedPullRequests const mergedPullRequests = prData.mergedPullRequests
const diffInfo = prData.diffInfo const diffInfo = prData.diffInfo
@@ -163,7 +165,8 @@ export class ReleaseNotesBuilder {
fetchReleaseInformation: this.fetchReleaseInformation || orgOptions.fetchReleaseInformation, fetchReleaseInformation: this.fetchReleaseInformation || orgOptions.fetchReleaseInformation,
fetchReviews: this.fetchReviews || orgOptions.fetchReviews, fetchReviews: this.fetchReviews || orgOptions.fetchReviews,
commitMode: this.commitMode || orgOptions.commitMode, commitMode: this.commitMode || orgOptions.commitMode,
configuration: this.configuration || orgOptions.configuration configuration: this.configuration || orgOptions.configuration,
repositoryUtils: this.repositoryUtils || orgOptions.repositoryUtils
} }
this.setOutputs(options, diffInfo, mergedPullRequests) this.setOutputs(options, diffInfo, mergedPullRequests)
+68
View File
@@ -0,0 +1,68 @@
import {TagInfo} from '../pr-collector/tags'
import {DiffInfo} from '../pr-collector/commits'
import moment from 'moment/moment'
import {PullRequestInfo} from '../pr-collector/pullRequests'
import * as core from '@actions/core'
import {createCommandManager} from '../pr-collector/gitHelper'
export abstract class BaseRepository {
proxy?: string
noProxyArray: string[]
// Define an abstract getter for the default URL
abstract get defaultUrl(): string
// Define the abstract getter for the home URL (also used for some replace patterns)
abstract get homeUrl(): string
protected constructor(
protected token: string,
protected url: string | undefined,
protected repositoryPath: string
) {
this.proxy = process.env.https_proxy || process.env.HTTPS_PROXY
const noProxy = process.env.no_proxy || process.env.NO_PROXY
this.noProxyArray = []
if (noProxy) {
this.noProxyArray = noProxy.split(',')
}
}
abstract getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]>
abstract fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise<TagInfo>
abstract getDiffRemote(owner: string, repo: string, base: string, head: string): Promise<DiffInfo>
abstract getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]>
abstract getBetweenDates(
owner: string,
repo: string,
fromDate: moment.Moment,
toDate: moment.Moment,
maxPullRequests: number
): Promise<PullRequestInfo[]>
abstract getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]>
abstract getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void>
protected async getTagByCreateTime(repositoryPath: string, tagInfo: TagInfo): Promise<TagInfo> {
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
}
}
+249
View File
@@ -0,0 +1,249 @@
import {BaseRepository} from './BaseRepository'
import {TagInfo} from '../pr-collector/tags'
import {CommentInfo, PullRequestInfo} from '../pr-collector/pullRequests'
import {DiffInfo} from '../pr-collector/commits'
import {Api, PullRequest, PullReview, giteaApi} from 'gitea-js'
import moment from 'moment'
import * as core from '@actions/core'
import {createCommandManager} from '../pr-collector/gitHelper'
interface Pulls {
closed: PullRequest[]
open: PullRequest[]
}
export class GiteaRepository extends BaseRepository {
private api: Api<unknown>
get defaultUrl(): string {
return 'https://gitea.com'
}
get homeUrl(): string {
return 'https://gitea.com'
}
constructor(token: string, url: string | undefined, repositoryPath: string) {
super(token, url, repositoryPath)
this.url = url || this.defaultUrl
this.api = giteaApi(this.url, {
token
})
}
async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise<TagInfo> {
const response = await this.api.repos.repoGetTag(owner, repo, tagInfo.name)
if (response.error === null) {
if (response.data.commit) {
tagInfo.date = moment(response.data.commit.created)
core.info(`️ Retrieved information about the release associated with ${tagInfo.name} from the Gitea API`)
return tagInfo
}
}
return await this.getTagByCreateTime(repositoryPath, tagInfo)
}
async getBetweenDates(
owner: string,
repo: string,
fromDate: moment.Moment,
toDate: moment.Moment,
maxPullRequests: number
): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = []
await this.getAllPullRequest(owner, repo, 'closed', maxPullRequests)
for (const pr of GiteaRepository.pulls.closed.filter(p => !!p.merged_at)) {
if (moment(pr.closed_at) > fromDate && moment(pr.closed_at) < toDate) {
mergedPRs.push(this.mapPullRequest(pr, 'merged'))
}
}
return mergedPRs
}
private mapPullRequest(pr: PullRequest, status: 'open' | 'merged' = 'open'): PullRequestInfo {
return {
number: pr.number || 0,
title: pr.title || '',
htmlURL: pr.html_url || '',
baseBranch: pr.base?.repo?.default_branch || '',
branch: pr.merge_base || '',
mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined,
mergeCommitSha: pr.merge_commit_sha || '',
author: pr.user?.login || '',
repoName: pr.base?.repo?.full_name || '',
labels: pr.labels?.map(label => label.name) as string[],
milestone: pr.milestone?.title || '',
body: pr.body || '',
assignees: pr.assignees?.map(user => user.full_name) as string[],
requestedReviewers: pr.requested_reviewers?.map(user => user.full_name) as string[],
approvedReviewers: [],
createdAt: moment(pr.created_at),
status,
reviews: undefined
}
}
/**
* WARNING: This does not actually get the diff from the remote, as Gitea does not offer a compareable API.
* This uses the local repository to get the diff. NOTE: As such, gitea integration requires the repo available.
*/
async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> {
let changedFilesCount = 0
let additionCount = 0
let deletionCount = 0
const changeCount = 0
let commitCount = 0
const gitHelper = await createCommandManager(this.repositoryPath)
// Get the diff stats between the two branches/commits
const diffStat = await gitHelper.execGit(['diff', '--stat', `${base}...${head}`])
const diffStatLines = diffStat.stdout.split('\n')
for (const line of diffStatLines) {
// Extract the addition and deletion counts from each line of the git diff output
const match = line.match(/(\d+) insertions?\(\+\), (\d+) deletions?\(-\)/)
if (match) {
additionCount += parseInt(match[1], 10)
deletionCount += parseInt(match[2], 10)
}
}
// Get the list of changed files
const diffNameOnly = await gitHelper.execGit(['diff', '--name-only', `${base}...${head}`])
const changedFiles = diffNameOnly.stdout.split('\n')
changedFilesCount = changedFiles.length - 1 // Subtract one for the empty line at the end
// Get the commit count between the two branches/commits
const logCount = await gitHelper.execGit(['rev-list', '--count', `${base}...${head}`])
commitCount = parseInt(logCount.stdout.trim(), 10)
// Now let's get the commit logs between the two branches/commits
const log = await gitHelper.execGit(['log', '--pretty=format:%H||||%an||||%ae||||%ad||||%cn||||%ce||||%cd||||%s', `${base}...${head}`])
const commitLogs = log.stdout.trim().split('\n')
// Process commit logs
const commitInfo = commitLogs.map(commitLog => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [sha, authorName, authorEmail, authorDate, committerName, committerEmail, committerDate, subject] = commitLog.split('||||')
return {
sha,
summary: subject,
message: '', // This would require another git command to get the full message if needed
author: authorName,
authorDate: moment(authorDate, 'ddd MMM DD HH:mm:ss YYYY ZZ', false),
committer: committerName,
commitDate: moment(committerDate, 'ddd MMM DD HH:mm:ss YYYY ZZ', false),
prNumber: undefined // This is not available directly from git, would require additional logic to associate commits with PRs
}
})
return {
changedFiles: changedFilesCount,
additions: additionCount,
deletions: deletionCount,
changes: changeCount,
commits: commitCount,
commitInfo
}
}
static pulls: Pulls = {
closed: [],
open: []
}
private async getAllPullRequest(owner: string, repo: string, state: 'closed' | 'open', maxPullRequests: number): Promise<void> {
if (GiteaRepository.pulls[state].length === 0) {
let page = 1
let count = 0
while (count < maxPullRequests) {
const limit = Math.min(50, maxPullRequests)
const response = await this.api.repos.repoListPullRequests(owner, repo, {
sort: 'recentupdate',
state,
limit,
page
})
if (response.error === null) {
GiteaRepository.pulls[state].push(...response.data)
} else {
core.error(`️ Some errors. ${response.error.message}`)
}
page++
count += response.data.length
if (response.data.length === 0) break
}
}
}
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = []
await this.getAllPullRequest(owner, repo, 'closed', maxPullRequests)
for (const pr of GiteaRepository.pulls.closed) {
if (pr.merge_commit_sha === commit_sha) {
mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open'))
}
}
core.debug(`Completed fetching PRs from API. Fetched: ${mergedPRs.length}`)
return mergedPRs
}
async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
await this.getAllPullRequest(owner, repo, 'open', maxPullRequests)
const openPrs: PullRequestInfo[] = []
for (const pr of GiteaRepository.pulls.open) {
openPrs.push(this.mapPullRequest(pr, 'open'))
}
return openPrs
}
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
const prReviews: CommentInfo[] = []
const response = await this.api.repos.repoListPullReviews(owner, repo, pr.number, {})
if (response.error === null) {
for (const comment of response.data) {
prReviews.push(this.mapComment(comment))
}
} else {
core.error(`️ Some errors. ${response.error.message}`)
}
pr.reviews = prReviews
}
private mapComment = (comment: PullReview): CommentInfo => ({
id: comment.id || 0,
htmlURL: comment.html_url || '',
submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined,
author: comment.user?.login || '',
body: comment.body || '',
state: comment.state
})
async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]> {
core.debug(`Start to get tag from gitea`)
const response = await this.api.repos.repoListTags(owner, repo, {
limit: maxTagsToFetch
})
const tagsInfo: TagInfo[] = []
if (response.error === null) {
for (const tag of response.data) {
tagsInfo.push({
name: tag.name || '',
commit: tag.commit?.sha
})
}
} else {
core.error(`️ Some errors. ${response.error.message}`)
}
core.info(`️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`)
return tagsInfo
}
}
+305
View File
@@ -0,0 +1,305 @@
import {BaseRepository} from './BaseRepository'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import {HttpsProxyAgent} from 'https-proxy-agent'
import * as core from '@actions/core'
import {TagInfo} from '../pr-collector/tags'
import moment from 'moment/moment'
import {DiffInfo} from '../pr-collector/commits'
import {CommentInfo, PullData, PullRequestInfo, PullReviewsData, PullsListData} from '../pr-collector/pullRequests'
import {Unpacked} from '../pr-collector/utils'
export class GithubRepository extends BaseRepository {
async getDiffRemote(owner: string, repo: string, base: string, head: string): 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'] = []
let compareHead = head
// eslint-disable-next-line no-constant-condition
while (true) {
const compareResult = await this.octokit.repos.compareCommits({
owner,
repo,
base,
head: compareHead
})
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}^`
}
core.info(`️ Found ${commits.length} commits from the GitHub API for ${owner}/${repo}`)
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,
author: commit.author?.login || '',
authorDate: moment(commit.commit.author?.date),
committer: commit.committer?.login || '',
commitDate: moment(commit.commit.committer?.date),
prNumber: undefined
}))
}
}
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = []
const options = this.octokit.repos.listPullRequestsAssociatedWithCommit.endpoint.merge({
owner,
repo,
commit_sha,
per_page: `${Math.min(10, maxPullRequests)}`,
direction: 'desc'
})
for await (const response of this.octokit.paginate.iterator(options)) {
const prs: PullsListData = response.data as PullsListData
for (const pr of prs) {
mergedPRs.push(this.mapPullRequest(pr, pr.merged_at ? 'merged' : 'open'))
}
}
return mergedPRs
}
async getBetweenDates(
owner: string,
repo: string,
fromDate: moment.Moment,
toDate: moment.Moment,
maxPullRequests: number
): Promise<PullRequestInfo[]> {
const mergedPRs: PullRequestInfo[] = []
const options = this.octokit.pulls.list.endpoint.merge({
owner,
repo,
state: 'closed',
sort: 'merged',
per_page: `${Math.min(100, maxPullRequests)}`,
direction: 'desc'
})
for await (const response of this.octokit.paginate.iterator(options)) {
const prs: PullsListData = response.data as PullsListData
for (const pr of prs.filter(p => !!p.merged_at)) {
mergedPRs.push(this.mapPullRequest(pr, 'merged'))
}
if (mergedPRs.length >= maxPullRequests) {
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (1)`)
break // bail out early to not keep iterating forever
} else if (prs.length > 0) {
if (this.fetchedEnough(prs, fromDate)) {
return mergedPRs // bail out early to not keep iterating on PRs super old
}
} else {
core.debug(`⚠️ No more PRs retrieved from API. Fetched so far: ${mergedPRs.length}`)
break
}
}
return mergedPRs
}
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(this.mapPullRequest(pr, 'open'))
}
const firstPR = prs[0]
if (firstPR === undefined || openPrs.length >= maxPullRequests) {
if (openPrs.length >= maxPullRequests) {
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests} (2)`)
}
break // bail out early to not keep iterating forever
}
}
return openPrs
}
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
const options = this.octokit.pulls.listReviews.endpoint.merge({
owner,
repo,
pull_number: pr.number,
sort: 'created',
direction: 'desc'
})
const prReviews: CommentInfo[] = []
for await (const response of this.octokit.paginate.iterator(options)) {
const comments: PullReviewsData = response.data as PullReviewsData
for (const comment of comments) {
prReviews.push(this.mapComment(comment))
}
}
pr.reviews = prReviews
}
get defaultUrl(): string {
return 'https://api.github.com'
}
get homeUrl(): string {
return this.url?.replace('api.', '') || 'https://github.com'
}
private octokit: Octokit
constructor(token: string, url: string | undefined, repositoryPath: string) {
super(token, url, repositoryPath)
this.url = url || this.defaultUrl
// load octokit instance
this.octokit = new Octokit({
auth: `token ${this.token}`,
baseUrl: this.url
})
if (this.proxy) {
const agent = new HttpsProxyAgent(this.proxy)
this.octokit.hook.before('request', options => {
if (this.noProxyArray.includes(options.request.hostname)) {
return
}
options.request.agent = agent
})
}
}
async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]> {
const tagsInfo: TagInfo[] = []
const options = this.octokit.repos.listTags.endpoint.merge({
owner,
repo,
direction: 'desc',
per_page: 100
})
for await (const response of this.octokit.paginate.iterator(options)) {
type TagsListData = RestEndpointMethodTypes['repos']['listTags']['response']['data']
const tags: TagsListData = response.data as TagsListData
for (const tag of tags) {
tagsInfo.push({
name: tag.name,
commit: tag.commit.sha
})
}
// for performance only fetch newest maxTagsToFetch tags!!
if (tagsInfo.length >= maxTagsToFetch) {
break
}
}
core.info(`️ Found ${tagsInfo.length} (fetching max: ${maxTagsToFetch}) tags from the GitHub API for ${owner}/${repo}`)
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) {
tagInfo = await this.getTagByCreateTime(repositoryPath, tagInfo)
}
return tagInfo
}
// helper function to add a special open label to prs not merged.
private attachSpecialLabels(status: 'open' | 'merged', labels: string[]): string[] {
labels.push(`--rcba-${status}`)
return labels
}
private mapPullRequest = (pr: PullData | Unpacked<PullsListData>, status: 'open' | 'merged' = 'open'): PullRequestInfo => ({
number: pr.number,
title: pr.title,
htmlURL: pr.html_url,
baseBranch: pr.base.ref,
branch: pr.head.ref,
createdAt: moment(pr.created_at),
mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined,
mergeCommitSha: pr.merge_commit_sha || '',
author: pr.user?.login || '',
repoName: pr.base.repo.full_name,
labels: this.attachSpecialLabels(status, 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 || '') || [],
approvedReviewers: [],
reviews: undefined,
status
})
private mapComment = (comment: Unpacked<PullReviewsData>): CommentInfo => ({
id: comment.id,
htmlURL: comment.html_url,
submittedAt: comment.submitted_at ? moment(comment.submitted_at) : undefined,
author: comment.user?.login || '',
body: comment.body,
state: comment.state
})
private fetchedEnough(pullRequests: PullsListData, fromDate: moment.Moment): boolean {
for (let i = 0; i < Math.min(pullRequests.length, 3); i++) {
const firstPR = pullRequests[i]
if (!firstPR.merged_at) {
// no merged_at timestamp -> look for the next
} else if (fromDate.isAfter(moment(firstPR.merged_at))) {
return true
} else {
break // not enough PRs yet, go further
}
}
return false
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ import {
} from './pr-collector/pullRequests' } from './pr-collector/pullRequests'
import {DiffInfo} from './pr-collector/commits' import {DiffInfo} from './pr-collector/commits'
import {validateTransformer} from './pr-collector/regexUtils' import {validateTransformer} from './pr-collector/regexUtils'
import {Transformer, RegexTransformer} from './pr-collector/types' import {RegexTransformer, Transformer} from './pr-collector/types'
import {ReleaseNotesOptions} from './releaseNotesBuilder' import {ReleaseNotesOptions} from './releaseNotesBuilder'
import {matchesRules} from './regexUtils' import {matchesRules} from './regexUtils'
@@ -388,7 +388,7 @@ function fillAdditionalPlaceholders(
} }
placeholderMap.set( placeholderMap.set(
'RELEASE_DIFF', 'RELEASE_DIFF',
`https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}` `${options.repositoryUtils.homeUrl}/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`
) )
} }
+10 -1
View File
@@ -7,6 +7,7 @@ import {DiffInfo} from './pr-collector/commits'
import {PullRequestInfo} from './pr-collector/pullRequests' import {PullRequestInfo} from './pr-collector/pullRequests'
import {Data, ReleaseNotesOptions} from './releaseNotesBuilder' import {Data, ReleaseNotesOptions} from './releaseNotesBuilder'
import {env} from 'process' import {env} from 'process'
/** /**
* Resolves the repository path, relatively to the GITHUB_WORKSPACE * Resolves the repository path, relatively to the GITHUB_WORKSPACE
*/ */
@@ -40,13 +41,21 @@ export function writeCacheData(data: Data, cacheOutput: string | null): void {
} }
try { try {
fs.writeFileSync(cacheFile, JSON.stringify(data)) // use replacer to not cache the repositoryUtils (as that would contain token information)
fs.writeFileSync(cacheFile, JSON.stringify(data, replacer))
core.setOutput(`cache`, cacheFile) core.setOutput(`cache`, cacheFile)
} catch (error) { } catch (error) {
core.warning(`Failed to write cache file. (${error})`) core.warning(`Failed to write cache file. (${error})`)
} }
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function replacer(key: string, value: any): any {
if (key === 'repositoryUtils') return undefined
if (key === 'token') return undefined
else return value
}
/** /**
* Retrieves the exported information from a previous run of the `release-changelog-builder-action`. * Retrieves the exported information from a previous run of the `release-changelog-builder-action`.
* If available, return a [ReleaseNotesData]. * If available, return a [ReleaseNotesData].
+2 -2
View File
@@ -7,7 +7,7 @@
"strict": true, /* Enable all strict type-checking options. */ "strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"lib": [ "ES2021.String" ] /* Enable custom `ES2021.String` extension in typescript for `replaceAll` */ "lib": [ "ES2021.String", "dom"] /* Enable custom `ES2021.String` extension in typescript for `replaceAll` */
}, },
"exclude": ["node_modules", "**/*.test.ts", "pr-collector"] "exclude": ["node_modules", "**/*.test.ts", "**/**/*.test.ts", "src/pr-collector"],
} }