From 3ee1bb323e8e891d3db0fd0ac63b8e7196c85abd Mon Sep 17 00:00:00 2001 From: ethernet Date: Mon, 10 Aug 2026 17:33:09 -0400 Subject: [PATCH] fix(ci): resolve fork PRs in the E2E evidence publisher The publisher read the PR number from the CI run's pull_requests payload. GitHub keeps that payload empty for fork runs, so the job printed 'No pull request is associated' and stopped on every fork PR. Resolve the PR from the run's head owner, branch, and SHA instead. The SHA match skips runs that a newer push superseded. A fork PR also has no CI review comment, because the live poller skips forks. The publisher now logs this and exits clean instead of raising; the evidence stays in the workflow artifact. --- .github/workflows/publish-e2e-evidence.yml | 13 +++++++++-- scripts/ci/publish_e2e_evidence.py | 13 +++++++++-- tests/ci/test_publish_e2e_evidence.py | 26 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-e2e-evidence.yml b/.github/workflows/publish-e2e-evidence.yml index 3d7fe32d78..c4f22c21d4 100644 --- a/.github/workflows/publish-e2e-evidence.yml +++ b/.github/workflows/publish-e2e-evidence.yml @@ -44,12 +44,21 @@ jobs: GH_SESSION_TOKEN: ${{ secrets.GH_IMAGE_SESSION_TOKEN }} SOURCE_REPO: ${{ github.repository }} SOURCE_RUN_ID: ${{ github.event.workflow_run.id }} + HEAD_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} run: | set -euo pipefail - PR_NUMBER=$(gh api "repos/$SOURCE_REPO/actions/runs/$SOURCE_RUN_ID" --jq '.pull_requests[0].number // empty') + # The run's own ``pull_requests`` payload is always empty for a + # fork PR, so resolve the PR from its head reference instead. + # The head-SHA match skips runs that a newer push superseded. + PR_NUMBER=$(gh api -X GET "repos/$SOURCE_REPO/pulls" \ + -f head="$HEAD_OWNER:$HEAD_BRANCH" -f state=open \ + --jq '.[] | select(.head.sha == $ENV.HEAD_SHA) | .number' \ + | head -n1) if [ -z "$PR_NUMBER" ]; then - echo "No pull request is associated with CI run $SOURCE_RUN_ID." + echo "No open pull request has head $HEAD_OWNER:$HEAD_BRANCH at $HEAD_SHA (CI run $SOURCE_RUN_ID)." exit 0 fi diff --git a/scripts/ci/publish_e2e_evidence.py b/scripts/ci/publish_e2e_evidence.py index 7d5de45627..8105259063 100644 --- a/scripts/ci/publish_e2e_evidence.py +++ b/scripts/ci/publish_e2e_evidence.py @@ -203,7 +203,7 @@ def _find_review_comment(comments: object) -> dict[str, Any] | None: return None -def _wait_for_review_comment(token: str, source_repo: str, pr_number: str) -> dict[str, Any]: +def _wait_for_review_comment(token: str, source_repo: str, pr_number: str) -> dict[str, Any] | None: """Wait briefly for GitHub's comment API to expose the completed marker.""" request = urllib.request.Request( f"{API_BASE}/repos/{source_repo}/issues/{pr_number}/comments?per_page=100", @@ -221,7 +221,7 @@ def _wait_for_review_comment(token: str, source_repo: str, pr_number: str) -> di return comment if attempt + 1 < COMMENT_LOOKUP_ATTEMPTS: time.sleep(COMMENT_LOOKUP_DELAY_SECONDS) - raise ValueError("CI review comment with E2E evidence marker is missing") + return None def upload_evidence( @@ -280,6 +280,15 @@ def publish( print("No inline E2E evidence to publish.") return False comment = _wait_for_review_comment(token, source_repo, pr_number) + if comment is None: + # A fork PR gets no CI review comment (the live poller needs a + # write token there), so there is no marker to patch. The + # evidence stays available in the workflow artifact. + print( + f"PR #{pr_number} has no CI review comment with an E2E evidence " + "marker; the evidence stays in the workflow artifact." + ) + return False try: attachment_urls = upload_evidence( files, evidence_dir, source_repo, session_token diff --git a/tests/ci/test_publish_e2e_evidence.py b/tests/ci/test_publish_e2e_evidence.py index e24a2d971e..365f344225 100644 --- a/tests/ci/test_publish_e2e_evidence.py +++ b/tests/ci/test_publish_e2e_evidence.py @@ -179,6 +179,32 @@ def test_publish_marks_evidence_upload_failure_in_pr_comment(tmp_path, monkeypat ] +def test_publish_skips_when_no_review_comment_exists(tmp_path, monkeypatch, capsys): + monkeypatch.setattr( + _mod, + "load_evidence", + lambda evidence_dir: ( + [_mod.EvidenceFile("shot.png", "new screenshot: shot.png")], + {}, + ), + ) + monkeypatch.setattr(_mod, "_wait_for_review_comment", lambda *args: None) + monkeypatch.setattr( + _mod, + "upload_evidence", + lambda *args: (_ for _ in ()).throw(AssertionError("must not upload")), + ) + + assert _mod.publish( + "github-token", + "NousResearch/hermes-agent", + tmp_path, + "83202", + "image-token", + ) is False + assert "no CI review comment" in capsys.readouterr().out + + def test_find_review_comment_requires_the_evidence_marker(): pending = "\n\npending\n"