77
88import json
99import os
10+ import pprint
1011import re
1112import subprocess as sp
1213import sys
5051DEFAULT_BRANCH = "master"
5152WORKFLOW_NAME = "CI" # Workflow that generates the benchmark artifacts
5253ARTIFACT_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
6254
6355# Don't run exhaustive tests if these files change, even if they contaiin a function
6456# definition.
@@ -80,6 +72,48 @@ def eprint(*args, **kwargs):
8072 print (* args , file = sys .stderr , ** kwargs )
8173
8274
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+
83117@dataclass
84118class PrInfo :
85119 """GitHub response for PR query"""
@@ -88,6 +122,7 @@ class PrInfo:
88122 commits : list [str ]
89123 created_at : str
90124 number : int
125+ cfg : PrCfg
91126
92127 @classmethod
93128 def load (cls , pr_number : int | str ) -> Self :
@@ -104,13 +139,9 @@ def load(cls, pr_number: int | str) -> Self:
104139 ],
105140 text = True ,
106141 )
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" ]))
114145
115146
116147class FunctionDef (TypedDict ):
@@ -223,10 +254,8 @@ def emit_workflow_output(self):
223254
224255 if pr_number is not None and len (pr_number ) > 0 :
225256 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
230259
231260 if skip_tests :
232261 eprint ("Skipping all extensive tests" )
@@ -257,12 +286,12 @@ def emit_workflow_output(self):
257286 eprint (f"may_skip_libm_ci={ may_skip } " )
258287 eprint (f"total extensive tests: { total_to_test } " )
259288
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 :
261290 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"
264293 " 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."
266295 )
267296 exit (1 )
268297
@@ -372,7 +401,7 @@ def handle_bench_regressions(args: list[str]):
372401 exit (1 )
373402
374403 pr = PrInfo .load (pr_number )
375- if pr .contains_directive ( REGRESSION_DIRECTIVE ) :
404+ if pr .cfg . allow_regressions :
376405 eprint ("PR allows regressions" )
377406 return
378407
0 commit comments