| |
| """Project task runner. |
| |
| This is an optional local wrapper around direct uv commands. The demo server |
| still runs directly through app.py. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import shutil |
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| DEFAULT_ENV = { |
| "UV_PROJECT_ENVIRONMENT": ".venv", |
| "UV_CACHE_DIR": ".uv-cache", |
| "UV_LINK_MODE": "copy", |
| } |
| CLEAN_PATHS = [ |
| ".pytest_cache", |
| "arena/__pycache__", |
| "tests_python/__pycache__", |
| ] |
|
|
|
|
| def main(argv: list[str] | None = None) -> int: |
| parser = argparse.ArgumentParser(description="Run project tasks") |
| parser.add_argument("task", choices=sorted(_tasks())) |
| parser.add_argument("args", nargs=argparse.REMAINDER) |
| parsed = parser.parse_args(argv) |
|
|
| env = task_env() |
| task_args = _strip_separator(parsed.args) |
| if parsed.task == "clean": |
| clean() |
| return 0 |
|
|
| if parsed.task == "verify": |
| return verify(env, task_args) |
|
|
| command, overrides = command_for_invocation(parsed.task, task_args) |
| run_env = env | overrides |
| return subprocess.run(command + task_args, cwd=ROOT, env=run_env, check=False).returncode |
|
|
|
|
| def task_env() -> dict[str, str]: |
| env = dict(os.environ) |
| _load_dotenv(ROOT / ".env", env) |
| for key, value in DEFAULT_ENV.items(): |
| env.setdefault(key, value) |
| return env |
|
|
|
|
| def command_for(task: str) -> tuple[list[str], dict[str, str]]: |
| command = _tasks().get(task) |
| if command is None: |
| raise ValueError(f"unknown task: {task}") |
| return command |
|
|
|
|
| def command_for_invocation(task: str, args: list[str]) -> tuple[list[str], dict[str, str]]: |
| command, overrides = command_for(task) |
| if task == "test" and args: |
| return ["uv", "run", "pytest"], overrides |
| return command, overrides |
|
|
|
|
| def clean() -> None: |
| for relative_path in CLEAN_PATHS: |
| shutil.rmtree(ROOT / relative_path, ignore_errors=True) |
|
|
|
|
| def verify(env: dict[str, str], args: list[str]) -> int: |
| if args: |
| raise ValueError("verify does not accept extra arguments") |
| for command, overrides in _verify_commands(): |
| result = subprocess.run(command, cwd=ROOT, env=env | overrides, check=False) |
| if result.returncode != 0: |
| return result.returncode |
| return 0 |
|
|
|
|
| def _verify_commands() -> list[tuple[list[str], dict[str, str]]]: |
| return [ |
| (["uv", "run", "pytest", "tests_python"], {}), |
| ( |
| [ |
| "uv", |
| "run", |
| "python", |
| "-m", |
| "arena.harness", |
| "--prompt", |
| "Build a tiny CLI", |
| "--test", |
| "test -f README.md", |
| ], |
| {}, |
| ), |
| ] |
|
|
|
|
| def _tasks() -> dict[str, tuple[list[str], dict[str, str]]]: |
| return { |
| "dev": ( |
| ["uv", "run", "uvicorn", "app:app", "--host", "127.0.0.1", "--port", "8790"], |
| {}, |
| ), |
| "gradio": (["uv", "run", "python", "app.py"], {}), |
| "harness": (["uv", "run", "python", "-m", "arena.harness"], {}), |
| "test": (["uv", "run", "pytest", "tests_python"], {}), |
| "verify": ([], {}), |
| "smoke": (["uv", "run", "python", "-m", "arena.smoke", "agent"], {}), |
| "validator-smoke": (["uv", "run", "python", "-m", "arena.smoke", "validator"], {}), |
| "daytona-validator-smoke": ( |
| ["uv", "run", "python", "-m", "arena.smoke", "validator"], |
| {"VALIDATOR_SANDBOX_PROVIDER": "daytona"}, |
| ), |
| "clean": ([], {}), |
| } |
|
|
|
|
| def _load_dotenv(path: Path, env: dict[str, str]) -> None: |
| if not path.exists(): |
| return |
| for raw_line in path.read_text().splitlines(): |
| line = raw_line.strip() |
| if not line or line.startswith("#") or "=" not in line: |
| continue |
| key, value = line.split("=", 1) |
| key = key.strip() |
| value = value.strip().strip("\"'") |
| if key and key not in env: |
| env[key] = value |
|
|
|
|
| def _strip_separator(args: list[str]) -> list[str]: |
| if args and args[0] == "--": |
| return args[1:] |
| return args |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main(sys.argv[1:])) |
|
|