#!/usr/bin/env python3 """Run repository validation commands for the Report Harness template.""" from __future__ import annotations import json import os import subprocess import sys from pathlib import Path DEFAULT_NPM_ORDER = ("lint", "build", "test") REQUIRED_DOCS = ( "PRD.md", "ARCHITECTURE.md", "ADR.md", "UI_GUIDE.md", "RESEARCH_LOG.md", "REPORT_DRAFT.md", "FEEDBACK.md", ) REQUIRED_SECTIONS = { "PRD.md": ( "## 보고서 주제", "## 용도", "## 핵심 키워드", "## 방향성", ), "RESEARCH_LOG.md": ( "## 출처 목록", "## 검증 필요", ), "REPORT_DRAFT.md": ( "## Executive Summary", "## 참고 출처", "## 사용자 피드백 질문", ), "FEEDBACK.md": ( "## 다음 반복에서 확인할 사항", ), } def load_env_commands() -> list[str]: raw = os.environ.get("HARNESS_VALIDATION_COMMANDS", "") return [line.strip() for line in raw.splitlines() if line.strip()] def load_npm_commands(root: Path) -> list[str]: package_json = root / "package.json" if not package_json.exists(): return [] try: payload = json.loads(package_json.read_text(encoding="utf-8")) except json.JSONDecodeError: return [] scripts = payload.get("scripts", {}) if not isinstance(scripts, dict): return [] commands = [] for name in DEFAULT_NPM_ORDER: value = scripts.get(name) if isinstance(value, str) and value.strip(): commands.append(f"npm run {name}") return commands def discover_commands(root: Path) -> list[str]: env_commands = load_env_commands() if env_commands: return env_commands return load_npm_commands(root) def validate_report_docs(root: Path) -> list[str]: errors: list[str] = [] docs_dir = root / "docs" if not docs_dir.is_dir(): return ["Missing docs/ directory."] for filename in REQUIRED_DOCS: path = docs_dir / filename if not path.exists(): errors.append(f"Missing docs/{filename}.") continue text = path.read_text(encoding="utf-8") if not text.strip(): errors.append(f"docs/{filename} is empty.") continue for heading in REQUIRED_SECTIONS.get(filename, ()): if heading not in text: errors.append(f"docs/{filename} missing section: {heading}") return errors def run_command(command: str, root: Path) -> subprocess.CompletedProcess: return subprocess.run( command, cwd=root, shell=True, capture_output=True, text=True, ) def emit_stream(prefix: str, content: str, *, stream) -> None: text = content.strip() if not text: return print(prefix, file=stream) print(text, file=stream) def main() -> int: root = Path(__file__).resolve().parent.parent doc_errors = validate_report_docs(root) if doc_errors: print("Report template validation failed:", file=sys.stderr) for error in doc_errors: print(f"- {error}", file=sys.stderr) return 1 commands = discover_commands(root) if not commands: print("Report template validation succeeded.") print("No extra validation commands configured.") print("Set HARNESS_VALIDATION_COMMANDS or add npm scripts for lint/build/test if needed.") return 0 for command in commands: print(f"$ {command}") result = run_command(command, root) emit_stream("[stdout]", result.stdout, stream=sys.stdout) emit_stream("[stderr]", result.stderr, stream=sys.stderr) if result.returncode != 0: print(f"Validation failed: {command}", file=sys.stderr) return result.returncode print("Validation succeeded.") return 0 if __name__ == "__main__": raise SystemExit(main())