|
59 | 59 | # accidentally spending huge amounts of CI time.
|
60 | 60 | ALLOW_MANY_EXTENSIVE_DIRECTIVE = "ci: allow-many-extensive"
|
61 | 61 | MANY_EXTENSIVE_THRESHOLD = 20
|
| 62 | +# Require tests for `libm` |
| 63 | +TEST_LIBM_DIRECTIVE = "ci: test-libm" |
62 | 64 |
|
63 | 65 | # Don't run exhaustive tests if these files change, even if they contaiin a function
|
64 | 66 | # definition.
|
@@ -90,8 +92,25 @@ class PrInfo:
|
90 | 92 | number: int
|
91 | 93 |
|
92 | 94 | @classmethod
|
93 |
| - def load(cls, pr_number: int | str) -> Self: |
94 |
| - """For a given PR number, query the body and commit list""" |
| 95 | + def from_env(cls, gh_ref: str | None = None) -> Self | None: |
| 96 | + """Create a PR object from the PR_NUMBER environment if set, using a ref |
| 97 | + as a fallback. Returns `None` is there is no PR found. |
| 98 | + """ |
| 99 | + pr_env = os.environ.get("PR_NUMBER") |
| 100 | + if pr_env is not None and len(pr_env) > 0: |
| 101 | + # `type(cls)` needs to be used rather than `PrInfo` to keep MyPy happy |
| 102 | + return type(cls).from_pr(pr_env) |
| 103 | + |
| 104 | + if gh_ref is None: |
| 105 | + return None |
| 106 | + |
| 107 | + # Split `refs/pull/1234/merge` |
| 108 | + pr_number = int(gh_ref.split("/")[2]) |
| 109 | + return type(cls).from_pr(pr_number) |
| 110 | + |
| 111 | + @classmethod |
| 112 | + def from_pr(cls, pr_number: int | str) -> Self: |
| 113 | + """For a given PR number, query the body and commit list.""" |
95 | 114 | pr_info = sp.check_output(
|
96 | 115 | [
|
97 | 116 | "gh",
|
@@ -207,22 +226,31 @@ def may_skip_libm_ci(self) -> bool:
|
207 | 226 | """If this is a PR and no libm files were changed, allow skipping libm
|
208 | 227 | jobs."""
|
209 | 228 |
|
210 |
| - if self.is_pr(): |
211 |
| - return all(not re.match(TRIGGER_LIBM_PR_CI, str(f)) for f in self.changed) |
| 229 | + # Always run on merge CI |
| 230 | + if not self.is_pr(): |
| 231 | + return False |
| 232 | + |
| 233 | + pr = PrInfo.from_env() |
| 234 | + if pr is None: |
| 235 | + eprint("Is a PR but couldn't load PrInfo") |
| 236 | + exit(1) |
| 237 | + |
| 238 | + # Allow opting in to libm tests |
| 239 | + if pr.contains_directive(TEST_LIBM_DIRECTIVE): |
| 240 | + return True |
212 | 241 |
|
213 |
| - return False |
| 242 | + return all(not re.match(TRIGGER_LIBM_PR_CI, str(f)) for f in self.changed) |
214 | 243 |
|
215 | 244 | def emit_workflow_output(self):
|
216 | 245 | """Create a JSON object a list items for each type's changed files, if any
|
217 | 246 | did change, and the routines that were affected by the change.
|
218 | 247 | """
|
219 | 248 |
|
220 |
| - pr_number = os.environ.get("PR_NUMBER") |
221 | 249 | skip_tests = False
|
222 | 250 | error_on_many_tests = False
|
223 | 251 |
|
224 |
| - if pr_number is not None and len(pr_number) > 0: |
225 |
| - pr = PrInfo.load(pr_number) |
| 252 | + pr = PrInfo.from_env() |
| 253 | + if pr is not None: |
226 | 254 | skip_tests = pr.contains_directive(SKIP_EXTENSIVE_DIRECTIVE)
|
227 | 255 | error_on_many_tests = not pr.contains_directive(
|
228 | 256 | ALLOW_MANY_EXTENSIVE_DIRECTIVE
|
@@ -371,7 +399,7 @@ def handle_bench_regressions(args: list[str]):
|
371 | 399 | eprint(USAGE)
|
372 | 400 | exit(1)
|
373 | 401 |
|
374 |
| - pr = PrInfo.load(pr_number) |
| 402 | + pr = PrInfo.from_pr(pr_number) |
375 | 403 | if pr.contains_directive(REGRESSION_DIRECTIVE):
|
376 | 404 | eprint("PR allows regressions")
|
377 | 405 | return
|
|
0 commit comments