diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e292ee5..7738b34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,9 @@ env: # is the reusable workflow ParakhAI-frontend's CD pipeline calls, not worth # touching for a cosmetic fix scoped to this file's own job summary). SANDBOX_ORG_SLUG: ${{ vars.SANDBOX_ORG_SLUG }} + # pending_pr marker (conftest.py): ParakhAI-Backend is private, so its PR state + # needs a GH_PR_TOKEN secret (fine-grained PAT, Pull requests: read on that repo) + GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN || github.token }} jobs: # ────────────────────────────────────────────── Change detection diff --git a/.github/workflows/run-smoke.yml b/.github/workflows/run-smoke.yml index 44cf7fa..7c92aa1 100644 --- a/.github/workflows/run-smoke.yml +++ b/.github/workflows/run-smoke.yml @@ -65,6 +65,10 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # pending_pr marker (conftest.py) reads PR merge state from the GitHub API + GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN || github.token }} + jobs: smoke: name: Smoke Tests diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 578d319..0dc498d 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -58,6 +58,9 @@ env: # Slug of the dedicated sandbox org. When set, write-marked regression tests # are allowed to mutate this org. When unset, those tests skip. SANDBOX_ORG_SLUG: ${{ secrets.SANDBOX_ORG_SLUG }} + # pending_pr marker (conftest.py): ParakhAI-Backend is private, so its PR state + # needs a GH_PR_TOKEN secret (fine-grained PAT, Pull requests: read on that repo) + GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN || github.token }} jobs: nightly-full-suite: diff --git a/conftest.py b/conftest.py index c7351b8..e47b679 100644 --- a/conftest.py +++ b/conftest.py @@ -47,3 +47,38 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: logger.warning("Could not generate Markdown report: %s", exc) else: logger.debug("JSON report not found at %s — skipping Markdown generation", json_path) + + +# --- pending_pr: tests written for an open product PR run only once it merges --- +import functools as _functools +import os +import json as _json +import urllib.request as _urlreq + + +@_functools.lru_cache(maxsize=None) +def _pending_pr_state(ref): + """Return None if `ref` ("Repo#N" or "owner/Repo#N") is merged, else a skip reason.""" + repo, num = ref.split("#") + if "/" not in repo: + repo = f"CivicDataLab/{repo}" + req = _urlreq.Request(f"https://api.github.com/repos/{repo}/pulls/{num}", + headers={"Accept": "application/vnd.github+json"}) + token = os.getenv("GH_PR_TOKEN") or os.getenv("GITHUB_TOKEN") + if token: + req.add_header("Authorization", f"Bearer {token}") + try: + with _urlreq.urlopen(req, timeout=10) as resp: + merged = _json.load(resp).get("merged_at") + except Exception as e: # 404 on a private repo without GH_PR_TOKEN lands here too + return f"pending_pr {ref}: could not read PR state ({e})" + return None if merged else f"pending_pr {ref}: not merged yet" + + +def pytest_collection_modifyitems(config, items): + for item in items: + marker = item.get_closest_marker("pending_pr") + if marker: + reason = _pending_pr_state(marker.args[0]) + if reason: + item.add_marker(pytest.mark.skip(reason=reason)) diff --git a/pytest.ini b/pytest.ini index 2447dda..469cabe 100644 --- a/pytest.ini +++ b/pytest.ini @@ -30,6 +30,7 @@ timeout_method = thread # ── Markers ─────────────────────────────────────────────────────────────────── markers = + pending_pr: pending_pr("Repo#N") — covers an unmerged product PR; skipped until GitHub reports it merged (see conftest.py) e2e: End-to-end browser UI tests accessibility: WCAG accessibility tests using axe-core visual: Visual regression screenshot comparison tests