Skip to content

Commit cc82f5d

Browse files
committed
test: add runtime smoke test and run it in CI
1 parent 72c1c4d commit cc82f5d

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,14 @@ jobs:
8787
fi
8888
8989
echo "Smoke checks passed"
90+
91+
- name: Run repository runtime smoke test
92+
shell: bash
93+
run: |
94+
set -euo pipefail
95+
if [ -f tests/run_runtime_smoke.py ]; then
96+
python tests/run_runtime_smoke.py
97+
else
98+
echo "No runtime smoke test file found"
99+
exit 1
100+
fi

tests/run_runtime_smoke.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python3
2+
"""Repository runtime smoke test.
3+
4+
This test intentionally avoids external services and validates:
5+
1) repo has at least one runnable/build marker
6+
2) Python source files in repo compile successfully
7+
"""
8+
9+
from __future__ import annotations
10+
11+
from pathlib import Path
12+
import py_compile
13+
import sys
14+
15+
REPO_ROOT = Path(__file__).resolve().parents[1]
16+
17+
SKIP_DIRS = {
18+
".git",
19+
".venv",
20+
"venv",
21+
"node_modules",
22+
"dist",
23+
"build",
24+
"__pycache__",
25+
}
26+
27+
28+
def has_runnable_marker(root: Path) -> bool:
29+
markers = [
30+
"package.json",
31+
"pyproject.toml",
32+
"requirements.txt",
33+
"setup.py",
34+
"manage.py",
35+
"pom.xml",
36+
"build.gradle",
37+
"build.gradle.kts",
38+
"go.mod",
39+
"Cargo.toml",
40+
"Dockerfile",
41+
"docker-compose.yml",
42+
"docker-compose.yaml",
43+
"Makefile",
44+
]
45+
for marker in markers:
46+
if list(root.rglob(marker)):
47+
return True
48+
49+
if list(root.rglob("*.csproj")) or list(root.rglob("*.sln")):
50+
return True
51+
52+
return False
53+
54+
55+
def iter_python_files(root: Path):
56+
for py_file in root.rglob("*.py"):
57+
rel = py_file.relative_to(root)
58+
if any(part in SKIP_DIRS for part in rel.parts):
59+
continue
60+
yield py_file
61+
62+
63+
def main() -> int:
64+
if not has_runnable_marker(REPO_ROOT):
65+
print("FAIL: no runnable/build marker found")
66+
return 1
67+
68+
py_files = list(iter_python_files(REPO_ROOT))
69+
if not py_files:
70+
print("PASS: no python files to compile; marker checks passed")
71+
return 0
72+
73+
failures = []
74+
for py_file in py_files:
75+
try:
76+
py_compile.compile(str(py_file), doraise=True)
77+
except py_compile.PyCompileError as exc:
78+
failures.append((py_file, exc.msg))
79+
80+
if failures:
81+
print(f"FAIL: {len(failures)} python files failed to compile")
82+
for py_file, msg in failures[:20]:
83+
rel = py_file.relative_to(REPO_ROOT)
84+
print(f" - {rel}: {msg}")
85+
return 1
86+
87+
print(f"PASS: compiled {len(py_files)} python files")
88+
return 0
89+
90+
91+
if __name__ == "__main__":
92+
sys.exit(main())

0 commit comments

Comments
 (0)