12
12
from operator import itemgetter
13
13
import pprint
14
14
import pathlib
15
- import requests
16
15
import subprocess
17
16
import textwrap
18
17
import unidiff
23
22
import io
24
23
import zipfile
25
24
from github import Github , Auth
25
+ from github .GithubException import GithubException
26
26
from github .Requester import Requester
27
27
from github .PaginatedList import PaginatedList
28
28
from github .WorkflowRun import WorkflowRun
@@ -60,6 +60,44 @@ class PRReview(TypedDict):
60
60
comments : List [PRReviewComment ]
61
61
62
62
63
+ class HashableComment :
64
+ def __init__ (self , body : str , line : int , path : str , side : str , ** kwargs ):
65
+ self .body = body
66
+ self .line = line
67
+ self .path = path
68
+ self .side = side
69
+
70
+ def __hash__ (self ):
71
+ return hash (
72
+ (
73
+ self .body ,
74
+ self .line ,
75
+ self .path ,
76
+ self .side ,
77
+ )
78
+ )
79
+
80
+ def __eq__ (self , other ):
81
+ return (
82
+ type (self ) is type (other )
83
+ and self .body == other .body
84
+ and self .line == self .line
85
+ and other .path == other .path
86
+ and self .side == other .side
87
+ )
88
+
89
+ def __lt__ (self , other ):
90
+ if self .path != other .path :
91
+ return self .path < other .path
92
+ if self .line != other .line :
93
+ return self .line < other .line
94
+ if self .side != other .side :
95
+ return self .side < other .side
96
+ if self .body != other .body :
97
+ return self .body < other .body
98
+ return id (self ) < id (other )
99
+
100
+
63
101
def add_auth_arguments (parser : argparse .ArgumentParser ):
64
102
# Token
65
103
parser .add_argument ("--token" , help = "github auth token" )
@@ -247,26 +285,15 @@ def pull_request(self):
247
285
self ._pull_request = self .repo .get_pull (int (self .pr_number ))
248
286
return self ._pull_request
249
287
250
- def headers (self , media_type : str ):
251
- return {
252
- "Accept" : f"application/vnd.github.{ media_type } " ,
253
- "Authorization" : f"token { self .token } " ,
254
- }
255
-
256
- @property
257
- def base_url (self ):
258
- return f"{ self .api_url } /repos/{ self .repo_name } /pulls/{ self .pr_number } "
259
-
260
- def get (self , media_type : str , extra : str = "" ) -> str :
261
- url = f"{ self .base_url } { extra } "
262
- response = requests .get (url , headers = self .headers (media_type ))
263
- response .raise_for_status ()
264
- return response .text
265
-
266
288
def get_pr_diff (self ) -> List [unidiff .PatchSet ]:
267
289
"""Download the PR diff, return a list of PatchedFile"""
268
290
269
- diffs = self .get ("v3.diff" )
291
+ _ , data = self .repo ._requester .requestJsonAndCheck (
292
+ "GET" ,
293
+ self .pull_request .url ,
294
+ headers = {"Accept" : f"application/vnd.github.{ 'v3.diff' } " },
295
+ )
296
+ diffs = data ["data" ]
270
297
271
298
# PatchSet is the easiest way to construct what we want, but the
272
299
# diff_line_no property on lines is counted from the top of the
@@ -292,7 +319,7 @@ def get_element(
292
319
return PaginatedList (
293
320
get_element ,
294
321
self .pull_request ._requester ,
295
- f" { self .base_url } /comments" ,
322
+ self .pull_request . review_comments_url ,
296
323
None ,
297
324
)
298
325
@@ -311,33 +338,9 @@ def post_lgtm_comment(self, body: str):
311
338
312
339
self .pull_request .create_issue_comment (body )
313
340
314
- def post_review (self , review ):
341
+ def post_review (self , review : PRReview ):
315
342
"""Submit a completed review"""
316
- headers = {
317
- "Accept" : "application/vnd.github.comfort-fade-preview+json" ,
318
- "Authorization" : f"token { self .token } " ,
319
- }
320
- url = f"{ self .base_url } /reviews"
321
-
322
- post_review_response = requests .post (url , json = review , headers = headers )
323
- print (post_review_response .text )
324
- try :
325
- post_review_response .raise_for_status ()
326
- except requests .exceptions .HTTPError as e :
327
- if e .response .status_code == 403 :
328
- print (
329
- "::error title=Missing permissions::This workflow does not have "
330
- "enough permissions to submit a review. This could be because "
331
- "the GitHub token specified for this workflow is invalid or "
332
- "missing permissions, or it could be because this pull request "
333
- "comes from a fork which reduces the default token permissions. "
334
- "To support forked workflows, see the "
335
- "https://github.com/ZedThree/clang-tidy-review#usage-in-fork-environments "
336
- "instructions"
337
- )
338
-
339
- # Re-raise the exception, causing an error in the workflow
340
- raise e
343
+ self .pull_request .create_review (** review )
341
344
342
345
def post_annotations (self , review ):
343
346
headers = {
@@ -346,8 +349,9 @@ def post_annotations(self, review):
346
349
}
347
350
url = f"{ self .api_url } /repos/{ self .repo_name } /check-runs"
348
351
349
- response = requests .post (url , json = review , headers = headers )
350
- response .raise_for_status ()
352
+ self .repo ._requester .requestJsonAndCheck (
353
+ "POST" , url , parameters = review , headers = headers
354
+ )
351
355
352
356
353
357
@contextlib .contextmanager
@@ -920,14 +924,17 @@ def download_artifacts(pull: PullRequest, workflow_id: int):
920
924
)
921
925
return None , None
922
926
923
- r = requests .get (artifact .archive_download_url , headers = pull .headers ("json" ))
924
- if not r .ok :
927
+ try :
928
+ _ , data = pull .repo ._requester .requestJsonAndCheck (
929
+ "GET" , artifact .archive_download_url , headers = pull .headers ("json" )
930
+ )
931
+ except GithubException as exc :
925
932
print (
926
- f"WARNING: Couldn't automatically download artifacts for workflow '{ workflow_id } ', response was: { r } : { r . reason } "
933
+ f"WARNING: Couldn't automatically download artifacts for workflow '{ workflow_id } ', response was: { exc } "
927
934
)
928
935
return None , None
929
936
930
- contents = b"" .join (r .iter_content ())
937
+ contents = b"" .join (data [ "data" ] .iter_content ())
931
938
932
939
data = zipfile .ZipFile (io .BytesIO (contents ))
933
940
filenames = data .namelist ()
@@ -983,39 +990,11 @@ def load_and_merge_reviews(review_files: List[pathlib.Path]) -> Optional[PRRevie
983
990
984
991
result = reviews [0 ]
985
992
986
- class Comment :
987
- def __init__ (self , data ):
988
- self .data = data
989
-
990
- def __hash__ (self ):
991
- return hash (
992
- (
993
- self .data ["body" ],
994
- self .data ["line" ],
995
- self .data ["path" ],
996
- self .data ["side" ],
997
- )
998
- )
999
-
1000
- def __eq__ (self , other ):
1001
- return type (other ) is Comment and self .data == other .data
1002
-
1003
- def __lt__ (self , other ):
1004
- if self .data ["path" ] != other .data ["path" ]:
1005
- return self .data ["path" ] < other .data ["path" ]
1006
- if self .data ["line" ] != other .data ["line" ]:
1007
- return self .data ["line" ] < other .data ["line" ]
1008
- if self .data ["side" ] != other .data ["side" ]:
1009
- return self .data ["side" ] < other .data ["side" ]
1010
- if self .data ["body" ] != other .data ["body" ]:
1011
- return self .data ["body" ] < other .data ["body" ]
1012
- return hash (self ) < hash (other )
1013
-
1014
993
comments = set ()
1015
994
for review in reviews :
1016
- comments .update (map (Comment , review ["comments" ]))
995
+ comments .update (map (lambda c : HashableComment ( ** c ) , review ["comments" ]))
1017
996
1018
- result ["comments" ] = [c .data for c in sorted (comments )]
997
+ result ["comments" ] = [c .__dict__ for c in sorted (comments )]
1019
998
1020
999
return result
1021
1000
@@ -1063,19 +1042,14 @@ def cull_comments(pull_request: PullRequest, review, max_comments):
1063
1042
1064
1043
"""
1065
1044
1066
- comments = pull_request .get_pr_comments ()
1067
-
1068
- for comment in comments :
1069
- review ["comments" ] = list (
1070
- filter (
1071
- lambda review_comment : not (
1072
- review_comment ["path" ] == comment ["path" ]
1073
- and review_comment ["line" ] == comment ["line" ]
1074
- and review_comment ["body" ] == comment ["body" ]
1075
- ),
1076
- review ["comments" ],
1077
- )
1078
- )
1045
+ unposted_comments = set (map (lambda c : HashableComment (** c ), review ["comments" ]))
1046
+ posted_comments = set (
1047
+ map (lambda c : HashableComment (** c ), pull_request .get_pr_comments ())
1048
+ )
1049
+
1050
+ review ["comments" ] = [
1051
+ c .__dict__ for c in sorted (unposted_comments - posted_comments )
1052
+ ]
1079
1053
1080
1054
if len (review ["comments" ]) > max_comments :
1081
1055
review ["body" ] += (
0 commit comments