From e81e88403e3596dbd5076d069fd5fd4c1b5c2c17 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 13:30:43 -0700 Subject: [PATCH 1/8] feat(ci): add script to manage CI labels across repos Add configure_ci_labels.py to create and sync CI-related labels (ci:, gfx, test:, test_filter:, etc.) across repositories. Cleanup only targets gfx, test:, and test_filter: prefixes to avoid touching other labels. Intended for local use only. Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100755 build_tools/github_actions/configure_ci_labels.py diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py new file mode 100755 index 00000000000..90f51c46e7a --- /dev/null +++ b/build_tools/github_actions/configure_ci_labels.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +""" +Manage CI labels for GitHub repositories. + +This script creates and cleans up GitHub labels for repos. It is intended to be +used LOCALLY only to sync CI-related labels across repositories. + +Usage: + # List labels that would be created (dry run) + python manage_ci_labels.py --repo OWNER/REPO --dry-run + + # Create missing labels + python manage_ci_labels.py --repo OWNER/REPO --create + + # Delete labels not in the defined list + python manage_ci_labels.py --repo OWNER/REPO --cleanup + + # Sync labels (create missing + delete extra) + python manage_ci_labels.py --repo OWNER/REPO --sync +""" + +import argparse +import subprocess +import json +import sys +from dataclasses import dataclass +from typing import Optional + +# ============================================================================= +# CI Labels Definition +# ============================================================================= +# Labels from ROCm/TheRock with prefixes: ci:, gfx, test:, test_filter: +# Format: (name, color, description) + +CI_LABELS: list[tuple[str, str, str]] = [ + # ci: labels + ("ci:skip", "FFFF00", "Skip all CI builds/tests for this PR"), + ("ci:run-all-archs", "FFFF00", "Opt-in to building for all architectures on a pull request"), + ("ci:run-multi-arch", "FFFF00", "Opt-in to running multi-arch CI on a pull request"), + ("ci:run-non-multi-arch", "FFFF00", "Opt-in to running non-multi-arch CI on a pull request"), + ("ci:build-jax", "FFFF00", "Enable Jax Build"), + ("ci:asan", "FFFF00", "Opt-in to building ASAN"), + ("ci:host-asan", "FFFF00", "Opt-in to running multi-arch host-asan CI on a pull request"), + # gfx labels + ("gfx103X-linux", "5A4D41", ""), + ("gfx103X", "f9d0c4", ""), + ("gfx90X-dcgpu", "5A4D41", ""), + ("gfx94X-dcgpu", "5A4D41", "Issue/PR relates to gfx94X-dcgpu family."), + ("gfx950-dcgpu", "5A4D41", "Issue/PR relates to gfx950-dcgpu family."), + ("gfx110X-dgpu", "5A4D41", "Issue/PR relates to gfx110X-dgpu family."), + ("gfx110X-all", "b04f4c", "Issue/PR related to gfx110X-all family"), + ("gfx1103", "795816", "Issue/PR relates to gfx1103"), + ("gfx1150", "5A4D41", ""), + ("gfx1151", "5A4D41", "Issue/PR relates to gfx1151."), + ("gfx1152", "5A4D41", ""), + ("gfx1153", "5A4D41", ""), + ("gfx120X-all", "5A4D41", "Issue/PR relates to gfx120X-all family"), + ("gfx125x", "5A4D41", "Issue/PR relates to gfx125x family"), + ("gfx125X-dcgpu", "5A4D41", "Issue/PR relates to gfx125X-dcgpu family."), + ("gfx900", "5A4D41", ""), + ("gfx906", "5A4D41", ""), + ("gfx908", "5A4D41", ""), + ("gfx90a", "5A4D41", ""), + ("gfx90c", "5A4D41", ""), + # test: labels + ("test:hipblaslt", "3FA7D6", "For pull requests, runs full tests for only hipblaslt and other labeled projects."), + ("test:hipcub", "3FA7D6", "For pull requests, runs full tests for only hipcub and other labeled projects."), + ("test:miopen", "3FA7D6", "For pull requests, runs full tests for only miopen and other labeled projects."), + ("test:rocblas", "3FA7D6", "For pull requests, runs full tests for only rocblas and other labeled projects."), + ("test:hipblas", "3FA7D6", "For pull requests, runs full tests for only hipblas and other labeled projects."), + ("test:rocprim", "3FA7D6", "For pull requests, runs full tests for only rocprim and other labeled projects."), + ("test:rocsolver", "3FA7D6", "For pull requests, runs full tests for only rocsolver and other labeled projects."), + ("test:rocthrust", "3FA7D6", "For pull requests, runs full tests for only rocthrust and other labeled projects."), + ("test:rocsparse", "3FA7D6", "For pull requests, runs full tests for only rocsparse and other labeled projects."), + ("test:hipsparse", "3FA7D6", "For pull requests, runs full tests for only hipsparse and other labeled projects."), + ("test:hipfft", "3FA7D6", "For pull requests, runs full tests for only hipfft and other labeled projects."), + ("test:hipsolver", "3FA7D6", "For pull requests, runs full tests for only hipsolver and other labeled projects."), + ("test:rocfft", "3FA7D6", "For pull requests, runs full tests for only rocfft and other labeled projects."), + ("test:hipsparselt", "3FA7D6", "For pull requests, runs full tests for only hipsparselt and other labeled projects."), + ("test:rccl", "3FA7D6", "For pull requests, runs full tests for only rccl and other labeled projects."), + ("test:hipdnn", "3FA7D6", "For pull requests, runs full tests for only hipdnn and other labeled projects."), + ("test:rocroller", "3FA7D6", "For pull requests, runs full tests for only rocroller and other labeled projects."), + ("test:composablekernel", "3FA7D6", "For pull requests, runs full tests for only composable_kernel and other labeled projects."), + ("test:libhipcxx_hipcc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hipcc and other labeled projects."), + ("test:libhipcxx_hiprtc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hiprtc and other labeled projects."), + ("test:ocltst", "03B8ED", "For pull requests, this label executes the ocltst and other labeled projects."), + ("test:hip-tests", "20566E", "For pull requests, this label executes the hip-tests"), + ("test:rocrtst", "3FA7D6", "For pull requests, this label executes the rocrtst and other labeled projects."), + ("test:origami", "3FA7D6", "For pull requests, this label executes the origami and other labeled projects."), + ("test:rocdecode", "3FA7D6", "For pull requests, this label executes the rocdecode and other labeled projects."), + ("test:rocjpeg", "3FA7D6", "For pull requests, this label executes the rocjpeg and other labeled projects."), + ("test:rocprofiler-systems", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-systems and other labeled projects."), + ("test:rocprofiler-sdk", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-sdk and other labeled projects."), + ("test:hipkernelprovider", "20566E", "For pull requests, runs full tests for only hipkernelprovider and other labeled projects."), + ("test:amdsmi", "277804", ""), + ("test:rocgdb-cpu", "20566E", "Run ROCgdb cpu tests only"), + ("test:rocgdb-gpu", "20566E", "Run ROCgdb gpu tests only"), + ("test:rocgdb", "20566E", "Test all test:rocgdb* labels"), + ("test:rocprofiler-sdk-spm", "3FA7D6", "To run rocprofier-sdk-spm jobs"), + ("test:rpp", "3FA7D6", ""), + ("test:miopen-dbsync", "3FA7D6", "For pull requests, runs the GPU-free miopen-dbsync (StaticFDBSync/rocjitsu) test component."), + # test_filter: labels + ("test_filter:quick", "a2fab4", "If enabled, the PR will run quick tests"), + ("test_filter:standard", "a2fab4", "If enabled, the PR will run standard tests"), + ("test_filter:comprehensive", "a2fab4", "If enabled, the PR will run comprehensive tests"), + ("test_filter:full", "a2fab4", "If enabled, the PR will run full tests"), + # test_runner: labels + ("test_runner:oem", "23edeb", "If added, the tests will run on a machine configured with `oem` kernel"), + # build_variant: labels + ("build_variant:asan", "4b398c", "If enabled, the pull request will run ASAN builds"), +] + + +@dataclass +class Label: + name: str + color: str + description: str + + +def run_gh_command(args: list[str], check: bool = True) -> subprocess.CompletedProcess: + """Run a GitHub CLI command.""" + cmd = ["gh"] + args + return subprocess.run(cmd, capture_output=True, text=True, check=check) + + +def get_repo_labels(repo: str) -> list[Label]: + """Get all labels from a repository.""" + result = run_gh_command( + ["label", "list", "--repo", repo, "--limit", "500", "--json", "name,color,description"] + ) + labels_data = json.loads(result.stdout) + return [Label(name=l["name"], color=l["color"], description=l["description"]) for l in labels_data] + + +def create_label(repo: str, name: str, color: str, description: str, dry_run: bool = False) -> bool: + """Create a label in the repository.""" + if dry_run: + print(f" [DRY RUN] Would create label: {name}") + return True + + args = ["label", "create", name, "--repo", repo, "--color", color, "--force"] + if description: + args.extend(["--description", description]) + + result = run_gh_command(args, check=False) + if result.returncode == 0: + print(f" Created label: {name}") + return True + else: + print(f" Failed to create label {name}: {result.stderr}") + return False + + +def delete_label(repo: str, name: str, dry_run: bool = False) -> bool: + """Delete a label from the repository.""" + if dry_run: + print(f" [DRY RUN] Would delete label: {name}") + return True + + result = run_gh_command(["label", "delete", name, "--repo", repo, "--yes"], check=False) + if result.returncode == 0: + print(f" Deleted label: {name}") + return True + else: + print(f" Failed to delete label {name}: {result.stderr}") + return False + + +def get_ci_label_names() -> set[str]: + """Get set of CI label names.""" + return {label[0] for label in CI_LABELS} + + +# Prefixes that are safe to clean up (delete if not in CI_LABELS) +# We only cleanup these specific prefixes to avoid touching other labels +CLEANUP_PREFIXES = ("gfx", "test:", "test_filter:") + + +def filter_cleanable_labels(labels: list[Label]) -> list[Label]: + """Filter labels to only those with prefixes safe to cleanup.""" + return [l for l in labels if any(l.name.startswith(p) for p in CLEANUP_PREFIXES)] + + +def list_labels(repo: str) -> None: + """List all CI labels defined and their status in the repo.""" + print(f"\nCI Labels defined in this script: {len(CI_LABELS)}") + print("-" * 60) + + existing_labels = get_repo_labels(repo) + existing_names = {l.name for l in existing_labels} + + print("\nLabels that would be created/updated:") + for name, color, description in CI_LABELS: + status = "exists" if name in existing_names else "MISSING" + print(f" [{status}] {name} (#{color})") + + ci_label_names = get_ci_label_names() + extra_cleanable_labels = [l for l in filter_cleanable_labels(existing_labels) if l.name not in ci_label_names] + + if extra_cleanable_labels: + print(f"\nCleanable labels in repo not in this script ({len(extra_cleanable_labels)}):") + print(f"(Only labels with prefixes {CLEANUP_PREFIXES} are subject to cleanup)") + for label in extra_cleanable_labels: + print(f" {label.name} (#{label.color})") + + +def create_labels(repo: str, dry_run: bool = False) -> None: + """Create missing CI labels in the repository.""" + print(f"\nCreating CI labels in {repo}...") + if dry_run: + print("(DRY RUN - no changes will be made)\n") + + existing_labels = get_repo_labels(repo) + existing_names = {l.name for l in existing_labels} + + created = 0 + skipped = 0 + for name, color, description in CI_LABELS: + if name in existing_names: + skipped += 1 + continue + if create_label(repo, name, color, description, dry_run): + created += 1 + + print(f"\nSummary: {created} created, {skipped} already existed") + + +def cleanup_labels(repo: str, dry_run: bool = False) -> None: + """Delete labels with cleanable prefixes that are not in the defined list. + + Only cleans up labels with prefixes: gfx, test:, test_filter: + Other labels (ci:, test_runner:, build_variant:, etc.) are NOT touched. + """ + print(f"\nCleaning up labels in {repo}...") + print(f"(Only cleaning labels with prefixes: {CLEANUP_PREFIXES})") + if dry_run: + print("(DRY RUN - no changes will be made)\n") + + existing_labels = get_repo_labels(repo) + ci_label_names = get_ci_label_names() + + # Only clean up labels with cleanable prefixes that aren't in our list + extra_labels = [l for l in filter_cleanable_labels(existing_labels) if l.name not in ci_label_names] + + if not extra_labels: + print("No extra labels to clean up.") + return + + deleted = 0 + for label in extra_labels: + if delete_label(repo, label.name, dry_run): + deleted += 1 + + print(f"\nSummary: {deleted} labels deleted") + + +def sync_labels(repo: str, dry_run: bool = False) -> None: + """Sync labels: create missing and optionally delete extra.""" + create_labels(repo, dry_run) + print() + cleanup_labels(repo, dry_run) + + +def main(): + parser = argparse.ArgumentParser( + description="Manage CI labels for GitHub repositories (LOCAL USE ONLY)", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + parser.add_argument( + "--repo", + required=True, + help="Repository in OWNER/REPO format (e.g., ROCm/TheRock)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be done without making changes", + ) + + action_group = parser.add_mutually_exclusive_group(required=True) + action_group.add_argument( + "--list", + action="store_true", + help="List CI labels and their status", + ) + action_group.add_argument( + "--create", + action="store_true", + help="Create missing CI labels", + ) + action_group.add_argument( + "--cleanup", + action="store_true", + help="Delete labels with prefixes (gfx, test:, test_filter:) not in the defined list", + ) + action_group.add_argument( + "--sync", + action="store_true", + help="Sync labels (create missing + delete extra)", + ) + + args = parser.parse_args() + + # Validate repo format + if "/" not in args.repo: + print(f"Error: Repository must be in OWNER/REPO format, got: {args.repo}") + sys.exit(1) + + try: + if args.list: + list_labels(args.repo) + elif args.create: + create_labels(args.repo, args.dry_run) + elif args.cleanup: + cleanup_labels(args.repo, args.dry_run) + elif args.sync: + sync_labels(args.repo, args.dry_run) + except subprocess.CalledProcessError as e: + print(f"Error running gh command: {e}") + print(f"stderr: {e.stderr}") + sys.exit(1) + + +if __name__ == "__main__": + main() From 36f5bf09be7a7ebb917e2067184b1e2ff371e603 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 13:59:13 -0700 Subject: [PATCH 2/8] refactor(ci): simplify label script and add ci: prefix to gfx labels - Remove cleanup feature from configure_ci_labels.py (create only) - Add ci: prefix to gfx labels (e.g., ci:gfx120X-all, ci:gfx950-dcgpu) - Update ci_behavior_manipulation.md docs to reflect new label format Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 184 +++++------------- docs/development/ci_behavior_manipulation.md | 2 +- 2 files changed, 54 insertions(+), 132 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index 90f51c46e7a..8cfe57ac70b 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -1,22 +1,19 @@ #!/usr/bin/env python3 """ -Manage CI labels for GitHub repositories. +Create CI labels for GitHub repositories. -This script creates and cleans up GitHub labels for repos. It is intended to be +This script creates CI-related GitHub labels for repos. It is intended to be used LOCALLY only to sync CI-related labels across repositories. Usage: - # List labels that would be created (dry run) - python manage_ci_labels.py --repo OWNER/REPO --dry-run + # List labels and their status in the repo + python configure_ci_labels.py --repo OWNER/REPO --list - # Create missing labels - python manage_ci_labels.py --repo OWNER/REPO --create - - # Delete labels not in the defined list - python manage_ci_labels.py --repo OWNER/REPO --cleanup + # Create missing labels (dry run) + python configure_ci_labels.py --repo OWNER/REPO --create --dry-run - # Sync labels (create missing + delete extra) - python manage_ci_labels.py --repo OWNER/REPO --sync + # Create missing labels + python configure_ci_labels.py --repo OWNER/REPO --create """ import argparse @@ -24,16 +21,15 @@ import json import sys from dataclasses import dataclass -from typing import Optional # ============================================================================= # CI Labels Definition # ============================================================================= -# Labels from ROCm/TheRock with prefixes: ci:, gfx, test:, test_filter: +# Labels from ROCm/TheRock for CI behavior manipulation. # Format: (name, color, description) CI_LABELS: list[tuple[str, str, str]] = [ - # ci: labels + # ci: general labels ("ci:skip", "FFFF00", "Skip all CI builds/tests for this PR"), ("ci:run-all-archs", "FFFF00", "Opt-in to building for all architectures on a pull request"), ("ci:run-multi-arch", "FFFF00", "Opt-in to running multi-arch CI on a pull request"), @@ -41,28 +37,28 @@ ("ci:build-jax", "FFFF00", "Enable Jax Build"), ("ci:asan", "FFFF00", "Opt-in to building ASAN"), ("ci:host-asan", "FFFF00", "Opt-in to running multi-arch host-asan CI on a pull request"), - # gfx labels - ("gfx103X-linux", "5A4D41", ""), - ("gfx103X", "f9d0c4", ""), - ("gfx90X-dcgpu", "5A4D41", ""), - ("gfx94X-dcgpu", "5A4D41", "Issue/PR relates to gfx94X-dcgpu family."), - ("gfx950-dcgpu", "5A4D41", "Issue/PR relates to gfx950-dcgpu family."), - ("gfx110X-dgpu", "5A4D41", "Issue/PR relates to gfx110X-dgpu family."), - ("gfx110X-all", "b04f4c", "Issue/PR related to gfx110X-all family"), - ("gfx1103", "795816", "Issue/PR relates to gfx1103"), - ("gfx1150", "5A4D41", ""), - ("gfx1151", "5A4D41", "Issue/PR relates to gfx1151."), - ("gfx1152", "5A4D41", ""), - ("gfx1153", "5A4D41", ""), - ("gfx120X-all", "5A4D41", "Issue/PR relates to gfx120X-all family"), - ("gfx125x", "5A4D41", "Issue/PR relates to gfx125x family"), - ("gfx125X-dcgpu", "5A4D41", "Issue/PR relates to gfx125X-dcgpu family."), - ("gfx900", "5A4D41", ""), - ("gfx906", "5A4D41", ""), - ("gfx908", "5A4D41", ""), - ("gfx90a", "5A4D41", ""), - ("gfx90c", "5A4D41", ""), - # test: labels + # ci:gfx labels (GPU architecture opt-in) + ("ci:gfx103X-linux", "5A4D41", "Opt-in to gfx103X-linux builds/tests"), + ("ci:gfx103X", "5A4D41", "Opt-in to gfx103X builds/tests"), + ("ci:gfx90X-dcgpu", "5A4D41", "Opt-in to gfx90X-dcgpu builds/tests"), + ("ci:gfx94X-dcgpu", "5A4D41", "Opt-in to gfx94X-dcgpu builds/tests"), + ("ci:gfx950-dcgpu", "5A4D41", "Opt-in to gfx950-dcgpu builds/tests"), + ("ci:gfx110X-dgpu", "5A4D41", "Opt-in to gfx110X-dgpu builds/tests"), + ("ci:gfx110X-all", "5A4D41", "Opt-in to gfx110X-all builds/tests"), + ("ci:gfx1103", "5A4D41", "Opt-in to gfx1103 builds/tests"), + ("ci:gfx1150", "5A4D41", "Opt-in to gfx1150 builds/tests"), + ("ci:gfx1151", "5A4D41", "Opt-in to gfx1151 builds/tests"), + ("ci:gfx1152", "5A4D41", "Opt-in to gfx1152 builds/tests"), + ("ci:gfx1153", "5A4D41", "Opt-in to gfx1153 builds/tests"), + ("ci:gfx120X-all", "5A4D41", "Opt-in to gfx120X-all builds/tests"), + ("ci:gfx125x", "5A4D41", "Opt-in to gfx125x builds/tests"), + ("ci:gfx125X-dcgpu", "5A4D41", "Opt-in to gfx125X-dcgpu builds/tests"), + ("ci:gfx900", "5A4D41", "Opt-in to gfx900 builds/tests"), + ("ci:gfx906", "5A4D41", "Opt-in to gfx906 builds/tests"), + ("ci:gfx908", "5A4D41", "Opt-in to gfx908 builds/tests"), + ("ci:gfx90a", "5A4D41", "Opt-in to gfx90a builds/tests"), + ("ci:gfx90c", "5A4D41", "Opt-in to gfx90c builds/tests"), + # test: labels (project-specific test opt-in) ("test:hipblaslt", "3FA7D6", "For pull requests, runs full tests for only hipblaslt and other labeled projects."), ("test:hipcub", "3FA7D6", "For pull requests, runs full tests for only hipcub and other labeled projects."), ("test:miopen", "3FA7D6", "For pull requests, runs full tests for only miopen and other labeled projects."), @@ -92,19 +88,19 @@ ("test:rocprofiler-systems", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-systems and other labeled projects."), ("test:rocprofiler-sdk", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-sdk and other labeled projects."), ("test:hipkernelprovider", "20566E", "For pull requests, runs full tests for only hipkernelprovider and other labeled projects."), - ("test:amdsmi", "277804", ""), + ("test:amdsmi", "277804", "For pull requests, runs full tests for only amdsmi and other labeled projects."), ("test:rocgdb-cpu", "20566E", "Run ROCgdb cpu tests only"), ("test:rocgdb-gpu", "20566E", "Run ROCgdb gpu tests only"), ("test:rocgdb", "20566E", "Test all test:rocgdb* labels"), - ("test:rocprofiler-sdk-spm", "3FA7D6", "To run rocprofier-sdk-spm jobs"), - ("test:rpp", "3FA7D6", ""), + ("test:rocprofiler-sdk-spm", "3FA7D6", "To run rocprofiler-sdk-spm jobs"), + ("test:rpp", "3FA7D6", "For pull requests, runs full tests for only rpp and other labeled projects."), ("test:miopen-dbsync", "3FA7D6", "For pull requests, runs the GPU-free miopen-dbsync (StaticFDBSync/rocjitsu) test component."), - # test_filter: labels + # test_filter: labels (test level override) ("test_filter:quick", "a2fab4", "If enabled, the PR will run quick tests"), ("test_filter:standard", "a2fab4", "If enabled, the PR will run standard tests"), ("test_filter:comprehensive", "a2fab4", "If enabled, the PR will run comprehensive tests"), ("test_filter:full", "a2fab4", "If enabled, the PR will run full tests"), - # test_runner: labels + # test_runner: labels (test machine selection) ("test_runner:oem", "23edeb", "If added, the tests will run on a machine configured with `oem` kernel"), # build_variant: labels ("build_variant:asan", "4b398c", "If enabled, the pull request will run ASAN builds"), @@ -134,12 +130,12 @@ def get_repo_labels(repo: str) -> list[Label]: def create_label(repo: str, name: str, color: str, description: str, dry_run: bool = False) -> bool: - """Create a label in the repository.""" + """Create a label in the repository. Skips if already exists.""" if dry_run: print(f" [DRY RUN] Would create label: {name}") return True - args = ["label", "create", name, "--repo", repo, "--color", color, "--force"] + args = ["label", "create", name, "--repo", repo, "--color", color] if description: args.extend(["--description", description]) @@ -152,36 +148,11 @@ def create_label(repo: str, name: str, color: str, description: str, dry_run: bo return False -def delete_label(repo: str, name: str, dry_run: bool = False) -> bool: - """Delete a label from the repository.""" - if dry_run: - print(f" [DRY RUN] Would delete label: {name}") - return True - - result = run_gh_command(["label", "delete", name, "--repo", repo, "--yes"], check=False) - if result.returncode == 0: - print(f" Deleted label: {name}") - return True - else: - print(f" Failed to delete label {name}: {result.stderr}") - return False - - def get_ci_label_names() -> set[str]: """Get set of CI label names.""" return {label[0] for label in CI_LABELS} -# Prefixes that are safe to clean up (delete if not in CI_LABELS) -# We only cleanup these specific prefixes to avoid touching other labels -CLEANUP_PREFIXES = ("gfx", "test:", "test_filter:") - - -def filter_cleanable_labels(labels: list[Label]) -> list[Label]: - """Filter labels to only those with prefixes safe to cleanup.""" - return [l for l in labels if any(l.name.startswith(p) for p in CLEANUP_PREFIXES)] - - def list_labels(repo: str) -> None: """List all CI labels defined and their status in the repo.""" print(f"\nCI Labels defined in this script: {len(CI_LABELS)}") @@ -190,19 +161,20 @@ def list_labels(repo: str) -> None: existing_labels = get_repo_labels(repo) existing_names = {l.name for l in existing_labels} - print("\nLabels that would be created/updated:") + missing_count = 0 + exists_count = 0 + + print("\nLabel status:") for name, color, description in CI_LABELS: - status = "exists" if name in existing_names else "MISSING" + if name in existing_names: + status = "exists" + exists_count += 1 + else: + status = "MISSING" + missing_count += 1 print(f" [{status}] {name} (#{color})") - ci_label_names = get_ci_label_names() - extra_cleanable_labels = [l for l in filter_cleanable_labels(existing_labels) if l.name not in ci_label_names] - - if extra_cleanable_labels: - print(f"\nCleanable labels in repo not in this script ({len(extra_cleanable_labels)}):") - print(f"(Only labels with prefixes {CLEANUP_PREFIXES} are subject to cleanup)") - for label in extra_cleanable_labels: - print(f" {label.name} (#{label.color})") + print(f"\nSummary: {exists_count} exist, {missing_count} missing") def create_labels(repo: str, dry_run: bool = False) -> None: @@ -226,45 +198,9 @@ def create_labels(repo: str, dry_run: bool = False) -> None: print(f"\nSummary: {created} created, {skipped} already existed") -def cleanup_labels(repo: str, dry_run: bool = False) -> None: - """Delete labels with cleanable prefixes that are not in the defined list. - - Only cleans up labels with prefixes: gfx, test:, test_filter: - Other labels (ci:, test_runner:, build_variant:, etc.) are NOT touched. - """ - print(f"\nCleaning up labels in {repo}...") - print(f"(Only cleaning labels with prefixes: {CLEANUP_PREFIXES})") - if dry_run: - print("(DRY RUN - no changes will be made)\n") - - existing_labels = get_repo_labels(repo) - ci_label_names = get_ci_label_names() - - # Only clean up labels with cleanable prefixes that aren't in our list - extra_labels = [l for l in filter_cleanable_labels(existing_labels) if l.name not in ci_label_names] - - if not extra_labels: - print("No extra labels to clean up.") - return - - deleted = 0 - for label in extra_labels: - if delete_label(repo, label.name, dry_run): - deleted += 1 - - print(f"\nSummary: {deleted} labels deleted") - - -def sync_labels(repo: str, dry_run: bool = False) -> None: - """Sync labels: create missing and optionally delete extra.""" - create_labels(repo, dry_run) - print() - cleanup_labels(repo, dry_run) - - def main(): parser = argparse.ArgumentParser( - description="Manage CI labels for GitHub repositories (LOCAL USE ONLY)", + description="Create CI labels for GitHub repositories (LOCAL USE ONLY)", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__, ) @@ -283,22 +219,12 @@ def main(): action_group.add_argument( "--list", action="store_true", - help="List CI labels and their status", + help="List CI labels and their status in the repo", ) action_group.add_argument( "--create", action="store_true", - help="Create missing CI labels", - ) - action_group.add_argument( - "--cleanup", - action="store_true", - help="Delete labels with prefixes (gfx, test:, test_filter:) not in the defined list", - ) - action_group.add_argument( - "--sync", - action="store_true", - help="Sync labels (create missing + delete extra)", + help="Create missing CI labels (skips existing)", ) args = parser.parse_args() @@ -313,10 +239,6 @@ def main(): list_labels(args.repo) elif args.create: create_labels(args.repo, args.dry_run) - elif args.cleanup: - cleanup_labels(args.repo, args.dry_run) - elif args.sync: - sync_labels(args.repo, args.dry_run) except subprocess.CalledProcessError as e: print(f"Error running gh command: {e}") print(f"stderr: {e.stderr}") diff --git a/docs/development/ci_behavior_manipulation.md b/docs/development/ci_behavior_manipulation.md index 866d295d52e..11bc9f03bb5 100644 --- a/docs/development/ci_behavior_manipulation.md +++ b/docs/development/ci_behavior_manipulation.md @@ -25,7 +25,7 @@ The following labels may be added to a pull request to modify CI behavior: | `ci:run-all-archs` | Build and test all possible architectures | | `ci:asan` | Enable ASAN CI builds and tests. ASAN CI is skipped by default on PRs unless this label is present. | | `ci:host-asan` | Alias for `ci:asan`. Enable ASAN CI builds and tests. | -| `gfx...` | Opt-in to building and testing the specified gfx family (e.g. `gfx120X`, `gfx950`) | +| `ci:gfx...` | Opt-in to building and testing the specified gfx family (e.g. `ci:gfx120X-all`, `ci:gfx950-dcgpu`) | | `test:...` | Run tests only for the specified projects (e.g. `test:rocthrust`, `test:hipblaslt`). Sets test level to `full` unless overridden by `test_filter:`. Multiple `test:` labels can be combined. | | `test_runner:...` | Run tests on only custom test machines (e.g. `test_runner:oem`). Single-arch CI only. | | `test_filter:...` | Override the test level (e.g. `test_filter:comprehensive`, `test_filter:quick`). Takes priority over all other test level logic. See [test_filtering.md](./test_filtering.md) for allowed values. | From 24d8d0e725e677852c1aed03e945fb13448fc263 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 14:07:12 -0700 Subject: [PATCH 3/8] feat(ci): update multi-arch CI to use ci:gfx labels Update configure_multi_arch_ci.py to recognize the new ci:gfx prefix for GPU architecture labels (e.g., ci:gfx120X-all instead of gfx120X-all). Also update tests to use the new label format. Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_multi_arch_ci.py | 11 ++++++----- .../tests/configure_multi_arch_ci_test.py | 14 +++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/build_tools/github_actions/configure_multi_arch_ci.py b/build_tools/github_actions/configure_multi_arch_ci.py index f1ef5f160a3..e0ec058ae71 100755 --- a/build_tools/github_actions/configure_multi_arch_ci.py +++ b/build_tools/github_actions/configure_multi_arch_ci.py @@ -859,7 +859,7 @@ def select_targets(ci_inputs: CIInputs) -> TargetSelection: - pull_request: Smallest default set (presubmit families). Designed for fast feedback on proposed changes. PR labels can opt in to additional - families (gfx* labels) or the full set (ci:run-all-archs). + families (ci:gfx* labels) or the full set (ci:run-all-archs). - push: Broader coverage (presubmit + postsubmit families). Runs on code that has landed, so we want more thorough validation than PRs without paying the full nightly cost. @@ -922,7 +922,7 @@ def select_targets(ci_inputs: CIInputs) -> TargetSelection: print(" Using caller-supplied GPU families") elif ci_inputs.is_pull_request: # Smallest default set for fast PR feedback. PR labels can extend - # the set below (gfx* for individual families, ci:run-all-archs + # the set below (ci:gfx* for individual families, ci:run-all-archs # for everything). defaults = list(get_all_families_for_trigger_types(["presubmit"]).keys()) linux_names = list(defaults) @@ -968,12 +968,13 @@ def select_targets(ci_inputs: CIInputs) -> TargetSelection: windows_names = list(all_families.keys()) print(" Label 'ci:run-all-archs' -> all families") break - if label.lower().startswith("gfx"): + if label.lower().startswith("ci:gfx"): # Trim suffixes from labels since amdgpu_family_matrix.py # specifies families with no suffix (e.g. `gfx94x`) but - # we have some labels like `gfx94X-dcgpu` or `gfx103X-linux`. + # we have some labels like `ci:gfx94X-dcgpu` or `ci:gfx103X-linux`. # Family keys are lowercase, so normalize the target. - target = label.split("-")[0].lower() + # Strip ci: prefix, then split on dash to get the base family. + target = label[3:].split("-")[0].lower() linux_names.append(target) windows_names.append(target) print(f" Label '{label}' -> adding target {target}") diff --git a/build_tools/github_actions/tests/configure_multi_arch_ci_test.py b/build_tools/github_actions/tests/configure_multi_arch_ci_test.py index 12643b4fbee..d2f0492ca1c 100644 --- a/build_tools/github_actions/tests/configure_multi_arch_ci_test.py +++ b/build_tools/github_actions/tests/configure_multi_arch_ci_test.py @@ -181,14 +181,14 @@ def test_pull_request_extracts_labels(self): event_payload={ "pull_request": { "labels": [ - {"name": "gfx950", "id": 1}, + {"name": "ci:gfx950", "id": 1}, {"name": "test:rocprim", "id": 2}, ] } }, commit_ref="feature-branch", ) - self.assertEqual(inputs.pr_labels, ["gfx950", "test:rocprim"]) + self.assertEqual(inputs.pr_labels, ["ci:gfx950", "test:rocprim"]) self.assertEqual(inputs.base_ref, "HEAD^") def test_pull_request_test_labels_extracted_to_test_labels(self): @@ -200,7 +200,7 @@ def test_pull_request_test_labels_extracted_to_test_labels(self): "labels": [ {"name": "test:rccl", "id": 1}, {"name": "test:rocprim", "id": 2}, - {"name": "gfx950", "id": 3}, + {"name": "ci:gfx950", "id": 3}, ] } }, @@ -987,7 +987,7 @@ def test_pull_request_gfx_label_adds_family(self): base_ref="HEAD^", build_variant="release", # gfx906 is nightly-only, not in presubmit+postsubmit defaults - pr_labels=["gfx906"], + pr_labels=["ci:gfx906"], ) result_without = cm.select_targets(inputs_without) result_with = cm.select_targets(inputs_with) @@ -1009,14 +1009,14 @@ def test_pull_request_run_all_archs_label(self): self.assertIn("gfx906", result.linux_families) def test_pull_request_unknown_gfx_label_raises(self): - """PR with an unknown gfx label fails fast.""" + """PR with an unknown ci:gfx label fails fast.""" inputs = cm.CIInputs( run_id="12345", event_name="pull_request", commit_ref="feature", base_ref="HEAD^", build_variant="release", - pr_labels=["gfx9999"], + pr_labels=["ci:gfx9999"], ) with self.assertRaises(ValueError, msg="Unknown GPU families"): cm.select_targets(inputs) @@ -2111,7 +2111,7 @@ class TestFamilyTestFilters(unittest.TestCase): # Mock family matrix for testing trigger_test_label_only behavior. # Family keys use "mock-" prefix to avoid conflict with label parsing - # (labels starting with "gfx" get special handling in select_targets). + # (labels starting with "ci:gfx" get special handling in select_targets). MOCK_FAMILIES_TRIGGER_TEST_LABEL = { # Presubmit family - always runs on PRs "mock-presubmit": { From 87ab77ee5a3fa916b1fa01a1bcb1408e81aa17f8 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 14:14:35 -0700 Subject: [PATCH 4/8] feat(ci): add --force flag to update existing label colors/descriptions When using --create --force, existing labels will be updated to match the color and description defined in the script. Without --force, existing labels are skipped (checked by name only). Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index 8cfe57ac70b..9602e0c3810 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -14,6 +14,9 @@ # Create missing labels python configure_ci_labels.py --repo OWNER/REPO --create + + # Create missing labels and update existing ones to match color/description + python configure_ci_labels.py --repo OWNER/REPO --create --force """ import argparse @@ -148,6 +151,25 @@ def create_label(repo: str, name: str, color: str, description: str, dry_run: bo return False +def update_label(repo: str, name: str, color: str, description: str, dry_run: bool = False) -> bool: + """Update an existing label's color and description.""" + if dry_run: + print(f" [DRY RUN] Would update label: {name}") + return True + + args = ["label", "edit", name, "--repo", repo, "--color", color] + if description: + args.extend(["--description", description]) + + result = run_gh_command(args, check=False) + if result.returncode == 0: + print(f" Updated label: {name}") + return True + else: + print(f" Failed to update label {name}: {result.stderr}") + return False + + def get_ci_label_names() -> set[str]: """Get set of CI label names.""" return {label[0] for label in CI_LABELS} @@ -177,25 +199,46 @@ def list_labels(repo: str) -> None: print(f"\nSummary: {exists_count} exist, {missing_count} missing") -def create_labels(repo: str, dry_run: bool = False) -> None: - """Create missing CI labels in the repository.""" +def create_labels(repo: str, dry_run: bool = False, force: bool = False) -> None: + """Create missing CI labels in the repository. + + Args: + repo: Repository in OWNER/REPO format. + dry_run: If True, only print what would be done. + force: If True, update existing labels to match color/description. + """ print(f"\nCreating CI labels in {repo}...") + if force: + print("(--force: existing labels will be updated to match)") if dry_run: print("(DRY RUN - no changes will be made)\n") existing_labels = get_repo_labels(repo) - existing_names = {l.name for l in existing_labels} + existing_map = {l.name: l for l in existing_labels} created = 0 + updated = 0 skipped = 0 for name, color, description in CI_LABELS: - if name in existing_names: - skipped += 1 + if name in existing_map: + if force: + existing = existing_map[name] + # Check if color or description differs (color comparison is case-insensitive) + if existing.color.lower() != color.lower() or existing.description != description: + if update_label(repo, name, color, description, dry_run): + updated += 1 + else: + skipped += 1 + else: + skipped += 1 continue if create_label(repo, name, color, description, dry_run): created += 1 - print(f"\nSummary: {created} created, {skipped} already existed") + summary_parts = [f"{created} created", f"{skipped} already matched"] + if force: + summary_parts.insert(1, f"{updated} updated") + print(f"\nSummary: {', '.join(summary_parts)}") def main(): @@ -214,6 +257,11 @@ def main(): action="store_true", help="Show what would be done without making changes", ) + parser.add_argument( + "--force", + action="store_true", + help="Update existing labels to match color/description (use with --create)", + ) action_group = parser.add_mutually_exclusive_group(required=True) action_group.add_argument( @@ -238,7 +286,7 @@ def main(): if args.list: list_labels(args.repo) elif args.create: - create_labels(args.repo, args.dry_run) + create_labels(args.repo, args.dry_run, args.force) except subprocess.CalledProcessError as e: print(f"Error running gh command: {e}") print(f"stderr: {e.stderr}") From a44ce10a7e18d5fe52c8359f1f48d9ac4b97b2f4 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 14:17:31 -0700 Subject: [PATCH 5/8] fix(ci): standardize test: label colors to 3FA7D6 All test: labels now use consistent color #3FA7D6 (light blue). Co-Authored-By: Claude Opus 4.6 --- build_tools/github_actions/configure_ci_labels.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index 9602e0c3810..b421786463b 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -82,19 +82,19 @@ ("test:composablekernel", "3FA7D6", "For pull requests, runs full tests for only composable_kernel and other labeled projects."), ("test:libhipcxx_hipcc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hipcc and other labeled projects."), ("test:libhipcxx_hiprtc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hiprtc and other labeled projects."), - ("test:ocltst", "03B8ED", "For pull requests, this label executes the ocltst and other labeled projects."), - ("test:hip-tests", "20566E", "For pull requests, this label executes the hip-tests"), + ("test:ocltst", "3FA7D6", "For pull requests, this label executes the ocltst and other labeled projects."), + ("test:hip-tests", "3FA7D6", "For pull requests, this label executes the hip-tests"), ("test:rocrtst", "3FA7D6", "For pull requests, this label executes the rocrtst and other labeled projects."), ("test:origami", "3FA7D6", "For pull requests, this label executes the origami and other labeled projects."), ("test:rocdecode", "3FA7D6", "For pull requests, this label executes the rocdecode and other labeled projects."), ("test:rocjpeg", "3FA7D6", "For pull requests, this label executes the rocjpeg and other labeled projects."), ("test:rocprofiler-systems", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-systems and other labeled projects."), ("test:rocprofiler-sdk", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-sdk and other labeled projects."), - ("test:hipkernelprovider", "20566E", "For pull requests, runs full tests for only hipkernelprovider and other labeled projects."), - ("test:amdsmi", "277804", "For pull requests, runs full tests for only amdsmi and other labeled projects."), - ("test:rocgdb-cpu", "20566E", "Run ROCgdb cpu tests only"), - ("test:rocgdb-gpu", "20566E", "Run ROCgdb gpu tests only"), - ("test:rocgdb", "20566E", "Test all test:rocgdb* labels"), + ("test:hipkernelprovider", "3FA7D6", "For pull requests, runs full tests for only hipkernelprovider and other labeled projects."), + ("test:amdsmi", "3FA7D6", "For pull requests, runs full tests for only amdsmi and other labeled projects."), + ("test:rocgdb-cpu", "3FA7D6", "Run ROCgdb cpu tests only"), + ("test:rocgdb-gpu", "3FA7D6", "Run ROCgdb gpu tests only"), + ("test:rocgdb", "3FA7D6", "Test all test:rocgdb* labels"), ("test:rocprofiler-sdk-spm", "3FA7D6", "To run rocprofiler-sdk-spm jobs"), ("test:rpp", "3FA7D6", "For pull requests, runs full tests for only rpp and other labeled projects."), ("test:miopen-dbsync", "3FA7D6", "For pull requests, runs the GPU-free miopen-dbsync (StaticFDBSync/rocjitsu) test component."), From 49be022fe185f7044b05bae6ec1d7261a540ebf4 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 14:18:35 -0700 Subject: [PATCH 6/8] feat(ci): show detailed changes in dry-run update output When using --force --dry-run, now shows what will change: [DRY RUN] Would update label: test:amdsmi (color: #277804 -> #3FA7D6) Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index b421786463b..067c0d1c684 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -151,10 +151,26 @@ def create_label(repo: str, name: str, color: str, description: str, dry_run: bo return False -def update_label(repo: str, name: str, color: str, description: str, dry_run: bool = False) -> bool: +def update_label( + repo: str, + name: str, + color: str, + description: str, + existing: Label, + dry_run: bool = False, +) -> bool: """Update an existing label's color and description.""" + changes = [] + if existing.color.lower() != color.lower(): + changes.append(f"color: #{existing.color} -> #{color}") + if existing.description != description: + old_desc = existing.description[:30] + "..." if len(existing.description) > 30 else existing.description + new_desc = description[:30] + "..." if len(description) > 30 else description + changes.append(f"desc: \"{old_desc}\" -> \"{new_desc}\"") + change_str = ", ".join(changes) + if dry_run: - print(f" [DRY RUN] Would update label: {name}") + print(f" [DRY RUN] Would update label: {name} ({change_str})") return True args = ["label", "edit", name, "--repo", repo, "--color", color] @@ -163,7 +179,7 @@ def update_label(repo: str, name: str, color: str, description: str, dry_run: bo result = run_gh_command(args, check=False) if result.returncode == 0: - print(f" Updated label: {name}") + print(f" Updated label: {name} ({change_str})") return True else: print(f" Failed to update label {name}: {result.stderr}") @@ -225,7 +241,7 @@ def create_labels(repo: str, dry_run: bool = False, force: bool = False) -> None existing = existing_map[name] # Check if color or description differs (color comparison is case-insensitive) if existing.color.lower() != color.lower() or existing.description != description: - if update_label(repo, name, color, description, dry_run): + if update_label(repo, name, color, description, existing, dry_run): updated += 1 else: skipped += 1 From 08b850b2c4d0ca98623bb3c281aa731bfb5b47d1 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Tue, 22 Sep 2026 14:27:51 -0700 Subject: [PATCH 7/8] style(ci): format configure_ci_labels.py for black compatibility - Add fmt: off/on around CI_LABELS to preserve readability - Shorten test: label descriptions to fit line limits - Fix other long lines to pass black formatter Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 106 +++++++++++------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index 067c0d1c684..4e409db6a1b 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -30,7 +30,7 @@ # ============================================================================= # Labels from ROCm/TheRock for CI behavior manipulation. # Format: (name, color, description) - +# fmt: off CI_LABELS: list[tuple[str, str, str]] = [ # ci: general labels ("ci:skip", "FFFF00", "Skip all CI builds/tests for this PR"), @@ -62,52 +62,53 @@ ("ci:gfx90a", "5A4D41", "Opt-in to gfx90a builds/tests"), ("ci:gfx90c", "5A4D41", "Opt-in to gfx90c builds/tests"), # test: labels (project-specific test opt-in) - ("test:hipblaslt", "3FA7D6", "For pull requests, runs full tests for only hipblaslt and other labeled projects."), - ("test:hipcub", "3FA7D6", "For pull requests, runs full tests for only hipcub and other labeled projects."), - ("test:miopen", "3FA7D6", "For pull requests, runs full tests for only miopen and other labeled projects."), - ("test:rocblas", "3FA7D6", "For pull requests, runs full tests for only rocblas and other labeled projects."), - ("test:hipblas", "3FA7D6", "For pull requests, runs full tests for only hipblas and other labeled projects."), - ("test:rocprim", "3FA7D6", "For pull requests, runs full tests for only rocprim and other labeled projects."), - ("test:rocsolver", "3FA7D6", "For pull requests, runs full tests for only rocsolver and other labeled projects."), - ("test:rocthrust", "3FA7D6", "For pull requests, runs full tests for only rocthrust and other labeled projects."), - ("test:rocsparse", "3FA7D6", "For pull requests, runs full tests for only rocsparse and other labeled projects."), - ("test:hipsparse", "3FA7D6", "For pull requests, runs full tests for only hipsparse and other labeled projects."), - ("test:hipfft", "3FA7D6", "For pull requests, runs full tests for only hipfft and other labeled projects."), - ("test:hipsolver", "3FA7D6", "For pull requests, runs full tests for only hipsolver and other labeled projects."), - ("test:rocfft", "3FA7D6", "For pull requests, runs full tests for only rocfft and other labeled projects."), - ("test:hipsparselt", "3FA7D6", "For pull requests, runs full tests for only hipsparselt and other labeled projects."), - ("test:rccl", "3FA7D6", "For pull requests, runs full tests for only rccl and other labeled projects."), - ("test:hipdnn", "3FA7D6", "For pull requests, runs full tests for only hipdnn and other labeled projects."), - ("test:rocroller", "3FA7D6", "For pull requests, runs full tests for only rocroller and other labeled projects."), - ("test:composablekernel", "3FA7D6", "For pull requests, runs full tests for only composable_kernel and other labeled projects."), - ("test:libhipcxx_hipcc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hipcc and other labeled projects."), - ("test:libhipcxx_hiprtc", "3FA7D6", "For pull requests, runs full tests for only libhipcxx_hiprtc and other labeled projects."), - ("test:ocltst", "3FA7D6", "For pull requests, this label executes the ocltst and other labeled projects."), - ("test:hip-tests", "3FA7D6", "For pull requests, this label executes the hip-tests"), - ("test:rocrtst", "3FA7D6", "For pull requests, this label executes the rocrtst and other labeled projects."), - ("test:origami", "3FA7D6", "For pull requests, this label executes the origami and other labeled projects."), - ("test:rocdecode", "3FA7D6", "For pull requests, this label executes the rocdecode and other labeled projects."), - ("test:rocjpeg", "3FA7D6", "For pull requests, this label executes the rocjpeg and other labeled projects."), - ("test:rocprofiler-systems", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-systems and other labeled projects."), - ("test:rocprofiler-sdk", "3FA7D6", "For pull requests, runs full tests for only rocprofiler-sdk and other labeled projects."), - ("test:hipkernelprovider", "3FA7D6", "For pull requests, runs full tests for only hipkernelprovider and other labeled projects."), - ("test:amdsmi", "3FA7D6", "For pull requests, runs full tests for only amdsmi and other labeled projects."), + ("test:hipblaslt", "3FA7D6", "Run full tests for hipblaslt"), + ("test:hipcub", "3FA7D6", "Run full tests for hipcub"), + ("test:miopen", "3FA7D6", "Run full tests for miopen"), + ("test:rocblas", "3FA7D6", "Run full tests for rocblas"), + ("test:hipblas", "3FA7D6", "Run full tests for hipblas"), + ("test:rocprim", "3FA7D6", "Run full tests for rocprim"), + ("test:rocsolver", "3FA7D6", "Run full tests for rocsolver"), + ("test:rocthrust", "3FA7D6", "Run full tests for rocthrust"), + ("test:rocsparse", "3FA7D6", "Run full tests for rocsparse"), + ("test:hipsparse", "3FA7D6", "Run full tests for hipsparse"), + ("test:hipfft", "3FA7D6", "Run full tests for hipfft"), + ("test:hipsolver", "3FA7D6", "Run full tests for hipsolver"), + ("test:rocfft", "3FA7D6", "Run full tests for rocfft"), + ("test:hipsparselt", "3FA7D6", "Run full tests for hipsparselt"), + ("test:rccl", "3FA7D6", "Run full tests for rccl"), + ("test:hipdnn", "3FA7D6", "Run full tests for hipdnn"), + ("test:rocroller", "3FA7D6", "Run full tests for rocroller"), + ("test:composablekernel", "3FA7D6", "Run full tests for composable_kernel"), + ("test:libhipcxx_hipcc", "3FA7D6", "Run full tests for libhipcxx_hipcc"), + ("test:libhipcxx_hiprtc", "3FA7D6", "Run full tests for libhipcxx_hiprtc"), + ("test:ocltst", "3FA7D6", "Run ocltst tests"), + ("test:hip-tests", "3FA7D6", "Run hip-tests"), + ("test:rocrtst", "3FA7D6", "Run rocrtst tests"), + ("test:origami", "3FA7D6", "Run origami tests"), + ("test:rocdecode", "3FA7D6", "Run rocdecode tests"), + ("test:rocjpeg", "3FA7D6", "Run rocjpeg tests"), + ("test:rocprofiler-systems", "3FA7D6", "Run full tests for rocprofiler-systems"), + ("test:rocprofiler-sdk", "3FA7D6", "Run full tests for rocprofiler-sdk"), + ("test:hipkernelprovider", "3FA7D6", "Run full tests for hipkernelprovider"), + ("test:amdsmi", "3FA7D6", "Run full tests for amdsmi"), ("test:rocgdb-cpu", "3FA7D6", "Run ROCgdb cpu tests only"), ("test:rocgdb-gpu", "3FA7D6", "Run ROCgdb gpu tests only"), ("test:rocgdb", "3FA7D6", "Test all test:rocgdb* labels"), - ("test:rocprofiler-sdk-spm", "3FA7D6", "To run rocprofiler-sdk-spm jobs"), - ("test:rpp", "3FA7D6", "For pull requests, runs full tests for only rpp and other labeled projects."), - ("test:miopen-dbsync", "3FA7D6", "For pull requests, runs the GPU-free miopen-dbsync (StaticFDBSync/rocjitsu) test component."), + ("test:rocprofiler-sdk-spm", "3FA7D6", "Run rocprofiler-sdk-spm tests"), + ("test:rpp", "3FA7D6", "Run full tests for rpp"), + ("test:miopen-dbsync", "3FA7D6", "Run miopen-dbsync (StaticFDBSync/rocjitsu) tests"), # test_filter: labels (test level override) ("test_filter:quick", "a2fab4", "If enabled, the PR will run quick tests"), ("test_filter:standard", "a2fab4", "If enabled, the PR will run standard tests"), ("test_filter:comprehensive", "a2fab4", "If enabled, the PR will run comprehensive tests"), ("test_filter:full", "a2fab4", "If enabled, the PR will run full tests"), # test_runner: labels (test machine selection) - ("test_runner:oem", "23edeb", "If added, the tests will run on a machine configured with `oem` kernel"), + ("test_runner:oem", "23edeb", "Run tests on a machine configured with `oem` kernel"), # build_variant: labels ("build_variant:asan", "4b398c", "If enabled, the pull request will run ASAN builds"), ] +# fmt: on @dataclass @@ -126,13 +127,27 @@ def run_gh_command(args: list[str], check: bool = True) -> subprocess.CompletedP def get_repo_labels(repo: str) -> list[Label]: """Get all labels from a repository.""" result = run_gh_command( - ["label", "list", "--repo", repo, "--limit", "500", "--json", "name,color,description"] + [ + "label", + "list", + "--repo", + repo, + "--limit", + "500", + "--json", + "name,color,description", + ] ) labels_data = json.loads(result.stdout) - return [Label(name=l["name"], color=l["color"], description=l["description"]) for l in labels_data] + return [ + Label(name=l["name"], color=l["color"], description=l["description"]) + for l in labels_data + ] -def create_label(repo: str, name: str, color: str, description: str, dry_run: bool = False) -> bool: +def create_label( + repo: str, name: str, color: str, description: str, dry_run: bool = False +) -> bool: """Create a label in the repository. Skips if already exists.""" if dry_run: print(f" [DRY RUN] Would create label: {name}") @@ -164,9 +179,13 @@ def update_label( if existing.color.lower() != color.lower(): changes.append(f"color: #{existing.color} -> #{color}") if existing.description != description: - old_desc = existing.description[:30] + "..." if len(existing.description) > 30 else existing.description + old_desc = ( + existing.description[:30] + "..." + if len(existing.description) > 30 + else existing.description + ) new_desc = description[:30] + "..." if len(description) > 30 else description - changes.append(f"desc: \"{old_desc}\" -> \"{new_desc}\"") + changes.append(f'desc: "{old_desc}" -> "{new_desc}"') change_str = ", ".join(changes) if dry_run: @@ -239,8 +258,11 @@ def create_labels(repo: str, dry_run: bool = False, force: bool = False) -> None if name in existing_map: if force: existing = existing_map[name] - # Check if color or description differs (color comparison is case-insensitive) - if existing.color.lower() != color.lower() or existing.description != description: + # Check if color or description differs + if ( + existing.color.lower() != color.lower() + or existing.description != description + ): if update_label(repo, name, color, description, existing, dry_run): updated += 1 else: From 0d278c52f55178a5f02bf373ea37ed446ffa20d3 Mon Sep 17 00:00:00 2001 From: geomin12 Date: Wed, 23 Sep 2026 13:15:08 -0700 Subject: [PATCH 8/8] refactor(ci): address PR review comments for label configuration - Convert CI_LABELS from list of tuples to list[Label] dataclass - Add named color constants for label categories (COLOR_CI_GENERAL, etc.) - Use label.removeprefix("ci:") instead of hardcoded label[3:] - Add logging for labels already up to date during dry run Co-Authored-By: Claude Opus 4.6 --- .../github_actions/configure_ci_labels.py | 199 ++++++++++-------- .../github_actions/configure_multi_arch_ci.py | 2 +- 2 files changed, 111 insertions(+), 90 deletions(-) diff --git a/build_tools/github_actions/configure_ci_labels.py b/build_tools/github_actions/configure_ci_labels.py index 4e409db6a1b..788377cd3fa 100755 --- a/build_tools/github_actions/configure_ci_labels.py +++ b/build_tools/github_actions/configure_ci_labels.py @@ -25,99 +25,111 @@ import sys from dataclasses import dataclass + +@dataclass +class Label: + name: str + color: str + description: str + + +# ============================================================================= +# Color Constants +# ============================================================================= +# Named constants for label colors to ensure consistency and easy updates. +COLOR_CI_GENERAL = "FFFF00" # Yellow - general CI behavior labels +COLOR_CI_GFX = "5A4D41" # Brown - GPU architecture opt-in labels +COLOR_TEST = "3FA7D6" # Blue - project-specific test labels +COLOR_TEST_FILTER = "a2fab4" # Light green - test level override labels +COLOR_TEST_RUNNER = "23edeb" # Cyan - test machine selection labels +COLOR_BUILD_VARIANT = "4b398c" # Purple - build variant labels + + # ============================================================================= # CI Labels Definition # ============================================================================= # Labels from ROCm/TheRock for CI behavior manipulation. -# Format: (name, color, description) # fmt: off -CI_LABELS: list[tuple[str, str, str]] = [ +CI_LABELS: list[Label] = [ # ci: general labels - ("ci:skip", "FFFF00", "Skip all CI builds/tests for this PR"), - ("ci:run-all-archs", "FFFF00", "Opt-in to building for all architectures on a pull request"), - ("ci:run-multi-arch", "FFFF00", "Opt-in to running multi-arch CI on a pull request"), - ("ci:run-non-multi-arch", "FFFF00", "Opt-in to running non-multi-arch CI on a pull request"), - ("ci:build-jax", "FFFF00", "Enable Jax Build"), - ("ci:asan", "FFFF00", "Opt-in to building ASAN"), - ("ci:host-asan", "FFFF00", "Opt-in to running multi-arch host-asan CI on a pull request"), + Label("ci:skip", COLOR_CI_GENERAL, "Skip all CI builds/tests for this PR"), + Label("ci:run-all-archs", COLOR_CI_GENERAL, "Opt-in to building for all architectures on a pull request"), + Label("ci:run-multi-arch", COLOR_CI_GENERAL, "Opt-in to running multi-arch CI on a pull request"), + Label("ci:run-non-multi-arch", COLOR_CI_GENERAL, "Opt-in to running non-multi-arch CI on a pull request"), + Label("ci:build-jax", COLOR_CI_GENERAL, "Enable Jax Build"), + Label("ci:asan", COLOR_CI_GENERAL, "Opt-in to building ASAN"), + Label("ci:host-asan", COLOR_CI_GENERAL, "Opt-in to running multi-arch host-asan CI on a pull request"), # ci:gfx labels (GPU architecture opt-in) - ("ci:gfx103X-linux", "5A4D41", "Opt-in to gfx103X-linux builds/tests"), - ("ci:gfx103X", "5A4D41", "Opt-in to gfx103X builds/tests"), - ("ci:gfx90X-dcgpu", "5A4D41", "Opt-in to gfx90X-dcgpu builds/tests"), - ("ci:gfx94X-dcgpu", "5A4D41", "Opt-in to gfx94X-dcgpu builds/tests"), - ("ci:gfx950-dcgpu", "5A4D41", "Opt-in to gfx950-dcgpu builds/tests"), - ("ci:gfx110X-dgpu", "5A4D41", "Opt-in to gfx110X-dgpu builds/tests"), - ("ci:gfx110X-all", "5A4D41", "Opt-in to gfx110X-all builds/tests"), - ("ci:gfx1103", "5A4D41", "Opt-in to gfx1103 builds/tests"), - ("ci:gfx1150", "5A4D41", "Opt-in to gfx1150 builds/tests"), - ("ci:gfx1151", "5A4D41", "Opt-in to gfx1151 builds/tests"), - ("ci:gfx1152", "5A4D41", "Opt-in to gfx1152 builds/tests"), - ("ci:gfx1153", "5A4D41", "Opt-in to gfx1153 builds/tests"), - ("ci:gfx120X-all", "5A4D41", "Opt-in to gfx120X-all builds/tests"), - ("ci:gfx125x", "5A4D41", "Opt-in to gfx125x builds/tests"), - ("ci:gfx125X-dcgpu", "5A4D41", "Opt-in to gfx125X-dcgpu builds/tests"), - ("ci:gfx900", "5A4D41", "Opt-in to gfx900 builds/tests"), - ("ci:gfx906", "5A4D41", "Opt-in to gfx906 builds/tests"), - ("ci:gfx908", "5A4D41", "Opt-in to gfx908 builds/tests"), - ("ci:gfx90a", "5A4D41", "Opt-in to gfx90a builds/tests"), - ("ci:gfx90c", "5A4D41", "Opt-in to gfx90c builds/tests"), + Label("ci:gfx103X-linux", COLOR_CI_GFX, "Opt-in to gfx103X-linux builds/tests"), + Label("ci:gfx103X", COLOR_CI_GFX, "Opt-in to gfx103X builds/tests"), + Label("ci:gfx90X-dcgpu", COLOR_CI_GFX, "Opt-in to gfx90X-dcgpu builds/tests"), + Label("ci:gfx94X-dcgpu", COLOR_CI_GFX, "Opt-in to gfx94X-dcgpu builds/tests"), + Label("ci:gfx950-dcgpu", COLOR_CI_GFX, "Opt-in to gfx950-dcgpu builds/tests"), + Label("ci:gfx110X-dgpu", COLOR_CI_GFX, "Opt-in to gfx110X-dgpu builds/tests"), + Label("ci:gfx110X-all", COLOR_CI_GFX, "Opt-in to gfx110X-all builds/tests"), + Label("ci:gfx1103", COLOR_CI_GFX, "Opt-in to gfx1103 builds/tests"), + Label("ci:gfx1150", COLOR_CI_GFX, "Opt-in to gfx1150 builds/tests"), + Label("ci:gfx1151", COLOR_CI_GFX, "Opt-in to gfx1151 builds/tests"), + Label("ci:gfx1152", COLOR_CI_GFX, "Opt-in to gfx1152 builds/tests"), + Label("ci:gfx1153", COLOR_CI_GFX, "Opt-in to gfx1153 builds/tests"), + Label("ci:gfx120X-all", COLOR_CI_GFX, "Opt-in to gfx120X-all builds/tests"), + Label("ci:gfx125x", COLOR_CI_GFX, "Opt-in to gfx125x builds/tests"), + Label("ci:gfx125X-dcgpu", COLOR_CI_GFX, "Opt-in to gfx125X-dcgpu builds/tests"), + Label("ci:gfx900", COLOR_CI_GFX, "Opt-in to gfx900 builds/tests"), + Label("ci:gfx906", COLOR_CI_GFX, "Opt-in to gfx906 builds/tests"), + Label("ci:gfx908", COLOR_CI_GFX, "Opt-in to gfx908 builds/tests"), + Label("ci:gfx90a", COLOR_CI_GFX, "Opt-in to gfx90a builds/tests"), + Label("ci:gfx90c", COLOR_CI_GFX, "Opt-in to gfx90c builds/tests"), # test: labels (project-specific test opt-in) - ("test:hipblaslt", "3FA7D6", "Run full tests for hipblaslt"), - ("test:hipcub", "3FA7D6", "Run full tests for hipcub"), - ("test:miopen", "3FA7D6", "Run full tests for miopen"), - ("test:rocblas", "3FA7D6", "Run full tests for rocblas"), - ("test:hipblas", "3FA7D6", "Run full tests for hipblas"), - ("test:rocprim", "3FA7D6", "Run full tests for rocprim"), - ("test:rocsolver", "3FA7D6", "Run full tests for rocsolver"), - ("test:rocthrust", "3FA7D6", "Run full tests for rocthrust"), - ("test:rocsparse", "3FA7D6", "Run full tests for rocsparse"), - ("test:hipsparse", "3FA7D6", "Run full tests for hipsparse"), - ("test:hipfft", "3FA7D6", "Run full tests for hipfft"), - ("test:hipsolver", "3FA7D6", "Run full tests for hipsolver"), - ("test:rocfft", "3FA7D6", "Run full tests for rocfft"), - ("test:hipsparselt", "3FA7D6", "Run full tests for hipsparselt"), - ("test:rccl", "3FA7D6", "Run full tests for rccl"), - ("test:hipdnn", "3FA7D6", "Run full tests for hipdnn"), - ("test:rocroller", "3FA7D6", "Run full tests for rocroller"), - ("test:composablekernel", "3FA7D6", "Run full tests for composable_kernel"), - ("test:libhipcxx_hipcc", "3FA7D6", "Run full tests for libhipcxx_hipcc"), - ("test:libhipcxx_hiprtc", "3FA7D6", "Run full tests for libhipcxx_hiprtc"), - ("test:ocltst", "3FA7D6", "Run ocltst tests"), - ("test:hip-tests", "3FA7D6", "Run hip-tests"), - ("test:rocrtst", "3FA7D6", "Run rocrtst tests"), - ("test:origami", "3FA7D6", "Run origami tests"), - ("test:rocdecode", "3FA7D6", "Run rocdecode tests"), - ("test:rocjpeg", "3FA7D6", "Run rocjpeg tests"), - ("test:rocprofiler-systems", "3FA7D6", "Run full tests for rocprofiler-systems"), - ("test:rocprofiler-sdk", "3FA7D6", "Run full tests for rocprofiler-sdk"), - ("test:hipkernelprovider", "3FA7D6", "Run full tests for hipkernelprovider"), - ("test:amdsmi", "3FA7D6", "Run full tests for amdsmi"), - ("test:rocgdb-cpu", "3FA7D6", "Run ROCgdb cpu tests only"), - ("test:rocgdb-gpu", "3FA7D6", "Run ROCgdb gpu tests only"), - ("test:rocgdb", "3FA7D6", "Test all test:rocgdb* labels"), - ("test:rocprofiler-sdk-spm", "3FA7D6", "Run rocprofiler-sdk-spm tests"), - ("test:rpp", "3FA7D6", "Run full tests for rpp"), - ("test:miopen-dbsync", "3FA7D6", "Run miopen-dbsync (StaticFDBSync/rocjitsu) tests"), + Label("test:hipblaslt", COLOR_TEST, "Run full tests for hipblaslt"), + Label("test:hipcub", COLOR_TEST, "Run full tests for hipcub"), + Label("test:miopen", COLOR_TEST, "Run full tests for miopen"), + Label("test:rocblas", COLOR_TEST, "Run full tests for rocblas"), + Label("test:hipblas", COLOR_TEST, "Run full tests for hipblas"), + Label("test:rocprim", COLOR_TEST, "Run full tests for rocprim"), + Label("test:rocsolver", COLOR_TEST, "Run full tests for rocsolver"), + Label("test:rocthrust", COLOR_TEST, "Run full tests for rocthrust"), + Label("test:rocsparse", COLOR_TEST, "Run full tests for rocsparse"), + Label("test:hipsparse", COLOR_TEST, "Run full tests for hipsparse"), + Label("test:hipfft", COLOR_TEST, "Run full tests for hipfft"), + Label("test:hipsolver", COLOR_TEST, "Run full tests for hipsolver"), + Label("test:rocfft", COLOR_TEST, "Run full tests for rocfft"), + Label("test:hipsparselt", COLOR_TEST, "Run full tests for hipsparselt"), + Label("test:rccl", COLOR_TEST, "Run full tests for rccl"), + Label("test:hipdnn", COLOR_TEST, "Run full tests for hipdnn"), + Label("test:rocroller", COLOR_TEST, "Run full tests for rocroller"), + Label("test:composablekernel", COLOR_TEST, "Run full tests for composable_kernel"), + Label("test:libhipcxx_hipcc", COLOR_TEST, "Run full tests for libhipcxx_hipcc"), + Label("test:libhipcxx_hiprtc", COLOR_TEST, "Run full tests for libhipcxx_hiprtc"), + Label("test:ocltst", COLOR_TEST, "Run ocltst tests"), + Label("test:hip-tests", COLOR_TEST, "Run hip-tests"), + Label("test:rocrtst", COLOR_TEST, "Run rocrtst tests"), + Label("test:origami", COLOR_TEST, "Run origami tests"), + Label("test:rocdecode", COLOR_TEST, "Run rocdecode tests"), + Label("test:rocjpeg", COLOR_TEST, "Run rocjpeg tests"), + Label("test:rocprofiler-systems", COLOR_TEST, "Run full tests for rocprofiler-systems"), + Label("test:rocprofiler-sdk", COLOR_TEST, "Run full tests for rocprofiler-sdk"), + Label("test:hipkernelprovider", COLOR_TEST, "Run full tests for hipkernelprovider"), + Label("test:amdsmi", COLOR_TEST, "Run full tests for amdsmi"), + Label("test:rocgdb-cpu", COLOR_TEST, "Run ROCgdb cpu tests only"), + Label("test:rocgdb-gpu", COLOR_TEST, "Run ROCgdb gpu tests only"), + Label("test:rocgdb", COLOR_TEST, "Test all test:rocgdb* labels"), + Label("test:rocprofiler-sdk-spm", COLOR_TEST, "Run rocprofiler-sdk-spm tests"), + Label("test:rpp", COLOR_TEST, "Run full tests for rpp"), + Label("test:miopen-dbsync", COLOR_TEST, "Run miopen-dbsync (StaticFDBSync/rocjitsu) tests"), # test_filter: labels (test level override) - ("test_filter:quick", "a2fab4", "If enabled, the PR will run quick tests"), - ("test_filter:standard", "a2fab4", "If enabled, the PR will run standard tests"), - ("test_filter:comprehensive", "a2fab4", "If enabled, the PR will run comprehensive tests"), - ("test_filter:full", "a2fab4", "If enabled, the PR will run full tests"), + Label("test_filter:quick", COLOR_TEST_FILTER, "If enabled, the PR will run quick tests"), + Label("test_filter:standard", COLOR_TEST_FILTER, "If enabled, the PR will run standard tests"), + Label("test_filter:comprehensive", COLOR_TEST_FILTER, "If enabled, the PR will run comprehensive tests"), + Label("test_filter:full", COLOR_TEST_FILTER, "If enabled, the PR will run full tests"), # test_runner: labels (test machine selection) - ("test_runner:oem", "23edeb", "Run tests on a machine configured with `oem` kernel"), + Label("test_runner:oem", COLOR_TEST_RUNNER, "Run tests on a machine configured with `oem` kernel"), # build_variant: labels - ("build_variant:asan", "4b398c", "If enabled, the pull request will run ASAN builds"), + Label("build_variant:asan", COLOR_BUILD_VARIANT, "If enabled, the pull request will run ASAN builds"), ] # fmt: on -@dataclass -class Label: - name: str - color: str - description: str - - def run_gh_command(args: list[str], check: bool = True) -> subprocess.CompletedProcess: """Run a GitHub CLI command.""" cmd = ["gh"] + args @@ -207,7 +219,7 @@ def update_label( def get_ci_label_names() -> set[str]: """Get set of CI label names.""" - return {label[0] for label in CI_LABELS} + return {label.name for label in CI_LABELS} def list_labels(repo: str) -> None: @@ -222,14 +234,14 @@ def list_labels(repo: str) -> None: exists_count = 0 print("\nLabel status:") - for name, color, description in CI_LABELS: - if name in existing_names: + for label in CI_LABELS: + if label.name in existing_names: status = "exists" exists_count += 1 else: status = "MISSING" missing_count += 1 - print(f" [{status}] {name} (#{color})") + print(f" [{status}] {label.name} (#{label.color})") print(f"\nSummary: {exists_count} exist, {missing_count} missing") @@ -254,23 +266,32 @@ def create_labels(repo: str, dry_run: bool = False, force: bool = False) -> None created = 0 updated = 0 skipped = 0 - for name, color, description in CI_LABELS: - if name in existing_map: + for label in CI_LABELS: + if label.name in existing_map: if force: - existing = existing_map[name] + existing = existing_map[label.name] # Check if color or description differs if ( - existing.color.lower() != color.lower() - or existing.description != description + existing.color.lower() != label.color.lower() + or existing.description != label.description ): - if update_label(repo, name, color, description, existing, dry_run): + if update_label( + repo, + label.name, + label.color, + label.description, + existing, + dry_run, + ): updated += 1 else: + if dry_run: + print(f" [DRY RUN] Already up to date: {label.name}") skipped += 1 else: skipped += 1 continue - if create_label(repo, name, color, description, dry_run): + if create_label(repo, label.name, label.color, label.description, dry_run): created += 1 summary_parts = [f"{created} created", f"{skipped} already matched"] diff --git a/build_tools/github_actions/configure_multi_arch_ci.py b/build_tools/github_actions/configure_multi_arch_ci.py index e0ec058ae71..292dfc7de24 100755 --- a/build_tools/github_actions/configure_multi_arch_ci.py +++ b/build_tools/github_actions/configure_multi_arch_ci.py @@ -974,7 +974,7 @@ def select_targets(ci_inputs: CIInputs) -> TargetSelection: # we have some labels like `ci:gfx94X-dcgpu` or `ci:gfx103X-linux`. # Family keys are lowercase, so normalize the target. # Strip ci: prefix, then split on dash to get the base family. - target = label[3:].split("-")[0].lower() + target = label.lower().removeprefix("ci:").split("-")[0] linux_names.append(target) windows_names.append(target) print(f" Label '{label}' -> adding target {target}")