Skip to content

Commit ca52c20

Browse files
committed
step
1 parent c64ce19 commit ca52c20

File tree

3 files changed

+223
-0
lines changed

3 files changed

+223
-0
lines changed

extra/ci_collect_logs.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import os
2+
import sys
3+
import json
4+
import re
5+
6+
def artifact_count(count, icon):
7+
"""
8+
Formats a count and an icon for display. If count is 0,
9+
it returns an empty string for the count and replaces the icon with :new_moon:.
10+
"""
11+
if count == 0:
12+
count = ""
13+
icon = ":new_moon:"
14+
15+
return f"<code>{count:>3}</code>{icon}"
16+
17+
def example_name(sketch_name):
18+
match = re.search(r'libraries/([^/]+)/(.*)', sketch_name)
19+
if match:
20+
lib = match.group(1)
21+
sketch = match.group(2)
22+
return f"<code>{lib}</code> <code>{sketch}</code>"
23+
else:
24+
return f"<code>{sketch_name}</code>"
25+
26+
TEST_MATRIX = {}
27+
BOARD_SUMMARY = {}
28+
29+
def log_test(name, board, icon, issues, job_link=None):
30+
"""
31+
Logs individual test results into the TEST_MATRIX dictionary.
32+
"""
33+
34+
if not board in BOARD_SUMMARY:
35+
BOARD_SUMMARY[board] = {
36+
'tests': 0,
37+
'errors': 0,
38+
'warnings': 0,
39+
'job_link': job_link,
40+
'icon': ':white_check_mark:'
41+
}
42+
43+
board_info = BOARD_SUMMARY[board]
44+
45+
if isinstance(issues, str):
46+
issues = [ issues ]
47+
48+
board_info['tests'] += 1
49+
if icon == ":fire:":
50+
board_info['icon'] = ":fire:"
51+
elif icon == ":red_circle:":
52+
board_info['icon'] = ":x:"
53+
board_info['errors'] += 1
54+
elif icon == ":yellow_circle:":
55+
board_info['warnings'] += 1
56+
# Only set to warning if not already an error
57+
if not board_info['icon']:
58+
board_info['icon'] = ":white_check_mark::grey_exclamation:"
59+
60+
if name not in TEST_MATRIX:
61+
TEST_MATRIX[name] = {}
62+
63+
TEST_MATRIX[name][board] = {
64+
'icon': icon,
65+
'issues': issues
66+
}
67+
68+
# --- Main Logic ---
69+
70+
def process_build_reports():
71+
"""
72+
The main script logic, converting the Bash loop structures.
73+
"""
74+
# 1. Environment Variable Checks
75+
ALL_BOARD_DATA_STR = os.environ.get('ALL_BOARD_DATA')
76+
GITHUB_REPOSITORY = os.environ.get('GITHUB_REPOSITORY')
77+
GITHUB_RUN_ID = os.environ.get('GITHUB_RUN_ID')
78+
79+
if not ALL_BOARD_DATA_STR or not GITHUB_REPOSITORY or not GITHUB_RUN_ID:
80+
print("Not in a Github CI run, cannot proceed.")
81+
sys.exit(0)
82+
83+
ALL_BOARD_DATA = json.loads(ALL_BOARD_DATA_STR)
84+
85+
for board_data in ALL_BOARD_DATA:
86+
# Extract common fields
87+
board = board_data.get('board')
88+
variant = board_data.get('variant')
89+
subarch = board_data.get('subarch')
90+
91+
# Filename preparation
92+
REPORT_FILE = f"arduino-{subarch}-{board}.json"
93+
94+
# 5. Report File Check
95+
if not os.path.exists(REPORT_FILE):
96+
log_test('CI test', board, ":fire:", "Report file not found.")
97+
continue # Skip to the next board
98+
99+
# 6. Process Report File
100+
try:
101+
with open(REPORT_FILE, 'r') as f:
102+
report_data = json.load(f)
103+
except Exception as e:
104+
log_test('CI test', board, ":fire:", f"Error reading report file: {e}")
105+
continue # Skip to the next board
106+
107+
# Extract data from the report file
108+
job_id = report_data.get('job_id')
109+
job_link = f"https://github.com/{GITHUB_REPOSITORY}/actions/runs/{GITHUB_RUN_ID}/job/{job_id}#step:5" if job_id else None
110+
111+
reports = report_data.get('boards', [{}])[0].get('sketches', [])
112+
if not reports:
113+
log_test('CI test', board, ":fire:", "Test report is empty, check full log.", job_link)
114+
continue # Skip to the next board
115+
116+
# 7. Sketch Loop: Iterate through individual sketch reports
117+
for report in reports:
118+
SKETCH_NAME = report.get('name', 'unknown_sketch')
119+
compilation_success = report.get('compilation_success', False)
120+
issues = report.get('issues', [])
121+
122+
# Replace long absolute paths with '...' for brevity.
123+
sketch_issues = [ re.sub(r'(/.+?)((/[^/]+){3}):', r'...\2:', issue) for issue in issues ]
124+
125+
# Logic to update counters and DETAILS string
126+
if not compilation_success:
127+
test_icon = ":red_circle:"
128+
elif len(sketch_issues): # Implies warnings/non-critical issues
129+
test_icon = ":yellow_circle:"
130+
else:
131+
test_icon = ":green_circle:"
132+
133+
log_test(SKETCH_NAME, board, test_icon, sketch_issues, job_link)
134+
135+
if not BOARD_SUMMARY[board]['icon']:
136+
# If no errors, set to green
137+
BOARD_SUMMARY[board]['icon'] = ":white_check_mark:"
138+
139+
artifacts = set(item['artifact'] for item in ALL_BOARD_DATA)
140+
141+
# Print the recap table
142+
for artifact in sorted(list(artifacts), reverse=True):
143+
print(f"### `{artifact}` test results:")
144+
145+
# 4. Inner Loop: Filter board data for the current artifact
146+
artifact_boards = [item for item in ALL_BOARD_DATA if item['artifact'] == artifact]
147+
148+
print()
149+
150+
# Print the test matrix
151+
152+
header_row = "<tr><th colspan=2>Sketch / Board</th>"
153+
for board_data in artifact_boards:
154+
board = board_data['board']
155+
icon = BOARD_SUMMARY[board]['icon']
156+
header_col = f"<code>{board}</code>"
157+
if BOARD_SUMMARY[board]['job_link']:
158+
link = BOARD_SUMMARY[board]['job_link']
159+
header_col = f"<a href='{link}'>{header_col}</a>"
160+
header_col += f"<br/>{icon}"
161+
header_row += f"<th>{header_col}</th>"
162+
header_row += "</tr>"
163+
164+
print("<table>")
165+
print(header_row)
166+
167+
for sketch_name, board_results in TEST_MATRIX.items():
168+
sketch_icon = None
169+
row_data = ""
170+
for board_data in artifact_boards:
171+
board_name = board_data['board']
172+
test_result = board_results.get(board_name, {})
173+
icon = test_result['icon'] if test_result else ":new_moon:"
174+
if icon == ":fire:" or icon == ":red_circle:":
175+
sketch_icon = icon
176+
elif icon == ":yellow_circle:" and not sketch_icon:
177+
sketch_icon = icon
178+
issues = test_result['issues'] if test_result else ""
179+
sketch_id = sketch_name.replace('/', '_').replace(' ', '_').replace('-', '_')
180+
icon_link = f"<a href='#{sketch_id}_{board_name}'>{icon}</a>" if issues else icon
181+
row_data += f"<td align='center'>{icon_link}</a></td>"
182+
if not sketch_icon:
183+
sketch_icon = ":green_circle:"
184+
print(f"<tr><td>{sketch_icon}</td><td><code>{example_name(sketch_name)}</code></td>{row_data}</tr>")
185+
print("</table>\n")
186+
187+
print(f"<details><summary><b>Error and warning logs for <code>{artifact}</code></b></summary>")
188+
print("<table>")
189+
print("<tr><th>Sketch</th><th>Build Details</th></tr>")
190+
for sketch_name, board_results in TEST_MATRIX.items():
191+
row_open = False
192+
for board_data in artifact_boards:
193+
board_name = board_data['board']
194+
if not board_name in board_results:
195+
continue
196+
197+
test_result = board_results[board_name]
198+
icon = test_result['icon']
199+
issues = test_result['issues']
200+
if not issues:
201+
continue
202+
203+
if not row_open:
204+
row_open = True
205+
print(f"<tr><td>{example_name(sketch_name)}</td>")
206+
print("<td>")
207+
208+
job_link = BOARD_SUMMARY[board]['job_link']
209+
if job_link:
210+
job_link = f" <a href='{job_link}'> :scroll:</a>"
211+
sketch_id = sketch_name.replace('/', '_').replace(' ', '_').replace('-', '_')
212+
print(f"<details id='{sketch_id}_{board_name}'><summary>{artifact_count(len(issues), icon)} <code>{board_name}</code>{job_link}</summary>")
213+
print("\n```\n" + "\n".join(issues) + "\n```")
214+
print("</details>")
215+
216+
if row_open:
217+
print("</td></tr>")
218+
print("</table></details>\n")
219+
220+
if __name__ == "__main__":
221+
process_build_reports()

variants/ek_ra8d1_r7fa8d1bhecbd/skip_these_examples.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# for example because it uses hardware features not present on the CPU or board.
44

55
libraries/Camera
6+
libraries/Ethernet # FIXME
67
libraries/Storage
78
libraries/WiFi
89
librarires/Zephyr_SDRAM

variants/frdm_mcxn947_mcxn947_cpu0/skip_these_examples.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# for example because it uses hardware features not present on the CPU or board.
44

55
libraries/Camera
6+
libraries/Ethernet # FIXME
67
libraries/Storage
78
libraries/WiFi
89
librarires/Zephyr_SDRAM

0 commit comments

Comments
 (0)