Skip to content

Commit 9d6262d

Browse files
authored
Block pull requests as long as changes are requested (#63)
* Block pull requests as long as changes are requested We add a new `lgtm/blocked` state that makes sure no pull request gets merged while it still has changes requested. Signed-off-by: Yarden Shoham <[email protected]>
1 parent 596d4cc commit 9d6262d

File tree

4 files changed

+39
-19
lines changed

4 files changed

+39
-19
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ included in any milestone.
4242
### LGTM
4343

4444
The script will maintain each pull request's LGTM count. It will add the
45-
appropriate label (one of `lgtm/need 2`, `lgtm/need 1`, or `lgtm/done`) based on
46-
the number of approvals the pull request has. It will also set the commit status
47-
to `success` if the pull request has 2 or more approvals (`pending` if not).
45+
appropriate label (one of `lgtm/need 2`, `lgtm/need 1`, `lgtm/done`, or
46+
`lgtm/blocked`) based on the number of approvals (or change requests) the pull
47+
request has. It will also set the commit status to `success` if the pull request
48+
has 2 or more approvals without changes requested (`pending` if not or `failure`
49+
if changes are requested).
4850

4951
## Usage
5052

src/github.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ export const getMilestones = async (): Promise<Milestone[]> => {
220220
return Object.values(earliestPatchVersions);
221221
};
222222

223-
export const getPrApprovers = async (prNumber: number) => {
223+
export const getPrReviewers = async (
224+
prNumber: number,
225+
): Promise<{ approvers: Set<string>; blockers: Set<string> }> => {
224226
// load all reviews
225227
const reviews: {
226228
state:
@@ -244,23 +246,29 @@ export const getPrApprovers = async (prNumber: number) => {
244246
page++;
245247
}
246248

247-
// count approvers by replaying all reviews (they are already sorted)
249+
// count approvers and blockers by replaying all reviews (they are already sorted)
248250
const approvers = new Set<string>();
251+
const blockers = new Set<string>();
249252
for (const review of reviews) {
250253
switch (review.state) {
251254
case "APPROVED":
252255
approvers.add(review.user.login);
256+
blockers.delete(review.user.login);
253257
break;
254258
case "DISMISSED":
259+
approvers.delete(review.user.login);
260+
blockers.delete(review.user.login);
261+
break;
255262
case "CHANGES_REQUESTED":
256263
approvers.delete(review.user.login);
264+
blockers.add(review.user.login);
257265
break;
258266
default:
259267
break;
260268
}
261269
}
262270

263-
return approvers;
271+
return { approvers, blockers };
264272
};
265273

266274
export const createBackportPr = async (
@@ -331,7 +339,7 @@ export const createBackportPr = async (
331339
);
332340

333341
// request review from original PR approvers
334-
const approvers = await getPrApprovers(originalPr.number);
342+
const { approvers } = await getPrReviewers(originalPr.number);
335343
await fetch(
336344
`${GITHUB_API}/repos/go-gitea/gitea/pulls/${json.number}/requested_reviewers`,
337345
{

src/github_test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
2-
import { fetchBranch, getPrApprovers } from "./github.ts";
2+
import { fetchBranch, getPrReviewers } from "./github.ts";
33

4-
Deno.test("getPrApprovers() returns the appropriate approvers", async () => {
4+
Deno.test("getPrReviewers() returns the appropriate approvers", async () => {
55
const prToApprovers = {
66
23993: new Set(["delvh", "jolheiser"]),
77
24051: new Set(["delvh", "silverwind"]),
@@ -13,7 +13,7 @@ Deno.test("getPrApprovers() returns the appropriate approvers", async () => {
1313
};
1414
await Promise.all(
1515
Object.entries(prToApprovers).map(async ([pr, approvers]) => {
16-
assertEquals(await getPrApprovers(Number(pr)), approvers);
16+
assertEquals((await getPrReviewers(Number(pr))).approvers, approvers);
1717
}),
1818
);
1919
});

src/lgtm.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
addLabels,
3-
getPrApprovers,
3+
getPrReviewers,
44
removeLabel,
55
setCommitStatus,
66
} from "./github.ts";
@@ -14,15 +14,15 @@ export const setPrStatusAndLabel = async (
1414
number: number;
1515
},
1616
) => {
17-
let approvers;
17+
let reviewers;
1818
try {
19-
approvers = await getPrApprovers(pr.number);
19+
reviewers = await getPrReviewers(pr.number);
2020
} catch (error) {
2121
console.error(error);
2222
return;
2323
}
2424

25-
const { state, message, desiredLabel } = getPrStatusAndLabel(approvers.size);
25+
const { state, message, desiredLabel } = getPrStatusAndLabel(reviewers);
2626
const currentLgtmLabels = pr.labels.filter((l) => l.name.startsWith("lgtm/"));
2727

2828
// remove any undesired lgtm labels
@@ -64,18 +64,28 @@ export const setPrStatusAndLabel = async (
6464
};
6565

6666
// returns the status, message, and label for a given number of approvals
67-
export const getPrStatusAndLabel = (approvals: number) => {
67+
export const getPrStatusAndLabel = (
68+
reviewers: { approvers: Set<string>; blockers: Set<string> },
69+
) => {
6870
let desiredLabel = "lgtm/need 2";
6971
let message = "Needs two more approvals";
70-
let state: "pending" | "success" = "pending";
72+
let state: "pending" | "success" | "failure" = "pending";
73+
74+
if (reviewers.blockers.size > 0) {
75+
desiredLabel = "lgtm/blocked";
76+
message = "Blocked by " + Array.from(reviewers.blockers).join(", ");
77+
state = "failure";
78+
return { state, message, desiredLabel };
79+
}
7180

72-
if (approvals === 1) {
81+
if (reviewers.approvers.size === 1) {
7382
desiredLabel = "lgtm/need 1";
7483
message = "Needs one more approval";
7584
}
76-
if (approvals >= 2) {
85+
86+
if (reviewers.approvers.size >= 2) {
7787
desiredLabel = "lgtm/done";
78-
message = `Approved by ${approvals} people`;
88+
message = `Approved by ${reviewers.approvers.size} people`;
7989
state = "success";
8090
}
8191

0 commit comments

Comments
 (0)