@@ -20,95 +20,145 @@ export async function script(octokit, repository, { author }) {
20
20
}
21
21
22
22
for ( const pr of pullRequests ) {
23
- const query = `query($htmlUrl: URI!) {
23
+ const query = `query prStatus ($htmlUrl: URI!) {
24
24
resource(url: $htmlUrl) {
25
25
... 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) {
31
33
nodes {
32
- path
34
+ viewerDidAuthor
33
35
}
34
36
}
37
+ # CI status
35
38
commits(last: 1) {
36
39
nodes {
37
40
commit {
38
41
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 {
51
43
state
52
- contexts {
53
- state
54
- targetUrl
55
- description
56
- context
57
- }
58
44
}
59
45
}
60
46
}
61
47
}
62
48
}
63
49
}
64
- }` ;
50
+ }
51
+ ` ;
65
52
66
53
const result = await octokit . graphql ( query , {
67
54
htmlUrl : pr . html_url ,
68
55
} ) ;
69
56
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
73
67
) ;
74
- const statuses = lastCommit . status ? lastCommit . status . contexts : [ ] ;
68
+ const latestCommitId = result . resource . commits . nodes [ 0 ] . commit . oid ;
75
69
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
+ } ;
87
79
88
- if ( unsuccessfulCheckRuns . length || unsuccessStatuses . length ) {
80
+ console . log ( `viewerCanUpdate` ) ;
81
+ console . log ( viewerCanUpdate ) ;
82
+
83
+ if ( ! viewerCanUpdate ) {
89
84
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
95
88
) ;
89
+ continue ;
90
+ }
96
91
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
+ }
108
101
102
+ if ( mergeable !== "MERGEABLE" ) {
103
+ octokit . log . info (
104
+ logData ,
105
+ `%s: mergable status is "%s". Skipping` ,
106
+ pr . html_url ,
107
+ mergeable
108
+ ) ;
109
109
continue ;
110
110
}
111
111
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
+
112
162
await octokit . request (
113
163
"PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge" ,
114
164
{
0 commit comments