From e64deb49d5ce127926149a55e2f8538c8df74d01 Mon Sep 17 00:00:00 2001 From: isVoid <13521008+isVoid@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:58:47 +0000 Subject: [PATCH 1/3] ci: automate WoA cross-repository dispatch --- .github/workflows/woa-main-automation.yml | 257 +++++++++++++++++++ ci/tools/woa_xrepo_select.py | 292 ++++++++++++++++++++++ 2 files changed, 549 insertions(+) create mode 100644 .github/workflows/woa-main-automation.yml create mode 100644 ci/tools/woa_xrepo_select.py diff --git a/.github/workflows/woa-main-automation.yml b/.github/workflows/woa-main-automation.yml new file mode 100644 index 00000000000..c5b1c0287c6 --- /dev/null +++ b/.github/workflows/woa-main-automation.yml @@ -0,0 +1,257 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: "WoA public-main automation" + +on: + workflow_run: + workflows: + - CI + types: + - completed + schedule: + - cron: "17 * * * *" + workflow_dispatch: + inputs: + public_run_id: + description: Optional exact public CI run; empty selects the newest eligible build + required: false + type: string + +permissions: {} + +jobs: + select: + if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion != 'cancelled' }} + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + actions: read + checks: read + contents: read + concurrency: + group: cuda-python-woa-public-selector + cancel-in-progress: false + outputs: + artifacts-json: ${{ steps.select.outputs.artifacts_json }} + baseline-sha: ${{ steps.select.outputs.baseline_sha }} + commit-count: ${{ steps.select.outputs.commit_count }} + correlation-id: ${{ steps.select.outputs.correlation_id }} + dispatch: ${{ steps.select.outputs.dispatch }} + public-run-attempt: ${{ steps.select.outputs.public_run_attempt }} + public-run-id: ${{ steps.select.outputs.public_run_id }} + public-sha: ${{ steps.select.outputs.public_sha }} + steps: + - name: Checkout trusted selector + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + ref: main + + - name: Select exact public batch + id: select + env: + EVENT_CANDIDATE_RUN_ID: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.id || '' }} + GITHUB_TOKEN: ${{ github.token }} + MANUAL_CANDIDATE_RUN_ID: ${{ inputs.public_run_id }} + run: | + set -euo pipefail + candidate_run_id="$EVENT_CANDIDATE_RUN_ID" + if [[ -n "$MANUAL_CANDIDATE_RUN_ID" ]]; then + candidate_run_id="$MANUAL_CANDIDATE_RUN_ID" + fi + selection=$(python ci/tools/woa_xrepo_select.py --run-id "$candidate_run_id") + jq . <<< "$selection" + echo "dispatch=$(jq -r .dispatch <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "artifacts_json=$(jq -c '.artifacts // []' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "baseline_sha=$(jq -r '.baseline_sha // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "commit_count=$(jq -r '.commit_count // 0' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "correlation_id=$(jq -r '.correlation_id // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "public_run_attempt=$(jq -r '.run_attempt // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "public_run_id=$(jq -r '.run_id // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "public_sha=$(jq -r '.sha // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "$(jq -r .reason <<< "$selection")" >> "$GITHUB_STEP_SUMMARY" + + dispatch: + needs: select + if: ${{ needs.select.outputs.dispatch == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: {} + steps: + - name: Create private dispatch token + id: private-app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + client-id: ${{ vars.CUDA_PYTHON_WOA_XREPO_CI_PRIVATE_APP_CLIENT_ID }} + private-key: ${{ secrets.CUDA_PYTHON_WOA_XREPO_CI_PRIVATE_APP_PRIVATE_KEY }} + owner: NVIDIA-dev + repositories: cuda-python-private + permission-actions: write + + - name: Dispatch exact private validation + env: + ARTIFACTS_JSON: ${{ needs.select.outputs.artifacts-json }} + BASELINE_SHA: ${{ needs.select.outputs.baseline-sha }} + COMMIT_COUNT: ${{ needs.select.outputs.commit-count }} + CORRELATION_ID: ${{ needs.select.outputs.correlation-id }} + GH_TOKEN: ${{ steps.private-app-token.outputs.token }} + PUBLIC_RUN_ATTEMPT: ${{ needs.select.outputs.public-run-attempt }} + PUBLIC_RUN_ID: ${{ needs.select.outputs.public-run-id }} + PUBLIC_SHA: ${{ needs.select.outputs.public-sha }} + run: | + set -euo pipefail + payload=$(jq -n \ + --arg artifacts_json "$ARTIFACTS_JSON" \ + --arg baseline_sha "$BASELINE_SHA" \ + --arg commit_count "$COMMIT_COUNT" \ + --arg correlation_id "$CORRELATION_ID" \ + --arg run_attempt "$PUBLIC_RUN_ATTEMPT" \ + --arg run_id "$PUBLIC_RUN_ID" \ + --arg sha "$PUBLIC_SHA" \ + '{ + ref: "ctk-next", + return_run_details: true, + inputs: { + schema_version: "1", + public_repository: "NVIDIA/cuda-python", + public_repository_id: "381173759", + public_workflow_id: "155304118", + public_run_id: $run_id, + public_run_attempt: $run_attempt, + public_sha: $sha, + artifacts_json: $artifacts_json, + baseline_sha: $baseline_sha, + commit_count: $commit_count, + correlation_id: $correlation_id + } + }') + response=$(gh api \ + --method POST \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + repos/NVIDIA-dev/cuda-python-private/actions/workflows/woa-main-validation.yml/dispatches \ + --input - <<< "$payload") + private_run_id=$(jq -er '.workflow_run_id | tostring' <<< "$response") + private_run=$(gh api \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + "repos/NVIDIA-dev/cuda-python-private/actions/runs/$private_run_id") + jq -e \ + --arg title "WoA validation $CORRELATION_ID" ' + .repository.id == 809898190 and + .path == ".github/workflows/woa-main-validation.yml" and + .event == "workflow_dispatch" and + .head_branch == "ctk-next" and + .display_title == $title + ' <<< "$private_run" >/dev/null + echo "Exact private validation accepted the selected public batch." >> "$GITHUB_STEP_SUMMARY" + + validate-cancellation: + if: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'cancelled' }} + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + actions: read + outputs: + correlation-id: ${{ steps.validate.outputs.correlation_id }} + public-run-attempt: ${{ steps.validate.outputs.public_run_attempt }} + public-run-id: ${{ steps.validate.outputs.public_run_id }} + public-sha: ${{ steps.validate.outputs.public_sha }} + steps: + - name: Validate exact cancelled public run + id: validate + env: + GH_TOKEN: ${{ github.token }} + PUBLIC_RUN_ID: ${{ github.event.workflow_run.id }} + run: | + set -euo pipefail + run=$(gh api \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + "repos/NVIDIA/cuda-python/actions/runs/$PUBLIC_RUN_ID") + jq -e ' + .repository.id == 381173759 and + .workflow_id == 155304118 and + .path == ".github/workflows/ci.yml" and + .event == "push" and + .head_branch == "main" and + .status == "completed" and + .conclusion == "cancelled" + ' <<< "$run" >/dev/null + public_sha=$(jq -r .head_sha <<< "$run") + public_run_attempt=$(jq -r '.run_attempt | tostring' <<< "$run") + correlation_id="v1:381173759:$PUBLIC_RUN_ID:$public_run_attempt:$public_sha" + echo "public_run_id=$PUBLIC_RUN_ID" >> "$GITHUB_OUTPUT" + echo "public_run_attempt=$public_run_attempt" >> "$GITHUB_OUTPUT" + echo "public_sha=$public_sha" >> "$GITHUB_OUTPUT" + echo "correlation_id=$correlation_id" >> "$GITHUB_OUTPUT" + + dispatch-cancellation: + needs: validate-cancellation + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: {} + steps: + - name: Create private dispatch token + id: private-app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + client-id: ${{ vars.CUDA_PYTHON_WOA_XREPO_CI_PRIVATE_APP_CLIENT_ID }} + private-key: ${{ secrets.CUDA_PYTHON_WOA_XREPO_CI_PRIVATE_APP_PRIVATE_KEY }} + owner: NVIDIA-dev + repositories: cuda-python-private + permission-actions: write + + - name: Locate exact private validation and dispatch cancellation + env: + CORRELATION_ID: ${{ needs.validate-cancellation.outputs.correlation-id }} + GH_TOKEN: ${{ steps.private-app-token.outputs.token }} + PUBLIC_RUN_ATTEMPT: ${{ needs.validate-cancellation.outputs.public-run-attempt }} + PUBLIC_RUN_ID: ${{ needs.validate-cancellation.outputs.public-run-id }} + PUBLIC_SHA: ${{ needs.validate-cancellation.outputs.public-sha }} + run: | + set -euo pipefail + runs=$(gh api \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + 'repos/NVIDIA-dev/cuda-python-private/actions/workflows/woa-main-validation.yml/runs?event=workflow_dispatch&per_page=100') + matches=$(jq -c \ + --arg title "WoA validation $CORRELATION_ID" ' + [.workflow_runs[] | select(.display_title == $title)] + ' <<< "$runs") + match_count=$(jq length <<< "$matches") + if (( match_count == 0 )); then + echo "No correlated private validation exists; cancellation is a no-op." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + if (( match_count != 1 )); then + echo "Expected one correlated private validation, found $match_count." >&2 + exit 1 + fi + private_run_id=$(jq -r '.[0].id | tostring' <<< "$matches") + + payload=$(jq -n \ + --arg correlation_id "$CORRELATION_ID" \ + --arg private_run_id "$private_run_id" \ + --arg run_attempt "$PUBLIC_RUN_ATTEMPT" \ + --arg run_id "$PUBLIC_RUN_ID" \ + --arg sha "$PUBLIC_SHA" ' + { + ref: "ctk-next", + return_run_details: true, + inputs: { + schema_version: "1", + public_repository_id: "381173759", + public_run_id: $run_id, + public_run_attempt: $run_attempt, + public_sha: $sha, + private_run_id: $private_run_id, + correlation_id: $correlation_id + } + }') + response=$(gh api \ + --method POST \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + repos/NVIDIA-dev/cuda-python-private/actions/workflows/woa-main-cancel.yml/dispatches \ + --input - <<< "$payload") + jq -e '.workflow_run_id | type == "number"' <<< "$response" >/dev/null + echo "Private cancellation controller accepted the exact request." >> "$GITHUB_STEP_SUMMARY" diff --git a/ci/tools/woa_xrepo_select.py b/ci/tools/woa_xrepo_select.py new file mode 100644 index 00000000000..6489fe47104 --- /dev/null +++ b/ci/tools/woa_xrepo_select.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import datetime +import json +import os +import re +import sys +import urllib.error +import urllib.parse +import urllib.request + + +PUBLIC_REPOSITORY = "NVIDIA/cuda-python" +PUBLIC_REPOSITORY_ID = 381173759 +PUBLIC_WORKFLOW_ID = 155304118 +PUBLIC_WORKFLOW_PATH = ".github/workflows/ci.yml" +REPORTING_APP_ID = 4954254 +CHECK_NAME = "cuda-python WoA integration" +MAX_LEDGER_COMMITS = 100 + + +class GitHubAPI: + def __init__(self, token): + self._token = token + + def get(self, path, params=None): + url = f"https://api.github.com/{path.lstrip('/')}" + if params: + url = f"{url}?{urllib.parse.urlencode(params)}" + request = urllib.request.Request( + url, + headers={ + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {self._token}", + "X-GitHub-Api-Version": "2026-03-10", + }, + ) + try: + with urllib.request.urlopen(request) as response: + return json.load(response) + except urllib.error.HTTPError as error: + body = error.read().decode("utf-8", errors="replace") + raise RuntimeError(f"GitHub API GET {path} failed: {error.code}: {body}") from error + + def paginate(self, path, item_key, params=None, max_pages=10): + items = [] + query = dict(params or {}) + query["per_page"] = 100 + for page in range(1, max_pages + 1): + query["page"] = page + response = self.get(path, query) + page_items = response[item_key] + items.extend(page_items) + if len(page_items) < 100: + break + return items + + +def parse_time(value): + return datetime.datetime.fromisoformat(value.replace("Z", "+00:00")) + + +def select_artifacts(artifacts, sha): + binding_name = f"cuda-bindings-python313-cuda13.4.2-win-arm64-{sha}" + core_name = f"cuda-core-python313-win-arm64-{sha}" + selected = [ + artifact + for artifact in artifacts + if not artifact["expired"] + and ( + artifact["name"] == "cuda-pathfinder-wheel" + or artifact["name"] == binding_name + or artifact["name"] == core_name + ) + ] + if len(selected) != 3: + return None + names = {artifact["name"] for artifact in selected} + if len(names) != 3: + return None + result = [] + for artifact in sorted(selected, key=lambda item: item["name"]): + digest = artifact.get("digest") + if not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest): + return None + result.append( + {"id": artifact["id"], "name": artifact["name"], "digest": digest} + ) + return result + + +def validate_run(api, run): + if not ( + run["repository"]["id"] == PUBLIC_REPOSITORY_ID + and run["workflow_id"] == PUBLIC_WORKFLOW_ID + and run["path"] == PUBLIC_WORKFLOW_PATH + and run["event"] == "push" + and run["head_branch"] == "main" + and run["status"] == "completed" + and run["conclusion"] == "success" + and re.fullmatch(r"[0-9a-f]{40}", run["head_sha"]) + ): + return None + + run_id = run["id"] + attempt = run["run_attempt"] + jobs = api.paginate( + f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/attempts/{attempt}/jobs", + "jobs", + ) + woa_jobs = [job for job in jobs if job["name"].startswith("Build win-arm64, CUDA ")] + if not woa_jobs or any( + job["status"] != "completed" or job["conclusion"] != "success" for job in woa_jobs + ): + return None + + artifacts = api.paginate( + f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/artifacts", "artifacts" + ) + selected_artifacts = select_artifacts(artifacts, run["head_sha"]) + if selected_artifacts is None: + return None + + comparison = api.get( + f"repos/{PUBLIC_REPOSITORY}/compare/{run['head_sha']}...main" + ) + if comparison["status"] not in {"ahead", "identical"}: + return None + + return { + "run_id": str(run_id), + "run_attempt": str(attempt), + "sha": run["head_sha"], + "artifacts": selected_artifacts, + } + + +def resolve_candidate(api, run_id): + if run_id: + run = api.get(f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}") + return validate_run(api, run) + + runs = api.get( + f"repos/{PUBLIC_REPOSITORY}/actions/workflows/ci.yml/runs", + { + "branch": "main", + "event": "push", + "status": "completed", + "per_page": 30, + }, + )["workflow_runs"] + for run in runs: + candidate = validate_run(api, run) + if candidate is not None: + return candidate + return None + + +def app_checks(api, sha): + response = api.get( + f"repos/{PUBLIC_REPOSITORY}/commits/{sha}/check-runs", + {"check_name": CHECK_NAME, "per_page": 100}, + ) + return [ + check + for check in response["check_runs"] + if check["name"] == CHECK_NAME and check["app"]["id"] == REPORTING_APP_ID + ] + + +def list_commits(api): + return api.get( + f"repos/{PUBLIC_REPOSITORY}/commits", + {"sha": "main", "per_page": MAX_LEDGER_COMMITS}, + ) + + +def select_batch(api, candidate, batch_size, max_wait_seconds, now): + candidate_checks = app_checks(api, candidate["sha"]) + external_id = ( + f"woa:v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:" + f"{candidate['run_attempt']}:{candidate['sha']}" + ) + if any( + check.get("external_id") == external_id + for check in candidate_checks + ): + return {"dispatch": False, "reason": "candidate already has an App-owned attempt"} + + commits = list_commits(api) + try: + candidate_index = next( + index for index, commit in enumerate(commits) if commit["sha"] == candidate["sha"] + ) + except StopIteration as error: + raise RuntimeError( + f"candidate SHA was not found on the latest {MAX_LEDGER_COMMITS} main commits" + ) from error + + baseline_index = None + baseline_sha = "" + for index in range(candidate_index + 1, len(commits)): + checks = app_checks(api, commits[index]["sha"]) + successful = [ + check + for check in checks + if check["status"] == "completed" and check["conclusion"] == "success" + ] + if successful: + baseline_index = index + baseline_sha = commits[index]["sha"] + break + + if baseline_index is None: + return { + "dispatch": True, + "reason": ( + "bootstrap: no successful App-owned baseline exists in the bounded ledger" + ), + "baseline_sha": "", + "commit_count": 1, + } + + commit_count = baseline_index - candidate_index + oldest_pending = commits[baseline_index - 1] + oldest_time = parse_time(oldest_pending["commit"]["committer"]["date"]) + age_seconds = max(0, int((now - oldest_time).total_seconds())) + if commit_count >= batch_size: + reason = f"batch threshold reached: {commit_count} commits" + dispatch = True + elif age_seconds >= max_wait_seconds: + reason = f"maximum wait reached: oldest pending commit is {age_seconds} seconds old" + dispatch = True + else: + reason = ( + f"deferred: {commit_count}/{batch_size} commits and oldest pending age " + f"{age_seconds}/{max_wait_seconds} seconds" + ) + dispatch = False + return { + "dispatch": dispatch, + "reason": reason, + "baseline_sha": baseline_sha, + "commit_count": commit_count, + } + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--run-id", default="") + parser.add_argument("--batch-size", type=int, default=3) + parser.add_argument("--max-wait-seconds", type=int, default=7200) + args = parser.parse_args() + if args.run_id and not args.run_id.isdigit(): + parser.error("--run-id must be numeric") + if args.batch_size < 1 or args.max_wait_seconds < 1: + parser.error("batch size and maximum wait must be positive") + + token = os.environ.get("GITHUB_TOKEN") + if not token: + raise SystemExit("GITHUB_TOKEN is required") + api = GitHubAPI(token) + candidate = resolve_candidate(api, args.run_id) + if candidate is None: + print(json.dumps({"dispatch": False, "reason": "no eligible public WoA build"})) + return + + selection = select_batch( + api, + candidate, + args.batch_size, + args.max_wait_seconds, + datetime.datetime.now(datetime.timezone.utc), + ) + selection.update(candidate) + selection["correlation_id"] = ( + f"v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:" + f"{candidate['run_attempt']}:{candidate['sha']}" + ) + print(json.dumps(selection, separators=(",", ":"), sort_keys=True)) + + +if __name__ == "__main__": + try: + main() + except Exception as error: + print(f"error: {error}", file=sys.stderr) + raise From bf3af4ed248d1f287e52bf3a83b26e96bef2a7dc Mon Sep 17 00:00:00 2001 From: isVoid <13521008+isVoid@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:03:28 +0000 Subject: [PATCH 2/3] ci: satisfy selector lint policy --- ci/tools/woa_xrepo_select.py | 88 ++++++++++++++---------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/ci/tools/woa_xrepo_select.py b/ci/tools/woa_xrepo_select.py index 6489fe47104..4fd9740cf9b 100644 --- a/ci/tools/woa_xrepo_select.py +++ b/ci/tools/woa_xrepo_select.py @@ -5,14 +5,12 @@ import argparse import datetime +import http.client import json import os import re import sys -import urllib.error import urllib.parse -import urllib.request - PUBLIC_REPOSITORY = "NVIDIA/cuda-python" PUBLIC_REPOSITORY_ID = 381173759 @@ -28,23 +26,28 @@ def __init__(self, token): self._token = token def get(self, path, params=None): - url = f"https://api.github.com/{path.lstrip('/')}" + request_path = f"/{path.lstrip('/')}" if params: - url = f"{url}?{urllib.parse.urlencode(params)}" - request = urllib.request.Request( - url, - headers={ - "Accept": "application/vnd.github+json", - "Authorization": f"Bearer {self._token}", - "X-GitHub-Api-Version": "2026-03-10", - }, - ) + request_path = f"{request_path}?{urllib.parse.urlencode(params)}" + connection = http.client.HTTPSConnection("api.github.com") try: - with urllib.request.urlopen(request) as response: - return json.load(response) - except urllib.error.HTTPError as error: - body = error.read().decode("utf-8", errors="replace") - raise RuntimeError(f"GitHub API GET {path} failed: {error.code}: {body}") from error + connection.request( + "GET", + request_path, + headers={ + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {self._token}", + "X-GitHub-Api-Version": "2026-03-10", + "User-Agent": "cuda-python-woa-selector", + }, + ) + response = connection.getresponse() + body = response.read().decode("utf-8", errors="replace") + if response.status >= 400: + raise RuntimeError(f"GitHub API GET {path} failed: {response.status}: {body}") + return json.loads(body) + finally: + connection.close() def paginate(self, path, item_key, params=None, max_pages=10): items = [] @@ -73,7 +76,7 @@ def select_artifacts(artifacts, sha): if not artifact["expired"] and ( artifact["name"] == "cuda-pathfinder-wheel" - or artifact["name"] == binding_name + or artifact["name"] == binding_name or artifact["name"] == core_name ) ] @@ -87,9 +90,7 @@ def select_artifacts(artifacts, sha): digest = artifact.get("digest") if not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest): return None - result.append( - {"id": artifact["id"], "name": artifact["name"], "digest": digest} - ) + result.append({"id": artifact["id"], "name": artifact["name"], "digest": digest}) return result @@ -113,21 +114,15 @@ def validate_run(api, run): "jobs", ) woa_jobs = [job for job in jobs if job["name"].startswith("Build win-arm64, CUDA ")] - if not woa_jobs or any( - job["status"] != "completed" or job["conclusion"] != "success" for job in woa_jobs - ): + if not woa_jobs or any(job["status"] != "completed" or job["conclusion"] != "success" for job in woa_jobs): return None - artifacts = api.paginate( - f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/artifacts", "artifacts" - ) + artifacts = api.paginate(f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/artifacts", "artifacts") selected_artifacts = select_artifacts(artifacts, run["head_sha"]) if selected_artifacts is None: return None - comparison = api.get( - f"repos/{PUBLIC_REPOSITORY}/compare/{run['head_sha']}...main" - ) + comparison = api.get(f"repos/{PUBLIC_REPOSITORY}/compare/{run['head_sha']}...main") if comparison["status"] not in {"ahead", "identical"}: return None @@ -181,35 +176,21 @@ def list_commits(api): def select_batch(api, candidate, batch_size, max_wait_seconds, now): candidate_checks = app_checks(api, candidate["sha"]) - external_id = ( - f"woa:v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:" - f"{candidate['run_attempt']}:{candidate['sha']}" - ) - if any( - check.get("external_id") == external_id - for check in candidate_checks - ): + external_id = f"woa:v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:{candidate['run_attempt']}:{candidate['sha']}" + if any(check.get("external_id") == external_id for check in candidate_checks): return {"dispatch": False, "reason": "candidate already has an App-owned attempt"} commits = list_commits(api) try: - candidate_index = next( - index for index, commit in enumerate(commits) if commit["sha"] == candidate["sha"] - ) + candidate_index = next(index for index, commit in enumerate(commits) if commit["sha"] == candidate["sha"]) except StopIteration as error: - raise RuntimeError( - f"candidate SHA was not found on the latest {MAX_LEDGER_COMMITS} main commits" - ) from error + raise RuntimeError(f"candidate SHA was not found on the latest {MAX_LEDGER_COMMITS} main commits") from error baseline_index = None baseline_sha = "" for index in range(candidate_index + 1, len(commits)): checks = app_checks(api, commits[index]["sha"]) - successful = [ - check - for check in checks - if check["status"] == "completed" and check["conclusion"] == "success" - ] + successful = [check for check in checks if check["status"] == "completed" and check["conclusion"] == "success"] if successful: baseline_index = index baseline_sha = commits[index]["sha"] @@ -218,9 +199,7 @@ def select_batch(api, candidate, batch_size, max_wait_seconds, now): if baseline_index is None: return { "dispatch": True, - "reason": ( - "bootstrap: no successful App-owned baseline exists in the bounded ledger" - ), + "reason": ("bootstrap: no successful App-owned baseline exists in the bounded ledger"), "baseline_sha": "", "commit_count": 1, } @@ -278,8 +257,7 @@ def main(): ) selection.update(candidate) selection["correlation_id"] = ( - f"v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:" - f"{candidate['run_attempt']}:{candidate['sha']}" + f"v1:{PUBLIC_REPOSITORY_ID}:{candidate['run_id']}:{candidate['run_attempt']}:{candidate['sha']}" ) print(json.dumps(selection, separators=(",", ":"), sort_keys=True)) From 9cd019a72c4f18230f28a46680296d9927ee5ae7 Mon Sep 17 00:00:00 2001 From: isVoid <13521008+isVoid@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:27:47 +0000 Subject: [PATCH 3/3] ci: dispatch exact WoA producer job --- .github/workflows/woa-main-automation.yml | 5 ++ ci/tools/woa_xrepo_select.py | 56 +++++++++++++---------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/.github/workflows/woa-main-automation.yml b/.github/workflows/woa-main-automation.yml index c5b1c0287c6..4ce1ecd46cb 100644 --- a/.github/workflows/woa-main-automation.yml +++ b/.github/workflows/woa-main-automation.yml @@ -39,6 +39,7 @@ jobs: commit-count: ${{ steps.select.outputs.commit_count }} correlation-id: ${{ steps.select.outputs.correlation_id }} dispatch: ${{ steps.select.outputs.dispatch }} + public-producer-job-id: ${{ steps.select.outputs.public_producer_job_id }} public-run-attempt: ${{ steps.select.outputs.public_run_attempt }} public-run-id: ${{ steps.select.outputs.public_run_id }} public-sha: ${{ steps.select.outputs.public_sha }} @@ -69,6 +70,7 @@ jobs: echo "baseline_sha=$(jq -r '.baseline_sha // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" echo "commit_count=$(jq -r '.commit_count // 0' <<< "$selection")" >> "$GITHUB_OUTPUT" echo "correlation_id=$(jq -r '.correlation_id // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" + echo "public_producer_job_id=$(jq -r '.producer_job_id // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" echo "public_run_attempt=$(jq -r '.run_attempt // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" echo "public_run_id=$(jq -r '.run_id // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" echo "public_sha=$(jq -r '.sha // ""' <<< "$selection")" >> "$GITHUB_OUTPUT" @@ -98,6 +100,7 @@ jobs: COMMIT_COUNT: ${{ needs.select.outputs.commit-count }} CORRELATION_ID: ${{ needs.select.outputs.correlation-id }} GH_TOKEN: ${{ steps.private-app-token.outputs.token }} + PUBLIC_PRODUCER_JOB_ID: ${{ needs.select.outputs.public-producer-job-id }} PUBLIC_RUN_ATTEMPT: ${{ needs.select.outputs.public-run-attempt }} PUBLIC_RUN_ID: ${{ needs.select.outputs.public-run-id }} PUBLIC_SHA: ${{ needs.select.outputs.public-sha }} @@ -108,6 +111,7 @@ jobs: --arg baseline_sha "$BASELINE_SHA" \ --arg commit_count "$COMMIT_COUNT" \ --arg correlation_id "$CORRELATION_ID" \ + --arg producer_job_id "$PUBLIC_PRODUCER_JOB_ID" \ --arg run_attempt "$PUBLIC_RUN_ATTEMPT" \ --arg run_id "$PUBLIC_RUN_ID" \ --arg sha "$PUBLIC_SHA" \ @@ -119,6 +123,7 @@ jobs: public_repository: "NVIDIA/cuda-python", public_repository_id: "381173759", public_workflow_id: "155304118", + public_producer_job_id: $producer_job_id, public_run_id: $run_id, public_run_attempt: $run_attempt, public_sha: $sha, diff --git a/ci/tools/woa_xrepo_select.py b/ci/tools/woa_xrepo_select.py index 4fd9740cf9b..7263d852584 100644 --- a/ci/tools/woa_xrepo_select.py +++ b/ci/tools/woa_xrepo_select.py @@ -18,6 +18,7 @@ PUBLIC_WORKFLOW_PATH = ".github/workflows/ci.yml" REPORTING_APP_ID = 4954254 CHECK_NAME = "cuda-python WoA integration" +PRODUCER_JOB_NAME = "Build win-arm64, CUDA 13.4.2 / py3.13" MAX_LEDGER_COMMITS = 100 @@ -67,31 +68,34 @@ def parse_time(value): return datetime.datetime.fromisoformat(value.replace("Z", "+00:00")) -def select_artifacts(artifacts, sha): - binding_name = f"cuda-bindings-python313-cuda13.4.2-win-arm64-{sha}" - core_name = f"cuda-core-python313-win-arm64-{sha}" - selected = [ - artifact - for artifact in artifacts - if not artifact["expired"] - and ( - artifact["name"] == "cuda-pathfinder-wheel" - or artifact["name"] == binding_name - or artifact["name"] == core_name - ) +def select_artifacts(api, run_id, sha): + names = [ + "cuda-pathfinder-wheel", + f"cuda-bindings-python313-cuda13.4.2-win-arm64-{sha}", + f"cuda-core-python313-win-arm64-{sha}", ] - if len(selected) != 3: - return None - names = {artifact["name"] for artifact in selected} - if len(names) != 3: - return None result = [] - for artifact in sorted(selected, key=lambda item: item["name"]): + for name in names: + response = api.get( + f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/artifacts", + {"name": name, "per_page": 100}, + ) + artifacts = response["artifacts"] + if response["total_count"] != 1 or len(artifacts) != 1: + return None + artifact = artifacts[0] digest = artifact.get("digest") - if not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest): + if not ( + artifact["name"] == name + and artifact["expired"] is False + and isinstance(artifact["id"], int) + and artifact["id"] > 0 + and isinstance(digest, str) + and re.fullmatch(r"sha256:[0-9a-f]{64}", digest) + ): return None result.append({"id": artifact["id"], "name": artifact["name"], "digest": digest}) - return result + return sorted(result, key=lambda item: item["name"]) def validate_run(api, run): @@ -113,12 +117,15 @@ def validate_run(api, run): f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/attempts/{attempt}/jobs", "jobs", ) - woa_jobs = [job for job in jobs if job["name"].startswith("Build win-arm64, CUDA ")] - if not woa_jobs or any(job["status"] != "completed" or job["conclusion"] != "success" for job in woa_jobs): + producer_jobs = [job for job in jobs if job["name"] == PRODUCER_JOB_NAME] + if not ( + len(producer_jobs) == 1 + and producer_jobs[0]["status"] == "completed" + and producer_jobs[0]["conclusion"] == "success" + ): return None - artifacts = api.paginate(f"repos/{PUBLIC_REPOSITORY}/actions/runs/{run_id}/artifacts", "artifacts") - selected_artifacts = select_artifacts(artifacts, run["head_sha"]) + selected_artifacts = select_artifacts(api, run_id, run["head_sha"]) if selected_artifacts is None: return None @@ -129,6 +136,7 @@ def validate_run(api, run): return { "run_id": str(run_id), "run_attempt": str(attempt), + "producer_job_id": str(producer_jobs[0]["id"]), "sha": run["head_sha"], "artifacts": selected_artifacts, }