- add capability to fetch review comments and use them as part of the target template

- allow array syntax to be used as part of reviewers, approvers, assignees
This commit is contained in:
Mike Penz
2022-12-09 13:03:23 +00:00
committed by GitHub
parent 6846cd4c4f
commit 38aabea998
5 changed files with 194 additions and 10 deletions
Generated Vendored
+96 -3
View File
@@ -637,7 +637,43 @@ class PullRequests {
}
finally { if (e_3) throw e_3.error; }
}
return [];
});
}
getReviews(owner, repo, pr) {
var _a, e_4, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
const options = this.octokit.pulls.listReviews.endpoint.merge({
owner,
repo,
pull_number: pr.number,
sort: 'created',
direction: 'desc'
});
const prReviews = [];
try {
for (var _d = true, _e = __asyncValues(this.octokit.paginate.iterator(options)), _f; _f = yield _e.next(), _a = _f.done, !_a;) {
_c = _f.value;
_d = false;
try {
const response = _c;
const comments = response.data;
for (const comment of comments) {
prReviews.push(mapComment(comment));
}
}
finally {
_d = true;
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_4) throw e_4.error; }
}
pr.reviews = prReviews;
});
}
}
@@ -705,7 +741,7 @@ const mapPullRequest = (pr, status = 'open') => {
baseBranch: pr.base.ref,
branch: pr.head.ref,
createdAt: (0, moment_1.default)(pr.created_at),
mergedAt: pr.merged_at ? (0, moment_1.default)(pr.merged_at) : null,
mergedAt: pr.merged_at ? (0, moment_1.default)(pr.merged_at) : undefined,
mergeCommitSha: pr.merge_commit_sha || '',
author: ((_a = pr.user) === null || _a === void 0 ? void 0 : _a.login) || '',
repoName: pr.base.repo.full_name,
@@ -715,9 +751,20 @@ const mapPullRequest = (pr, status = 'open') => {
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: [],
reviews: undefined,
status
});
};
const mapComment = (comment) => {
var _a;
return ({
id: comment.id,
htmlURL: comment.html_url,
submittedAt: comment.submitted_at ? (0, moment_1.default)(comment.submitted_at) : undefined,
author: ((_a = comment.user) === null || _a === void 0 ? void 0 : _a.login) || '',
body: comment.body
});
};
/***/ }),
@@ -833,8 +880,10 @@ class ReleaseNotes {
});
}
getMergedPullRequests(octokit) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
const { owner, repo, includeOpen, fetchReviewers, configuration } = this.options;
const fetchReviews = true; // TEMPORARY!!
const diffInfo = yield this.getCommitHistory(octokit);
const commits = diffInfo.commitInfo;
if (commits.length === 0) {
@@ -904,6 +953,19 @@ class ReleaseNotes {
else {
core.debug(`️ Fetching reviewers was disabled`);
}
if (fetchReviews) {
core.info(`️ Fetching reviews was enabled`);
// update PR information with reviewers who approved
for (const pr of finalPrs) {
yield pullRequestsApi.getReviews(owner, repo, pr);
if ((((_a = pr.reviews) === null || _a === void 0 ? void 0 : _a.length) || 0) > 0) {
core.info(`️ Retrieved ${((_b = pr.reviews) === null || _b === void 0 ? void 0 : _b.length) || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`);
}
}
}
else {
core.debug(`️ Fetching reviews was disabled`);
}
return [diffInfo, finalPrs];
});
}
@@ -1731,6 +1793,10 @@ function fillAdditionalPlaceholders(options, placeholderMap /* placeholderKey an
}
function fillPrTemplate(pr, template, placeholders /* placeholders to apply */, placeholderPrMap /* map to keep replaced placeholder values with their key */) {
var _a, _b, _c, _d, _e, _f;
let transformed = replaceArrayPlaceholders(template, 'ASSIGNEES', pr.assignees || []);
transformed = replaceArrayPlaceholders(transformed, 'REVIEWERS', pr.requestedReviewers || []);
transformed = replaceArrayPlaceholders(transformed, 'APPROVERS', pr.approvedReviewers || []);
transformed = replaceReviewPlaceholders(transformed, 'REVIEWS', pr.reviews || []);
const placeholderMap = new Map();
placeholderMap.set('NUMBER', pr.number.toString());
placeholderMap.set('TITLE', pr.title);
@@ -1748,7 +1814,7 @@ function fillPrTemplate(pr, template, placeholders /* placeholders to apply */,
placeholderMap.set('APPROVERS', ((_f = pr.approvedReviewers) === null || _f === void 0 ? void 0 : _f.join(', ')) || '');
placeholderMap.set('BRANCH', pr.branch || '');
placeholderMap.set('BASE_BRANCH', pr.baseBranch);
return replacePlaceholders(template, placeholderMap, placeholders, placeholderPrMap);
return replacePlaceholders(transformed, placeholderMap, placeholders, placeholderPrMap);
}
function replacePlaceholders(template, placeholderMap /* placeholderKey and original value */, placeholders /* placeholders to apply */, placeholderPrMap /* map to keep replaced placeholder values with their key */) {
let transformed = template;
@@ -1791,6 +1857,33 @@ function replacePrPlaceholders(template, placeholderPrMap /* map with all pr rel
}
return transformed;
}
function replaceArrayPlaceholders(template, key, values) {
let transformed = template;
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(`\${{${key}[${i}]}}`, values[i]);
}
transformed = transformed.replaceAll(`\${{${key}[*]}}`, values.join(', '));
return transformed;
}
function replaceReviewPlaceholders(template, parentKey, values) {
var _a;
let transformed = template;
// retrieve the keys from the CommentInfo object
const comment = {
id: 0,
htmlURL: '',
submittedAt: undefined,
author: '',
body: ''
};
for (const childKey of Object.keys(comment)) {
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(`\${{${parentKey}[${i}].${childKey}}}`, ((_a = values[i][childKey]) === null || _a === void 0 ? void 0 : _a.toLocaleString('en')) || '');
}
transformed = transformed.replaceAll(`\${{${parentKey}[*].${childKey}}}`, values.map(value => { var _a; return ((_a = value[childKey]) === null || _a === void 0 ? void 0 : _a.toLocaleString('en')) || ''; }).join(', '));
}
return transformed;
}
function cleanupPrPlaceHolders(template, placeholders /* placeholders to apply */) {
let transformed = template;
for (const [, phs] of placeholders) {
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+41 -4
View File
@@ -11,7 +11,7 @@ export interface PullRequestInfo {
baseBranch: string
branch?: string
createdAt: moment.Moment
mergedAt: moment.Moment | null
mergedAt: moment.Moment | undefined
mergeCommitSha: string
author: string
repoName: string
@@ -21,15 +21,26 @@ export interface PullRequestInfo {
assignees: string[]
requestedReviewers: string[]
approvedReviewers: string[]
reviews?: CommentInfo[]
status: 'open' | 'merged'
}
export interface CommentInfo {
id: number
htmlURL: string
submittedAt: moment.Moment | undefined
author: string
body: string
}
type PullData = RestEndpointMethodTypes['pulls']['get']['response']['data']
type PullsListData = RestEndpointMethodTypes['pulls']['list']['response']['data']
type PullReviewData = RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
type PullReviewsData = RestEndpointMethodTypes['pulls']['listReviews']['response']['data']
export class PullRequests {
constructor(private octokit: Octokit) {}
@@ -122,7 +133,7 @@ export class PullRequests {
return sortPrs(openPrs)
}
async getReviewers(owner: string, repo: string, pr: PullRequestInfo): Promise<PullReviewData[]> {
async getReviewers(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
const options = this.octokit.pulls.listReviews.endpoint.merge({
owner,
repo,
@@ -136,8 +147,25 @@ export class PullRequests {
.map(r => r.user?.login)
.filter(r => !!r) as string[]
}
}
return []
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(mapComment(comment))
}
}
pr.reviews = prReviews
}
}
@@ -204,7 +232,7 @@ const mapPullRequest = (
baseBranch: pr.base.ref,
branch: pr.head.ref,
createdAt: moment(pr.created_at),
mergedAt: pr.merged_at ? moment(pr.merged_at) : null,
mergedAt: pr.merged_at ? moment(pr.merged_at) : undefined,
mergeCommitSha: pr.merge_commit_sha || '',
author: pr.user?.login || '',
repoName: pr.base.repo.full_name,
@@ -214,5 +242,14 @@ const mapPullRequest = (
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
})
+14
View File
@@ -95,6 +95,7 @@ export class ReleaseNotes {
private async getMergedPullRequests(octokit: Octokit): Promise<[DiffInfo, PullRequestInfo[]]> {
const {owner, repo, includeOpen, fetchReviewers, configuration} = this.options
const fetchReviews = true // TEMPORARY!!
const diffInfo = await this.getCommitHistory(octokit)
const commits = diffInfo.commitInfo
@@ -198,6 +199,19 @@ export class ReleaseNotes {
core.debug(`️ Fetching reviewers was disabled`)
}
if (fetchReviews) {
core.info(`️ Fetching reviews was enabled`)
// update PR information with reviewers who approved
for (const pr of finalPrs) {
await pullRequestsApi.getReviews(owner, repo, pr)
if ((pr.reviews?.length || 0) > 0) {
core.info(`️ Retrieved ${pr.reviews?.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`)
}
}
} else {
core.debug(`️ Fetching reviews was disabled`)
}
return [diffInfo, finalPrs]
}
+42 -2
View File
@@ -1,6 +1,6 @@
import * as core from '@actions/core'
import {Category, DefaultConfiguration, Extractor, Placeholder, Transformer} from './configuration'
import {PullRequestInfo, sortPullRequests} from './pullRequests'
import {CommentInfo, PullRequestInfo, sortPullRequests} from './pullRequests'
import {ReleaseNotesOptions} from './releaseNotes'
import {DiffInfo} from './commits'
import {createOrSet, haveCommonElements, haveEveryElements} from './utils'
@@ -299,6 +299,11 @@ function fillPrTemplate(
placeholders: Map<string, Placeholder[]> /* placeholders to apply */,
placeholderPrMap: Map<string, string[]> /* map to keep replaced placeholder values with their key */
): string {
let transformed = replaceArrayPlaceholders(template, 'ASSIGNEES', pr.assignees || [])
transformed = replaceArrayPlaceholders(transformed, 'REVIEWERS', pr.requestedReviewers || [])
transformed = replaceArrayPlaceholders(transformed, 'APPROVERS', pr.approvedReviewers || [])
transformed = replaceReviewPlaceholders(transformed, 'REVIEWS', pr.reviews || [])
const placeholderMap = new Map<string, string>()
placeholderMap.set('NUMBER', pr.number.toString())
placeholderMap.set('TITLE', pr.title)
@@ -316,7 +321,7 @@ function fillPrTemplate(
placeholderMap.set('APPROVERS', pr.approvedReviewers?.join(', ') || '')
placeholderMap.set('BRANCH', pr.branch || '')
placeholderMap.set('BASE_BRANCH', pr.baseBranch)
return replacePlaceholders(template, placeholderMap, placeholders, placeholderPrMap)
return replacePlaceholders(transformed, placeholderMap, placeholders, placeholderPrMap)
}
function replacePlaceholders(
@@ -375,6 +380,41 @@ function replacePrPlaceholders(
return transformed
}
function replaceArrayPlaceholders(template: string, key: string, values: string[]): string {
let transformed = template
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(`\${{${key}[${i}]}}`, values[i])
}
transformed = transformed.replaceAll(`\${{${key}[*]}}`, values.join(', '))
return transformed
}
function replaceReviewPlaceholders(template: string, parentKey: string, values: CommentInfo[]): string {
let transformed = template
// retrieve the keys from the CommentInfo object
const comment: CommentInfo = {
id: 0,
htmlURL: '',
submittedAt: undefined,
author: '',
body: ''
}
for (const childKey of Object.keys(comment)) {
for (let i = 0; i < values.length; i++) {
transformed = transformed.replaceAll(
`\${{${parentKey}[${i}].${childKey}}}`,
values[i][childKey as keyof CommentInfo]?.toLocaleString('en') || ''
)
}
transformed = transformed.replaceAll(
`\${{${parentKey}[*].${childKey}}}`,
values.map(value => value[childKey as keyof CommentInfo]?.toLocaleString('en') || '').join(', ')
)
}
return transformed
}
function cleanupPrPlaceHolders(
template: string,
placeholders: Map<string, Placeholder[]> /* placeholders to apply */