Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/repos-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@
"auto_subtree_push": true,
"monorepo_source_of_truth": true
},
{
"name": "tensilelite",
"url": "ROCm/hipBLASLt",
"branch": "develop",
"category": "projects/hipblaslt",
"auto_subtree_pull": false,
"auto_subtree_push": false,
"monorepo_source_of_truth": true
},
{
"name": "hipcub",
"url": "ROCm/hipCUB",
Expand Down
39 changes: 31 additions & 8 deletions .github/scripts/pr_detect_changed_subtrees.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,37 @@ def get_valid_prefixes(
def find_matched_subtrees(
changed_files: List[str], valid_prefixes: Set[str]
) -> List[str]:
"""Find subtrees that match the changed files."""
changed_subtrees = {
"/".join(path.split("/", 2)[:2])
for path in changed_files
if len(path.split("/")) >= 2
}
matched = sorted(changed_subtrees & valid_prefixes)
skipped = sorted(changed_subtrees - valid_prefixes)
"""Find subtrees that match the changed files via longest-prefix match.

A changed file's subtree is the LONGEST registered prefix (`category/name`,
or a nested `category/name/subname`) that matches its path -- checked from
most to least specific -- not a fixed 2-segment truncation. A fixed
2-segment truncation would make a nested subtree registered in
repos-config.json (e.g. `projects/hipblaslt/tensilelite`, nested inside
`projects/hipblaslt`) indistinguishable from a change to its parent: both
would collapse to `projects/hipblaslt`, silently losing the more specific
match. Matching longest-prefix-first, and attributing each changed file to
exactly one subtree, keeps a tensilelite-only change from also being
reported as a hipblaslt-proper change.
"""
# Longest prefixes first, so a nested subtree wins over its parent.
prefixes_by_specificity = sorted(
valid_prefixes, key=lambda p: p.count("/"), reverse=True
)
matched_set: Set[str] = set()
skipped_set: Set[str] = set()
for path in changed_files:
segments = path.split("/")
for prefix in prefixes_by_specificity:
prefix_segments = prefix.split("/")
if segments[: len(prefix_segments)] == prefix_segments:
matched_set.add(prefix)
break
else:
if len(segments) >= 2:
skipped_set.add("/".join(segments[:2]))
matched = sorted(matched_set)
skipped = sorted(skipped_set - matched_set)
Comment thread
tony-davis marked this conversation as resolved.
Resolved
if skipped:
logger.debug(f"Skipped subtrees: {skipped}")
logger.debug(f"Matched subtrees: {matched}")
Expand Down
30 changes: 30 additions & 0 deletions .github/scripts/tests/test_pr_detect_changed_subtrees.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,36 @@ def test_deeply_nested_file_uses_first_two_path_components(self):
)
self.assertEqual(result, ["projects/rocblas"])

def test_nested_subtree_wins_over_its_registered_parent(self):
# A registered nested subtree (e.g. projects/hipblaslt/tensilelite,
# from repos-config.json) must win over its 2-segment parent for files
# inside it -- longest-prefix match, not a fixed 2-segment truncation.
prefixes = {"projects/hipblaslt", "projects/hipblaslt/tensilelite"}
result = sut.find_matched_subtrees(
["projects/hipblaslt/tensilelite/Tensile/KernelWriter.py"], prefixes
)
self.assertEqual(result, ["projects/hipblaslt/tensilelite"])

def test_parent_only_change_does_not_match_registered_nested_subtree(self):
prefixes = {"projects/hipblaslt", "projects/hipblaslt/tensilelite"}
result = sut.find_matched_subtrees(
["projects/hipblaslt/library/src/Handle.cpp"], prefixes
)
self.assertEqual(result, ["projects/hipblaslt"])

def test_mixed_nested_and_parent_changes_match_both_independently(self):
prefixes = {"projects/hipblaslt", "projects/hipblaslt/tensilelite"}
result = sut.find_matched_subtrees(
[
"projects/hipblaslt/tensilelite/Tensile/KernelWriter.py",
"projects/hipblaslt/library/src/Handle.cpp",
],
prefixes,
)
self.assertEqual(
result, ["projects/hipblaslt", "projects/hipblaslt/tensilelite"]
)


# ---------------------------------------------------------------------------
# output_subtrees
Expand Down
16 changes: 16 additions & 0 deletions .github/scripts/tests/therock_matrix_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ def test_collect_projects_to_run_without_additional_option(self):
)
self.assertTrue(blas_entry["run_rocjitsu_race_check"])

def test_collect_projects_to_run_for_nested_tensilelite_subtree(self):
# projects/hipblaslt/tensilelite is a registered nested subtree
# (repos-config.json), distinct from projects/hipblaslt in change
# detection, but must still behave like a hipblaslt change here: same
# "blas" bucket, same sparselt activation, same rocjitsu selection.
subtrees = ["projects/hipblaslt/tensilelite"]

project_to_run = therock_matrix.collect_projects_to_run(subtrees)
self.assertEqual(len(project_to_run), 1)
blas_entry = project_to_run[0]
self.assertIn(
"hipsparselt",
blas_entry["projects_to_test"].split(","),
)
self.assertTrue(blas_entry["run_rocjitsu_race_check"])

def test_rocjitsu_race_check_does_not_run_for_rocblas_only(self):
project_to_run = therock_matrix.collect_projects_to_run(["projects/rocblas"])
self.assertEqual(len(project_to_run), 1)
Expand Down
14 changes: 14 additions & 0 deletions .github/scripts/therock_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
"projects/hipblas": "blas",
"projects/hipblas-common": "blas",
"projects/hipblaslt": "blas",
# Registered as its own repos-config.json subtree (nested inside hipblaslt)
# so change detection can distinguish it from hipblaslt-proper; it maps to
# the same "blas" bucket, which already tests tensilelite (see below), so
# legacy matrix selection is unaffected either way.
"projects/hipblaslt/tensilelite": "blas",
"projects/hipcub": "prim",
"projects/hipdnn": "hipdnn",
"projects/hipfft": "fft",
Expand Down Expand Up @@ -206,10 +211,19 @@
# its additional_options merge into the parent job (e.g. hipSPARSELt depends on hipBLASLt).
SUBTREE_EXTRA_MATRIX_PROJECTS = {
"projects/hipblaslt": "sparselt",
# TensileLite is also a real hipSPARSELt dependency (a separate kernel
# generator copy lives there too), so a TensileLite-only change must
# activate "sparselt" the same way a hipblaslt-proper change does.
"projects/hipblaslt/tensilelite": "sparselt",
}

ROCJITSU_RACE_CHECK_SUBTREES = {
"projects/hipblaslt",
# A TensileLite-only change is still a hipBLASLt change from the race
# check's perspective (same build artifact); registering the nested
# repos-config.json subtree separately (for change-detection specificity)
# must not silently drop it from this set.
"projects/hipblaslt/tensilelite",
}


Expand Down
Loading