From a63cf7221be2f192d9da24a6cbd78c542d1411e1 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 13 Apr 2021 14:12:32 -0700 Subject: [PATCH] feat(dev-infra): prevent attempting to merge draft, closed or merged pull requests (#41604) During pull request validation, assert that the pull request is not in draft mode, already merged or already closed. PR Close #41604 --- dev-infra/ng-dev.js | 24 ++++++++++++++++++++++++ dev-infra/pr/merge/failures.ts | 12 ++++++++++++ dev-infra/pr/merge/pull-request.ts | 21 ++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/dev-infra/ng-dev.js b/dev-infra/ng-dev.js index d5214b1940..d20c1495ff 100755 --- a/dev-infra/ng-dev.js +++ b/dev-infra/ng-dev.js @@ -3379,6 +3379,15 @@ var PullRequestFailure = /** @class */ (function () { PullRequestFailure.notMergeReady = function () { return new this("Not marked as merge ready."); }; + PullRequestFailure.isDraft = function () { + return new this('Pull request is still in draft.'); + }; + PullRequestFailure.isClosed = function () { + return new this('Pull request is already closed.'); + }; + PullRequestFailure.isMerged = function () { + return new this('Pull request is already merged.'); + }; PullRequestFailure.mismatchingTargetBranch = function (allowedBranches) { return new this("Pull request is set to wrong base branch. Please update the PR in the Github UI " + ("to one of the following branches: " + allowedBranches.join(', ') + ".")); @@ -3490,6 +3499,7 @@ function loadAndValidatePullRequest(_a, prNumber, ignoreNonFatalFailures) { } commitsInPr = prData.commits.nodes.map(function (n) { return parseCommitMessage(n.commit.message); }); try { + assertPendingState(prData); assertChangesAllowForTargetLabel(commitsInPr, targetLabel, config); assertCorrectBreakingChangeLabeling(commitsInPr, labels, config); } @@ -3541,6 +3551,8 @@ function loadAndValidatePullRequest(_a, prNumber, ignoreNonFatalFailures) { /* Graphql schema for the response body the requested pull request. */ var PR_SCHEMA$2 = { url: typedGraphqlify.types.string, + isDraft: typedGraphqlify.types.boolean, + state: typedGraphqlify.types.oneOf(['OPEN', 'MERGED', 'CLOSED']), number: typedGraphqlify.types.number, // Only the last 100 commits from a pull request are obtained as we likely will never see a pull // requests with more than 100 commits. @@ -3646,6 +3658,18 @@ function assertCorrectBreakingChangeLabeling(commits, labels, config) { throw PullRequestFailure.missingBreakingChangeCommit(); } } +/** Assert the pull request is pending, not closed, merged or in draft. */ +function assertPendingState(pr) { + if (pr.isDraft) { + throw PullRequestFailure.isDraft(); + } + switch (pr.state) { + case 'CLOSED': + throw PullRequestFailure.isClosed(); + case 'MERGED': + throw PullRequestFailure.isMerged(); + } +} /** * @license diff --git a/dev-infra/pr/merge/failures.ts b/dev-infra/pr/merge/failures.ts index 5675eb5ad0..587892c8b3 100644 --- a/dev-infra/pr/merge/failures.ts +++ b/dev-infra/pr/merge/failures.ts @@ -36,6 +36,18 @@ export class PullRequestFailure { return new this(`Not marked as merge ready.`); } + static isDraft() { + return new this('Pull request is still in draft.'); + } + + static isClosed() { + return new this('Pull request is already closed.'); + } + + static isMerged() { + return new this('Pull request is already merged.'); + } + static mismatchingTargetBranch(allowedBranches: string[]) { return new this( `Pull request is set to wrong base branch. Please update the PR in the Github UI ` + diff --git a/dev-infra/pr/merge/pull-request.ts b/dev-infra/pr/merge/pull-request.ts index ea38f4c4d0..b7986269eb 100644 --- a/dev-infra/pr/merge/pull-request.ts +++ b/dev-infra/pr/merge/pull-request.ts @@ -82,6 +82,7 @@ export async function loadAndValidatePullRequest( const commitsInPr = prData.commits.nodes.map(n => parseCommitMessage(n.commit.message)); try { + assertPendingState(prData); assertChangesAllowForTargetLabel(commitsInPr, targetLabel, config); assertCorrectBreakingChangeLabeling(commitsInPr, labels, config); } catch (error) { @@ -136,6 +137,8 @@ export async function loadAndValidatePullRequest( /* Graphql schema for the response body the requested pull request. */ const PR_SCHEMA = { url: graphqlTypes.string, + isDraft: graphqlTypes.boolean, + state: graphqlTypes.oneOf(['OPEN', 'MERGED', 'CLOSED'] as const), number: graphqlTypes.number, // Only the last 100 commits from a pull request are obtained as we likely will never see a pull // requests with more than 100 commits. @@ -159,11 +162,13 @@ const PR_SCHEMA = { }), }; +/** A pull request retrieved from github via the graphql API. */ +type RawPullRequest = typeof PR_SCHEMA; /** Fetches a pull request from Github. Returns null if an error occurred. */ async function fetchPullRequestFromGithub( - git: GitClient, prNumber: number): Promise { + git: GitClient, prNumber: number): Promise { try { const x = await getPr(PR_SCHEMA, prNumber, git); return x; @@ -241,3 +246,17 @@ function assertCorrectBreakingChangeLabeling( throw PullRequestFailure.missingBreakingChangeCommit(); } } + + +/** Assert the pull request is pending, not closed, merged or in draft. */ +function assertPendingState(pr: RawPullRequest) { + if (pr.isDraft) { + throw PullRequestFailure.isDraft(); + } + switch (pr.state) { + case 'CLOSED': + throw PullRequestFailure.isClosed(); + case 'MERGED': + throw PullRequestFailure.isMerged(); + } +}