#!/usr/bin/env python3 """Run repository validation commands for the Harness template.""" from __future__ import annotations import json import os import subprocess import sys from pathlib import Path DEFAULT_NPM_ORDER = ("lint", "build", "test") 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 quote_path(path: Path) -> str: return f'"{path}"' def preferred_python(root: Path) -> Path: candidates = [ root / "venv" / "python.exe", root / "venv" / "Scripts" / "python.exe", root / ".venv" / "Scripts" / "python.exe", root / ".venv" / "bin" / "python", ] for candidate in candidates: if candidate.exists(): return candidate return Path(sys.executable) def load_python_commands(root: Path) -> list[str]: scripts_dir = root / "scripts" if not scripts_dir.exists(): return [] python = quote_path(preferred_python(root)) py_compile_targets = [ path for path in [ scripts_dir / "execute.py", scripts_dir / "validate_workspace.py", ] if path.exists() ] commands: list[str] = [] if py_compile_targets: rel_targets = " ".join(str(path.relative_to(root)) for path in py_compile_targets) commands.append(f"{python} -m py_compile {rel_targets}") test_targets = sorted(path for path in scripts_dir.glob("test_*.py") if path.is_file()) tests_dir = root / "tests" if tests_dir.exists(): test_targets.append(tests_dir) if test_targets: rel_tests = " ".join(str(path.relative_to(root)) for path in test_targets) commands.append(f"{python} -m pytest {rel_tests}") return commands def discover_commands(root: Path) -> list[str]: env_commands = load_env_commands() if env_commands: return env_commands npm_commands = load_npm_commands(root) if npm_commands: return npm_commands return load_python_commands(root) 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 commands = discover_commands(root) if not commands: print("No validation commands configured.") print("Set HARNESS_VALIDATION_COMMANDS or add npm scripts for lint/build/test.") 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())