-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
114 lines (81 loc) · 4.79 KB
/
Copy pathMakefile
File metadata and controls
114 lines (81 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
.DEFAULT_GOAL := help
SHELL := /bin/bash
# Load .env if it exists — set -a exports all sourced vars to subprocesses
ifneq (,$(wildcard .env))
ENV_PREFIX := set -a && . ./.env && set +a &&
endif
# ── Auto-install sentinels ──────────────────────────────────────────
.installed-py: pyproject.toml uv.lock
uv sync --extra dev
@touch $@
frontend/node_modules/.installed: frontend/package.json
cd frontend && npm install
@touch $@
# ── Dev servers ─────────────────────────────────────────────────────
BACKEND_PORT ?= 8501
FRONTEND_PORT ?= 3500
# Kill anything on a given port.
# Uses taskkill /T to kill the entire process tree (prevents zombie children on Windows).
define kill-port
@if [ -f .$(1).pid ]; then \
taskkill //F //T //PID $$(cat .$(1).pid) 2>/dev/null; rm -f .$(1).pid; \
fi; \
if netstat -ano 2>/dev/null | grep -q ':$(2).*LISTEN'; then \
powershell -Command "Get-NetTCPConnection -LocalPort $(2) -ErrorAction SilentlyContinue | ForEach-Object { taskkill /F /T /PID \$$_.OwningProcess 2>\$$null }" 2>/dev/null; \
for i in 1 2 3 4 5; do \
netstat -ano 2>/dev/null | grep -q ':$(2).*LISTEN' || break; \
sleep 1; \
done; \
fi
endef
.PHONY: backend frontend dev
backend: .installed-py ## Start FastAPI backend
$(call kill-port,backend,$(BACKEND_PORT))
$(ENV_PREFIX) PYTHONUNBUFFERED=1 uv run uvicorn backend.main:app --reload --reload-exclude .venv --log-level $$(echo $${TELL_LOG_LEVEL:-INFO} | tr A-Z a-z) --port $(BACKEND_PORT)
frontend: frontend/node_modules/.installed ## Start Next.js dev server
$(call kill-port,frontend,$(FRONTEND_PORT))
$(ENV_PREFIX) cd frontend && PORT=$(FRONTEND_PORT) npm run dev
dev: .installed-py frontend/node_modules/.installed ## Start backend + frontend together
$(call kill-port,backend,$(BACKEND_PORT))
$(call kill-port,frontend,$(FRONTEND_PORT))
@trap 'taskkill //F //T //PID $$(cat .backend.pid) 2>/dev/null; rm -f .backend.pid; kill 0 2>/dev/null' EXIT; \
$(ENV_PREFIX) PYTHONUNBUFFERED=1 uv run uvicorn backend.main:app --reload --reload-exclude .venv --log-level $$(echo $${TELL_LOG_LEVEL:-INFO} | tr A-Z a-z) --port $(BACKEND_PORT) & \
echo $$! > .backend.pid; \
$(ENV_PREFIX) cd frontend && PORT=$(FRONTEND_PORT) npm run dev
# ── Test ────────────────────────────────────────────────────────────
.PHONY: test
test: .installed-py ## Run all tests
uv run pytest tests/ -x -q
coverage: .installed-py ## Run tests with coverage report
uv run pytest tests/ --cov=backend --cov=schemas --cov-report=term-missing -q
# ── Lint / Format ──────────────────────────────────────────────────
.PHONY: lint
lint: .installed-py ## Lint + format Python (ruff check --fix + ruff format)
uv run ruff check --fix .
uv run ruff format .
# ── Type Check ─────────────────────────────────────────────────
.PHONY: typecheck
typecheck: .installed-py ## Run mypy type checker
uv run mypy backend/ schemas/
# ── Codegen ────────────────────────────────────────────────────
.PHONY: codegen
codegen: .installed-py frontend/node_modules/.installed ## Regenerate frontend types from backend OpenAPI spec
uv run python scripts/export-openapi.py
cd frontend && npm run codegen
# ── Stop ──────────────────────────────────────────────────────────
.PHONY: down
down: ## Kill dev servers
$(call kill-port,backend,$(BACKEND_PORT))
$(call kill-port,frontend,$(FRONTEND_PORT))
@echo "Stopped"
# ── Clean ───────────────────────────────────────────────────────────
.PHONY: clean
clean: ## Remove caches and build artifacts
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
rm -rf frontend/.next frontend/node_modules/.cache
rm -f .installed-py
# ── Help ────────────────────────────────────────────────────────────
.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'