init: company-haness 설계
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
"""run_all.py — 단일 테스트 러너 (finding #18). CI·로컬 공통 진입점.
|
||||
|
||||
.claude/tests/test_*.py 를 모두 발견해 순차 실행하고, 스위트별 결과와 총계를 출력한다.
|
||||
하나라도 실패하면 exit 1(CI가 빨간불). 실행 무결성(doctor·lint_refs)도 함께 게이트한다.
|
||||
|
||||
Usage:
|
||||
CLAUDE_PROJECT_DIR="$PWD" ORGOS_WORKSPACE=_sandbox python3 .claude/tests/run_all.py
|
||||
python3 .claude/tests/run_all.py --no-preflight # doctor/lint_refs 생략(테스트만)
|
||||
|
||||
각 test_*.py 는 exit 0=통과 / 비0=실패 규약을 따른다(기존 스위트와 동일).
|
||||
"""
|
||||
import glob
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(os.path.dirname(HERE))
|
||||
PY = sys.executable
|
||||
|
||||
# CI/로컬에서 workspace 미지정 시 조용한 실패 대신 명확히 _sandbox 로 고정(운영 아님·테스트용).
|
||||
ENV = dict(os.environ)
|
||||
ENV.setdefault("CLAUDE_PROJECT_DIR", ROOT)
|
||||
ENV.setdefault("ORGOS_WORKSPACE", "_sandbox")
|
||||
|
||||
|
||||
# 스위트별 타임아웃(초). 한 테스트가 멈춰도 CI 전체가 정지하지 않게 한다(재리뷰 지적).
|
||||
SUITE_TIMEOUT = int(os.environ.get("ORGOS_TEST_TIMEOUT", "300"))
|
||||
|
||||
|
||||
def run(cmd, label):
|
||||
print(f"\n{'='*60}\n▶ {label}\n{'='*60}")
|
||||
process = None
|
||||
try:
|
||||
process = subprocess.Popen(cmd, env=ENV, start_new_session=True)
|
||||
return process.wait(timeout=SUITE_TIMEOUT) == 0
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"⏱️ TIMEOUT after {SUITE_TIMEOUT}s — {label} (fail)", file=sys.stderr)
|
||||
if process is not None:
|
||||
try:
|
||||
os.killpg(process.pid, signal.SIGTERM)
|
||||
process.wait(timeout=3)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
no_preflight = "--no-preflight" in sys.argv[1:]
|
||||
results = []
|
||||
|
||||
if not no_preflight:
|
||||
results.append(("artifact-registry", run(
|
||||
[PY, os.path.join(ROOT, ".claude/hooks/compile_artifact_registry.py"), "--check"],
|
||||
"preflight: compiled artifact registry")))
|
||||
results.append(("orgos-registry", run(
|
||||
[PY, os.path.join(ROOT, ".claude/hooks/compile_orgos_registry.py"), "--check"],
|
||||
"preflight: compiled Org OS registries + architecture views")))
|
||||
results.append(("design-system", run(
|
||||
[PY, os.path.join(ROOT, ".claude/hooks/compile_design_system.py"), "--check"],
|
||||
"preflight: compiled organization design system")))
|
||||
results.append(("doctor.py", run(
|
||||
[PY, os.path.join(ROOT, ".claude/hooks/doctor.py")], "preflight: doctor.py")))
|
||||
results.append(("lint_refs.py", run(
|
||||
[PY, os.path.join(ROOT, ".claude/hooks/lint_refs.py")], "preflight: lint_refs.py")))
|
||||
|
||||
suites = sorted(glob.glob(os.path.join(HERE, "test_*.py")))
|
||||
if not suites:
|
||||
print("no test_*.py found", file=sys.stderr)
|
||||
return 1
|
||||
for s in suites:
|
||||
name = os.path.basename(s)
|
||||
results.append((name, run([PY, s], name)))
|
||||
|
||||
print(f"\n{'='*60}\nSUMMARY\n{'='*60}")
|
||||
passed = [n for n, ok in results if ok]
|
||||
failed = [n for n, ok in results if not ok]
|
||||
for n, ok in results:
|
||||
print(f" {'✅' if ok else '❌'} {n}")
|
||||
print(f"\n{len(passed)}/{len(results)} green · failed: {failed or '없음'}")
|
||||
return 0 if not failed else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user