CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
99 lines
3.7 KiB
Python
99 lines
3.7 KiB
Python
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Tests validating that artifact descriptors don't have overlapping basedirs.
|
|
|
|
When two artifact descriptors claim the same stage directory for any component
|
|
type, extracting both artifacts into the same output directory causes file
|
|
collisions.
|
|
|
|
This was the root cause of https://github.com/ROCm/TheRock/issues/3758, where
|
|
both `artifact-rocprofiler-sdk.toml` and `artifact-aqlprofile-tests.toml`
|
|
included `profiler/aqlprofile/stage`, causing concurrent extraction to race
|
|
(and fail with "file exists" errors) on overlapping files.
|
|
|
|
This test loads the real artifact-*.toml descriptors from the source tree and
|
|
checks that each stage directory (basedir) belongs to exactly one descriptor.
|
|
|
|
Limitations (cases this test does NOT catch):
|
|
- Two artifacts with *different* basedirs whose installed files collide
|
|
after flattening (basedir prefix stripped). If two subprojects both
|
|
install a file to the same relative path (e.g., "lib/libfoo.so" or
|
|
"bin/sequence.yaml") in their respective stage dirs, flattening produces
|
|
duplicate paths even though the basedirs are distinct. Current projects
|
|
avoid this by using unique file names, but it's convention rather than
|
|
enforcement. Catching this requires inspecting actual build output.
|
|
"""
|
|
|
|
import tomllib
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
THEROCK_DIR = Path(__file__).resolve().parent.parent.parent
|
|
|
|
|
|
def get_basedirs(descriptor_path: Path) -> set[str]:
|
|
"""Extract all basedir paths from an artifact descriptor.
|
|
|
|
The TOML structure for basedirs is:
|
|
[components.{component_name}."{basedir_path}"]
|
|
|
|
Within a component's dict, string-keyed dict values are basedir entries.
|
|
Non-dict values (like "extends") are component-level fields.
|
|
"""
|
|
with open(descriptor_path, "rb") as f:
|
|
data = tomllib.load(f)
|
|
|
|
basedirs: set[str] = set()
|
|
for _comp_name, comp_data in data.get("components", {}).items():
|
|
if not isinstance(comp_data, dict):
|
|
continue
|
|
for key, value in comp_data.items():
|
|
if isinstance(value, dict):
|
|
basedirs.add(key)
|
|
return basedirs
|
|
|
|
|
|
class ArtifactDescriptorOverlapTest(unittest.TestCase):
|
|
"""Verifies no two artifact descriptors claim the same stage directory."""
|
|
|
|
def test_no_duplicate_basedirs_across_descriptors(self):
|
|
"""Each stage directory must belong to exactly one artifact descriptor.
|
|
|
|
If two descriptors reference the same basedir, their tarballs will
|
|
contain overlapping files, causing extraction failures.
|
|
"""
|
|
# basedir -> first descriptor that claims it
|
|
seen: dict[str, Path] = {}
|
|
errors: list[str] = []
|
|
|
|
descriptors = sorted(THEROCK_DIR.rglob("artifact-*.toml"))
|
|
self.assertGreater(
|
|
len(descriptors),
|
|
0,
|
|
f"No artifact descriptors found, check THEROCK_DIR ('{THEROCK_DIR}')",
|
|
)
|
|
|
|
for descriptor_path in descriptors:
|
|
relpath = descriptor_path.relative_to(THEROCK_DIR)
|
|
for basedir in get_basedirs(descriptor_path):
|
|
if basedir in seen:
|
|
errors.append(
|
|
f"basedir '{basedir}' claimed by both "
|
|
f"{seen[basedir]} and {relpath}"
|
|
)
|
|
else:
|
|
seen[basedir] = relpath
|
|
|
|
if errors:
|
|
self.fail(
|
|
"Duplicate basedirs across artifact descriptors will cause "
|
|
"extraction collisions (see "
|
|
"https://github.com/ROCm/TheRock/issues/3758):\n"
|
|
+ "\n".join(f" - {e}" for e in errors)
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|