angular-docs-cn/dev-infra/release/publish/pull-request-state.ts
George Kalpakas c76dda6aeb fix(dev-infra): correctly check for commit that closes PR (#39135)
The `ng-dev release publish` command needs to check whether a commit
closed a pull request. This is implemented via checking the commit
message for specific closing keywords referencing the pull request
number.

The regex used previously failed to correctly ensure that the specified
pull request was referenced. For example, it would allow `#12345` to
also match for `#1234`.

This commit fixes the regex.

PR Close #39135
2020-10-12 10:47:13 -07:00

73 lines
3.5 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as Octokit from '@octokit/rest';
import {GitClient} from '../../utils/git/index';
/** State of a pull request in Github. */
export type PullRequestState = 'merged'|'closed'|'open';
/** Gets whether a given pull request has been merged. */
export async function getPullRequestState(api: GitClient, id: number): Promise<PullRequestState> {
const {data} = await api.github.pulls.get({...api.remoteParams, pull_number: id});
if (data.merged) {
return 'merged';
} else if (data.closed_at !== null) {
return await isPullRequestClosedWithAssociatedCommit(api, id) ? 'merged' : 'closed';
} else {
return 'open';
}
}
/**
* Whether the pull request has been closed with an associated commit. This is usually
* the case if a PR has been merged using the autosquash merge script strategy. Since
* the merge is not fast-forward, Github does not consider the PR as merged and instead
* shows the PR as closed. See for example: https://github.com/angular/angular/pull/37918.
*/
async function isPullRequestClosedWithAssociatedCommit(api: GitClient, id: number) {
const request =
api.github.issues.listEvents.endpoint.merge({...api.remoteParams, issue_number: id});
const events: Octokit.IssuesListEventsResponse = await api.github.paginate(request);
// Iterate through the events of the pull request in reverse. We want to find the most
// recent events and check if the PR has been closed with a commit associated with it.
// If the PR has been closed through a commit, we assume that the PR has been merged
// using the autosquash merge strategy. For more details. See the `AutosquashMergeStrategy`.
for (let i = events.length - 1; i >= 0; i--) {
const {event, commit_id} = events[i];
// If we come across a "reopened" event, we abort looking for referenced commits. Any
// commits that closed the PR before, are no longer relevant and did not close the PR.
if (event === 'reopened') {
return false;
}
// If a `closed` event is captured with a commit assigned, then we assume that
// this PR has been merged properly.
if (event === 'closed' && commit_id) {
return true;
}
// If the PR has been referenced by a commit, check if the commit closes this pull
// request. Note that this is needed besides checking `closed` as PRs could be merged
// into any non-default branch where the `Closes <..>` keyword does not work and the PR
// is simply closed without an associated `commit_id`. For more details see:
// https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords#:~:text=non-default.
if (event === 'referenced' && commit_id &&
await isCommitClosingPullRequest(api, commit_id, id)) {
return true;
}
}
return false;
}
/** Checks whether the specified commit is closing the given pull request. */
async function isCommitClosingPullRequest(api: GitClient, sha: string, id: number) {
const {data} = await api.github.repos.getCommit({...api.remoteParams, ref: sha});
// Matches the closing keyword supported in commit messages. See:
// https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords.
return data.commit.message.match(new RegExp(`close[sd]? #${id}(?!\\d)`, 'i'));
}