- introduce new configuration fetchViaCommits which is best suited for environments with shorter diffs and/or squash merging (1 api request per commit)

This commit is contained in:
Mike Penz
2023-07-28 10:08:07 +00:00
committed by GitHub
parent 80ac8b0a0c
commit e463dcb45c
7 changed files with 103 additions and 37 deletions
+1
View File
@@ -333,6 +333,7 @@ For advanced use cases additional settings can be provided to the action
| `includeOpen` | Enables to also fetch currently open PRs. Default: false | | `includeOpen` | Enables to also fetch currently open PRs. Default: false |
| `ignorePreReleases` | Allows to ignore pre-releases for changelog generation (E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick). Only used if `fromTag` was not specified. Default: false | | `ignorePreReleases` | Allows to ignore pre-releases for changelog generation (E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick). Only used if `fromTag` was not specified. Default: false |
| `failOnError` | Defines if the action will result in a build failure if problems occurred. Default: false | | `failOnError` | Defines if the action will result in a build failure if problems occurred. Default: false |
| `fetchViaCommits` | Enables PRs to get fetched via the commits identified between from->to tag. This will do 1 API request per commit -> Best for scenarios with squash merges | Or shorter from->to diffs (< 10 commits) | Also effective for shorters diffs for very old PRs. Default: false |
| `fetchReviewers` | Will enable fetching the users/reviewers who approved the PR. Default: false | | `fetchReviewers` | Will enable fetching the users/reviewers who approved the PR. Default: false |
| `fetchReleaseInformation` | Will enable fetching additional release information from tags. Default: false | | `fetchReleaseInformation` | Will enable fetching additional release information from tags. Default: false |
| `fetchReviews` | Will enable fetching the reviews on of the PR. Default: false | | `fetchReviews` | Will enable fetching the reviews on of the PR. Default: false |
+3
View File
@@ -28,6 +28,9 @@ inputs:
failOnError: failOnError:
description: 'Defines if the action should result in a build failure, if an error was discovered' description: 'Defines if the action should result in a build failure, if an error was discovered'
default: "false" default: "false"
fetchViaCommits:
description: 'Defines if PRs are fetched via the commits identified. This will do 1 API request per commit -> Best for scenarios with squash merges | Or shorter from-to diffs (< 10 commits) | Also effective for shorters diffs for very old PRs'
default: "false"
fetchReviewers: fetchReviewers:
description: 'Will enable fetching the users/reviewers who approved the PR' description: 'Will enable fetching the users/reviewers who approved the PR'
default: "false" default: "false"
Generated Vendored
+92 -34
View File
@@ -141,12 +141,13 @@ function run() {
const includeOpen = core.getInput('includeOpen') === 'true'; const includeOpen = core.getInput('includeOpen') === 'true';
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'; const ignorePreReleases = core.getInput('ignorePreReleases') === 'true';
const failOnError = core.getInput('failOnError') === 'true'; const failOnError = core.getInput('failOnError') === 'true';
const fetchViaCommits = core.getInput('fetchViaCommits') === 'true';
const fetchReviewers = core.getInput('fetchReviewers') === 'true'; const fetchReviewers = core.getInput('fetchReviewers') === 'true';
const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true'; const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true';
const fetchReviews = core.getInput('fetchReviews') === 'true'; const fetchReviews = core.getInput('fetchReviews') === 'true';
const commitMode = core.getInput('commitMode') === 'true'; const commitMode = core.getInput('commitMode') === 'true';
const exportOnly = core.getInput('exportOnly') === 'true'; const exportOnly = core.getInput('exportOnly') === 'true';
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, fetchReleaseInformation, fetchReviews, commitMode, exportOnly, configuration).build(); const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchViaCommits, fetchReviewers, fetchReleaseInformation, fetchReviews, commitMode, exportOnly, configuration).build();
core.setOutput('changelog', result); core.setOutput('changelog', result);
// write the result in changelog to file if possible // write the result in changelog to file if possible
const outputFile = core.getInput('outputFile'); const outputFile = core.getInput('outputFile');
@@ -282,7 +283,7 @@ const transform_1 = __nccwpck_require__(1644);
const github_pr_collector_1 = __nccwpck_require__(3196); const github_pr_collector_1 = __nccwpck_require__(3196);
const utils_2 = __nccwpck_require__(853); const utils_2 = __nccwpck_require__(853);
class ReleaseNotesBuilder { class ReleaseNotesBuilder {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, fetchReleaseInformation = false, fetchReviews = false, commitMode = false, exportOnly = false, configuration) { constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchViaCommits = false, fetchReviewers = false, fetchReleaseInformation = false, fetchReviews = false, commitMode = false, exportOnly = false, configuration) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.token = token; this.token = token;
this.repositoryPath = repositoryPath; this.repositoryPath = repositoryPath;
@@ -293,6 +294,7 @@ class ReleaseNotesBuilder {
this.includeOpen = includeOpen; this.includeOpen = includeOpen;
this.failOnError = failOnError; this.failOnError = failOnError;
this.ignorePreReleases = ignorePreReleases; this.ignorePreReleases = ignorePreReleases;
this.fetchViaCommits = fetchViaCommits;
this.fetchReviewers = fetchReviewers; this.fetchReviewers = fetchReviewers;
this.fetchReleaseInformation = fetchReleaseInformation; this.fetchReleaseInformation = fetchReleaseInformation;
this.fetchReviews = fetchReviews; this.fetchReviews = fetchReviews;
@@ -319,7 +321,7 @@ class ReleaseNotesBuilder {
core.debug(`Resolved 'repo' as ${this.repo}`); core.debug(`Resolved 'repo' as ${this.repo}`);
} }
core.endGroup(); core.endGroup();
const prData = yield new github_pr_collector_1.PullRequestCollector(this.baseUrl, this.token, this.repositoryPath, this.owner, this.repo, this.fromTag, this.toTag, this.includeOpen, this.failOnError, this.ignorePreReleases, this.fetchReviewers, this.fetchReleaseInformation, this.fetchReviews, this.commitMode, this.configuration).build(); const prData = yield new github_pr_collector_1.PullRequestCollector(this.baseUrl, this.token, this.repositoryPath, this.owner, this.repo, this.fromTag, this.toTag, this.includeOpen, this.failOnError, this.ignorePreReleases, this.fetchViaCommits, this.fetchReviewers, this.fetchReleaseInformation, this.fetchReviews, this.commitMode, this.configuration).build();
if (prData == null) { if (prData == null) {
return null; return null;
} }
@@ -26557,7 +26559,7 @@ const https_proxy_agent_1 = __nccwpck_require__(7219);
const pullRequests_1 = __nccwpck_require__(1948); const pullRequests_1 = __nccwpck_require__(1948);
const commits_1 = __nccwpck_require__(5789); const commits_1 = __nccwpck_require__(5789);
class PullRequestCollector { class PullRequestCollector {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, fetchReleaseInformation = false, fetchReviews = false, commitMode = false, configuration) { constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchViaCommits = false, fetchReviewers = false, fetchReleaseInformation = false, fetchReviews = false, commitMode = false, configuration) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.token = token; this.token = token;
this.repositoryPath = repositoryPath; this.repositoryPath = repositoryPath;
@@ -26568,6 +26570,7 @@ class PullRequestCollector {
this.includeOpen = includeOpen; this.includeOpen = includeOpen;
this.failOnError = failOnError; this.failOnError = failOnError;
this.ignorePreReleases = ignorePreReleases; this.ignorePreReleases = ignorePreReleases;
this.fetchViaCommits = fetchViaCommits;
this.fetchReviewers = fetchReviewers; this.fetchReviewers = fetchReviewers;
this.fetchReleaseInformation = fetchReleaseInformation; this.fetchReleaseInformation = fetchReleaseInformation;
this.fetchReviews = fetchReviews; this.fetchReviews = fetchReviews;
@@ -26632,6 +26635,7 @@ class PullRequestCollector {
toTag: thisTag, toTag: thisTag,
includeOpen: this.includeOpen, includeOpen: this.includeOpen,
failOnError: this.failOnError, failOnError: this.failOnError,
fetchViaCommits: this.fetchViaCommits,
fetchReviewers: this.fetchReviewers, fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation, fetchReleaseInformation: this.fetchReleaseInformation,
fetchReviews: this.fetchReviews, fetchReviews: this.fetchReviews,
@@ -26774,15 +26778,47 @@ class PullRequests {
} }
}); });
} }
getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests) { getForCommitHash(owner, repo, commit_sha, maxPullRequests) {
var _a, e_1, _b, _c; var _a, e_1, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
const mergedPRs = [];
const options = this.octokit.repos.listPullRequestsAssociatedWithCommit.endpoint.merge({
owner,
repo,
commit_sha,
per_page: `${Math.min(10, maxPullRequests)}`,
direction: 'desc'
});
try {
for (var _d = true, _e = __asyncValues(this.octokit.paginate.iterator(options)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const response = _c;
const prs = response.data;
for (const pr of prs) {
mergedPRs.push(mapPullRequest(pr, !!pr.merged_at ? 'merged' : 'open'));
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
return sortPrs(mergedPRs);
});
}
getBetweenDates(owner, repo, fromDate, toDate, maxPullRequests) {
var _a, e_2, _b, _c;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const mergedPRs = []; const mergedPRs = [];
const options = this.octokit.pulls.list.endpoint.merge({ const options = this.octokit.pulls.list.endpoint.merge({
owner, owner,
repo, repo,
state: 'closed', state: 'closed',
sort: 'updated', sort: 'merged',
per_page: `${Math.min(100, maxPullRequests)}`, per_page: `${Math.min(100, maxPullRequests)}`,
direction: 'desc' direction: 'desc'
}); });
@@ -26803,22 +26839,22 @@ class PullRequests {
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests}`); core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests}`);
} }
// bail out early to not keep iterating on PRs super old // bail out early to not keep iterating on PRs super old
return sortPrs(mergedPRs); break;
} }
} }
} }
catch (e_1_1) { e_1 = { error: e_1_1 }; } catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally { finally {
try { try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
} }
finally { if (e_1) throw e_1.error; } finally { if (e_2) throw e_2.error; }
} }
return sortPrs(mergedPRs); return sortPrs(mergedPRs);
}); });
} }
getOpen(owner, repo, maxPullRequests) { getOpen(owner, repo, maxPullRequests) {
var _a, e_2, _b, _c; var _a, e_3, _b, _c;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const openPrs = []; const openPrs = [];
const options = this.octokit.pulls.list.endpoint.merge({ const options = this.octokit.pulls.list.endpoint.merge({
@@ -26844,22 +26880,22 @@ class PullRequests {
core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests}`); core.warning(`⚠️ Reached 'maxPullRequests' count ${maxPullRequests}`);
} }
// bail out early to not keep iterating on PRs super old // bail out early to not keep iterating on PRs super old
return sortPrs(openPrs); break;
} }
} }
} }
catch (e_2_1) { e_2 = { error: e_2_1 }; } catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally { finally {
try { try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
} }
finally { if (e_2) throw e_2.error; } finally { if (e_3) throw e_3.error; }
} }
return sortPrs(openPrs); return sortPrs(openPrs);
}); });
} }
getReviews(owner, repo, pr) { getReviews(owner, repo, pr) {
var _a, e_3, _b, _c; var _a, e_4, _b, _c;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const options = this.octokit.pulls.listReviews.endpoint.merge({ const options = this.octokit.pulls.listReviews.endpoint.merge({
owner, owner,
@@ -26880,12 +26916,12 @@ class PullRequests {
} }
} }
} }
catch (e_3_1) { e_3 = { error: e_3_1 }; } catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally { finally {
try { try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
} }
finally { if (e_3) throw e_3.error; } finally { if (e_4) throw e_4.error; }
} }
pr.reviews = prReviews; pr.reviews = prReviews;
}); });
@@ -26909,27 +26945,49 @@ class PullRequests {
fromDate = maxFromDate; fromDate = maxFromDate;
} }
core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`); core.info(`️ Fetching PRs between dates ${fromDate.toISOString()} to ${toDate.toISOString()} for ${owner}/${repo}`);
const pullRequests = yield this.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests);
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} in date range from API`);
const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches); const prCommits = (0, commits_1.filterCommits)(commits, configuration.exclude_merge_branches);
core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`); core.info(`️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`);
// create array of commits for this release // create array of commits for this release
const releaseCommitHashes = prCommits.map(commmit => { const releaseCommitHashes = prCommits.map(commit => {
return commmit.sha; return commit.sha;
}); });
// filter out pull requests not associated with this release let pullRequests;
const mergedPullRequests = pullRequests.filter(pr => { if (options.fetchViaCommits) {
return releaseCommitHashes.includes(pr.mergeCommitSha); // fetch PRs based on commits instead (will get associated PRs per commit found)
}); const prsForReleaseCommits = new Map();
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`); for (const commit of prCommits) {
let allPullRequests = mergedPullRequests; const result = yield this.getForCommitHash(owner, repo, commit.sha, configuration.max_pull_requests);
if (includeOpen) { result.forEach(pr => prsForReleaseCommits.set(pr.number, pr));
// retrieve all open pull requests }
const openPullRequests = yield this.getOpen(owner, repo, configuration.max_pull_requests); const dedupedPrsForReleaseCommits = Array.from(prsForReleaseCommits.values());
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`); if (!includeOpen) {
// all pull requests pullRequests = dedupedPrsForReleaseCommits.filter(pr => pr.status !== 'open');
allPullRequests = allPullRequests.concat(openPullRequests); core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} based on the release commit hashes`);
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`); }
else {
pullRequests = dedupedPrsForReleaseCommits;
core.info(`️ Retrieved ${pullRequests.length} PRs for ${owner}/${repo} based on the release commit hashes (including open)`);
}
}
else {
// fetch PRs based on the date range identified
const pullRequestsBetweenDate = yield this.getBetweenDates(owner, repo, fromDate, toDate, configuration.max_pull_requests);
core.info(`️ Retrieved ${pullRequestsBetweenDate.length} PRs for ${owner}/${repo} in date range from API`);
// filter out pull requests not associated with this release
const mergedPullRequests = pullRequestsBetweenDate.filter(pr => {
return releaseCommitHashes.includes(pr.mergeCommitSha);
});
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`);
let allPullRequests = mergedPullRequests;
if (includeOpen) {
// retrieve all open pull requests
const openPullRequests = yield this.getOpen(owner, repo, configuration.max_pull_requests);
core.info(`️ Retrieved ${openPullRequests.length} open PRs for ${owner}/${repo}`);
// all pull requests
allPullRequests = allPullRequests.concat(openPullRequests);
core.info(`️ Retrieved ${allPullRequests.length} total PRs for ${owner}/${repo}`);
}
pullRequests = allPullRequests;
} }
// retrieve base branches we allow // retrieve base branches we allow
const baseBranches = configuration.base_branches; const baseBranches = configuration.base_branches;
@@ -26937,7 +26995,7 @@ class PullRequests {
return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu'); return new RegExp(baseBranch.replace('\\\\', '\\'), 'gu');
}); });
// return only prs if the baseBranch is matching the configuration // return only prs if the baseBranch is matching the configuration
const finalPrs = allPullRequests.filter(pr => { const finalPrs = pullRequests.filter(pr => {
if (baseBranches.length !== 0) { if (baseBranches.length !== 0) {
return baseBranchPatterns.some(pattern => { return baseBranchPatterns.some(pattern => {
return pr.baseBranch.match(pattern) !== null; return pr.baseBranch.match(pattern) !== null;
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -238,7 +238,7 @@ export class PullRequests {
}) })
let pullRequests: PullRequestInfo[] let pullRequests: PullRequestInfo[]
if (releaseCommitHashes.length < 10) { if (options.fetchViaCommits) {
// fetch PRs based on commits instead (will get associated PRs per commit found) // fetch PRs based on commits instead (will get associated PRs per commit found)
const prsForReleaseCommits: Map<number, PullRequestInfo> = new Map() const prsForReleaseCommits: Map<number, PullRequestInfo> = new Map()
for (const commit of prCommits) { for (const commit of prCommits) {
@@ -264,7 +264,7 @@ export class PullRequests {
}) })
core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`) core.info(`️ Retrieved ${mergedPullRequests.length} merged PRs for ${owner}/${repo}`)
let allPullRequests = mergedPullRequests let allPullRequests = mergedPullRequests
if (includeOpen) { if (includeOpen) {
// retrieve all open pull requests // retrieve all open pull requests
+2
View File
@@ -50,6 +50,7 @@ async function run(): Promise<void> {
const includeOpen = core.getInput('includeOpen') === 'true' const includeOpen = core.getInput('includeOpen') === 'true'
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true' const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
const failOnError = core.getInput('failOnError') === 'true' const failOnError = core.getInput('failOnError') === 'true'
const fetchViaCommits = core.getInput('fetchViaCommits') === 'true'
const fetchReviewers = core.getInput('fetchReviewers') === 'true' const fetchReviewers = core.getInput('fetchReviewers') === 'true'
const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true' const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true'
const fetchReviews = core.getInput('fetchReviews') === 'true' const fetchReviews = core.getInput('fetchReviews') === 'true'
@@ -67,6 +68,7 @@ async function run(): Promise<void> {
includeOpen, includeOpen,
failOnError, failOnError,
ignorePreReleases, ignorePreReleases,
fetchViaCommits,
fetchReviewers, fetchReviewers,
fetchReleaseInformation, fetchReleaseInformation,
fetchReviews, fetchReviews,
+2
View File
@@ -40,6 +40,7 @@ export class ReleaseNotesBuilder {
private includeOpen: boolean = false, private includeOpen: boolean = false,
private failOnError: boolean, private failOnError: boolean,
private ignorePreReleases: boolean, private ignorePreReleases: boolean,
private fetchViaCommits: boolean = false,
private fetchReviewers: boolean = false, private fetchReviewers: boolean = false,
private fetchReleaseInformation: boolean = false, private fetchReleaseInformation: boolean = false,
private fetchReviews: boolean = false, private fetchReviews: boolean = false,
@@ -77,6 +78,7 @@ export class ReleaseNotesBuilder {
this.includeOpen, this.includeOpen,
this.failOnError, this.failOnError,
this.ignorePreReleases, this.ignorePreReleases,
this.fetchViaCommits,
this.fetchReviewers, this.fetchReviewers,
this.fetchReleaseInformation, this.fetchReleaseInformation,
this.fetchReviews, this.fetchReviews,