144
144
sender:
145
145
login: pdxjohnny
146
146
webhook_workflow: |
147
+ name: 'Webhook Workflow Name'
147
148
on:
148
149
push:
149
150
branches:
154
155
runs-on: ubuntu-latest
155
156
steps:
156
157
- run: |
157
- echo Hi
158
+ echo ::error path=test.py:: Hi
158
159
```
159
160
160
161
```bash
213
214
"""
214
215
import os
215
216
import io
217
+ import re
216
218
import sys
217
219
import json
218
220
import time
@@ -473,11 +475,12 @@ class GitHubWebhookEventSender(BaseModel):
473
475
474
476
jobs:
475
477
test:
478
+ name: "My Job"
476
479
runs-on: self-hosted
477
480
steps:
478
- - uses: actions/checkout@v4
479
481
- run: |
480
- echo Hello World
482
+ # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
483
+ echo "::error file=app.js,line=1,col=5,endColumn=7::Missing semicolon"
481
484
"""
482
485
)
483
486
@@ -1019,6 +1022,52 @@ def step_io_update_stack_output_and_env_github_actions(context, request, step):
1019
1022
stack ["env" ].update (context_env_updates )
1020
1023
1021
1024
1025
+ def step_parse_annotations_github_actions_line (context , step , line ):
1026
+ line = line .strip ().strip ("::" )
1027
+ annotation_level , message = line .split ("::" , maxsplit = 1 )
1028
+ details = {
1029
+ "message" : message ,
1030
+ "raw_details" : line ,
1031
+ }
1032
+ if " " in annotation_level :
1033
+ annotation_level , details_string = annotation_level .split (" " , maxsplit = 1 )
1034
+ details .update (urllib .parse .parse_qsl (details_string .replace ("," , "&" )))
1035
+ details ["annotation_level" ] = annotation_level
1036
+ details_items = list (details .items ())
1037
+ for key , value in details_items :
1038
+ del details [key ]
1039
+ key = re .sub (r'(?<!^)(?=[A-Z])' , '_' , key ).lower ()
1040
+ details [key ] = value
1041
+ return GitHubCheckSuiteAnnotation .model_validate (details )
1042
+
1043
+
1044
+ def step_parse_annotations_github_actions (context , step , step_annotations_string ):
1045
+ # TODO Groups
1046
+ # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines
1047
+ return [
1048
+ step_parse_annotations_github_actions_line (
1049
+ context ,
1050
+ step ,
1051
+ line ,
1052
+ )
1053
+ for line in step_annotations_string .split ("\n " )
1054
+ if line .startswith ("::" )
1055
+ ]
1056
+
1057
+
1058
+ def step_io_update_stack_annotations_github_actions (context , request , step ):
1059
+ stack = request .context ["stack" ][- 1 ]
1060
+ for annotation in itertools .chain (
1061
+ step_parse_annotations_github_actions (
1062
+ context ,
1063
+ step ,
1064
+ pathlib .Path (stack ["console_output" ]).read_text (),
1065
+ )
1066
+ ):
1067
+ stack ["annotations" ].setdefault (annotation .annotation_level , [])
1068
+ stack ["annotations" ][annotation .annotation_level ].append (annotation )
1069
+
1070
+
1022
1071
def execute_step_uses (context , request , step ):
1023
1072
stack = request .context ["stack" ][- 1 ]
1024
1073
@@ -1060,6 +1109,11 @@ def execute_step_uses(context, request, step):
1060
1109
request ,
1061
1110
step ,
1062
1111
)
1112
+ step_io_update_stack_annotations_github_actions (
1113
+ context ,
1114
+ request ,
1115
+ step ,
1116
+ )
1063
1117
try :
1064
1118
completed_proc .check_returncode ()
1065
1119
except Exception as error :
@@ -1151,6 +1205,11 @@ def execute_step_run(context, request, step):
1151
1205
request ,
1152
1206
step ,
1153
1207
)
1208
+ step_io_update_stack_annotations_github_actions (
1209
+ context ,
1210
+ request ,
1211
+ step ,
1212
+ )
1154
1213
1155
1214
try :
1156
1215
completed_proc .check_returncode ()
@@ -1230,6 +1289,9 @@ def celery_run_workflow_context_stack_push(context, request, stack):
1230
1289
def celery_run_workflow_context_stack_pop (context , request ):
1231
1290
# TODO Deal with ordering of lines by time, logging module?
1232
1291
popped_stack = request .context ["stack" ].pop ()
1292
+ for annotation in itertools .chain (* popped_stack ["annotations" ].values ()):
1293
+ request .context ["annotations" ].setdefault (annotation .annotation_level , [])
1294
+ request .context ["annotations" ][annotation .annotation_level ].append (annotation )
1233
1295
request .context ["console_output" ].append (
1234
1296
[
1235
1297
popped_stack ["stack_path" ],
@@ -1283,6 +1345,8 @@ async def celery_run_workflow_context_init(
1283
1345
request .context ["stack" ] = []
1284
1346
if force_init or "console_output" not in request .context :
1285
1347
request .context ["console_output" ] = []
1348
+ if force_init or "annotations" not in request .context :
1349
+ request .context ["annotations" ] = {}
1286
1350
if force_init or "_init" not in request .context :
1287
1351
request .context ["_init" ] = True
1288
1352
for extra_init in context .extra_inits :
@@ -1717,11 +1781,11 @@ async def async_celery_run_workflow(context, request):
1717
1781
job_error = Exception (job_error )
1718
1782
raise job_error
1719
1783
1720
-
1721
1784
detail = PolicyEngineComplete (
1722
1785
id = "" ,
1723
1786
exit_status = PolicyEngineCompleteExitStatuses .SUCCESS ,
1724
1787
outputs = {},
1788
+ annotations = request .context ["annotations" ],
1725
1789
)
1726
1790
request_status = PolicyEngineStatus (
1727
1791
status = PolicyEngineStatuses .COMPLETE ,
@@ -2168,13 +2232,13 @@ async def test_read_main(
2168
2232
2169
2233
2170
2234
class GitHubCheckSuiteAnnotation (BaseModel ):
2171
- path : str
2172
- annotation_level : str
2173
- title : str
2174
- message : str
2175
- raw_details : str
2176
- start_line : int
2177
- end_line : int
2235
+ path : str = ""
2236
+ annotation_level : str = ""
2237
+ title : str = ""
2238
+ message : str = ""
2239
+ raw_details : str = ""
2240
+ start_line : int = 0
2241
+ end_line : int = 0
2178
2242
2179
2243
2180
2244
async def async_workflow_run_github_app_gidgethub (
@@ -2247,17 +2311,6 @@ async def async_workflow_run_github_app_gidgethub(
2247
2311
warning_count = 0
2248
2312
notice_count = 0
2249
2313
annotations = []
2250
- annotations .append (
2251
- GitHubCheckSuiteAnnotation (
2252
- path = "somepath" ,
2253
- annotation_level = "warning" ,
2254
- title = "" ,
2255
- message = "" ,
2256
- raw_details = "" ,
2257
- start_line = 0 ,
2258
- end_line = 0 ,
2259
- )
2260
- )
2261
2314
2262
2315
if hasattr (context .state , "github_app" ):
2263
2316
# GitHub App, use check-runs API
@@ -2274,12 +2327,19 @@ async def async_workflow_run_github_app_gidgethub(
2274
2327
.strftime ("%Y-%m-%dT%H:%M:%SZ" ),
2275
2328
"output" : {
2276
2329
"title" : request .workflow .name ,
2277
- "summary" : "There are {failure_count} failures, {warning_count} warnings, and {notice_count} notices." ,
2330
+ "summary" : f "There are { failure_count } failures, { warning_count } warnings, and { notice_count } notices." ,
2278
2331
"text" : "" ,
2279
- "annotations" : [
2280
- json .loads (annotation .model_dump_json ())
2281
- for annotation in annotations
2282
- ],
2332
+ "annotations" : list (
2333
+ itertools .chain (
2334
+ [
2335
+ [
2336
+ json .loads (annotation .model_dump_json ())
2337
+ for annotations in annotations [annotation_level ]
2338
+ ]
2339
+ for annotation_level in annotations
2340
+ ]
2341
+ )
2342
+ ),
2283
2343
"images" : [
2284
2344
{
2285
2345
"alt" : "Super bananas" ,
@@ -2311,6 +2371,7 @@ async def async_workflow_run_github_app_gidgethub(
2311
2371
id = "" ,
2312
2372
exit_status = PolicyEngineCompleteExitStatuses .SUCCESS ,
2313
2373
outputs = {},
2374
+ annotations = request .context ["annotations" ],
2314
2375
)
2315
2376
request_status = PolicyEngineStatus (
2316
2377
status = PolicyEngineStatuses .COMPLETE ,
0 commit comments