Skip to content

how to contribute tooling

github-actions[bot] edited this page Jun 19, 2026 · 1 revision

Tooling

The build, lint, type, container, and migration tools for the 360Ghar backend. For the workflow that uses them, see development-workflow.md. For the conventions they enforce, see patterns-and-conventions.md.

Build system

The project uses uv for dependency management. Dependencies are declared in pyproject.toml and locked in uv.lock. Runtime dependencies include FastAPI, SQLAlchemy 2.x async, Pydantic v2, httpx, APScheduler, pgvector, GeoAlchemy2, Supabase, FastMCP 3.0.1, Pydantic AI, Pillow, Cloudinary, and Sentry SDK. Dev dependencies (pytest, ruff, mypy, respx) live in the dev optional group.

uv sync                  # Runtime deps only
uv sync --extra dev      # Runtime + dev deps
uv run python run.py     # Start the API through uv's managed venv

Always prefix commands with uv run. Running python or fastapi directly uses the system Python, which lacks pgvector and other project dependencies.

Linters

Two linters run in CI and locally:

ruff

uv run ruff check app/ --output-format=github

Ruff enforces import style, type annotation modernization, exception chaining, equality, naming, whitespace, and a handful of other rules. The enforced rules:

  • I001, UP035, F401, E402from __future__ import annotations first; list/dict/set/tuple/type instead of typing.*; Callable, Awaitable, AsyncIterator, Sequence from collections.abc; no unused imports; imports before non-import code.
  • UP045, UP006, UP007, UP037X | None not Optional[X]; X | Y not Union[X, Y]; list[X] not List[X]; no unnecessary quotes in annotations.
  • B904raise NewException(...) from e or from None inside except.
  • E712 — no == True / == False on columns. Use the column directly or bitwise negation.
  • E741 — no single-letter l variable name.
  • F841 — no unused variables. Use _ or _name to discard.
  • W291, W292, W293 — no trailing whitespace, no whitespace on blank lines, files end with a newline.
  • B905zip(a, b, strict=True).
  • C401 — set comprehensions, not set(gen).
  • F541 — no f-strings without placeholders.
  • F811 — no name redefinitions.

mypy

uv run mypy app/ --ignore-missing-imports --no-error-summary

mypy runs against app/ with --ignore-missing-imports. Full type hints are expected everywhere, consistent with the ruff annotation rules.

CI

GitHub Actions (.github/workflows/tests.yml) runs on push and PR to main and develop. Three jobs:

Job What it does
test Boots PostGIS (built from .github/Dockerfile.test-db) and Redis 7, creates vector and postgis extensions, runs uv run pytest tests/ -v --cov=app --cov-report=xml --cov-fail-under=90, uploads coverage to Codecov.
lint Runs uv run ruff check app/ --output-format=github and uv run mypy app/ --ignore-missing-imports --no-error-summary.

All three must pass before merge. The test job uses uv sync --extra dev to install dev dependencies.

Docker

docker-compose.yml starts three services:

  • db — PostGIS 15
  • redis — Redis 7
  • api — the backend on hypercorn
docker-compose up -d           # Start everything
docker-compose up -d db redis  # Start only database services for local dev

The Dockerfile uses python:3.12-slim with uv sync as the entry point. Railway deploys from main via railway.toml with a healthcheck on /health and ON_FAILURE restart policy.

Supabase CLI

Database migrations live in supabase/migrations/. Use the Supabase CLI to apply and inspect them:

supabase db reset   # Reset local database
supabase db push    # Apply migrations
supabase db diff    # Check pending changes

Some lightweight DDL (enum additions, column adds) that cannot go through the Supabase CLI is applied at startup in app/infrastructure/lifespan.py. See systems/infrastructure.md for the startup migration behavior.

Scripts

The scripts/ directory holds operational scripts that are not part of the runtime:

Script Purpose
scripts/optimize_seed_images.py Reduces seed image size to shrink the Docker image. Supports --dry-run for preview.

Seed data scripts live separately in seed_data/ and are documented in development-workflow.md.

Local dev loop

A typical local loop:

docker-compose up -d db redis
cp .env.example .env          # Fill in DATABASE_URL, SUPABASE_*, REDIS_URL, AI keys
uv sync --extra dev
uv run python run.py          # API on 0.0.0.0:3600

Swagger UI at http://localhost:3600/api/v1/docs, ReDoc at http://localhost:3600/api/v1/redoc, health at http://localhost:3600/health.

Where to go next

Clone this wiki locally