Workflow dispatch from PR forks (#607)

Co-authored-by: UltralyticsAssistant <[email protected]>
This commit is contained in:
Glenn Jocher
2025-10-24 01:29:35 +02:00
committed by GitHub
co-authored by UltralyticsAssistant
parent 23eea14597
commit dc9b4a21be
4 changed files with 58 additions and 20 deletions
+1 -1
View File
@@ -23,4 +23,4 @@
# ├── test_summarize_pr.py
# └── ...
__version__ = "0.1.9"
__version__ = "0.2.0"
+49 -11
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import os
import time
from datetime import datetime
@@ -14,6 +15,9 @@ WORKFLOW_FILES = ["ci.yml", "docker.yml"]
def get_pr_branch(event) -> tuple[str, str | None]:
"""Gets the PR branch name, creating temp branch for forks, returning (branch, temp_branch_to_delete)."""
import subprocess
import tempfile
pr_number = event.event_data["issue"]["number"]
pr_data = event.get_repo_data(f"pulls/{pr_number}")
head = pr_data.get("head", {})
@@ -22,12 +26,43 @@ def get_pr_branch(event) -> tuple[str, str | None]:
is_fork = head.get("repo") and head["repo"]["id"] != pr_data["base"]["repo"]["id"]
if is_fork:
# Create temp branch in base repo for fork PRs
# Create temp branch in base repo by pushing fork code
temp_branch = f"temp-ci-{pr_number}-{int(time.time() * 1000)}"
repo = event.repository
event.post(
f"{GITHUB_API_URL}/repos/{repo}/git/refs", json={"ref": f"refs/heads/{temp_branch}", "sha": head["sha"]}
)
fork_repo = head["repo"]["full_name"]
fork_branch = head["ref"]
base_repo = event.repository
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable is not set")
with tempfile.TemporaryDirectory() as tmp_dir:
repo_dir = os.path.join(tmp_dir, "repo")
base_url = f"https://x-access-token:{token}@github.com/{base_repo}.git"
fork_url = f"https://github.com/{fork_repo}.git"
try:
# Clone base repo (minimal)
subprocess.run(["git", "clone", "--depth", "1", base_url, repo_dir], check=True, capture_output=True)
# Add fork as remote and fetch the PR branch
subprocess.run(
["git", "remote", "add", "fork", fork_url], cwd=repo_dir, check=True, capture_output=True
)
subprocess.run(
["git", "fetch", "fork", f"{fork_branch}:{temp_branch}"],
cwd=repo_dir,
check=True,
capture_output=True,
)
# Push temp branch to base repo
subprocess.run(["git", "push", "origin", temp_branch], cwd=repo_dir, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
# Sanitize error output to prevent token leakage
stderr = e.stderr.decode() if e.stderr else "No stderr output"
stderr = stderr.replace(token, "***TOKEN***")
raise RuntimeError(f"Failed to create tmp branch from fork (exit code {e.returncode}): {stderr}") from e
return temp_branch, temp_branch
return head.get("ref", "main"), None
@@ -81,12 +116,15 @@ def update_comment(event, comment_body: str, triggered_actions: list[dict], bran
return
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")
summary = (
f"\n\n## ⚡ Actions Trigger\n\n"
f"<sub>Made with ❤️ by [Ultralytics Actions](https://www.ultralytics.com/actions)<sub>\n\n"
f"GitHub Actions below triggered via workflow dispatch on this "
f"PR branch `{branch}` at {timestamp} with `{RUN_CI_KEYWORD}` command:\n\n"
)
summary = f"""
## ⚡ Actions Trigger
<sub>Made with ❤️ by [Ultralytics Actions](https://www.ultralytics.com/actions)<sub>
GitHub Actions below triggered via workflow dispatch for this PR at {timestamp} with `{RUN_CI_KEYWORD}` command:
"""
for action in triggered_actions:
run_info = f" run {action['run_number']}" if action["run_number"] else ""
+2 -2
View File
@@ -122,8 +122,8 @@ class Action:
self._pr_summary_cache = None
self._username_cache = None
self._default_status = {
"get": [200],
"post": [200, 201],
"get": [200, 204],
"post": [200, 201, 204],
"put": [200, 201, 204],
"patch": [200],
"delete": [200, 204],
+6 -6
View File
@@ -34,19 +34,19 @@ def test_get_pr_branch_fork():
mock_event.event_data = {"issue": {"number": 456}}
mock_event.repository = "base/repo"
mock_event.get_repo_data.return_value = {
"head": {"ref": "fork-branch", "sha": "abc123", "repo": {"id": 2}},
"head": {"ref": "fork-branch", "sha": "abc123", "repo": {"id": 2, "full_name": "fork/repo"}},
"base": {"repo": {"id": 1}},
}
with patch("time.time", return_value=1234567.890):
branch, temp_branch = get_pr_branch(mock_event)
with patch("subprocess.run") as mock_run:
with patch("os.environ.get", return_value="test-token"):
branch, temp_branch = get_pr_branch(mock_event)
assert branch == "temp-ci-456-1234567890"
assert temp_branch == "temp-ci-456-1234567890"
mock_event.post.assert_called_once_with(
"https://api.github.com/repos/base/repo/git/refs",
json={"ref": "refs/heads/temp-ci-456-1234567890", "sha": "abc123"},
)
# Verify git commands were called
assert mock_run.call_count == 4 # clone, remote add, fetch, push
def test_trigger_and_get_workflow_info():