- move pr collection into npm module

(publish without any furhter notes or docs)
- refactor action to use npm dependency
This commit is contained in:
Mike Penz
2023-06-03 11:22:52 +00:00
committed by GitHub
parent 4a9ea3cd6a
commit e7dc26611c
28 changed files with 3529 additions and 1808 deletions
+11 -1
View File
@@ -33,10 +33,20 @@ jobs:
node-version: 16.x node-version: 16.x
- name: Install dependencies - name: Install dependencies
run: npm ci run: |
cd pr-collector
npm ci
cd ..
npm ci
- name: Rebuild the dist/ directory - name: Rebuild the dist/ directory
run: | run: |
cd pr-collector
npm run build
npm run package
cd ..
npm run build npm run build
npm run package npm run package
+4
View File
@@ -28,6 +28,10 @@ jobs:
- name: Run NPM - name: Run NPM
run: | run: |
cd pr-collector
npm run all
cd ..
npm run all npm run all
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3
View File
@@ -97,3 +97,6 @@ Thumbs.db
# Ignore built ts files # Ignore built ts files
__tests__/runner/* __tests__/runner/*
lib/**/* lib/**/*
lib
pr-collector/dist
+3 -1
View File
@@ -13,5 +13,7 @@ it('Configurations are merged correctly', async () => {
const mergedConfiguration = mergeConfiguration(configurationJson, configurationFile) const mergedConfiguration = mergeConfiguration(configurationJson, configurationFile)
console.log(mergedConfiguration) console.log(mergedConfiguration)
expect(JSON.stringify(mergedConfiguration)).toEqual(`{\"max_tags_to_fetch\":200,\"max_pull_requests\":1000,\"max_back_track_time_days\":1000,\"exclude_merge_branches\":[],\"sort\":\"DESC\",\"template\":\"$\{\{CHANGELOG}}\",\"pr_template\":\"- $\{\{TITLE}}\\n - PR: #$\{\{NUMBER}}\",\"empty_template\":\"- no magic changes\",\"categories\":[{\"title\":\"## 🚀 Features\",\"labels\":[\"feature\"]},{\"title\":\"## 🐛 Fixes\",\"labels\":[\"fix\"]},{\"title\":\"## 🧪 Tests\",\"labels\":[\"test\"]}],\"ignore_labels\":[\"ignore\"],\"label_extractor\":[],\"transformers\":[],\"tag_resolver\":{\"method\":\"semver\"},\"base_branches\":[],\"custom_placeholders\":[],\"trim_values\":true}`) expect(JSON.stringify(mergedConfiguration)).toEqual(
`{\"max_tags_to_fetch\":200,\"max_pull_requests\":1000,\"max_back_track_time_days\":1000,\"exclude_merge_branches\":[],\"sort\":\"DESC\",\"template\":\"$\{\{CHANGELOG}}\",\"pr_template\":\"- $\{\{TITLE}}\\n - PR: #$\{\{NUMBER}}\",\"empty_template\":\"- no magic changes\",\"categories\":[{\"title\":\"## 🚀 Features\",\"labels\":[\"feature\"]},{\"title\":\"## 🐛 Fixes\",\"labels\":[\"fix\"]},{\"title\":\"## 🧪 Tests\",\"labels\":[\"test\"]}],\"ignore_labels\":[\"ignore\"],\"label_extractor\":[],\"transformers\":[],\"tag_resolver\":{\"method\":\"semver\"},\"base_branches\":[],\"custom_placeholders\":[],\"trim_values\":true}`
)
}) })
+181 -131
View File
@@ -1,7 +1,7 @@
import {mergeConfiguration, resolveConfiguration} from '../src/utils' import {mergeConfiguration, resolveConfiguration} from '../src/utils'
import {pullData} from '../src/releaseNotesBuilder' import {pullData} from '../src/releaseNotesBuilder'
import {Octokit} from '@octokit/rest' import {Octokit} from '@octokit/rest'
import { buildChangelog } from '../src/transform' import {buildChangelog} from '../src/transform'
jest.setTimeout(180000) jest.setTimeout(180000)
@@ -13,19 +13,24 @@ const octokit = new Octokit({
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'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.0.1'}, owner: 'mikepenz',
toTag: {name: 'v0.0.2'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.0.1'},
failOnError: false, toTag: {name: 'v0.0.2'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -34,19 +39,24 @@ it('Should have empty changelog (tags)', async () => {
it('Should match generated changelog (tags)', async () => { it('Should match generated changelog (tags)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.0.1'}, owner: 'mikepenz',
toTag: {name: 'v0.0.3'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.0.1'},
failOnError: false, toTag: {name: 'v0.0.3'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -60,19 +70,24 @@ it('Should match generated changelog (tags)', async () => {
it('Should match generated changelog (refs)', async () => { it('Should match generated changelog (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_all_placeholders.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_all_placeholders.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'}, owner: 'mikepenz',
toTag: {name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'},
failOnError: false, toTag: {name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -94,19 +109,24 @@ nhoelzl
it('Should match generated changelog and replace all occurrences (refs)', async () => { it('Should match generated changelog and replace all occurrences (refs)', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_replace_all_placeholders.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_replace_all_placeholders.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'}, owner: 'mikepenz',
toTag: {name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3'},
failOnError: false, toTag: {name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -130,19 +150,24 @@ nhoelzl
it('Should match ordered ASC', async () => { it('Should match ordered ASC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_asc.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_asc.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.3.0'}, owner: 'mikepenz',
toTag: {name: 'v0.5.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.3.0'},
failOnError: false, toTag: {name: 'v0.5.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -151,19 +176,24 @@ it('Should match ordered ASC', async () => {
it('Should match ordered DESC', async () => { it('Should match ordered DESC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_desc.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_desc.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.3.0'}, owner: 'mikepenz',
toTag: {name: 'v0.5.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.3.0'},
failOnError: false, toTag: {name: 'v0.5.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -172,19 +202,24 @@ it('Should match ordered DESC', async () => {
it('Should match ordered by title ASC', async () => { it('Should match ordered by title ASC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_asc.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_asc.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.3.0'}, owner: 'mikepenz',
toTag: {name: 'v0.5.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.3.0'},
failOnError: false, toTag: {name: 'v0.5.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -195,19 +230,24 @@ it('Should match ordered by title ASC', async () => {
it('Should match ordered by title DESC', async () => { it('Should match ordered by title DESC', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_desc.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_sort_title_desc.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v0.3.0'}, owner: 'mikepenz',
toTag: {name: 'v0.5.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v0.3.0'},
failOnError: false, toTag: {name: 'v0.5.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -218,19 +258,24 @@ it('Should match ordered by title DESC', async () => {
it('Should ignore PRs not merged into develop branch', async () => { it('Should ignore PRs not merged into develop branch', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_develop.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_develop.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v1.3.1'}, owner: 'mikepenz',
toTag: {name: 'v1.4.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v1.3.1'},
failOnError: false, toTag: {name: 'v1.4.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
@@ -239,19 +284,24 @@ it('Should ignore PRs not merged into develop branch', async () => {
it('Should ignore PRs not merged into main branch', async () => { it('Should ignore PRs not merged into main branch', async () => {
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_main.json')) const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs_test/configuration_base_branches_main.json'))
const data = await pullData(octokit, { const data = await pullData(
owner: 'mikepenz', octokit,
repo: 'release-changelog-builder-action', {
fromTag: {name: 'v1.3.1'}, owner: 'mikepenz',
toTag: {name: 'v1.4.0'}, repo: 'release-changelog-builder-action',
includeOpen: false, fromTag: {name: 'v1.3.1'},
failOnError: false, toTag: {name: 'v1.4.0'},
fetchReviewers: false, includeOpen: false,
fetchReleaseInformation: false, failOnError: false,
fetchReviews: false, fetchReviewers: false,
commitMode: false, fetchReleaseInformation: false,
configuration fetchReviews: false,
}, false, false) commitMode: false,
configuration
},
false,
false
)
const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options) const changeLog = buildChangelog(data!.diffInfo, data!.mergedPullRequests, data!.options)
console.log(changeLog) console.log(changeLog)
+3 -9
View File
@@ -23,9 +23,7 @@ it('Should order tags correctly using semver', async () => {
}) })
.join(',') .join(',')
expect(sorted).toStrictEqual( expect(sorted).toStrictEqual(`2020.4.0,2020.4.0-rc02,2020.3.2,v2020.3.1,2020.3.1-rc03,2020.3.1-rc01,2020.3.1-b01,v2020.3.0`)
`2020.4.0,2020.4.0-rc02,2020.3.2,v2020.3.1,2020.3.1-rc03,2020.3.1-rc01,2020.3.1-b01,v2020.3.0`
)
}) })
it('Should order tags correctly using semver', async () => { it('Should order tags correctly using semver', async () => {
@@ -54,9 +52,7 @@ it('Should order tags correctly using semver', async () => {
}) })
.join(',') .join(',')
expect(sorted).toStrictEqual( expect(sorted).toStrictEqual(`1000.0.0,100.0.0,20.0.2,10.1.0,10.1.0-2,10.0.0,2.0.0,1.0.0,1.0.0-a01,0.1.0,0.1.0-b01,0.0.1,0.0.1-rc01`)
`1000.0.0,100.0.0,20.0.2,10.1.0,10.1.0-2,10.0.0,2.0.0,1.0.0,1.0.0-a01,0.1.0,0.1.0-b01,0.0.1,0.0.1-rc01`
)
}) })
it('Should order tags alphabetical', async () => { it('Should order tags alphabetical', async () => {
@@ -85,9 +81,7 @@ it('Should order tags alphabetical', async () => {
}) })
.join(',') .join(',')
expect(sorted).toStrictEqual( expect(sorted).toStrictEqual(`a,20.0.2,2.0.0,1000.0.0,10.1.0,10.1.0-2,10.0.0,1.0.0,1.0.0-a01,v1,0.1.0-b01,0.0.1,0.0.1-rc01`)
`a,20.0.2,2.0.0,1000.0.0,10.1.0,10.1.0-2,10.0.0,1.0.0,1.0.0-a01,v1,0.1.0-b01,0.0.1,0.0.1-rc01`
)
}) })
it('Should filter tags correctly using the regex', async () => { it('Should filter tags correctly using the regex', async () => {
+49 -53
View File
@@ -313,41 +313,41 @@ pullRequestsWithLabels.push(
const openPullRequestsWithLabels: PullRequestInfo[] = [] const openPullRequestsWithLabels: PullRequestInfo[] = []
openPullRequestsWithLabels.push( openPullRequestsWithLabels.push(
{ {
number: 6, number: 6,
title: 'Still pending open pull request (Current)', title: 'Still pending open pull request (Current)',
htmlURL: '', htmlURL: '',
baseBranch: '', baseBranch: '',
createdAt: moment(), createdAt: moment(),
mergedAt: moment(), mergedAt: moment(),
mergeCommitSha: 'sha1', mergeCommitSha: 'sha1',
author: 'Mike', author: 'Mike',
repoName: 'test-repo', repoName: 'test-repo',
labels: ['feature'], labels: ['feature'],
milestone: '', milestone: '',
body: 'Some fancy body message', body: 'Some fancy body message',
assignees: [], assignees: [],
requestedReviewers: [], requestedReviewers: [],
approvedReviewers: [], approvedReviewers: [],
status: 'open' status: 'open'
}, },
{ {
number: 7, number: 7,
title: 'Still pending open pull request', title: 'Still pending open pull request',
htmlURL: '', htmlURL: '',
baseBranch: '', baseBranch: '',
createdAt: moment(), createdAt: moment(),
mergedAt: moment(), mergedAt: moment(),
mergeCommitSha: 'sha1', mergeCommitSha: 'sha1',
author: 'Mike', author: 'Mike',
repoName: 'test-repo', repoName: 'test-repo',
labels: [], labels: [],
milestone: '', milestone: '',
body: 'Some fancy body message', body: 'Some fancy body message',
assignees: [], assignees: [],
requestedReviewers: [], requestedReviewers: [],
approvedReviewers: [], approvedReviewers: [],
status: 'open' status: 'open'
} }
) )
it('Match multiple labels exhaustive for category', async () => { it('Match multiple labels exhaustive for category', async () => {
@@ -457,9 +457,7 @@ it('Release Diff', async () => {
configuration: customConfig configuration: customConfig
}) })
expect(resultChangelog).toStrictEqual( expect(resultChangelog).toStrictEqual(`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`)
`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`
)
}) })
it('Use exclude labels to not include a PR within a category.', async () => { it('Use exclude labels to not include a PR within a category.', async () => {
@@ -532,7 +530,6 @@ it('Extract custom placeholder from PR body and replace in global template', asy
) )
}) })
it('Use Rules to include a PR within a Category.', async () => { it('Use Rules to include a PR within a Category.', async () => {
const customConfig = Object.assign({}, DefaultConfiguration) const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.categories = [ customConfig.categories = [
@@ -542,12 +539,12 @@ it('Use Rules to include a PR within a Category.', async () => {
exclude_labels: ['Fix'], exclude_labels: ['Fix'],
rules: [ rules: [
{ {
pattern: "\[ABC-1234\]", pattern: '[ABC-1234]',
on_property: "title" on_property: 'title'
}, },
{ {
pattern: "merged", pattern: 'merged',
on_property: "status" on_property: 'status'
} }
], ],
exhaustive: true exhaustive: true
@@ -568,8 +565,8 @@ it('Use Rules to get all open PRs in a Category.', async () => {
title: '## Open PRs only', title: '## Open PRs only',
rules: [ rules: [
{ {
pattern: "open", pattern: 'open',
on_property: "status" on_property: 'status'
} }
] ]
} }
@@ -577,7 +574,6 @@ it('Use Rules to get all open PRs in a Category.', async () => {
expect(buildChangelogTest(customConfig, prs)).toStrictEqual(`## Open PRs only\n\n- Still pending open pull request\n - PR: #6\n\n`) expect(buildChangelogTest(customConfig, prs)).toStrictEqual(`## Open PRs only\n\n- Still pending open pull request\n - PR: #6\n\n`)
}) })
it('Use Rules to get current open PR and merged categorised.', async () => { it('Use Rules to get current open PR and merged categorised.', async () => {
let prs = Array.from(pullRequestsWithLabels) let prs = Array.from(pullRequestsWithLabels)
prs = prs.concat(Array.from(openPullRequestsWithLabels)) prs = prs.concat(Array.from(openPullRequestsWithLabels))
@@ -599,7 +595,8 @@ it('Use Rules to get current open PR and merged categorised.', async () => {
], ],
exhaustive: true, exhaustive: true,
exhaustive_rules: false exhaustive_rules: false
},{ },
{
title: '## 🐛 Issues', title: '## 🐛 Issues',
labels: ['Issue'], labels: ['Issue'],
rules: [ rules: [
@@ -627,8 +624,8 @@ it('Use Rules to get all open PRs in one Category and merged categorised.', asyn
title: '## Open PRs only', title: '## Open PRs only',
rules: [ rules: [
{ {
pattern: "open", pattern: 'open',
on_property: "status" on_property: 'status'
} }
] ]
}, },
@@ -637,11 +634,11 @@ it('Use Rules to get all open PRs in one Category and merged categorised.', asyn
labels: ['Feature', 'Issue'], labels: ['Feature', 'Issue'],
rules: [ rules: [
{ {
pattern: "merged", pattern: 'merged',
on_property: "status" on_property: 'status'
} }
], ],
exhaustive: true, exhaustive: true
} }
] ]
expect(buildChangelogTest(customConfig, prs)).toStrictEqual( expect(buildChangelogTest(customConfig, prs)).toStrictEqual(
@@ -649,7 +646,6 @@ it('Use Rules to get all open PRs in one Category and merged categorised.', asyn
) )
}) })
function buildChangelogTest(config: Configuration, prs: PullRequestInfo[]): string { function buildChangelogTest(config: Configuration, prs: PullRequestInfo[]): string {
return buildChangelog(DefaultDiffInfo, prs, { return buildChangelog(DefaultDiffInfo, prs, {
owner: 'mikepenz', owner: 'mikepenz',
Generated Vendored
+1511 -1382
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
+3
View File
@@ -541,6 +541,9 @@ 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.
github-pr-collector
Apache 2.0
has-flag has-flag
MIT MIT
MIT License MIT License
+28 -6
View File
@@ -13,16 +13,11 @@
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1", "@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.11", "@octokit/rest": "^19.0.11",
"https-proxy-agent": "^7.0.0", "github-pr-collector": "file:pr-collector"
"moment": "^2.29.4",
"semver": "^7.5.1",
"tunnel": "^0.0.6",
"webpack": "^5.85.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/node": "^20.2.5", "@types/node": "^20.2.5",
"@types/semver": "^7.5.0",
"@typescript-eslint/parser": "^5.59.8", "@typescript-eslint/parser": "^5.59.8",
"@vercel/ncc": "^0.36.1", "@vercel/ncc": "^0.36.1",
"eslint": "^8.42.0", "eslint": "^8.42.0",
@@ -3768,6 +3763,10 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/github-pr-collector": {
"resolved": "pr-collector",
"link": true
},
"node_modules/glob": { "node_modules/glob": {
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -7063,6 +7062,29 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"pr-collector": {
"name": "github-pr-collector",
"version": "v1.0.0",
"license": "Apache 2.0",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.11",
"https-proxy-agent": "^7.0.0",
"moment": "^2.29.4",
"semver": "^7.5.1",
"webpack": "^5.85.0"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/semver": "^7.5.0",
"@vercel/ncc": "^0.36.1",
"js-yaml": "^4.1.0",
"prettier": "2.8.8",
"typescript": "^5.1.3"
}
} }
} }
} }
+1 -6
View File
@@ -36,16 +36,11 @@
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1", "@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.11", "@octokit/rest": "^19.0.11",
"https-proxy-agent": "^7.0.0", "github-pr-collector": "file:pr-collector"
"moment": "^2.29.4",
"semver": "^7.5.1",
"tunnel": "^0.0.6",
"webpack": "^5.85.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/node": "^20.2.5", "@types/node": "^20.2.5",
"@types/semver": "^7.5.0",
"@typescript-eslint/parser": "^5.59.8", "@typescript-eslint/parser": "^5.59.8",
"@vercel/ncc": "^0.36.1", "@vercel/ncc": "^0.36.1",
"eslint": "^8.42.0", "eslint": "^8.42.0",
+1322
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
{
"name": "github-pr-collector",
"version": "v1.0.0",
"description": "Library to fetch GitHub pull request between 2 tags/sha1 hashes.",
"main": "lib/prCollector.js",
"types": "lib/prCollector.d.ts",
"scripts": {
"build": "tsc",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"package": "ncc build --source-map --license licenses.txt",
"test": "jest --passWithNoTests",
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mikepenz/release-changelog-builder.git"
},
"keywords": [
"github",
"actions",
"changelog",
"release-notes",
"release",
"notes",
"change",
"release-automation",
"pull-requests",
"issues",
"labels"
],
"author": "Mike Penz",
"license": "Apache 2.0",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.11",
"https-proxy-agent": "^7.0.0",
"moment": "^2.29.4",
"semver": "^7.5.1",
"webpack": "^5.85.0"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/semver": "^7.5.0",
"@vercel/ncc": "^0.36.1",
"js-yaml": "^4.1.0",
"prettier": "2.8.8",
"typescript": "^5.1.3"
}
}
@@ -2,8 +2,8 @@ import * as core from '@actions/core'
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest' import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import moment from 'moment' import moment from 'moment'
import {failOrError} from './utils' import {failOrError} from './utils'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
import {PullRequestInfo} from './pullRequests' import {PullRequestInfo} from './pullRequests'
import {Options} from './prCollector'
export interface DiffInfo { export interface DiffInfo {
changedFiles: number changedFiles: number
@@ -121,7 +121,7 @@ export class Commits {
return commitsResult return commitsResult
} }
async getCommitHistory(options: ReleaseNotesOptions): Promise<DiffInfo> { async getCommitHistory(options: Options): Promise<DiffInfo> {
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}'`)
@@ -141,7 +141,7 @@ export class Commits {
return diffInfo return diffInfo
} }
async generateCommitPRs(options: ReleaseNotesOptions): Promise<[DiffInfo, PullRequestInfo[]]> { async generateCommitPRs(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, configuration} = options const {owner, repo, configuration} = options
const diffInfo = await this.getCommitHistory(options) const diffInfo = await this.getCommitHistory(options)
+55
View File
@@ -0,0 +1,55 @@
export interface Configuration {
max_tags_to_fetch: number
max_pull_requests: number
max_back_track_time_days: number
exclude_merge_branches: string[]
sort: Sort | string // "ASC" or "DESC"
tag_resolver: TagResolver
base_branches: string[]
}
/**
* Defines the properties of the PullRequestInfo useable in different configurations
*/
export type Property =
| 'number'
| 'title'
| 'branch'
| 'author'
| 'labels'
| 'milestone'
| 'body'
| 'assignees'
| 'requestedReviewers'
| 'approvedReviewers'
| 'status'
export interface Rule extends Regex {
on_property?: Property // retrieve the property to apply the rule on
}
export interface Sort {
order: 'ASC' | 'DESC' // the sorting order
on_property: 'mergedAt' | 'title' // the property to sort on. (mergedAt falls back to createdAt)
}
export interface TagResolver {
method: string // semver, sort
filter?: Regex // the regex to filter the tags, prior to sorting
transformer?: Transformer // transforms the tag name using the regex, run after the filter
}
export interface Regex {
pattern: string // the regex pattern to match
flags?: string // the regex flag to use for RegExp
}
export interface Transformer extends Regex {
target?: string // the target string to transform the source string using the regex to
}
export interface Extractor extends Transformer {
on_property?: Property[] | Property | undefined // retrieve the property to extract the value from
method?: 'replace' | 'match' | undefined // the method to use to extract the value, `match` will not use the `target` property
on_empty?: string | undefined // in case the regex results in an empty string, this value is gonna be used instead (only for label_extractor currently)
}
+159
View File
@@ -0,0 +1,159 @@
import * as core from '@actions/core'
import {Configuration} from './configuration'
import {Octokit} from '@octokit/rest'
import {TagInfo, Tags} from './tags'
import {failOrError} from './utils'
import {HttpsProxyAgent} from 'https-proxy-agent'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Commits, DiffInfo} from './commits'
export interface Options {
owner: string // the owner of the repository
repo: string // the repository
fromTag: TagInfo // the tag/ref to start from
toTag: TagInfo // the tag/ref up to
includeOpen: boolean // defines if we should also fetch open pull requests
failOnError: boolean // defines if we should fail the action in case of an error
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
fetchReleaseInformation: boolean // defines if the action should fetch the release information for the from and to tag - e.g. the creation date for the associated release
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
configuration: Configuration // the configuration as defined in `configuration.ts`
}
export interface Data {
diffInfo: DiffInfo
mergedPullRequests: PullRequestInfo[]
options: Options
}
export class PullRequestCollector {
constructor(
private baseUrl: string | null,
private token: string | null,
private repositoryPath: string,
private owner: string,
private repo: string,
private fromTag: string | null,
private toTag: string | null,
private includeOpen: boolean = false,
private failOnError: boolean,
private ignorePreReleases: boolean,
private fetchReviewers: boolean = false,
private fetchReleaseInformation: boolean = false,
private fetchReviews: boolean = false,
private commitMode: boolean = false,
private configuration: Configuration
) {}
async build(): Promise<Data | null> {
// 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
core.startGroup(`🔖 Resolve tags`)
const tagsApi = new Tags(octokit)
const tagRange = await tagsApi.retrieveRange(
this.repositoryPath,
this.owner,
this.repo,
this.fromTag,
this.toTag,
this.ignorePreReleases,
this.configuration.max_tags_to_fetch,
this.configuration.tag_resolver
)
let thisTag = tagRange.to
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
} else {
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError)
return null
}
core.setOutput('fromTag', previousTag.name)
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
if (this.fetchReleaseInformation) {
// load release information from the GitHub API
core.info(`️ Fetching release information was enabled`)
thisTag = await tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, thisTag)
previousTag = await tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, previousTag)
} else {
core.debug(`️ Fetching release information was disabled`)
}
core.endGroup()
const options = {
owner: this.owner,
repo: this.repo,
fromTag: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
fetchReviews: this.fetchReviews,
commitMode: this.commitMode,
configuration: this.configuration
}
return await pullData(octokit, options)
}
}
export async function pullData(octokit: Octokit, options: Options): Promise<Data | null> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
const commitsApi = new Commits(octokit)
if (!options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
const pullRequestsApi = new PullRequests(octokit, commitsApi)
const [info, prs] = await pullRequestsApi.getMergedPullRequests(options)
mergedPullRequests = prs
diffInfo = info
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
const [info, prs] = await commitsApi.generateCommitPRs(options)
mergedPullRequests = prs
diffInfo = info
}
core.endGroup()
return {
diffInfo,
mergedPullRequests,
options
}
}
@@ -4,7 +4,7 @@ import {Unpacked} from './utils'
import moment from 'moment' import moment from 'moment'
import {Property, Sort} from './configuration' import {Property, Sort} from './configuration'
import {Commits, DiffInfo, filterCommits} from './commits' import {Commits, DiffInfo, filterCommits} from './commits'
import {ReleaseNotesOptions} from './releaseNotesBuilder' import {Options} from './prCollector'
export interface PullRequestInfo { export interface PullRequestInfo {
number: number number: number
@@ -162,7 +162,7 @@ export class PullRequests {
pr.reviews = prReviews pr.reviews = prReviews
} }
async getMergedPullRequests(options: ReleaseNotesOptions): Promise<[DiffInfo, PullRequestInfo[]]> { async getMergedPullRequests(options: Options): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration} = options const {owner, repo, includeOpen, fetchReviewers, fetchReviews, configuration} = options
const diffInfo = await this.commits.getCommitHistory(options) const diffInfo = await this.commits.getCommitHistory(options)
+49
View File
@@ -0,0 +1,49 @@
import * as core from '@actions/core'
import * as fs from 'fs'
/**
* Will automatically either report the message to the log, or mark the action as failed. Additionally defining the output failed, allowing it to be read in by other actions
*/
export function failOrError(message: string | Error, failOnError: boolean): void {
// if we report any failure, consider the action to have failed, may not make the build fail
core.setOutput('failed', true)
if (failOnError) {
core.setFailed(message)
} else {
core.error(message)
}
}
/**
* Checks if a given directory exists
*/
export function directoryExistsSync(inputPath: string, required?: boolean): boolean {
if (!inputPath) {
throw new Error("Arg 'path' must not be empty")
}
let stats: fs.Stats
try {
stats = fs.statSync(inputPath)
} catch (error: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) {
if (error.code === 'ENOENT') {
if (!required) {
return false
}
throw new Error(`Directory '${inputPath}' does not exist`)
}
throw new Error(`Encountered an error when checking whether path '${inputPath}' exists: ${error.message}`)
}
if (stats.isDirectory()) {
return true
} else if (!required) {
return false
}
throw new Error(`Directory '${inputPath}' does not exist`)
}
export type Unpacked<T> = T extends (infer U)[] ? U : T
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"strict": true, /* Enable all strict type-checking options. */
"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'. */
"lib": [ "ES2021.String" ], /* Enable custom `ES2021.String` extension in typescript for `replaceAll` */
"declaration": true
},
"exclude": ["node_modules", "**/*.test.ts", "lib"]
}
+2 -24
View File
@@ -1,3 +1,5 @@
import {Extractor, Regex, Rule, Sort, Transformer} from 'github-pr-collector/lib/configuration'
export interface Configuration { export interface Configuration {
max_tags_to_fetch: number max_tags_to_fetch: number
max_pull_requests: number max_pull_requests: number
@@ -45,30 +47,6 @@ export type Property =
| 'approvedReviewers' | 'approvedReviewers'
| 'status' | 'status'
export interface Rule extends Regex {
on_property?: Property // retrieve the property to apply the rule on
}
export interface Sort {
order: 'ASC' | 'DESC' // the sorting order
on_property: 'mergedAt' | 'title' // the property to sort on. (mergedAt falls back to createdAt)
}
export interface Regex {
pattern: string // the regex pattern to match
flags?: string // the regex flag to use for RegExp
}
export interface Transformer extends Regex {
target?: string // the target string to transform the source string using the regex to
}
export interface Extractor extends Transformer {
on_property?: Property[] | Property | undefined // retrieve the property to extract the value from
method?: 'replace' | 'match' | undefined // the method to use to extract the value, `match` will not use the `target` property
on_empty?: string | undefined // in case the regex results in an empty string, this value is gonna be used instead (only for label_extractor currently)
}
export interface TagResolver { export interface TagResolver {
method: string // semver, sort method: string // semver, sort
filter?: Regex // the regex to filter the tags, prior to sorting filter?: Regex // the regex to filter the tags, prior to sorting
+60 -131
View File
@@ -1,12 +1,12 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Configuration} from './configuration' import {Configuration} from './configuration'
import {Octokit} from '@octokit/rest' import {checkExportedData} from './utils'
import {TagInfo, Tags} from './tags'
import {checkExportedData, failOrError} from './utils'
import {HttpsProxyAgent} from 'https-proxy-agent'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Commits, DiffInfo} from './commits'
import {buildChangelog} from './transform' import {buildChangelog} from './transform'
import {PullRequestCollector} from 'github-pr-collector'
import {failOrError} from 'github-pr-collector/lib/utils'
import {TagInfo} from 'github-pr-collector/lib/tags'
import {DiffInfo} from 'github-pr-collector/lib/commits'
import {PullRequestInfo} from 'github-pr-collector/lib/pullRequests'
export interface ReleaseNotesOptions { export interface ReleaseNotesOptions {
owner: string // the owner of the repository owner: string // the owner of the repository
@@ -22,12 +22,11 @@ export interface ReleaseNotesOptions {
configuration: Configuration // the configuration as defined in `configuration.ts` configuration: Configuration // the configuration as defined in `configuration.ts`
} }
export interface ReleaseNotesData { export interface Data {
diffInfo: DiffInfo diffInfo: DiffInfo
mergedPullRequests: PullRequestInfo[] mergedPullRequests: PullRequestInfo[]
options: ReleaseNotesOptions options: ReleaseNotesOptions
} }
export class ReleaseNotesBuilder { export class ReleaseNotesBuilder {
constructor( constructor(
private baseUrl: string | null, private baseUrl: string | null,
@@ -69,77 +68,34 @@ export class ReleaseNotesBuilder {
} }
core.endGroup() core.endGroup()
// check proxy setup for GHES environments const prData = await new PullRequestCollector(
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY this.baseUrl,
const noProxy = process.env.no_proxy || process.env.NO_PROXY this.token,
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
core.startGroup(`🔖 Resolve tags`)
const tagsApi = new Tags(octokit)
const tagRange = await tagsApi.retrieveRange(
this.repositoryPath, this.repositoryPath,
this.owner, this.owner,
this.repo, this.repo,
this.fromTag, this.fromTag,
this.toTag, this.toTag,
this.includeOpen,
this.failOnError,
this.ignorePreReleases, this.ignorePreReleases,
this.configuration.max_tags_to_fetch, this.fetchReviewers,
this.configuration.tag_resolver this.fetchReleaseInformation,
) this.fetchReviews,
this.commitMode,
this.configuration
).build()
let thisTag = tagRange.to if (prData == null) {
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
} else {
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError)
return null return null
} }
core.setOutput('fromTag', previousTag.name)
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
if (this.fetchReleaseInformation) { const resolvedOptions = prData.options
// load release information from the GitHub API const options: ReleaseNotesOptions = {
core.info(`️ Fetching release information was enabled`)
thisTag = await tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, thisTag)
previousTag = await tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, previousTag)
} else {
core.debug(`️ Fetching release information was disabled`)
}
core.endGroup()
const options = {
owner: this.owner, owner: this.owner,
repo: this.repo, repo: this.repo,
fromTag: previousTag, fromTag: resolvedOptions.fromTag,
toTag: thisTag, toTag: resolvedOptions.toTag,
includeOpen: this.includeOpen, includeOpen: this.includeOpen,
failOnError: this.failOnError, failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers, fetchReviewers: this.fetchReviewers,
@@ -148,8 +104,44 @@ export class ReleaseNotesBuilder {
commitMode: this.commitMode, commitMode: this.commitMode,
configuration: this.configuration configuration: this.configuration
} }
const mergedPullRequests = prData.mergedPullRequests
const diffInfo = prData.diffInfo
releaseNotesData = await pullData(octokit, options, this.exportCollected, this.exportOnly) // define the included PRs within this release as output
core.setOutput(
'pull_requests',
mergedPullRequests
.map(pr => {
return pr.number
})
.join(',')
)
core.setOutput('changed_files', diffInfo.changedFiles)
core.setOutput('additions', diffInfo.additions)
core.setOutput('deletions', diffInfo.deletions)
core.setOutput('changes', diffInfo.changes)
core.setOutput('commits', diffInfo.commits)
if (this.exportCollected) {
core.info('📦 Exporting collected data')
core.exportVariable(`RCBA_EXPORT_diffInfo`, JSON.stringify(diffInfo))
//fs.writeFileSync(path.resolve('diffInfo.json'), JSON.stringify(diffInfo))
core.exportVariable(`RCBA_EXPORT_mergedPullRequests`, JSON.stringify(mergedPullRequests))
//fs.writeFileSync(path.resolve('mergedPullRequests.json'), JSON.stringify(mergedPullRequests))
core.exportVariable(`RCBA_EXPORT_options`, JSON.stringify(options))
//fs.writeFileSync(path.resolve('options.json'), JSON.stringify(options))
if (this.exportOnly) {
core.endGroup()
return null
}
}
releaseNotesData = {
mergedPullRequests,
diffInfo,
options
}
} else { } else {
core.info(`️ Retrieved previously exported collected data`) core.info(`️ Retrieved previously exported collected data`)
@@ -196,66 +188,3 @@ export class ReleaseNotesBuilder {
} }
} }
} }
export async function pullData(
octokit: Octokit,
options: ReleaseNotesOptions,
exportCollected: boolean,
exportOnly: boolean
): Promise<ReleaseNotesData | null> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
const commitsApi = new Commits(octokit)
if (!options.commitMode) {
core.startGroup(`🚀 Load pull requests`)
const pullRequestsApi = new PullRequests(octokit, commitsApi)
const [info, prs] = await pullRequestsApi.getMergedPullRequests(options)
mergedPullRequests = prs
diffInfo = info
} else {
core.startGroup(`🚀 Load commit history`)
core.info(`⚠️ Executing experimental commit mode`)
const [info, prs] = await commitsApi.generateCommitPRs(options)
mergedPullRequests = prs
diffInfo = info
}
// define the included PRs within this release as output
core.setOutput(
'pull_requests',
mergedPullRequests
.map(pr => {
return pr.number
})
.join(',')
)
core.setOutput('changed_files', diffInfo.changedFiles)
core.setOutput('additions', diffInfo.additions)
core.setOutput('deletions', diffInfo.deletions)
core.setOutput('changes', diffInfo.changes)
core.setOutput('commits', diffInfo.commits)
if (exportCollected) {
core.info('📦 Exporting collected data')
core.exportVariable(`RCBA_EXPORT_diffInfo`, JSON.stringify(diffInfo))
//fs.writeFileSync(path.resolve('diffInfo.json'), JSON.stringify(diffInfo))
core.exportVariable(`RCBA_EXPORT_mergedPullRequests`, JSON.stringify(mergedPullRequests))
//fs.writeFileSync(path.resolve('mergedPullRequests.json'), JSON.stringify(mergedPullRequests))
core.exportVariable(`RCBA_EXPORT_options`, JSON.stringify(options))
//fs.writeFileSync(path.resolve('options.json'), JSON.stringify(options))
if (exportOnly) {
core.endGroup()
return null
}
}
core.endGroup()
return {
diffInfo,
mergedPullRequests,
options
}
}
+6 -5
View File
@@ -1,10 +1,11 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {Category, Configuration, Placeholder, Property, Transformer} from './configuration' import {Category, Configuration, Placeholder, Property} from './configuration'
import {CommentInfo, EMPTY_COMMENT_INFO, PullRequestInfo, retrieveProperty, sortPullRequests} from './pullRequests'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
import {DiffInfo} from './commits'
import {createOrSet, haveCommonElementsArr, haveEveryElementsArr} from './utils' import {createOrSet, haveCommonElementsArr, haveEveryElementsArr} from './utils'
import {matchesRules, RegexTransformer, validateTransformer} from './regexUtils' import {CommentInfo, EMPTY_COMMENT_INFO, PullRequestInfo, retrieveProperty, sortPullRequests} from 'github-pr-collector/lib/pullRequests'
import {DiffInfo} from 'github-pr-collector/lib/commits'
import {RegexTransformer, matchesRules, validateTransformer} from 'github-pr-collector/lib/regexUtils'
import {Transformer} from 'github-pr-collector/lib/configuration'
import {ReleaseNotesOptions} from './releaseNotesBuilder'
const EMPTY_MAP = new Map<string, string>() const EMPTY_MAP = new Map<string, string>()
+4 -51
View File
@@ -2,10 +2,10 @@ import * as core from '@actions/core'
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
import {Configuration, DefaultConfiguration} from './configuration' import {Configuration, DefaultConfiguration} from './configuration'
import {ReleaseNotesData, ReleaseNotesOptions} from './releaseNotesBuilder'
import {DiffInfo} from './commits'
import {PullRequestInfo} from './pullRequests'
import moment from 'moment' import moment from 'moment'
import {DiffInfo} from 'github-pr-collector/lib/commits'
import {PullRequestInfo} from 'github-pr-collector/lib/pullRequests'
import {Data, ReleaseNotesOptions} from './releaseNotesBuilder'
/** /**
* Resolves the repository path, relatively to the GITHUB_WORKSPACE * Resolves the repository path, relatively to the GITHUB_WORKSPACE
*/ */
@@ -23,24 +23,11 @@ export function retrieveRepositoryPath(providedPath: string): string {
return repositoryPath return repositoryPath
} }
/**
* Will automatically either report the message to the log, or mark the action as failed. Additionally defining the output failed, allowing it to be read in by other actions
*/
export function failOrError(message: string | Error, failOnError: boolean): void {
// if we report any failure, consider the action to have failed, may not make the build fail
core.setOutput('failed', true)
if (failOnError) {
core.setFailed(message)
} else {
core.error(message)
}
}
/** /**
* 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].
*/ */
export function checkExportedData(): ReleaseNotesData | null { export function checkExportedData(): Data | null {
const rawDiffInfo = process.env[`RCBA_EXPORT_diffInfo`] const rawDiffInfo = process.env[`RCBA_EXPORT_diffInfo`]
const rawMergedPullRequests = process.env[`RCBA_EXPORT_mergedPullRequests`] const rawMergedPullRequests = process.env[`RCBA_EXPORT_mergedPullRequests`]
const rawOptions = process.env[`RCBA_EXPORT_options`] const rawOptions = process.env[`RCBA_EXPORT_options`]
@@ -153,38 +140,6 @@ export function mergeConfiguration(jc?: Configuration, fc?: Configuration): Conf
} }
} }
/**
* Checks if a given directory exists
*/
export function directoryExistsSync(inputPath: string, required?: boolean): boolean {
if (!inputPath) {
throw new Error("Arg 'path' must not be empty")
}
let stats: fs.Stats
try {
stats = fs.statSync(inputPath)
} catch (error: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) {
if (error.code === 'ENOENT') {
if (!required) {
return false
}
throw new Error(`Directory '${inputPath}' does not exist`)
}
throw new Error(`Encountered an error when checking whether path '${inputPath}' exists: ${error.message}`)
}
if (stats.isDirectory()) {
return true
} else if (!required) {
return false
}
throw new Error(`Directory '${inputPath}' does not exist`)
}
/** /**
* Writes the changelog to the given the file * Writes the changelog to the given the file
*/ */
@@ -200,8 +155,6 @@ export function writeOutput(githubWorkspacePath: string, outputFile: string, cha
} }
} }
export type Unpacked<T> = T extends (infer U)[] ? U : T
export function createOrSet<T>(map: Map<string, T[]>, key: string, value: T): void { export function createOrSet<T>(map: Map<string, T[]>, key: string, value: T): void {
const entry = map.get(key) const entry = map.get(key)
if (!entry) { if (!entry) {
+1 -1
View File
@@ -9,5 +9,5 @@
"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" ] /* Enable custom `ES2021.String` extension in typescript for `replaceAll` */
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": ["node_modules", "**/*.test.ts", "pr-collector"]
} }