Contributing¶
Prerequisites¶
| Tool | Version | Notes |
|---|---|---|
| Python | 3.12+ | |
| uv | latest | brew install uv on macOS. Use uv, not pip. |
| Node.js | 20+ | Frontend only |
| Docker | 20.10+ | Integration tests and local Postgres |
Setting up locally¶
git clone https://github.com/schinnam/lingo
cd lingo
# Start Postgres
docker compose up postgres -d
# Install dependencies and run migrations
uv sync
LINGO_DEV_MODE=true uv run alembic upgrade head
# Start the server
LINGO_DEV_MODE=true uv run uvicorn lingo.main:app --reload
Server: http://localhost:8000 — LINGO_DEV_MODE=true disables Slack auth so you can test locally. Log in via http://localhost:8000/auth/dev/login?email=you@example.com instead of going through Slack.
Frontend development¶
Two modes:
Note
If you test via http://localhost:8000 without rebuilding, you'll see stale output.
Running tests¶
Test approach¶
Follow red/green TDD: write a failing test first, then implement. PRs without tests for new behavior will be asked to add them before merge.
- Backend unit tests —
tests/unit/, in-memory SQLite, no Docker required, fast - Backend integration tests —
tests/integration/, real Postgres via Docker asyncio_mode = "auto"— already set inpyproject.toml; don't add@pytest.mark.asyncioto every testLINGO_DEV_MODE=true— set automatically in test fixtures;X-User-Idauth works in tests, not in production
Making changes¶
Backend¶
- Keep async throughout — all database calls go through SQLAlchemy async sessions.
-
New database columns require an Alembic migration:
Review the generated file before committing — autogenerate misses check constraints and PostgreSQL-specific types. - The scheduler runs in-process; keep scheduled jobs stateless and idempotent. - New API endpoints must require authentication. Do not add unauthenticated endpoints without a strong reason.
Frontend¶
- Components live in
frontend/src/components/. Data fetching goes through TanStack Query hooks infrontend/src/hooks/useTerms.ts. - Run
npm run lintbefore pushing — ESLint is strict on React hooks rules. - After UI changes, run
npm run buildto regeneratesrc/lingo/static/. Commit the built output.
Migrations¶
Always write both upgrade() and downgrade() in migrations. Missing downgrade() that drops enum types causes "type already exists" errors on re-upgrade.
Submitting a PR¶
- Branch off
main. - Keep PRs focused — one logical change per PR.
- Update
CHANGELOG.mdunder## [Unreleased]following the Keep a Changelog format. - Ensure
make testpasses locally.
Key gotchas¶
| Gotcha | Why it matters |
|---|---|
Use uv run for all Python commands |
Ensures the managed virtualenv is used, not system Python |
lingo CLI not on PATH after uv pip install -e . |
uv pip install installs into the project venv, not your global shell. Use uv run lingo or uv tool install . |
lingo export shows empty output |
Default status filter is official. Use --status pending to see terms that haven't been promoted yet. |
LINGO_DEV_MODE=true is dev-only |
Never set this in production — it disables auth entirely |
--workers 1 in production |
APScheduler runs in-process; multiple workers cause duplicate job execution |
| Rebuild frontend after UI changes | src/lingo/static/ is served directly by FastAPI — stale builds cause confusing behavior |