44"""Script for getting explanations from the premerge advisor."""
55
66import argparse
7- import os
87import platform
98import sys
9+ import json
10+
11+ # TODO(boomanaiden154): Remove the optional call once we can require Python
12+ # 3.10.
13+ from typing import Optional
1014
1115import requests
16+ import github
17+ import github .PullRequest
1218
1319import generate_test_report_lib
1420
1521PREMERGE_ADVISOR_URL = (
1622 "http://premerge-advisor.premerge-advisor.svc.cluster.local:5000/explain"
1723)
24+ COMMENT_TAG = "<!--PREMERGE ADVISOR COMMENT: {platform}-->"
25+
26+
27+ def get_comment_id (platform : str , pr : github .PullRequest .PullRequest ) -> Optional [int ]:
28+ platform_comment_tag = COMMENT_TAG .format (platform = platform )
29+ for comment in pr .as_issue ().get_comments ():
30+ if platform_comment_tag in comment .body :
31+ return comment .id
32+ return None
33+
34+
35+ def get_comment (
36+ github_token : str ,
37+ pr_number : int ,
38+ body : str ,
39+ ) -> dict [str , str ]:
40+ repo = github .Github (github_token ).get_repo ("llvm/llvm-project" )
41+ pr = repo .get_issue (pr_number ).as_pull_request ()
42+ comment = {"body" : body }
43+ comment_id = get_comment_id (platform .system (), pr )
44+ if comment_id :
45+ comment ["id" ] = comment_id
46+ return comment
1847
1948
20- def main (commit_sha : str , build_log_files : list [str ]):
49+ def main (
50+ commit_sha : str ,
51+ build_log_files : list [str ],
52+ github_token : str ,
53+ pr_number : int ,
54+ return_code : int ,
55+ ):
56+ """The main entrypoint for the script.
57+
58+ This function parses failures from files, requests information from the
59+ premerge advisor, and may write a Github comment depending upon the output.
60+ There are four different scenarios:
61+ 1. There has never been a previous failure and the job passes - We do not
62+ create a comment. We write out an empty file to the comment path so the
63+ issue-write workflow knows not to create anything.
64+ 2. There has never been a previous failure and the job fails - We create a
65+ new comment containing the failure information and any possible premerge
66+ advisor findings.
67+ 3. There has been a previous failure and the job passes - We update the
68+ existing comment by passing its ID and a passed message to the
69+ issue-write workflow.
70+ 4. There has been a previous failure and the job fails - We update the
71+ existing comment in the same manner as above, but generate the comment
72+ as if we have a failure.
73+
74+ Args:
75+ commit_sha: The base commit SHA for this PR run.
76+ build_log_files: The list of JUnit XML files and ninja logs.
77+ github_token: The token to use to access the Github API.
78+ pr_number: The number of the PR associated with this run.
79+ return_code: The numerical return code of ninja/CMake.
80+ """
81+ if return_code == 0 :
82+ with open ("comment" , "w" ) as comment_file_handle :
83+ comment = get_comment (
84+ github_token ,
85+ pr_number ,
86+ ":white_check_mark: With the latest revision this PR passed "
87+ "the premerge checks." ,
88+ )
89+ if "id" in comment :
90+ json .dump ([comment ], comment_file_handle )
2191 junit_objects , ninja_logs = generate_test_report_lib .load_info_from_files (
2292 build_log_files
2393 )
@@ -45,13 +115,31 @@ def main(commit_sha: str, build_log_files: list[str]):
45115 )
46116 if advisor_response .status_code == 200 :
47117 print (advisor_response .json ())
118+ comments = [
119+ get_comment (
120+ github_token ,
121+ pr_number ,
122+ generate_test_report_lib .generate_report (
123+ generate_test_report_lib .compute_platform_title (),
124+ return_code ,
125+ junit_objects ,
126+ ninja_logs ,
127+ failure_explanations_list = advisor_response .json (),
128+ ),
129+ )
130+ ]
131+ with open ("comment" , "w" ) as comment_file_handle :
132+ json .dump (comments , comment_file_handle )
48133 else :
49134 print (advisor_response .reason )
50135
51136
52137if __name__ == "__main__" :
53138 parser = argparse .ArgumentParser ()
54139 parser .add_argument ("commit_sha" , help = "The base commit SHA for the test." )
140+ parser .add_argument ("return_code" , help = "The build's return code" , type = int )
141+ parser .add_argument ("github_token" , help = "Github authentication token" , type = str )
142+ parser .add_argument ("pr_number" , help = "The PR number" , type = int )
55143 parser .add_argument (
56144 "build_log_files" , help = "Paths to JUnit report files and ninja logs." , nargs = "*"
57145 )
@@ -62,4 +150,10 @@ def main(commit_sha: str, build_log_files: list[str]):
62150 if platform .machine () == "arm64" :
63151 sys .exit (0 )
64152
65- main (args .commit_sha , args .build_log_files )
153+ main (
154+ args .commit_sha ,
155+ args .build_log_files ,
156+ args .github_token ,
157+ args .pr_number ,
158+ args .return_code ,
159+ )
0 commit comments