Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions .github/workflows/contract.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Verifies the API contract between frontend and backend by booting the real
# backend and hitting the endpoints the frontend uses (frontend/src/api/backend.ts).
# Verifies the contract between frontend and backend, two ways:
# 1. structurally — the committed API types (frontend/src/api/schema.d.ts)
# must match the OpenAPI schema FastAPI generates today, so any change to
# an endpoint, parameter or response model shows up here;
# 2. behaviourally — the real backend answers the endpoints the frontend calls
# (frontend/src/api/backend.ts).
# This is what makes automated backend syncs from python-copier-template safe:
# a template change that breaks the contract turns this check red.
name: Contract
Expand All @@ -22,10 +26,35 @@ jobs:
with:
working-directory: backend

- uses: actions/setup-node@v7
with:
node-version-file: frontend/.nvmrc
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install backend dependencies
working-directory: backend
run: uv sync

- name: Install frontend dependencies
working-directory: frontend
run: npm ci

# Fails if backend/app/api.py changed without running `make generate-types`.
# Uses git status, not git diff: the latter ignores an untracked or deleted
# file and would pass vacuously.
- name: API types match the backend schema
run: |
make generate-types
if [ -n "$(git status --porcelain -- frontend/src/api/schema.d.ts)" ]; then
echo "::error::frontend/src/api/schema.d.ts is stale or missing — run 'make generate-types' and commit the result"
git diff -- frontend/src/api/schema.d.ts
exit 1
fi

- name: Start backend
working-directory: backend
run: |
uv sync
uv run uvicorn app.api:app --port 7000 &
timeout 30 bash -c 'until curl -sf http://localhost:7000/health > /dev/null; do sleep 1; done'

Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: install dev-frontend dev-backend test lint build up down
.PHONY: install dev-frontend dev-backend test lint generate-types build up down

# Convenience targets delegating to the two projects. See frontend/README.md
# and backend/README.md for the full command reference of each side.
Expand All @@ -23,6 +23,13 @@ lint:
npm --prefix frontend run format:check
cd backend && uv run ruff check . && uv run ruff format --check . && uv run ty check app tests

# Regenerate the frontend's API types from the backend's OpenAPI schema.
# Run this after changing backend/app/api.py; CI fails if the committed types
# are stale (see .github/workflows/contract.yml).
generate-types:
cd backend && uv run python -c "import json; from app.api import app; print(json.dumps(app.openapi()))" > ../frontend/openapi.json
npm --prefix frontend run generate-types

build:
npm --prefix frontend run build

Expand Down
3 changes: 3 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ npm-debug.log*
# TypeScript / tooling caches
*.tsbuildinfo
.eslintcache

# Generated on demand by `make generate-types`
openapi.json
1 change: 1 addition & 0 deletions frontend/.prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
coverage
package-lock.json
src/api/schema.d.ts
Loading