Skip to main content

CLI (components/cli/)

Development CLI for the Polo monorepo. Single entry point for local dev, testing, building, and deploying.

Design Decisions

Why a CLI instead of a Makefile:

  • Subcommand groups (polo test unit, polo deploy worker) are more discoverable than flat make targets
  • Click gives us --help for free at every level
  • Options with defaults and validation (e.g. --env staging, --days 30)
  • Python — same language as the rest of the backend, easy to extend with logic when a command outgrows a shell one-liner
  • Makefile syntax is arcane for anything beyond trivial recipes (eval/shell timing, escaping, no real control flow)

Thin wrapper, not a framework:

  • Commands delegate to existing tools (pytest, wrangler, docker compose) via subprocess.run
  • No abstraction layer over the underlying commands — if pytest args change, the CLI command changes
  • _run() is the only helper: runs a command, exits with its return code
  • No config files, no plugins, no registries

Subcommand structure:

  • Top-level commands for daily-driver operations: up, down, migrate, seed, sync, health
  • test group for test suites: schema, unit, integration, perf, e2e, all
  • build group for artifacts: ui
  • deploy group for deployment: worker
  • Groups exist where there are (or will be) multiple related subcommands

Command Reference

Local Development

CommandDescription
polo upStart ClickHouse via Docker Compose
polo downStop ClickHouse
polo migrateApply schema migrations to local ClickHouse
polo migrate-remote <id>Run migrations against remote CH via SSM port forwarding
polo seedSeed test data into local ClickHouse
polo syncSnapshot production data into local instance
polo healthRun repo health checks

Testing

CommandDescription
polo test schemaSchema tests (requires CH)
polo test unitUnit tests (no CH needed)
polo test integrationIntegration tests (requires CH)
polo test perfPerformance benchmarks
polo test e2eE2E tests (requires AWS + CH)
polo test allAll of the above in order

Build & Deploy

CommandDescription
polo build uiBuild the React SPA
polo deploy worker [--env staging]Deploy the Cloudflare Worker

Adding Commands

New commands go in components/cli/commands.py. Pattern:

@polo.command()
@click.option("--flag", default="value", help="Description.")
def my_command(flag: str) -> None:
"""One-line description shown in --help."""
_run(["some-tool", "--flag", flag])

For a new subcommand under an existing group:

@deploy.command("infra")
@click.option("--env", default="staging")
def deploy_infra(env: str) -> None:
"""Deploy AWS infrastructure via CDK."""
_run(["cdk", "deploy", "--all"], env={"ENV": env})

Guidelines:

  • Keep commands thin — delegate to the underlying tool
  • Use _run() for subprocess execution (handles exit codes)
  • Use cwd parameter when a command must run from a specific directory
  • Use env parameter for environment variable overrides