7
7
8
8
import json
9
9
import os
10
+ import pprint
10
11
import re
11
12
import subprocess as sp
12
13
import sys
50
51
DEFAULT_BRANCH = "master"
51
52
WORKFLOW_NAME = "CI" # Workflow that generates the benchmark artifacts
52
53
ARTIFACT_PREFIX = "baseline-icount*"
53
- # Place this in a PR body to skip regression checks (must be at the start of a line).
54
- REGRESSION_DIRECTIVE = "ci: allow-regressions"
55
- # Place this in a PR body to skip extensive tests
56
- SKIP_EXTENSIVE_DIRECTIVE = "ci: skip-extensive"
57
- # Place this in a PR body to allow running a large number of extensive tests. If not
58
- # set, this script will error out if a threshold is exceeded in order to avoid
59
- # accidentally spending huge amounts of CI time.
60
- ALLOW_MANY_EXTENSIVE_DIRECTIVE = "ci: allow-many-extensive"
61
- MANY_EXTENSIVE_THRESHOLD = 20
62
54
63
55
# Don't run exhaustive tests if these files change, even if they contaiin a function
64
56
# definition.
@@ -80,6 +72,48 @@ def eprint(*args, **kwargs):
80
72
print (* args , file = sys .stderr , ** kwargs )
81
73
82
74
75
+ @dataclass (init = False )
76
+ class PrCfg :
77
+ """Directives that we allow in the commit body to control test behavior.
78
+
79
+ These are of the form `ci: foo`, at the start of a line.
80
+ """
81
+
82
+ # Skip regression checks (must be at the start of a line).
83
+ allow_regressions : bool = False
84
+ # Don't run extensive tests
85
+ skip_extensive : bool = False
86
+
87
+ # Allow running a large number of extensive tests. If not set, this script
88
+ # will error out if a threshold is exceeded in order to avoid accidentally
89
+ # spending huge amounts of CI time.
90
+ allow_many_extensive : bool = False
91
+
92
+ # Max number of extensive tests to run by default
93
+ MANY_EXTENSIVE_THRESHOLD : int = 20
94
+
95
+ # String values of directive names
96
+ DIR_ALLOW_REGRESSIONS : str = "allow-regressions"
97
+ DIR_SKIP_EXTENSIVE : str = "skip-extensive"
98
+ DIR_ALLOW_MANY_EXTENSIVE : str = "allow-many-extensive"
99
+
100
+ def __init__ (self , body : str ):
101
+ directives = re .finditer (r"^\s*ci:\s*(?P<dir_name>\S*)" , body , re .MULTILINE )
102
+ for dir in directives :
103
+ name = dir .group ("dir_name" )
104
+ if name == self .DIR_ALLOW_REGRESSIONS :
105
+ self .allow_regressions = True
106
+ elif name == self .DIR_SKIP_EXTENSIVE :
107
+ self .skip_extensive = True
108
+ elif name == self .DIR_ALLOW_MANY_EXTENSIVE :
109
+ self .allow_many_extensive = True
110
+ else :
111
+ eprint (f"Found unexpected directive `{ name } `" )
112
+ exit (1 )
113
+
114
+ pprint .pp (self )
115
+
116
+
83
117
@dataclass
84
118
class PrInfo :
85
119
"""GitHub response for PR query"""
@@ -88,6 +122,7 @@ class PrInfo:
88
122
commits : list [str ]
89
123
created_at : str
90
124
number : int
125
+ cfg : PrCfg
91
126
92
127
@classmethod
93
128
def load (cls , pr_number : int | str ) -> Self :
@@ -104,13 +139,9 @@ def load(cls, pr_number: int | str) -> Self:
104
139
],
105
140
text = True ,
106
141
)
107
- eprint ("PR info:" , json .dumps (pr_info , indent = 4 ))
108
- return cls (** json .loads (pr_info ))
109
-
110
- def contains_directive (self , directive : str ) -> bool :
111
- """Return true if the provided directive is on a line in the PR body"""
112
- lines = self .body .splitlines ()
113
- return any (line .startswith (directive ) for line in lines )
142
+ pr_json = json .loads (pr_info )
143
+ eprint ("PR info:" , json .dumps (pr_json , indent = 4 ))
144
+ return cls (** json .loads (pr_info ), cfg = PrCfg (pr_json ["body" ]))
114
145
115
146
116
147
class FunctionDef (TypedDict ):
@@ -223,10 +254,8 @@ def emit_workflow_output(self):
223
254
224
255
if pr_number is not None and len (pr_number ) > 0 :
225
256
pr = PrInfo .load (pr_number )
226
- skip_tests = pr .contains_directive (SKIP_EXTENSIVE_DIRECTIVE )
227
- error_on_many_tests = not pr .contains_directive (
228
- ALLOW_MANY_EXTENSIVE_DIRECTIVE
229
- )
257
+ skip_tests = pr .cfg .skip_extensive
258
+ error_on_many_tests = not pr .cfg .allow_many_extensive
230
259
231
260
if skip_tests :
232
261
eprint ("Skipping all extensive tests" )
@@ -257,12 +286,12 @@ def emit_workflow_output(self):
257
286
eprint (f"may_skip_libm_ci={ may_skip } " )
258
287
eprint (f"total extensive tests: { total_to_test } " )
259
288
260
- if error_on_many_tests and total_to_test > MANY_EXTENSIVE_THRESHOLD :
289
+ if error_on_many_tests and total_to_test > PrCfg . MANY_EXTENSIVE_THRESHOLD :
261
290
eprint (
262
- f"More than { MANY_EXTENSIVE_THRESHOLD } tests would be run; add"
263
- f" `{ ALLOW_MANY_EXTENSIVE_DIRECTIVE } ` to the PR body if this is"
291
+ f"More than { PrCfg . MANY_EXTENSIVE_THRESHOLD } tests would be run; add"
292
+ f" `{ PrCfg . DIR_ALLOW_MANY_EXTENSIVE } ` to the PR body if this is"
264
293
" intentional. If this is refactoring that happens to touch a lot of"
265
- f" files, `{ SKIP_EXTENSIVE_DIRECTIVE } ` can be used instead."
294
+ f" files, `{ PrCfg . DIR_SKIP_EXTENSIVE } ` can be used instead."
266
295
)
267
296
exit (1 )
268
297
@@ -372,7 +401,7 @@ def handle_bench_regressions(args: list[str]):
372
401
exit (1 )
373
402
374
403
pr = PrInfo .load (pr_number )
375
- if pr .contains_directive ( REGRESSION_DIRECTIVE ) :
404
+ if pr .cfg . allow_regressions :
376
405
eprint ("PR allows regressions" )
377
406
return
378
407
0 commit comments