From 3ddfb5e0fbe9454eff83c88eac4de064dd3492f7 Mon Sep 17 00:00:00 2001 From: David Liu Date: Thu, 8 Oct 2026 14:58:26 +0000 Subject: [PATCH] Skip Windows installation tests on fork PRs The job logs in to JFrog with a GitHub OIDC token, which pull requests from forks never receive, so it failed on every fork PR. Guard it like the other credentialed integration jobs. The contract test now lists the integration jobs that skip fork PRs, checks that against the workflow, and rejects secrets or OIDC in any job that runs on forks. Co-authored-by: Isaac --- .github/workflows/integration.yml | 1 + tests/test_integration_contract.py | 50 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 7124d45d1..6dee17d30 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -89,6 +89,7 @@ jobs: # are fixed, remove continue-on-error and add this job to the cujs required set. installation-windows: name: Windows installation tests + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} runs-on: windows-server-latest continue-on-error: true timeout-minutes: 20 diff --git a/tests/test_integration_contract.py b/tests/test_integration_contract.py index 10af6ef48..cce7fdc1a 100644 --- a/tests/test_integration_contract.py +++ b/tests/test_integration_contract.py @@ -67,6 +67,56 @@ def test_dedicated_cuj_ci_discovers_the_whole_folder(): assert 'result["result"] != "success"' in gate +FORK_GUARD = ( + "github.event_name != 'pull_request' || " + "github.event.pull_request.head.repo.full_name == github.repository" +) +# Fork PRs get no secrets or OIDC token, so jobs that need either must skip them. +SKIPPED_ON_FORK_PRS = { + "installation-windows", + "workspace", + "smoke", + "headless-windows", + "full", + "opencode", + "managed", + "dedicated-cuj", + "cujs", +} + + +def _integration_jobs(): + workflow = (Path(__file__).parent.parent / ".github/workflows/integration.yml").read_text() + body = workflow.split("\njobs:\n", 1)[1] + return dict(re.findall(r"(?ms)^ ([a-z0-9-]+):\n(.*?)(?=^ [a-z0-9-]+:\n|\Z)", body)) + + +def _skips_fork_prs(jobs, name): + job = jobs[name] + condition = re.search(r"(?m)^ if: (.*)$", job) + if condition and FORK_GUARD in condition.group(1): + return True + needs = re.search(r"(?m)^ needs: \[?([a-z0-9-, ]+)\]?$", job) + for need in [need.strip() for need in needs.group(1).split(",")] if needs else []: + requires_success = ( + condition is None or f"needs.{need}.result == 'success'" in condition.group(1) + ) + if requires_success and _skips_fork_prs(jobs, need): + return True + return False + + +def test_integration_jobs_that_run_on_fork_pull_requests_need_no_credentials(): + jobs = _integration_jobs() + skipped = {name for name in jobs if _skips_fork_prs(jobs, name)} + + assert skipped == SKIPPED_ON_FORK_PRS + for name in set(jobs) - skipped: + assert "secrets." not in jobs[name] and "id-token: write" not in jobs[name], ( + f"{name} runs on fork PRs, which get no secrets or OIDC token" + ) + + def test_windows_integration_ci_uses_shared_claude_version(): workflow = Path(__file__).parent.parent / ".github/workflows/integration.yml" contents = workflow.read_text()