ci: introduce dynamic matrix balancer for GHA#17036
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a GitHub Actions matrix balancer script and its corresponding unit tests to optimize CI performance by isolating heavy-load packages. The review feedback correctly identifies several robustness issues: the script needs to ensure a valid JSON output even when no packages are found to prevent workflow failures, handle potential division-by-zero or value errors in bucket calculations, and normalize input paths to correctly identify packages and generate job labels.
| if not packages: | ||
| return | ||
|
|
||
| # Protect against GitHub's 256-job hard limit | ||
| max_buckets = min(250 // args.matrix_multiplier, args.max_vms) | ||
| buckets = distribute_packages(packages, max_buckets) | ||
|
|
||
| jobs_json = json.dumps(build_github_actions_jobs(buckets)) |
There was a problem hiding this comment.
This section has two robustness issues:
- If no valid packages are found, the script returns early without setting the
bucketsoutput. This will causefromJson()to fail in GitHub Actions workflows. It is better to always output a valid JSON array (e.g.,[]). - The
max_bucketscalculation can crash with aZeroDivisionErrorifmatrix-multiplieris 0, or cause aValueErrorindistribute_packagesifmax_bucketsevaluates to 0 (which happens ifmatrix-multiplier > 250ormax-vmsis 0).
jobs = []
if packages:
# Protect against GitHub's 256-job hard limit and ensure at least 1 bucket
multiplier = max(1, args.matrix_multiplier)
max_buckets = max(1, min(250 // multiplier, args.max_vms))
buckets = distribute_packages(packages, max_buckets)
jobs = build_github_actions_jobs(buckets)
jobs_json = json.dumps(jobs)| parser.add_argument("--max-vms", type=int, default=20) | ||
| args = parser.parse_args() | ||
|
|
||
| changed_dirs = os.environ.get("CHANGED_DIRS", "").split() |
There was a problem hiding this comment.
If CHANGED_DIRS contains paths with trailing slashes (e.g., packages/my-pkg/), os.path.basename() will return an empty string. This breaks the heavy lifter detection and results in invalid job labels like + 5. Normalizing the paths ensures consistent behavior across different environments and input formats.
| changed_dirs = os.environ.get("CHANGED_DIRS", "").split() | |
| changed_dirs = [os.path.normpath(d) for d in os.environ.get("CHANGED_DIRS", "").split()] |
Adds
gha_matrix_balancer.pyto dynamically chunk and distribute Python packages across GitHub Actions runners.This orchestrator sets the foundation to replace legacy sequential bash loops, eliminating massive execution latency and 6-hour CI timeouts.
It provides:
Note: This is a zero-risk foundation PR. It adds the script and tests but does not yet modify any active
.yamlworkflows.