Files
computer-playbook/tests/unit/python/scripts/meta/wait/test_images.py
T
kevinveenbirkenbachandClaude Opus 5.5 3ab0153856 fix(ci): find the privileged run of a fork PR by its head commit
The wait for fork CI images never found the privileged workflow run it
watches. It selected a run only when the run's pull_requests array held
the PR number, and for a run whose head repository is a fork the API
returns that array empty. The job only runs for fork PRs, so the lookup
matched in no case it exists for.

The early exit for a failed privileged run was therefore unreachable.
When that run failed, the job kept polling until its timeout of 1980
attempts and held a runner for about five and a half hours.

The lookup now asks the API for the runs of the PR head commit and
selects by head_sha. That also replaces a paginated listing of every run
of the workflow with one small request per attempt. Without a known head
commit the match by PR number stays.

Refs #784

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 20:55:54 +02:00

129 lines
4.4 KiB
Python

from __future__ import annotations
import json
import os
import subprocess
import tempfile
import unittest
from pathlib import Path
from utils.cache.files import PROJECT_ROOT
SCRIPT = PROJECT_ROOT / "scripts" / "meta" / "wait" / "images.sh"
HEAD_SHA = "5b002678df222f1095fae2f42e3bb10964adb8ef"
STUBS = {
"docker": '#!/usr/bin/env bash\n[[ -e "${STUB_DIR}/images-ready" ]]\n',
"gh": (
"#!/usr/bin/env bash\n"
'printf "gh %s\\n" "$*" >&2\n'
'if [[ -n "${STUB_IMAGES_APPEAR:-}" ]]; then\n'
'\ttouch "${STUB_DIR}/images-ready"\n'
"fi\n"
'if [[ -e "${STUB_DIR}/gh-response" ]]; then\n'
'\tcat "${STUB_DIR}/gh-response"\n'
"\texit 0\n"
"fi\n"
'echo "HTTP 504" >&2\n'
"exit 1\n"
),
}
def wait_for_images(
tmp: str, runs: list[dict] | None = None, **overrides: str
) -> subprocess.CompletedProcess:
"""Run the wait script against stubbed ``docker`` and ``gh``.
Args:
tmp: directory that holds the stubs and their state.
runs: workflow runs ``gh`` answers with; without them every lookup fails.
overrides: environment entries layered over the defaults.
"""
stub_dir = Path(tmp)
for name, body in STUBS.items():
stub = stub_dir / name
stub.write_text(body)
stub.chmod(0o755)
if runs is not None:
(stub_dir / "gh-response").write_text(json.dumps({"workflow_runs": runs}))
inherited = {k: v for k, v in os.environ.items() if k != "BASH_ENV"}
env = {
**inherited,
"PATH": f"{stub_dir}{os.pathsep}{os.environ['PATH']}",
"STUB_DIR": str(stub_dir),
"GITHUB_REPOSITORY": "acme/widgets",
"GH_TOKEN": "unused",
"WORKFLOW_FILE": "entry.yml",
"TARGET_EVENT": "pull_request_target",
"PR_NUMBER": "1",
"PR_HEAD_SHA": HEAD_SHA,
"IMAGE_TAG": "ci-test",
"INFINITO_DISTROS": "debian",
"WAIT_ATTEMPTS": "100",
"WAIT_SLEEP_SECONDS": "0",
**overrides,
}
return subprocess.run(
["bash", str(SCRIPT)],
cwd=PROJECT_ROOT,
env=env,
capture_output=True,
text=True,
check=False,
)
class TestWaitForImages(unittest.TestCase):
def test_failed_lookup_does_not_end_the_wait(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
result = wait_for_images(tmp, STUB_IMAGES_APPEAR="true")
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn(
"Lookup of the privileged workflow run failed (1/30)", result.stdout
)
self.assertIn("All required CI images are available.", result.stdout)
def test_lookup_that_keeps_failing_ends_the_wait(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
result = wait_for_images(tmp)
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
self.assertIn("failed 30 times in a row", result.stderr)
def test_failed_privileged_run_of_a_fork_ends_the_wait(self) -> None:
fork_run = {
"id": 7,
"status": "completed",
"conclusion": "failure",
"created_at": "2026-10-04T12:41:18Z",
"head_sha": HEAD_SHA,
"pull_requests": [],
}
with tempfile.TemporaryDirectory() as tmp:
result = wait_for_images(tmp, runs=[fork_run])
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
self.assertIn(
"Privileged run 7 finished with conclusion=failure", result.stderr
)
self.assertIn(f"head_sha={HEAD_SHA}", result.stderr)
def test_run_of_another_head_is_ignored(self) -> None:
other_run = {
"id": 8,
"status": "completed",
"conclusion": "failure",
"created_at": "2026-10-04T12:41:18Z",
"head_sha": "0" * 40,
"pull_requests": [],
}
with tempfile.TemporaryDirectory() as tmp:
result = wait_for_images(tmp, runs=[other_run], WAIT_ATTEMPTS="2")
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
self.assertIn(
"No matching privileged workflow run found yet", result.stdout
)
self.assertIn("Timed out waiting for CI images", result.stderr)
if __name__ == "__main__":
unittest.main()