14
14
import os
15
15
import re
16
16
import sys
17
- from typing import Any , Dict , List
17
+ from typing import Any , List , Optional , Set
18
18
19
- import junit_xml
20
19
import requests
21
20
22
- from materialize import ROOT , ci_util
21
+ from materialize import ci_util , spawn
23
22
24
23
CI_RE = re .compile ("ci-regexp: (.*)" )
24
+ CI_APPLY_TO = re .compile ("ci-apply-to: (.*)" )
25
25
ERROR_RE = re .compile (
26
26
r"""
27
27
( panicked\ at
55
55
56
56
57
57
class KnownIssue :
58
- def __init__ (self , regex : str , info : Any ):
58
+ def __init__ (self , regex : str , apply_to : Optional [ str ], info : Any ):
59
59
self .regex = re .compile (regex )
60
+ self .apply_to = apply_to
60
61
self .info = info
61
62
62
63
@@ -83,98 +84,108 @@ def main() -> int:
83
84
return 0
84
85
85
86
87
+ def annotate_errors (errors : List [str ], title : str , style : str ) -> None :
88
+ if not errors :
89
+ return
90
+
91
+ suite_name = os .getenv ("BUILDKITE_LABEL" ) or "Logged Errors"
92
+
93
+ error_str = "\n " .join (f"* { error } " for error in errors )
94
+
95
+ if style == "info" :
96
+ markdown = f"""<details><summary>{ suite_name } : { title } </summary>
97
+
98
+ { error_str }
99
+ </details>"""
100
+ else :
101
+ markdown = f"""{ suite_name } : { title }
102
+
103
+ { error_str } """
104
+
105
+ spawn .runv (
106
+ [
107
+ "buildkite-agent" ,
108
+ "annotate" ,
109
+ f"--style={ style } " ,
110
+ f"--context={ os .environ ['BUILDKITE_JOB_ID' ]} -{ style } " ,
111
+ ],
112
+ stdin = markdown .encode (),
113
+ )
114
+
115
+
86
116
def annotate_logged_errors (log_files : List [str ]) -> None :
87
117
error_logs = get_error_logs (log_files )
88
118
89
119
if not error_logs :
90
120
return
91
121
92
- known_issues = get_known_issues_from_github ()
122
+ step_key : str = os .getenv ("BUILDKITE_STEP_KEY" , "" )
123
+ buildkite_label : str = os .getenv ("BUILDKITE_LABEL" , "" )
93
124
94
- step_key = os .getenv ("BUILDKITE_STEP_KEY" )
95
- suite_name = step_key or "Logged Errors"
96
- junit_suite = junit_xml .TestSuite (suite_name )
125
+ known_issues = get_known_issues_from_github ()
97
126
98
127
artifacts = ci_util .get_artifacts ()
99
128
job = os .getenv ("BUILDKITE_JOB_ID" )
100
129
101
- # Keep track of known errors so we log each only once, and attach the
102
- # additional occurences to the same junit-xml test case.
103
- dict_issue_number_to_test_case_index : Dict [int , int ] = {}
130
+ unknown_errors : List [str ] = []
131
+ known_errors : List [str ] = []
132
+
133
+ # Keep track of known errors so we log each only once
134
+ already_reported_issue_numbers : Set [int ] = set ()
104
135
105
136
for error in error_logs :
106
137
for artifact in artifacts :
107
138
if artifact ["job_id" ] == job and artifact ["path" ] == error .file :
108
139
org = os .environ ["BUILDKITE_ORGANIZATION_SLUG" ]
109
140
pipeline = os .environ ["BUILDKITE_PIPELINE_SLUG" ]
110
141
build = os .environ ["BUILDKITE_BUILD_NUMBER" ]
111
- linked_file = f'<a href=" https://buildkite.com/organizations/{ org } /pipelines/{ pipeline } /builds/{ build } /jobs/{ artifact ["job_id" ]} /artifacts/{ artifact ["id" ]} "> { error . file } </a> '
142
+ linked_file = f'[ { error . file } ]( https://buildkite.com/organizations/{ org } /pipelines/{ pipeline } /builds/{ build } /jobs/{ artifact ["job_id" ]} /artifacts/{ artifact ["id" ]} ) '
112
143
break
113
144
else :
114
145
linked_file = error .file
115
146
116
147
for issue in known_issues :
117
148
match = issue .regex .search (error .line )
118
149
if match and issue .info ["state" ] == "open" :
119
- message = f"Known error in logs: <a href=\" { issue .info ['html_url' ]} \" >{ issue .info ['title' ]} (#{ issue .info ['number' ]} )</a><br/>In { linked_file } :{ error .line_nr } :"
120
- if issue .info ["number" ] in dict_issue_number_to_test_case_index :
121
- junit_suite .test_cases [
122
- dict_issue_number_to_test_case_index [issue .info ["number" ]]
123
- ].add_failure_info (message = message , output = error .line )
124
- else :
125
- test_case = junit_xml .TestCase (
126
- f"log error { len (junit_suite .test_cases ) + 1 } (known)" ,
127
- suite_name ,
128
- allow_multiple_subelements = True ,
150
+ if issue .apply_to and issue .apply_to not in (
151
+ step_key .lower (),
152
+ buildkite_label .lower (),
153
+ ):
154
+ continue
155
+
156
+ if issue .info ["number" ] not in already_reported_issue_numbers :
157
+ known_errors .append (
158
+ f"[{ issue .info ['title' ]} (#{ issue .info ['number' ]} )]({ issue .info ['html_url' ]} ) in { linked_file } :{ error .line_nr } : \n ``{ error .line } ``"
129
159
)
130
- test_case .add_failure_info (message = message , output = error .line )
131
- dict_issue_number_to_test_case_index [issue .info ["number" ]] = len (
132
- junit_suite .test_cases
133
- )
134
- junit_suite .test_cases .append (test_case )
160
+ already_reported_issue_numbers .add (issue .info ["number" ])
135
161
break
136
162
else :
137
163
for issue in known_issues :
138
164
match = issue .regex .search (error .line )
139
165
if match and issue .info ["state" ] == "closed" :
140
- message = f"Potential regression in logs: <a href=\" { issue .info ['html_url' ]} \" >{ issue .info ['title' ]} (#{ issue .info ['number' ]} , closed)</a><br/>In { linked_file } :{ error .line_nr } :"
141
- if issue .info ["number" ] in dict_issue_number_to_test_case_index :
142
- junit_suite .test_cases [
143
- dict_issue_number_to_test_case_index [issue .info ["number" ]]
144
- ].add_failure_info (message = message , output = error .line )
145
- else :
146
- test_case = junit_xml .TestCase (
147
- f"log error { len (junit_suite .test_cases ) + 1 } (regression)" ,
148
- suite_name ,
149
- allow_multiple_subelements = True ,
150
- )
151
- test_case .add_failure_info (
152
- message = message ,
153
- output = error .line ,
166
+ if issue .apply_to and issue .apply_to not in (
167
+ step_key .lower (),
168
+ buildkite_label .lower (),
169
+ ):
170
+ continue
171
+
172
+ if issue .info ["number" ] not in already_reported_issue_numbers :
173
+ unknown_errors .append (
174
+ f"Potential regression [{ issue .info ['title' ]} (#{ issue .info ['number' ]} , closed)]({ issue .info ['html_url' ]} ) in { linked_file } :{ error .line_nr } : \n ``{ error .line } ``"
154
175
)
155
- dict_issue_number_to_test_case_index [
156
- issue .info ["number" ]
157
- ] = len (junit_suite .test_cases )
158
- junit_suite .test_cases .append (test_case )
176
+ already_reported_issue_numbers .add (issue .info ["number" ])
159
177
break
160
178
else :
161
- message = f'Unknown error in logs (<a href="https://github.com/MaterializeInc/materialize/blob/main/doc/developer/ci-regexp.md">ci-regexp guide</a>)<br/>In { linked_file } :{ error .line_nr } :'
162
- test_case = junit_xml .TestCase (
163
- f"log error { len (junit_suite .test_cases ) + 1 } (new)" ,
164
- suite_name ,
165
- allow_multiple_subelements = True ,
179
+ unknown_errors .append (
180
+ f"Unknown error in { linked_file } :{ error .line_nr } : \n ``{ error .line } ``"
166
181
)
167
- test_case .add_failure_info (message = message , output = error .line )
168
- junit_suite .test_cases .append (test_case )
169
182
170
- junit_name = f"{ step_key } _logged_errors" if step_key else "logged_errors"
171
-
172
- junit_report = ci_util .junit_report_filename (junit_name )
173
- with junit_report .open ("w" ) as f :
174
- junit_xml .to_xml_report_file (f , [junit_suite ])
175
-
176
- if "BUILDKITE_ANALYTICS_TOKEN_LOGGED_ERRORS" in os .environ :
177
- ci_util .upload_junit_report ("logged_errors" , ROOT / junit_report )
183
+ annotate_errors (
184
+ unknown_errors ,
185
+ "Unknown errors and regressions in logs (see [ci-regexp](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/ci-regexp.md))" ,
186
+ "error" ,
187
+ )
188
+ annotate_errors (known_errors , "Known errors in logs, ignoring" , "info" )
178
189
179
190
180
191
def get_error_logs (log_files : List [str ]) -> List [ErrorLog ]:
@@ -212,8 +223,15 @@ def get_known_issues_from_github() -> list[KnownIssue]:
212
223
known_issues = []
213
224
for issue in issues_json ["items" ]:
214
225
matches = CI_RE .findall (issue ["body" ])
226
+ matches_apply_to = CI_APPLY_TO .findall (issue ["body" ])
215
227
for match in matches :
216
- known_issues .append (KnownIssue (match .strip (), issue ))
228
+ if matches_apply_to :
229
+ for match_apply_to in matches_apply_to :
230
+ known_issues .append (
231
+ KnownIssue (match .strip (), match_apply_to .strip ().lower (), issue )
232
+ )
233
+ else :
234
+ known_issues .append (KnownIssue (match .strip (), None , issue ))
217
235
return known_issues
218
236
219
237
0 commit comments