diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 571fda5..f2e0e48 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -1,6 +1,5 @@ -# Checks for backend/, which is rendered from python-copier-template -# (see docs/backend-sync.md). Kept as a thin repo-owned workflow because the -# rendered workflows assume the project sits at the repository root. +# Checks for backend/. Kept as a repo-owned workflow because the ones rendered +# from python-copier-template assume the project sits at the repository root. name: Backend on: diff --git a/CLAUDE.md b/CLAUDE.md index 5c7adf7..b5a88ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,33 +1,18 @@ # CLAUDE.md -Fullstack template: React frontend (`frontend/`) + FastAPI backend (`backend/`). - -## Ownership model — IMPORTANT - -- `backend/` is a **rendered mirror** of - [python-copier-template](https://github.com/Komorebi-AI/python-copier-template) - (answers in `backend/.copier-answers.yml`). Do NOT edit files under - `backend/` in this repo: a sync job re-renders the template and replaces the - whole directory, wiping local changes. Backend changes belong in - python-copier-template. Exceptions applied by the sync itself: rendered - `.github/` is dropped, and `[tool.setuptools_scm] root = ".."` is set in - `backend/pyproject.toml`. Files marked `TEMPORARY` carry patches for known - upstream bugs. See `docs/backend-sync.md`. -- `frontend/` and everything at the repo root are owned by this repo and - edited normally. +React frontend (`frontend/`) + FastAPI backend (`backend/`). ## Commands -- Frontend (`cd frontend`): npm. `npm run dev` (port 5173), `npm test`, +- Frontend (`cd frontend`): npm. `npm run dev` (5173), `npm test`, `npm run lint`, `npm run build`. -- Backend (`cd backend`): uv, never pip. `uv run python app/api.py` (port - 7000), `uv run pytest`, `uv run ruff check .`, `uv run ty check app tests`. -- Full stack: `docker compose up --build` (port 8080). Root `Makefile` has - delegating targets. +- Backend (`cd backend`): uv, never pip. `uv run python app/api.py` (7000), + `uv run pytest`, `uv run ruff check .`, `uv run ty check app tests`. +- Both: root `Makefile` delegates (`make test`, `make lint`, `make up`). -## API contract +## Conventions -The frontend example calls `GET /api/` (version) and `POST /api/predict`; -both proxies (Vite dev, nginx) strip the `/api` prefix before the backend. -If a template sync changes `backend/app/api.py`, update -`frontend/src/api/backend.ts` and its tests to match. +- The frontend calls `/api/*`; both proxies (Vite dev, nginx) strip the prefix + before the backend. Keep endpoint wrappers in `frontend/src/api/`, typed and + matching `backend/app/api.py`. +- Keep `GET /health`: the compose healthcheck and CI depend on it. diff --git a/README.md b/README.md index 342407f..7569e59 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,20 @@ Fullstack template at Komorebi AI: a **React frontend** (`frontend/`) paired with a **FastAPI backend** (`backend/`). Use it as the starting point for technical tests, -experiments and app frontends — delete `backend/` if you only need the frontend. +experiments and apps. + +Need only one half? Delete `frontend/` or `backend/` — or start from +[python-template](https://github.com/Komorebi-AI/python-template) for a backend-only +project. ## Structure ``` frontend/ React + TypeScript + Vite + MUI + TanStack Query → frontend/README.md -backend/ FastAPI example, rendered from python-copier-template (do not edit here!) -compose.yaml, Makefile, .github/ → repo-owned glue +backend/ FastAPI + uv, ruff, ty, pytest → backend/README.md +compose.yaml, Makefile, .github/ full-stack glue ``` -**Ownership model**: `backend/` is a rendered mirror of -[python-copier-template](https://github.com/Komorebi-AI/python-copier-template) and is -replaced wholesale by an automated sync — backend changes belong in that template, not -here. See [docs/backend-sync.md](docs/backend-sync.md). Everything else is owned and -edited in this repo. - ## Quickstart Requirements: [Node 24](https://nodejs.org/) (see `frontend/.nvmrc`) and @@ -53,10 +51,10 @@ make test # frontend Vitest + backend pytest make lint # ESLint/Prettier + ruff/ty ``` -CI runs three path-filtered workflows: **Frontend** (lint, format, test, build), -**Backend** (ruff, ty, pytest) and **Contract** — the latter boots the real backend and -exercises the endpoints the frontend uses, so a backend sync that breaks the API -contract fails visibly. +CI runs path-filtered workflows: **Frontend** (lint, format, test, build), **Backend** +(ruff, ty, pytest), **Contract** — boots the real backend and exercises the endpoints +the frontend calls, so frontend/backend drift fails visibly — and **Docker** (builds +both images and checks the same contract through nginx). ## API contract @@ -76,9 +74,13 @@ endpoints, since the compose healthcheck and CI smoke tests rely on it. ## Starting a project from this template 1. Use GitHub's **"Use this template"** (or clone and re-init git). -2. Rename things: `frontend/package.json` name, `backend` answers if you re-render. +2. Rename things: `frontend/package.json` name, `backend/Makefile` image name, + `backend/pyproject.toml` name and description. 3. Replace the example page and endpoints with your app. Keep the patterns: - typed API wrappers, useQuery/useMutation, the `/api` prefix convention. -4. The backend keeps its link to python-copier-template via - `backend/.copier-answers.yml` — run `copier update` inside `backend/` to pull - future template improvements into your (by then customized) backend. + typed API wrappers, useQuery/useMutation, the `/api` prefix convention, and the + `/health` endpoint the compose healthcheck and CI rely on. + +--- + +Maintaining this template itself: `backend/` is regenerated automatically, so changes +under it are overwritten — make them in our Python template instead. diff --git a/backend/.copier-answers.yml b/backend/.copier-answers.yml deleted file mode 100644 index 78a0c47..0000000 --- a/backend/.copier-answers.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Changes here will be overwritten by Copier -_commit: d788f09 -_src_path: https://github.com/Komorebi-AI/python-copier-template.git -github_repo: react-template -include_api: true -include_cli: false -include_docker: true -package_name: app -project_description: FastAPI backend for react-template -project_name: React Template Backend -project_type: application -python_version: '3.14' diff --git a/docs/backend-sync.md b/docs/backend-sync.md deleted file mode 100644 index ac973ce..0000000 --- a/docs/backend-sync.md +++ /dev/null @@ -1,172 +0,0 @@ -# Backend sync from python-copier-template - -`backend/` is not hand-written: it is rendered from -[python-copier-template](https://github.com/Komorebi-AI/python-copier-template), the -same way [python-template](https://github.com/Komorebi-AI/python-template) is. A CI job -in python-copier-template re-renders it and opens a PR here whenever the template -changes, replacing `backend/` **wholesale** — which is why backend files must never be -edited in this repo. - -## Deterministic render recipe - -The sync renders with the answers recorded in `backend/.copier-answers.yml` and applies -three post-render adjustments: - -1. **Drop the rendered `.github/`** — workflows are repo-owned here - (`.github/workflows/backend.yml` + `contract.yml`), because the rendered ones assume - the project sits at the repository root. -2. **Set `[tool.setuptools_scm] root = ".."`** in `backend/pyproject.toml` — the - backend's pyproject is not at the git root, so the version must be derived from this - repo's metadata. (Cleaner long-term fix: an `scm_root` question in the template.) -3. **Point `_src_path`** in `.copier-answers.yml` at the template's GitHub URL and keep - the file — projects created from react-fastapi-template use it to run `copier update` on - their own backends. - -Then `uv lock` (with `SETUPTOOLS_SCM_PRETEND_VERSION`) and `uvx sync-with-uv`, exactly -like the python-template sync. - -## Known temporary deviations (pending upstream fixes) - -> **Status**: both fixes (plus the sync workflow below and a rendered `docker.yml` -> smoke-test workflow) are in -> [python-copier-template#41](https://github.com/Komorebi-AI/python-copier-template/pull/41). -> Merge order matters: land **this repo's PR first**, then #41 — its first sync run -> renders `backend/` against this repo's `main`, which must already contain it. - -Two bugs were found in the rendered output while building this repo; both are patched -locally in `backend/` with `TEMPORARY` comments and must be fixed in -python-copier-template **before** the sync workflow is enabled (a sync would revert the -local patches and break this repo): - -1. **Dockerfile base images**: the pinned `uv:-python3.14-bookworm-slim` tag does - not exist upstream (Python 3.14 uv images ship on trixie). Patched to - `...-trixie-slim` (builder and runtime). Affects python-template as well — its - Docker image cannot build. -2. **PyYAML missing**: uvicorn needs PyYAML to load `log_conf.yaml` via `--log-config`, - but it is not in the rendered dependencies — the Docker CMD and the - `python app/api.py` dev entrypoint both crash. Patched by adding `pyyaml` to - dependencies (plus the deptry DEP002 ignore). Affects python-template as well. - -## Safety - -Auto-merge relies on branch protection requiring the **Backend** and **Contract** -checks. The Contract workflow boots the rendered backend and exercises the endpoints -the frontend depends on, so a template change that breaks the API contract produces a -red PR instead of a silent break. - -## Workflow for python-copier-template - -Ready to drop in as `.github/workflows/sync-react-fastapi-template.yml` (mirrors the existing -`sync-template.yml`; requires the same `GH_TOKEN` secret): - -```yaml -name: sync-react-fastapi-template - -on: - push: - branches: [main] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Checkout copier template - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 - - - name: Install copier - run: uv tool install copier - - - name: Render template - run: | - copier copy --defaults --vcs-ref=HEAD \ - --data project_name="React Template Backend" \ - --data project_description="FastAPI backend for react-fastapi-template" \ - --data package_name="app" \ - --data github_repo="react-fastapi-template" \ - --data project_type="application" \ - --data python_version="3.14" \ - --data include_api=true \ - --data include_cli=false \ - --data include_docker=true \ - . /tmp/rendered - - # backend/ is a subdirectory of react-fastapi-template, not a repo root: its - # workflows are owned by react-fastapi-template (rendered ones assume repo root) - # and its version must derive from react-fastapi-template's git metadata. - # Keep .copier-answers.yml (pointed at this repo's URL) so projects - # created from react-fastapi-template can `copier update` their backends. - - name: Adjust render for subdirectory embedding - run: | - rm -rf /tmp/rendered/.github - sed -i 's|^\[tool.setuptools_scm\]$|[tool.setuptools_scm]\n# Backend lives in a subdirectory of the react-fastapi-template repo; version comes\n# from the repo root git metadata (applied by the template sync job)\nroot = ".."|' \ - /tmp/rendered/pyproject.toml - sed -i 's|^_src_path:.*|_src_path: https://github.com/Komorebi-AI/python-copier-template.git|' \ - /tmp/rendered/.copier-answers.yml - - - name: Generate lock file - run: uv lock - working-directory: /tmp/rendered - env: - SETUPTOOLS_SCM_PRETEND_VERSION: "0.0.0" - - - name: Sync pre-commit hook revs with uv.lock - run: uvx sync-with-uv - working-directory: /tmp/rendered - - - name: Checkout react-fastapi-template - uses: actions/checkout@v6 - with: - repository: Komorebi-AI/react-fastapi-template - token: ${{ secrets.GH_TOKEN }} - path: react-fastapi-template - - - name: Replace backend/ - run: | - rm -rf react-fastapi-template/backend - mkdir react-fastapi-template/backend - cp -r /tmp/rendered/. react-fastapi-template/backend/ - - - name: Check for changes - id: changes - working-directory: react-fastapi-template - run: | - git add -A - if git diff --cached --quiet; then - echo "has_changes=false" >> "$GITHUB_OUTPUT" - else - echo "has_changes=true" >> "$GITHUB_OUTPUT" - fi - - - name: Create pull request - if: steps.changes.outputs.has_changes == 'true' - working-directory: react-fastapi-template - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - BRANCH="sync/copier-template-backend" - git checkout -B "$BRANCH" - git commit -m "sync: update backend/ from python-copier-template" - - git push -u origin "$BRANCH" --force - - if ! gh pr list --head "$BRANCH" --json number --jq '.[0].number' | grep -q .; then - gh pr create \ - --title "sync: update backend/ from python-copier-template" \ - --body "Automated sync of backend/ from [python-copier-template](https://github.com/Komorebi-AI/python-copier-template)." - fi - - # Requires react-fastapi-template branch protection with the Backend and - # Contract checks required, and "Allow auto-merge" enabled. - gh pr merge "$BRANCH" --auto --squash -```