[ci] Invoke arm64 macOS tests if hal/drivers/metal changes (#14700)
This commit changes CI rules to enable running arm64
macOS builds and tests on presubmit if we see changes
to `hal/drivers/metal` directory. This can guard against
breaking changes.
diff --git a/build_tools/github_actions/configure_ci.py b/build_tools/github_actions/configure_ci.py
index f55081c..7b3861b 100755
--- a/build_tools/github_actions/configure_ci.py
+++ b/build_tools/github_actions/configure_ci.py
@@ -45,7 +45,7 @@
import subprocess
import sys
import textwrap
-from typing import Iterable, List, Mapping, Sequence, Set, Tuple
+from typing import Iterable, List, Mapping, Optional, Sequence, Set, Tuple
import yaml
@@ -115,7 +115,9 @@
CONTROL_JOBS = frozenset(["setup", "summary"])
-POSTSUBMIT_ONLY_JOBS = frozenset(
+# Jobs to run only on postsubmit by default.
+# They may also run on presubmit only under certain conditions.
+DEFAULT_POSTSUBMIT_ONLY_JOBS = frozenset(
[
"build_test_all_windows",
"build_test_all_macos_arm64",
@@ -125,6 +127,13 @@
]
)
+# Jobs to run in presumbit if files under the corresponding path see changes.
+# Each tuple consists of the CI job name and a list of file paths to match.
+# The file paths should be specified using Unix shell-style wildcards.
+PRESUBMIT_TOUCH_ONLY_JOBS = [
+ ("build_test_all_macos_arm64", ["runtime/src/iree/hal/drivers/metal/*"]),
+]
+
DEFAULT_BENCHMARK_PRESET_GROUP = [
"cuda",
"x86_64",
@@ -291,27 +300,30 @@
return (trailer_map, labels)
-def get_modified_paths(base_ref: str) -> Iterable[str]:
- return subprocess.run(
- ["git", "diff", "--name-only", base_ref],
- stdout=subprocess.PIPE,
- check=True,
- text=True,
- timeout=60,
- ).stdout.splitlines()
-
-
-def modifies_included_path() -> bool:
- base_ref = os.environ["BASE_REF"]
+def get_modified_paths(base_ref: str) -> Optional[Iterable[str]]:
+ """Returns the paths of modified files in this code change."""
try:
- return any(not skip_path(p) for p in get_modified_paths(base_ref))
+ return subprocess.run(
+ ["git", "diff", "--name-only", base_ref],
+ stdout=subprocess.PIPE,
+ check=True,
+ text=True,
+ timeout=60,
+ ).stdout.splitlines()
except TimeoutError as e:
print(
"Computing modified files timed out. Not using PR diff to determine"
" jobs to run.",
file=sys.stderr,
)
+ return None
+
+
+def modifies_non_skip_paths(paths: Optional[Iterable[str]]) -> bool:
+ """Returns true if not all modified paths are in the skip set."""
+ if paths is None:
return True
+ return any(not skip_path(p) for p in paths)
def get_runner_env(trailers: Mapping[str, str]) -> str:
@@ -381,8 +393,20 @@
all_jobs: Set[str],
*,
is_pr: bool,
- modifies: bool,
+ modified_paths: Optional[Iterable[str]],
) -> Set[str]:
+ """Returns the CI jobs to run.
+
+ Args:
+ trailers: trailers from PR description.
+ all_jobs: all known supported jobs.
+ is_pr: whether this is for pull requests or not.
+ modified_paths: the paths of the files changed. These paths are
+ relative to the repo root directory.
+
+ Returns:
+ The list of CI jobs to run.
+ """
if not is_pr:
print(
"Running all jobs because run was not triggered by a pull request"
@@ -432,14 +456,21 @@
f" '{Trailer.EXTRA_JOBS}', but found {ambiguous_jobs}"
)
- default_jobs = all_jobs - POSTSUBMIT_ONLY_JOBS
+ default_jobs = all_jobs - DEFAULT_POSTSUBMIT_ONLY_JOBS
- if not modifies:
+ if not modifies_non_skip_paths(modified_paths):
print(
"Not including any jobs by default because all modified files"
" are marked as excluded."
)
default_jobs = frozenset()
+ else:
+ # Add jobs if the monitored files are changed.
+ for modified_path in modified_paths:
+ for job, match_paths in PRESUBMIT_TOUCH_ONLY_JOBS:
+ for match_path in match_paths:
+ if fnmatch.fnmatch(modified_path, match_path):
+ default_jobs |= {job}
return (default_jobs | extra_jobs) - skip_jobs
@@ -525,8 +556,8 @@
repo = os.environ["GITHUB_REPOSITORY"]
workflow_ref = os.environ["GITHUB_WORKFLOW_REF"]
workflow_file = parse_path_from_workflow_ref(repo=repo, workflow_ref=workflow_ref)
+ base_ref = os.environ["BASE_REF"]
- modifies = modifies_included_path()
try:
benchmark_presets = get_benchmark_presets(
trailers, labels, is_pr, is_llvm_integrate_pr
@@ -535,7 +566,7 @@
enabled_jobs = get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=get_modified_paths(base_ref),
is_pr=is_pr,
)
except ValueError as e:
diff --git a/build_tools/github_actions/configure_ci_test.py b/build_tools/github_actions/configure_ci_test.py
index 66282c7..9b3aada 100644
--- a/build_tools/github_actions/configure_ci_test.py
+++ b/build_tools/github_actions/configure_ci_test.py
@@ -147,11 +147,11 @@
trailers = {}
all_jobs = {"job1", "job2", "job3"}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, all_jobs)
@@ -159,14 +159,14 @@
def test_get_enabled_jobs_postsubmit(self):
trailers = {}
default_jobs = {"job1", "job2", "job3"}
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
all_jobs = default_jobs | {postsubmit_job}
is_pr = False
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, all_jobs)
@@ -174,14 +174,14 @@
def test_get_enabled_jobs_no_postsubmit(self):
trailers = {}
default_jobs = {"job1", "job2", "job3"}
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, default_jobs)
@@ -189,14 +189,14 @@
def test_get_enabled_jobs_no_modifies(self):
trailers = {}
default_jobs = {"job1", "job2", "job3"}
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = False
+ modified_paths = ["experimental/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, {})
@@ -204,14 +204,14 @@
def test_get_enabled_jobs_skip(self):
trailers = {configure_ci.Trailer.SKIP_JOBS: "job1,job2"}
default_jobs = {"job1", "job2", "job3"}
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, {"job3"})
@@ -219,48 +219,62 @@
def test_get_enabled_jobs_skip_all(self):
trailers = {configure_ci.Trailer.SKIP_JOBS: "all"}
default_jobs = {"job1", "job2", "job3"}
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, {})
def test_get_enabled_jobs_extra(self):
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
trailers = {configure_ci.Trailer.EXTRA_JOBS: postsubmit_job}
default_jobs = {"job1", "job2", "job3"}
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, all_jobs)
def test_get_enabled_jobs_exactly(self):
- postsubmit_job = next(iter(configure_ci.POSTSUBMIT_ONLY_JOBS))
+ postsubmit_job = next(iter(configure_ci.DEFAULT_POSTSUBMIT_ONLY_JOBS))
trailers = {configure_ci.Trailer.EXACTLY_JOBS: postsubmit_job}
default_jobs = {"job1", "job2", "job3"}
all_jobs = default_jobs | {postsubmit_job}
is_pr = True
- modifies = True
+ modified_paths = ["runtime/file"]
jobs = configure_ci.get_enabled_jobs(
trailers,
all_jobs,
- modifies=modifies,
+ modified_paths=modified_paths,
is_pr=is_pr,
)
self.assertCountEqual(jobs, {postsubmit_job})
+ def test_get_enabled_jobs_metal(self):
+ trailers = {}
+ all_jobs = {"job1"}
+ is_pr = True
+ modified_paths = ["runtime/src/iree/hal/drivers/metal/file"]
+ jobs = configure_ci.get_enabled_jobs(
+ trailers,
+ all_jobs,
+ modified_paths=modified_paths,
+ is_pr=is_pr,
+ )
+ expected_jobs = {"job1", "build_test_all_macos_arm64"}
+ self.assertCountEqual(jobs, expected_jobs)
+
def test_parse_path_from_workflow_ref(self):
path = configure_ci.parse_path_from_workflow_ref(
"octocat/example", "octocat/example/.github/test.yml@1234"