Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions .github/scripts/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,25 @@ class DispatchPlan:
# Reading build.toml and selecting workflows


def read_backends(kernel_name: str) -> list[str] | None:
build_toml = Path(kernel_name) / "build.toml"
if not build_toml.exists():
return None
with open(build_toml, "rb") as f:
config = tomllib.load(f)
def read_backends(kernel_name: str, ref: str = "") -> list[str] | None:
# ref reads build.toml from that revision (the PR branch), not the working tree.
if ref:
try:
raw = subprocess.run(
["git", "show", f"{ref}:{kernel_name}/build.toml"],
capture_output=True,
text=True,
check=True,
).stdout
except (FileNotFoundError, subprocess.CalledProcessError):
return None
config = tomllib.loads(raw)
else:
build_toml = Path(kernel_name) / "build.toml"
if not build_toml.exists():
return None
with open(build_toml, "rb") as f:
config = tomllib.load(f)
backends = config.get("general", {}).get("backends")
if backends is None:
backends = config.get("backends")
Expand All @@ -109,8 +122,10 @@ def read_backends(kernel_name: str) -> list[str] | None:
return None


def select_workflows(kernel_name: str, *, notes: list[str]) -> set[str]:
backends = read_backends(kernel_name)
def select_workflows(
kernel_name: str, *, notes: list[str], metadata_ref: str = ""
) -> set[str]:
backends = read_backends(kernel_name, metadata_ref)
if backends is None:
notes.append(
f"Could not read backends for {kernel_name}, dispatching all workflows"
Expand Down Expand Up @@ -232,9 +247,12 @@ def _plan_build_actions(
head_sha: str,
target_branch: str,
upload: bool,
metadata_ref: str,
) -> None:
backends = read_backends(kernel_name) or []
workflows = select_workflows(kernel_name, notes=plan.notes)
backends = read_backends(kernel_name, metadata_ref) or []
workflows = select_workflows(
kernel_name, notes=plan.notes, metadata_ref=metadata_ref
)
plan.skipped = sorted(set(WORKFLOWS["build"]) - workflows)

for workflow in sorted(workflows):
Expand Down Expand Up @@ -294,6 +312,7 @@ def plan_dispatch(
upload: bool = True,
run_security: bool = False,
security_only: bool = False,
metadata_ref: str = "",
) -> DispatchPlan:
want_security = run_security or security_only
plan = DispatchPlan(kernel_name=kernel_name, head_sha=head_sha)
Expand All @@ -306,6 +325,7 @@ def plan_dispatch(
mode=mode,
repo_prefix=repo_prefix,
dispatch_key_prefix=dispatch_key_prefix,
metadata_ref=metadata_ref,
skip_build=skip_build,
pr_number=pr_number,
head_sha=head_sha,
Expand Down Expand Up @@ -460,6 +480,7 @@ def dispatch(
upload: bool = True,
run_security: bool = False,
security_only: bool = False,
metadata_ref: str = "",
) -> DispatchResult:
if not security_only and (not kernel_name or not KERNEL_NAME_RE.match(kernel_name)):
result = DispatchResult(kernel_name=kernel_name)
Expand All @@ -481,6 +502,7 @@ def dispatch(
upload=upload,
run_security=run_security,
security_only=security_only,
metadata_ref=metadata_ref,
)
if dry_run:
return _result_from_plan(plan)
Expand Down
2 changes: 2 additions & 0 deletions .github/scripts/pr_comment_kernel_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,8 @@ def main(*, dry_run: bool = False):
# The audit is per-PR, so request it only once (on the first kernel).
run_security=run_security and index == 0,
dry_run=dry_run,
# Read backends from the PR commit (dry-run reads the working tree).
metadata_ref="" if dry_run else (pr_head_sha or ""),
)
emit_dispatch_diagnostics(release_result, dry_run=dry_run)
for wf, dk in release_result.dispatched:
Expand Down
26 changes: 26 additions & 0 deletions .github/scripts/test_dispatch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import io
import os
import subprocess
import sys
import unittest
import urllib.error
Expand Down Expand Up @@ -68,6 +69,31 @@ def test_security_only_plans_only_security(self):
self.assertEqual(action.body["inputs"]["head_sha"], "deadbeef")


# Metadata read from a git ref (PR branch) instead of the working tree.
class MetadataRefTest(unittest.TestCase):
def test_read_backends_from_ref_uses_git_show(self):
run = mock.Mock(return_value=mock.Mock(stdout='backends = ["cuda", "rocm"]\n'))
with mock.patch.object(dispatch.subprocess, "run", run):
backends = dispatch.read_backends("somekernel", ref="abc123")
self.assertEqual(backends, ["cuda", "rocm"])
self.assertEqual(
run.call_args.args[0], ["git", "show", "abc123:somekernel/build.toml"]
)

def test_read_backends_from_ref_missing_returns_none(self):
err = subprocess.CalledProcessError(128, ["git", "show"])
with mock.patch.object(dispatch.subprocess, "run", side_effect=err):
self.assertIsNone(dispatch.read_backends("missing", ref="abc123"))

def test_metadata_ref_routes_metal_less_kernel_without_mac_build(self):
# Regression: a cuda-only kernel read from the ref must not trigger build-mac.yaml.
run = mock.Mock(return_value=mock.Mock(stdout='backends = ["cuda"]\n'))
with mock.patch.object(dispatch.subprocess, "run", run):
plan = dispatch.plan_dispatch("newkernel", metadata_ref="prsha")
builds = _workflows([a for a in plan.actions if a.kind == "build"])
self.assertEqual(builds, ["build.yaml"])


# Orchestration: kernel-name validation and the dry-run no-I/O contract.
class DispatchOrchestratorTest(unittest.TestCase):
def test_invalid_kernel_name_marks_all_builds_failed(self):
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr-comment-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 1
- name: Fetch PR head for metadata
# Make the PR commit available so the bot can read its build.toml (data only).
run: git fetch --depth=1 origin "refs/pull/${{ github.event.issue.number }}/head"
- name: Handle /kernel-bot command
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down