Skip to content

Commit 64e0685

Browse files
authored
Merge pull request #193 from genomoncology/add-python-docs-pr-ci-gate
ci: add contracts job to PR CI and make test-contracts local target
2 parents 2ec932d + a9a2ba9 commit 64e0685

File tree

5 files changed

+104
-5
lines changed

5 files changed

+104
-5
lines changed

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,32 @@ jobs:
2828
- run: cargo fmt --check
2929
- run: cargo clippy -- -D warnings
3030
- run: cargo test
31+
32+
contracts:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- name: Cache protoc
38+
uses: actions/cache@v4
39+
with:
40+
path: ${{ runner.tool_cache }}/protoc
41+
key: protoc-${{ runner.os }}-${{ runner.arch }}-v28.3
42+
43+
- uses: arduino/setup-protoc@v3
44+
continue-on-error: true
45+
with:
46+
version: "28.3"
47+
repo-token: ${{ secrets.GITHUB_TOKEN }}
48+
49+
- uses: dtolnay/rust-toolchain@stable
50+
51+
- uses: actions/setup-python@v5
52+
with:
53+
python-version: "3.12"
54+
55+
- uses: astral-sh/setup-uv@v4
56+
57+
- run: uv sync --extra dev
58+
- run: uv run pytest tests/ -v --mcp-cmd "biomcp serve"
59+
- run: uv run mkdocs build --strict

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
.PHONY: build test check run clean spec validate-skills
1+
.PHONY: build test check run clean spec validate-skills test-contracts
22

33
build:
44
cargo build --release
55

66
test:
77
cargo test
88

9+
test-contracts:
10+
uv sync --extra dev
11+
uv run pytest tests/ -v --mcp-cmd "biomcp serve"
12+
uv run mkdocs build --strict
13+
914
check:
1015
cargo clippy -- -D warnings
1116
cargo fmt --check

RUN.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ Owned routes:
7878
| `ONCOKB_TOKEN` | Canonical OncoKB production token |
7979
| `ALPHAGENOME_API_KEY` | Required for AlphaGenome variant prediction |
8080

81+
## Pre-Merge Checks
82+
83+
Run the Rust gate locally:
84+
85+
```bash
86+
make check
87+
make test
88+
```
89+
90+
Run the Python/docs contract gate (same as PR CI `contracts` job):
91+
92+
```bash
93+
make test-contracts
94+
```
95+
96+
`make test-contracts` runs `uv sync --extra dev`, `pytest tests/`, and
97+
`mkdocs build --strict` - the same steps that PR CI and release validation
98+
require. Use this to catch docs-contract and Python regressions before
99+
pushing.
100+
81101
## Smoke Checks
82102

83103
```bash

analysis/technical/overview.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ share one limiter budget and one Streamable HTTP `/mcp` surface.
8181
2. Commit and push to `main`
8282
3. Cut a GitHub release with a semver tag
8383
4. GitHub Actions validates and publishes:
84-
- CI (`.github/workflows/ci.yml`) runs `cargo fmt --check`,
85-
`cargo clippy -- -D warnings`, and `cargo test`.
84+
- CI (`.github/workflows/ci.yml`) runs two parallel jobs: `check`
85+
(`cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`) and
86+
`contracts` (`uv sync --extra dev`, `uv run pytest tests/ -v --mcp-cmd "biomcp serve"`,
87+
`uv run mkdocs build --strict`).
8688
- Release validation runs the Rust checks again, then
8789
`uv run pytest tests/ -v --mcp-cmd "biomcp serve"` and
8890
`uv run mkdocs build --strict`.
@@ -100,7 +102,7 @@ BioMCP has three verification layers:
100102

101103
### 1. GitHub Workflows
102104

103-
CI (`.github/workflows/ci.yml`) runs `cargo fmt --check`, `cargo clippy -- -D warnings`, and `cargo test`.
105+
CI (`.github/workflows/ci.yml`) runs two parallel jobs: `check` (`cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`) and `contracts` (`uv sync --extra dev`, `uv run pytest tests/ -v --mcp-cmd "biomcp serve"`, `uv run mkdocs build --strict`).
104106
Contract smoke checks run in `.github/workflows/contracts.yml` on a schedule
105107
and by manual dispatch via `bash scripts/contract-smoke.sh`.
106108
release validation runs `pytest tests/` and `mkdocs build --strict` after the

tests/test_upstream_planning_analysis_docs.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import re
45
from pathlib import Path
56

67
import pytest
@@ -21,6 +22,20 @@ def _read_planning(path: str) -> str:
2122
return (_planning_root() / path).read_text(encoding="utf-8")
2223

2324

25+
def _workflow_job_block(workflow: str, job_name: str) -> str:
26+
match = re.search(
27+
rf"^ {re.escape(job_name)}:\n(.*?)(?=^ [A-Za-z0-9_-]+:\n|\Z)",
28+
workflow,
29+
flags=re.MULTILINE | re.DOTALL,
30+
)
31+
assert match is not None, f"missing workflow job {job_name}"
32+
return match.group(1)
33+
34+
35+
def _workflow_run_steps(job_block: str) -> list[str]:
36+
return re.findall(r"^\s+- run: (.+)$", job_block, flags=re.MULTILINE)
37+
38+
2439
def test_planning_contract_uses_repo_fixture_fallback_by_default(
2540
monkeypatch: pytest.MonkeyPatch,
2641
) -> None:
@@ -112,7 +127,9 @@ def test_technical_and_ux_docs_match_current_cli_and_workflow_contracts() -> Non
112127
technical = _read_repo("analysis/technical/overview.md")
113128
ux = _read_repo("analysis/ux/cli-reference.md")
114129

115-
assert "CI (`.github/workflows/ci.yml`) runs `cargo fmt --check`, `cargo clippy -- -D warnings`, and `cargo test`." in technical
130+
assert "CI (`.github/workflows/ci.yml`) runs two parallel jobs" in technical
131+
assert "`check` (`cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`)" in technical
132+
assert '`contracts` (`uv sync --extra dev`, `uv run pytest tests/ -v --mcp-cmd "biomcp serve"`, `uv run mkdocs build --strict`)' in technical
116133
assert "The spec suite is repo-local executable documentation; no GitHub workflow currently runs `make spec`." in technical
117134
assert "Contract smoke checks run in `.github/workflows/contracts.yml`" in technical
118135
assert "release validation runs `pytest tests/` and `mkdocs build --strict`" in technical
@@ -136,6 +153,31 @@ def test_technical_and_ux_docs_match_current_cli_and_workflow_contracts() -> Non
136153
assert "biomcp serve-sse → removed compatibility command; use `biomcp serve-http`" in ux
137154

138155

156+
def test_pull_request_contract_gate_matches_release_validation() -> None:
157+
ci = _read_repo(".github/workflows/ci.yml")
158+
release = _read_repo(".github/workflows/release.yml")
159+
contracts_smoke = _read_repo(".github/workflows/contracts.yml")
160+
expected_contract_runs = [
161+
"uv sync --extra dev",
162+
'uv run pytest tests/ -v --mcp-cmd "biomcp serve"',
163+
"uv run mkdocs build --strict",
164+
]
165+
166+
ci_contracts = _workflow_job_block(ci, "contracts")
167+
release_validate = _workflow_job_block(release, "validate")
168+
169+
assert 'python-version: "3.12"' in ci_contracts
170+
assert 'python-version: "3.12"' in release_validate
171+
assert _workflow_run_steps(ci_contracts) == expected_contract_runs
172+
assert _workflow_run_steps(release_validate)[-3:] == expected_contract_runs
173+
174+
assert "name: Contract Smoke Tests" in contracts_smoke
175+
assert 'cron: "0 6 * * *"' in contracts_smoke
176+
assert "workflow_dispatch:" in contracts_smoke
177+
assert "continue-on-error: true" in contracts_smoke
178+
assert "- run: bash scripts/contract-smoke.sh" in contracts_smoke
179+
180+
139181
def test_runtime_contract_docs_and_scripts_align_on_release_target() -> None:
140182
staging_demo = _read_repo("analysis/technical/staging-demo.md")
141183
runbook = _read_repo("RUN.md")
@@ -172,6 +214,7 @@ def test_runtime_contract_docs_and_scripts_align_on_release_target() -> None:
172214
assert "tests/test_mcp_http_surface.py" in runbook
173215
assert "tests/test_mcp_http_transport.py" in runbook
174216
assert "make spec" in runbook
217+
assert "make test-contracts" in runbook
175218
assert "docs/user-guide/cli-reference.md" in runbook
176219
assert "docs/reference/mcp-server.md" in runbook
177220

0 commit comments

Comments
 (0)