- introduce new flag allowing to fetch reviewers who approved PRs
- add new testcase which verifies approvers being included - adjust config for some test cases to verify reviewers not being pulled if disabled
This commit is contained in:
@@ -18,6 +18,7 @@ it('Should have empty changelog (tags)', async () => {
|
||||
toTag: 'v0.0.2',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -36,6 +37,7 @@ it('Should match generated changelog (tags)', async () => {
|
||||
toTag: 'v0.0.3',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -62,6 +64,7 @@ it('Should match generated changelog (refs)', async () => {
|
||||
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -96,6 +99,7 @@ it('Should match generated changelog and replace all occurrences (refs)', async
|
||||
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -132,6 +136,7 @@ it('Should match ordered ASC', async () => {
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -155,6 +160,7 @@ it('Should match ordered DESC', async () => {
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -178,6 +184,7 @@ it('Should ignore PRs not merged into develop branch', async () => {
|
||||
toTag: 'v1.4.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -199,6 +206,7 @@ it('Should ignore PRs not merged into main branch', async () => {
|
||||
toTag: 'v1.4.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -43,6 +44,7 @@ it('Should match generated changelog (unspecified tags)', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -67,6 +69,7 @@ it('Should use empty placeholder', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -92,6 +95,7 @@ it('Should fill empty placeholders', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -119,6 +123,7 @@ it('Should fill `template` placeholders', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -146,6 +151,7 @@ it('Should fill `template` placeholders, ignore', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -173,6 +179,7 @@ it('Uncategorized category', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
false,
|
||||
configuration
|
||||
)
|
||||
@@ -200,6 +207,7 @@ it('Verify commit based changelog', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
true,
|
||||
configuration
|
||||
)
|
||||
@@ -227,6 +235,7 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false, // enable to fetch reviewers
|
||||
true,
|
||||
configuration
|
||||
)
|
||||
@@ -254,6 +263,7 @@ it('Verify default inclusion of open PRs', async () => {
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
false, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
@@ -281,6 +291,7 @@ it('Verify custom categorisation of open PRs', async () => {
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
false, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
@@ -291,3 +302,31 @@ it('Verify custom categorisation of open PRs', async () => {
|
||||
`## 🚀 Features Merged\n\n- A feature to be going to v2 (nr3) -- (#3) [merged] {feature}\n\n## 🚀 Features Open\n\n- New feature to keep open (nr5) -- (#7) [open] {feature}\n\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Verify reviewers who approved are fetched', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_approvers.json'
|
||||
)
|
||||
const releaseNotesBuilder = new ReleaseNotesBuilder(
|
||||
null, // baseUrl
|
||||
null, // token
|
||||
'.', // repoPath
|
||||
'mikepenz', // user
|
||||
'release-changelog-builder-action-playground', // repo
|
||||
'1.5.0', // fromTag
|
||||
'2.0.0', // toTag
|
||||
true, // includeOpen
|
||||
false, // failOnError
|
||||
false, // ignorePrePrelease
|
||||
true, // enable to fetch reviewers
|
||||
false, // commitMode
|
||||
configuration // configuration
|
||||
)
|
||||
|
||||
const changeLog = await releaseNotesBuilder.build()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features\n\n- A feature to be going to v2 (nr3) -- (#3) [merged] --- \n- New feature to keep open (nr5) -- (#7) [open] --- gabrielpopa\n\n`
|
||||
)
|
||||
})
|
||||
@@ -43,6 +43,7 @@ mergedPullRequests.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -60,6 +61,7 @@ mergedPullRequests.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -77,6 +79,7 @@ mergedPullRequests.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -94,6 +97,7 @@ mergedPullRequests.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
)
|
||||
@@ -113,6 +117,7 @@ const pullRequestWithLabelInBody: PullRequestInfo = {
|
||||
body: '[Issue][Feature][AB-1234321] - no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
|
||||
@@ -132,6 +137,7 @@ it('Extract label from title, combined regex', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -160,6 +166,7 @@ it('Extract label from title and body, combined regex', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -190,6 +197,7 @@ it('Extract label from title, split regex', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -220,6 +228,7 @@ it('Extract label from title, match', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -245,6 +254,7 @@ it('Extract label from title, match multiple', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -271,6 +281,7 @@ it('Extract label from title, match multiple, custon non matching label', async
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
@@ -298,6 +309,7 @@ pullRequestsWithLabels.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -315,6 +327,7 @@ pullRequestsWithLabels.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -332,6 +345,7 @@ pullRequestsWithLabels.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
},
|
||||
{
|
||||
@@ -349,6 +363,7 @@ pullRequestsWithLabels.push(
|
||||
body: 'no magic body for this matter',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: "merged"
|
||||
}
|
||||
)
|
||||
@@ -380,6 +395,7 @@ it('Match multiple labels exhaustive for category', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -404,6 +420,7 @@ it('Deduplicate duplicated PRs', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -429,6 +446,7 @@ it('Deduplicate duplicated PRs DESC', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -450,6 +468,7 @@ it('Commit SHA-1 in commitMode', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: true,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -470,6 +489,7 @@ it('Release Diff', async () => {
|
||||
toTag: 'v2.8.1',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: true,
|
||||
configuration: customConfig
|
||||
})
|
||||
@@ -508,6 +528,7 @@ it('Use exclude labels to not include a PR within a category.', async () => {
|
||||
toTag: '2.0.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration: customConfig
|
||||
})
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"template": "${{CHANGELOG}}",
|
||||
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] ${{REVIEWERS}} --- ${{APPROVERS}}"
|
||||
}
|
||||
@@ -12,5 +12,5 @@
|
||||
}
|
||||
],
|
||||
"template": "${{CHANGELOG}}",
|
||||
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] {${{LABELS}}}"
|
||||
"pr_template": "- ${{TITLE}} -- (#${{NUMBER}}) [${{STATUS}}] {${{LABELS}}}${{APPROVERS}}"
|
||||
}
|
||||
@@ -6,5 +6,5 @@
|
||||
}
|
||||
],
|
||||
"template": "${{CHANGELOG}}\n\n\nUncategorized\n${{UNCATEGORIZED}}\n\n\nOpen\n${{OPEN}}",
|
||||
"pr_template": "- ${{TITLE}} (#${{NUMBER}}) ${{STATUS}}"
|
||||
"pr_template": "- ${{TITLE}} (#${{NUMBER}}) ${{STATUS}}${{APPROVERS}}"
|
||||
}
|
||||
+50
-5
@@ -380,8 +380,9 @@ function run() {
|
||||
const includeOpen = core.getInput('includeOpen') === 'true';
|
||||
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true';
|
||||
const failOnError = core.getInput('failOnError') === 'true';
|
||||
const fetchReviewers = core.getInput('fetchReviewers') === 'true';
|
||||
const commitMode = core.getInput('commitMode') === 'true';
|
||||
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, commitMode, configuration).build();
|
||||
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, commitMode, configuration).build();
|
||||
core.setOutput('changelog', result);
|
||||
// write the result in changelog to file if possible
|
||||
const outputFile = core.getInput('outputFile');
|
||||
@@ -551,6 +552,34 @@ class PullRequests {
|
||||
return sortPullRequests(openPrs, true);
|
||||
});
|
||||
}
|
||||
getReviewers(owner, repo, pr) {
|
||||
var e_3, _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const options = this.octokit.pulls.listReviews.endpoint.merge({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number
|
||||
});
|
||||
try {
|
||||
for (var _b = __asyncValues(this.octokit.paginate.iterator(options)), _c; _c = yield _b.next(), !_c.done;) {
|
||||
const response = _c.value;
|
||||
const reviews = response.data;
|
||||
pr.approvedReviewers = reviews
|
||||
.filter(r => r.state === 'APPROVED')
|
||||
.map(r => { var _a; return (_a = r.user) === null || _a === void 0 ? void 0 : _a.login; })
|
||||
.filter(r => !!r);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.PullRequests = PullRequests;
|
||||
function sortPullRequests(pullRequests, ascending) {
|
||||
@@ -605,6 +634,7 @@ const mapPullRequest = (pr, status = 'open') => {
|
||||
body: pr.body || '',
|
||||
assignees: ((_d = pr.assignees) === null || _d === void 0 ? void 0 : _d.map(asignee => (asignee === null || asignee === void 0 ? void 0 : asignee.login) || '')) || [],
|
||||
requestedReviewers: ((_e = pr.requested_reviewers) === null || _e === void 0 ? void 0 : _e.map(reviewer => (reviewer === null || reviewer === void 0 ? void 0 : reviewer.login) || '')) || [],
|
||||
approvedReviewers: [],
|
||||
status
|
||||
});
|
||||
};
|
||||
@@ -714,7 +744,7 @@ class ReleaseNotes {
|
||||
}
|
||||
getMergedPullRequests(octokit) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const { owner, repo, includeOpen, configuration } = this.options;
|
||||
const { owner, repo, includeOpen, fetchReviewers, configuration } = this.options;
|
||||
const commits = yield this.getCommitHistory(octokit);
|
||||
if (commits.length === 0) {
|
||||
return [];
|
||||
@@ -761,7 +791,7 @@ class ReleaseNotes {
|
||||
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu');
|
||||
});
|
||||
// return only prs if the baseBranch is matching the configuration
|
||||
return allPullRequests.filter(pr => {
|
||||
const finalPrs = allPullRequests.filter(pr => {
|
||||
if (baseBranches.length !== 0) {
|
||||
return baseBranchPatterns.some(pattern => {
|
||||
return pr.baseBranch.match(pattern) !== null;
|
||||
@@ -769,6 +799,17 @@ class ReleaseNotes {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (fetchReviewers) {
|
||||
core.info(`ℹ️ Fetching reviewers was enabled`);
|
||||
// update PR information with reviewers who approved
|
||||
for (const pr of finalPrs) {
|
||||
yield pullRequestsApi.getReviewers(owner, repo, pr);
|
||||
if (pr.approvedReviewers.length > 0) {
|
||||
core.info(`ℹ️ Retrieved ${pr.approvedReviewers.length} reviewer(s) for PR ${owner}/${repo}/#${pr.number}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalPrs;
|
||||
});
|
||||
}
|
||||
generateCommitPRs(octokit) {
|
||||
@@ -797,6 +838,7 @@ class ReleaseNotes {
|
||||
body: commit.message || '',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: 'merged'
|
||||
};
|
||||
});
|
||||
@@ -855,7 +897,7 @@ const tags_1 = __nccwpck_require__(7532);
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
const transform_1 = __nccwpck_require__(1644);
|
||||
class ReleaseNotesBuilder {
|
||||
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, commitMode, configuration) {
|
||||
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, commitMode, configuration) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.token = token;
|
||||
this.repositoryPath = repositoryPath;
|
||||
@@ -866,6 +908,7 @@ class ReleaseNotesBuilder {
|
||||
this.includeOpen = includeOpen;
|
||||
this.failOnError = failOnError;
|
||||
this.ignorePreReleases = ignorePreReleases;
|
||||
this.fetchReviewers = fetchReviewers;
|
||||
this.commitMode = commitMode;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
@@ -925,6 +968,7 @@ class ReleaseNotesBuilder {
|
||||
toTag: this.toTag,
|
||||
includeOpen: this.includeOpen,
|
||||
failOnError: this.failOnError,
|
||||
fetchReviewers: this.fetchReviewers,
|
||||
commitMode: this.commitMode,
|
||||
configuration: this.configuration
|
||||
};
|
||||
@@ -1499,7 +1543,7 @@ function haveEveryElements(arr1, arr2) {
|
||||
return arr1.every(item => arr2.has(item));
|
||||
}
|
||||
function fillTemplate(pr, template) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
var _a, _b, _c, _d, _e, _f;
|
||||
let transformed = template;
|
||||
transformed = transformed.replace(/\${{NUMBER}}/g, pr.number.toString());
|
||||
transformed = transformed.replace(/\${{TITLE}}/g, pr.title);
|
||||
@@ -1514,6 +1558,7 @@ function fillTemplate(pr, template) {
|
||||
transformed = transformed.replace(/\${{BODY}}/g, pr.body);
|
||||
transformed = transformed.replace(/\${{ASSIGNEES}}/g, ((_d = pr.assignees) === null || _d === void 0 ? void 0 : _d.join(', ')) || '');
|
||||
transformed = transformed.replace(/\${{REVIEWERS}}/g, ((_e = pr.requestedReviewers) === null || _e === void 0 ? void 0 : _e.join(', ')) || '');
|
||||
transformed = transformed.replace(/\${{APPROVERS}}/g, ((_f = pr.approvedReviewers) === null || _f === void 0 ? void 0 : _f.join(', ')) || '');
|
||||
return transformed;
|
||||
}
|
||||
function transform(filled, transformers) {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -35,6 +35,7 @@ async function run(): Promise<void> {
|
||||
const includeOpen = core.getInput('includeOpen') === 'true'
|
||||
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
|
||||
const failOnError = core.getInput('failOnError') === 'true'
|
||||
const fetchReviewers = core.getInput('fetchReviewers') === 'true'
|
||||
const commitMode = core.getInput('commitMode') === 'true'
|
||||
|
||||
const result = await new ReleaseNotesBuilder(
|
||||
@@ -48,6 +49,7 @@ async function run(): Promise<void> {
|
||||
includeOpen,
|
||||
failOnError,
|
||||
ignorePreReleases,
|
||||
fetchReviewers,
|
||||
commitMode,
|
||||
configuration
|
||||
).build()
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface PullRequestInfo {
|
||||
body: string
|
||||
assignees: string[]
|
||||
requestedReviewers: string[]
|
||||
approvedReviewers: string[]
|
||||
status: 'open' | 'merged'
|
||||
}
|
||||
|
||||
@@ -26,6 +27,9 @@ type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data']
|
||||
type PullsListData =
|
||||
RestEndpointMethodTypes['pulls']['list']['response']['data']
|
||||
|
||||
type PullReviewData =
|
||||
RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
|
||||
|
||||
export class PullRequests {
|
||||
constructor(private octokit: Octokit) {}
|
||||
|
||||
@@ -127,6 +131,28 @@ export class PullRequests {
|
||||
|
||||
return sortPullRequests(openPrs, true)
|
||||
}
|
||||
|
||||
async getReviewers(
|
||||
owner: string,
|
||||
repo: string,
|
||||
pr: PullRequestInfo
|
||||
): Promise<PullReviewData[]> {
|
||||
const options = this.octokit.pulls.listReviews.endpoint.merge({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number
|
||||
})
|
||||
|
||||
for await (const response of this.octokit.paginate.iterator(options)) {
|
||||
const reviews: PullReviewData = response.data as PullReviewData
|
||||
pr.approvedReviewers = reviews
|
||||
.filter(r => r.state === 'APPROVED')
|
||||
.map(r => r.user?.login)
|
||||
.filter(r => !!r) as string[]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export function sortPullRequests(
|
||||
@@ -192,5 +218,6 @@ const mapPullRequest = (
|
||||
assignees: pr.assignees?.map(asignee => asignee?.login || '') || [],
|
||||
requestedReviewers:
|
||||
pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || [],
|
||||
approvedReviewers: [],
|
||||
status
|
||||
})
|
||||
|
||||
+20
-2
@@ -13,6 +13,7 @@ export interface ReleaseNotesOptions {
|
||||
toTag: string // the tag/ref up to
|
||||
includeOpen: boolean // defines if we should also fetch open pull requests
|
||||
failOnError: boolean // defines if we should fail the action in case of an error
|
||||
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
|
||||
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported
|
||||
configuration: Configuration // the configuration as defined in `configuration.ts`
|
||||
}
|
||||
@@ -81,7 +82,8 @@ export class ReleaseNotes {
|
||||
private async getMergedPullRequests(
|
||||
octokit: Octokit
|
||||
): Promise<PullRequestInfo[]> {
|
||||
const {owner, repo, includeOpen, configuration} = this.options
|
||||
const {owner, repo, includeOpen, fetchReviewers, configuration} =
|
||||
this.options
|
||||
|
||||
const commits = await this.getCommitHistory(octokit)
|
||||
if (commits.length === 0) {
|
||||
@@ -169,7 +171,7 @@ export class ReleaseNotes {
|
||||
})
|
||||
|
||||
// return only prs if the baseBranch is matching the configuration
|
||||
return allPullRequests.filter(pr => {
|
||||
const finalPrs = allPullRequests.filter(pr => {
|
||||
if (baseBranches.length !== 0) {
|
||||
return baseBranchPatterns.some(pattern => {
|
||||
return pr.baseBranch.match(pattern) !== null
|
||||
@@ -177,6 +179,21 @@ export class ReleaseNotes {
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if (fetchReviewers) {
|
||||
core.info(`ℹ️ Fetching reviewers was enabled`)
|
||||
// update PR information with reviewers who approved
|
||||
for (const pr of finalPrs) {
|
||||
await pullRequestsApi.getReviewers(owner, repo, pr)
|
||||
if (pr.approvedReviewers.length > 0) {
|
||||
core.info(
|
||||
`ℹ️ Retrieved ${pr.approvedReviewers.length} reviewer(s) for PR ${owner}/${repo}/#${pr.number}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalPrs
|
||||
}
|
||||
|
||||
private async generateCommitPRs(
|
||||
@@ -213,6 +230,7 @@ export class ReleaseNotes {
|
||||
body: commit.message || '',
|
||||
assignees: [],
|
||||
requestedReviewers: [],
|
||||
approvedReviewers: [],
|
||||
status: 'merged'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -15,9 +15,10 @@ export class ReleaseNotesBuilder {
|
||||
private repo: string | null,
|
||||
private fromTag: string | null,
|
||||
private toTag: string | null,
|
||||
private includeOpen: boolean,
|
||||
private includeOpen: boolean = false,
|
||||
private failOnError: boolean,
|
||||
private ignorePreReleases: boolean,
|
||||
private fetchReviewers: boolean = false,
|
||||
private commitMode: boolean,
|
||||
private configuration: Configuration
|
||||
) {}
|
||||
@@ -91,6 +92,7 @@ export class ReleaseNotesBuilder {
|
||||
toTag: this.toTag,
|
||||
includeOpen: this.includeOpen,
|
||||
failOnError: this.failOnError,
|
||||
fetchReviewers: this.fetchReviewers,
|
||||
commitMode: this.commitMode,
|
||||
configuration: this.configuration
|
||||
}
|
||||
|
||||
@@ -335,6 +335,10 @@ function fillTemplate(pr: PullRequestInfo, template: string): string {
|
||||
/\${{REVIEWERS}}/g,
|
||||
pr.requestedReviewers?.join(', ') || ''
|
||||
)
|
||||
transformed = transformed.replace(
|
||||
/\${{APPROVERS}}/g,
|
||||
pr.approvedReviewers?.join(', ') || ''
|
||||
)
|
||||
return transformed
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user