|
1 | 1 | # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
2 | 2 | # SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | | -# ruff: noqa: S603 |
5 | | - |
6 | | -import json |
7 | | -import os |
8 | | -import shutil |
9 | | -import subprocess |
10 | | -import sys |
11 | 4 | from pathlib import Path |
12 | 5 |
|
13 | 6 | import pytest |
14 | 7 | import yaml |
15 | 8 |
|
16 | 9 | ROOT = Path(__file__).resolve().parents[3] |
17 | 10 | WORKFLOW = yaml.safe_load((ROOT / ".github/workflows/woa-main-ci.yml").read_text()) |
18 | | -STEPS = WORKFLOW["jobs"]["resolve-public-build"]["steps"] |
19 | | -SHA = "a" * 40 |
20 | | - |
21 | | - |
22 | | -@pytest.fixture |
23 | | -def run_step(tmp_path): |
24 | | - mock = tmp_path / "gh" |
25 | | - mock.write_text( |
26 | | - f"#!{sys.executable}\n" |
27 | | - "import json, os, sys\n" |
28 | | - "from pathlib import Path\n" |
29 | | - "args = sys.argv[1:]\n" |
30 | | - "endpoint = next(arg for arg in args if arg.startswith('repos/'))\n" |
31 | | - "run = json.loads(os.environ['RUN'])\n" |
32 | | - "if endpoint.endswith('/ci.yml/runs'):\n" |
33 | | - " print(json.dumps({'workflow_runs': [dict(run, id=999, event='schedule'), " |
34 | | - "dict(run, id=998, head_sha='b' * 40), run]}))\n" |
35 | | - "elif endpoint.endswith('/artifacts'):\n" |
36 | | - " name = next(arg[5:] for arg in args if arg.startswith('name='))\n" |
37 | | - " with Path(os.environ['REQUESTS']).open('a') as output:\n" |
38 | | - " output.write(name + '\\n')\n" |
39 | | - " ready = os.environ['MODE'] != 'missing' and " |
40 | | - "(os.environ['MODE'] != 'delayed' or len(Path(os.environ['REQUESTS']).read_text().splitlines()) > 1)\n" |
41 | | - " print(json.dumps({'total_count': int(ready), 'artifacts': " |
42 | | - "[{'name': name, 'expired': False}] if ready else []}))\n" |
43 | | - "else:\n" |
44 | | - " print(json.dumps(run))\n" |
45 | | - ) |
46 | | - mock.chmod(0o755) |
47 | | - for name, body in { |
48 | | - "sleep": "exit 0", |
49 | | - "yq": "case $* in *supported*) echo 3.11; echo 3.14t;; *) echo 13.4.2;; esac", |
50 | | - }.items(): |
51 | | - executable = tmp_path / name |
52 | | - executable.write_text(f"#!/bin/bash\n{body}\n") |
53 | | - executable.chmod(0o755) |
54 | | - |
55 | | - def invoke(name, *, mode="ready", status="in_progress", conclusion=None): |
56 | | - run = { |
57 | | - "id": 123, |
58 | | - "repository": {"full_name": "NVIDIA/cuda-python"}, |
59 | | - "workflow_id": 155304118, |
60 | | - "path": ".github/workflows/ci.yml", |
61 | | - "event": "push", |
62 | | - "head_branch": "main", |
63 | | - "head_sha": SHA, |
64 | | - "status": status, |
65 | | - "conclusion": conclusion, |
66 | | - } |
67 | | - result = subprocess.run( |
68 | | - [shutil.which("bash"), "-c", next(step["run"] for step in STEPS if step["name"] == name)], |
69 | | - env=dict( |
70 | | - os.environ, |
71 | | - PATH=str(tmp_path) + os.pathsep + os.environ["PATH"], |
72 | | - PUBLIC_RUN_ID="123", |
73 | | - PUBLIC_SHA=SHA, |
74 | | - RUN=json.dumps(run), |
75 | | - MODE=mode, |
76 | | - REQUESTS=str(tmp_path / "requests"), |
77 | | - GITHUB_OUTPUT=str(tmp_path / "outputs"), |
78 | | - GITHUB_STEP_SUMMARY=str(tmp_path / "summary"), |
79 | | - ), |
80 | | - capture_output=True, |
81 | | - text=True, |
82 | | - timeout=10, |
83 | | - ) |
84 | | - return result, tmp_path |
85 | | - |
86 | | - return invoke |
87 | 11 |
|
88 | 12 |
|
89 | 13 | @pytest.mark.agent_authored(model="gpt-6") |
90 | | -def test_entry_is_main_push_only(): |
91 | | - assert WORKFLOW[True] == {"push": {"branches": ["main"]}} |
92 | | - ci = yaml.safe_load((ROOT / ".github/workflows/ci.yml").read_text()) |
93 | | - assert "woa-main-dispatch" not in ci["jobs"] |
| 14 | +def test_entry_allows_reusable_and_manual_dispatch_without_cron(): |
| 15 | + assert set(WORKFLOW[True]) == {"workflow_call", "workflow_dispatch"} |
| 16 | + assert WORKFLOW[True]["workflow_call"]["inputs"]["public_run_id"]["required"] is True |
| 17 | + assert WORKFLOW[True]["workflow_dispatch"]["inputs"]["public_run_id"]["required"] is False |
| 18 | + assert WORKFLOW["jobs"]["resolve-public-build"]["if"] == ( |
| 19 | + "${{ github.repository == 'NVIDIA/cuda-python' && github.ref == 'refs/heads/main' }}" |
| 20 | + ) |
94 | 21 | assert WORKFLOW["concurrency"]["cancel-in-progress"] is False |
95 | 22 |
|
96 | 23 |
|
97 | 24 | @pytest.mark.agent_authored(model="gpt-6") |
98 | | -def test_resolver_selects_exact_push_sha_not_nightly(run_step): |
99 | | - result, directory = run_step("Resolve exact public build", status="queued") |
100 | | - assert result.returncode == 0, result.stderr |
101 | | - assert "public_run_id=123\n" in (directory / "outputs").read_text() |
102 | | - assert f"public_sha={SHA}\n" in (directory / "outputs").read_text() |
103 | | - |
104 | | - |
105 | | -@pytest.mark.agent_authored(model="gpt-6") |
106 | | -@pytest.mark.parametrize("mode", ["ready", "delayed"]) |
107 | | -def test_waiter_accepts_ready_wheels_before_ci_finishes(run_step, mode): |
108 | | - result, directory = run_step("Wait for public WoA wheel artifacts", mode=mode) |
109 | | - assert result.returncode == 0, result.stderr |
110 | | - expected = [ |
111 | | - "cuda-pathfinder-wheel", |
112 | | - f"cuda-bindings-python311-cuda13.4.2-win-arm64-{SHA}", |
113 | | - f"cuda-core-python311-win-arm64-{SHA}", |
114 | | - f"cuda-bindings-python314t-cuda13.4.2-win-arm64-{SHA}", |
115 | | - f"cuda-core-python314t-win-arm64-{SHA}", |
116 | | - ] |
117 | | - if mode == "delayed": |
118 | | - expected.insert(0, "cuda-pathfinder-wheel") |
119 | | - assert (directory / "requests").read_text().splitlines() == expected |
120 | | - |
121 | | - |
122 | | -@pytest.mark.agent_authored(model="gpt-6") |
123 | | -@pytest.mark.parametrize("conclusion", ["failure", "cancelled", "success"]) |
124 | | -def test_waiter_rejects_unsuccessful_ci_or_missing_wheels(run_step, conclusion): |
125 | | - result, _ = run_step( |
126 | | - "Wait for public WoA wheel artifacts", mode="missing", status="completed", conclusion=conclusion |
127 | | - ) |
128 | | - assert result.returncode != 0 |
129 | | - assert "::error::" in result.stdout |
| 25 | +def test_ci_dispatch_requires_successful_builds_on_main_push(): |
| 26 | + ci = yaml.safe_load((ROOT / ".github/workflows/ci.yml").read_text()) |
| 27 | + dispatch = ci["jobs"]["woa-main-dispatch"] |
| 28 | + assert dispatch["needs"] == ["build-linux-64", "build-windows-arm64"] |
| 29 | + for condition in ( |
| 30 | + "github.event_name == 'push'", |
| 31 | + "github.ref == 'refs/heads/main'", |
| 32 | + "needs.build-linux-64.result == 'success'", |
| 33 | + "needs.build-windows-arm64.result == 'success'", |
| 34 | + ): |
| 35 | + assert condition in dispatch["if"] |
| 36 | + assert dispatch["uses"] == "./.github/workflows/woa-main-ci.yml" |
| 37 | + assert dispatch["with"]["public_run_id"] == "${{ github.run_id }}" |
| 38 | + steps = WORKFLOW["jobs"]["resolve-public-build"]["steps"] |
| 39 | + assert "Wait for public WoA wheel artifacts" not in {step["name"] for step in steps} |
0 commit comments