Skip to content

Commit ee30672

Browse files
authored
feat: check for mergeable and required reviews status. Approve PR if applicable (#2)
1 parent 9553e1c commit ee30672

File tree

1 file changed

+108
-58
lines changed

1 file changed

+108
-58
lines changed

script.js

+108-58
Original file line numberDiff line numberDiff line change
@@ -20,95 +20,145 @@ export async function script(octokit, repository, { author }) {
2020
}
2121

2222
for (const pr of pullRequests) {
23-
const query = `query($htmlUrl: URI!) {
23+
const query = `query prStatus($htmlUrl: URI!) {
2424
resource(url: $htmlUrl) {
2525
... on PullRequest {
26-
state
27-
author {
28-
login
29-
}
30-
files(first:2) {
26+
# merge status
27+
mergeable
28+
# review status
29+
reviewDecision
30+
viewerCanUpdate
31+
viewerDidAuthor
32+
latestOpinionatedReviews(first:10,writersOnly:true) {
3133
nodes {
32-
path
34+
viewerDidAuthor
3335
}
3436
}
37+
# CI status
3538
commits(last: 1) {
3639
nodes {
3740
commit {
3841
oid
39-
checkSuites(first: 100) {
40-
nodes {
41-
checkRuns(first: 100) {
42-
nodes {
43-
name
44-
conclusion
45-
permalink
46-
}
47-
}
48-
}
49-
}
50-
status {
42+
statusCheckRollup {
5143
state
52-
contexts {
53-
state
54-
targetUrl
55-
description
56-
context
57-
}
5844
}
5945
}
6046
}
6147
}
6248
}
6349
}
64-
}`;
50+
}
51+
`;
6552

6653
const result = await octokit.graphql(query, {
6754
htmlUrl: pr.html_url,
6855
});
6956

70-
const [{ commit: lastCommit }] = result.resource.commits.nodes;
71-
const checkRuns = [].concat(
72-
...lastCommit.checkSuites.nodes.map((node) => node.checkRuns.nodes)
57+
const {
58+
reviewDecision,
59+
mergeable,
60+
viewerCanUpdate,
61+
viewerDidAuthor,
62+
} = result.resource;
63+
const combinedStatus =
64+
result.resource.commits.nodes[0].commit.statusCheckRollup.state;
65+
const viewerDidApprove = !!result.resource.latestOpinionatedReviews.nodes.find(
66+
(node) => node.viewerDidAuthor
7367
);
74-
const statuses = lastCommit.status ? lastCommit.status.contexts : [];
68+
const latestCommitId = result.resource.commits.nodes[0].commit.oid;
7569

76-
const unsuccessfulCheckRuns = checkRuns
77-
.filter(
78-
(checkRun) =>
79-
checkRun.conclusion !== "SUCCESS" && checkRun.conclusion !== "NEUTRAL"
80-
)
81-
.filter((checkRun) => {
82-
return checkRun.conclusion !== null;
83-
});
84-
const unsuccessStatuses = statuses.filter(
85-
(status) => status.state !== "SUCCESS"
86-
);
70+
const logData = {
71+
pr: {
72+
number: pr.number,
73+
reviewDecision,
74+
mergeable,
75+
combinedStatus,
76+
viewerCanUpdate,
77+
},
78+
};
8779

88-
if (unsuccessfulCheckRuns.length || unsuccessStatuses.length) {
80+
console.log(`viewerCanUpdate`);
81+
console.log(viewerCanUpdate);
82+
83+
if (!viewerCanUpdate) {
8984
octokit.log.info(
90-
`${
91-
unsuccessfulCheckRuns.length + unsuccessStatuses.length
92-
} checks/statuses out of ${
93-
checkRuns.length + statuses.length
94-
} are not successful:`
85+
logData,
86+
`%s: you cannot update this PR. Skipping`,
87+
pr.html_url
9588
);
89+
continue;
90+
}
9691

97-
for (const checkRun of unsuccessfulCheckRuns) {
98-
octokit.log.info(`- Check run by "${checkRun.name}"
99-
Conclusion: ${checkRun.conclusion}
100-
${checkRun.permalink}`);
101-
}
102-
103-
for (const status of unsuccessStatuses) {
104-
octokit.log.info(`- Status run by "${status.context}"
105-
state: ${status.state}
106-
${status.targetUrl}`);
107-
}
92+
if (combinedStatus !== "SUCCESS") {
93+
octokit.log.info(
94+
logData,
95+
`%s: status is "%s". Skipping`,
96+
pr.html_url,
97+
combinedStatus
98+
);
99+
continue;
100+
}
108101

102+
if (mergeable !== "MERGEABLE") {
103+
octokit.log.info(
104+
logData,
105+
`%s: mergable status is "%s". Skipping`,
106+
pr.html_url,
107+
mergeable
108+
);
109109
continue;
110110
}
111111

112+
let approved;
113+
if (reviewDecision !== "APPROVED") {
114+
if (!viewerDidAuthor && !viewerDidApprove) {
115+
// attempt to add approval
116+
await octokit.request(
117+
"POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews",
118+
{
119+
owner: repository.owner.login,
120+
repo: repository.name,
121+
pull_number: pr.number,
122+
event: "APPROVE",
123+
commit_id: latestCommitId,
124+
}
125+
);
126+
approved = true;
127+
128+
// check if PR is now approved
129+
const {
130+
resource: { reviewDecision: newReviewDecision },
131+
} = await octokit.graphql(
132+
`query prStatus($htmlUrl: URI!) {
133+
resource(url: $htmlUrl) {
134+
... on PullRequest {
135+
reviewDecision
136+
}
137+
}
138+
}`,
139+
{
140+
htmlUrl: pr.html_url,
141+
}
142+
);
143+
144+
if (newReviewDecision !== "APPROVED") {
145+
octokit.log.info(
146+
logData,
147+
"%s: awaiting approval. Skipping",
148+
pr.html_url
149+
);
150+
continue;
151+
}
152+
} else {
153+
octokit.log.info(
154+
logData,
155+
"%s: awaiting approval. Skipping",
156+
pr.html_url
157+
);
158+
continue;
159+
}
160+
}
161+
112162
await octokit.request(
113163
"PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge",
114164
{

0 commit comments

Comments
 (0)