# Makefile - Ultiplate Template .PHONY: help init bootstrap venv deps lock envlink fmt lint type test clean doctor .PHONY: up down logs ps rebuild .PHONY: dev run.webapp run.webapp.simple run.backend run.worker run.ml .PHONY: lift lift.minio lift.tensorboard lift.mlflow lift.logging lift.database .PHONY: etl train infer seed .PHONY: nx.graph nx.projects nx.affected .DEFAULT_GOAL := help WD := $(shell pwd) ENV := $(shell readlink -f .env 2>/dev/null || echo .env) PYTHON_VER := 3.12 PYTHON := python$(PYTHON_VER) UV := $(shell command -v uv 2>/dev/null || echo uv) BUN := $(shell command -v bun 2>/dev/null || echo bun) NX := $(BUN) x nx ## ── Quick Start ────────────────────────────────────────────────────────────── init: bootstrap ## First-time setup (alias for bootstrap) dev: run.webapp ## Start the Next.js webapp (fastest entry point) ## ── Environment Setup ──────────────────────────────────────────────────────── bootstrap: venv envlink deps ## Full initial setup: venv + deps + env linking @echo "Bootstrap complete. Activate Python env: source .venv/bin/activate" venv: ## Create Python virtual environment (idempotent) @if [ ! -d ".venv" ]; then \ echo "Creating uv-managed venv (Python $(PYTHON_VER))..."; \ $(UV) venv --python $(PYTHON_VER) .venv; \ fi deps: venv ## Install/update Python dependencies @$(UV) sync @if [ -f package.json ]; then $(BUN) install; fi @cd apps/webapp && $(BUN) install --frozen-lockfile 2>/dev/null || $(BUN) install lock: ## Refresh uv lockfile @$(UV) lock envlink: ## Propagate root .env to all sub-apps @mkdir -p apps/webapp apps/worker ml @touch "$(WD)/apps/webapp/.env" "$(WD)/apps/worker/.env" "$(WD)/ml/.env" @if [ -f "$(ENV)" ]; then \ ln -sf "$(ENV)" "$(WD)/apps/webapp/.env"; \ ln -sf "$(ENV)" "$(WD)/apps/worker/.env"; \ ln -sf "$(ENV)" "$(WD)/ml/.env"; \ fi doctor: ## Verify toolchain (bun, docker, python) @echo "Checking toolchain..." @$(PYTHON) --version || (echo "python$(PYTHON_VER) not found"; exit 1) @$(UV) --version || echo "uv not found - install: curl -LsSf https://astral.sh/uv/install.sh | sh" @$(BUN) --version || echo "bun not found - install: curl -fsSL https://bun.sh/install | bash" @docker --version || echo "docker not found" @docker compose version || echo "docker compose not found" @echo "OK" ## ── Code Quality ───────────────────────────────────────────────────────────── fmt: venv ## Format Python with black @$(UV) run black src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps" lint: venv ## Lint Python with ruff @$(UV) run ruff check src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps" type: venv ## Type check Python with mypy @$(UV) run mypy src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps" test: venv ## Run pytest @$(UV) run pytest tests/ -v 2>/dev/null || echo "No tests yet - create tests/" ## ── Docker ─────────────────────────────────────────────────────────────────── up: ## Start core services (redis, ml-inference, worker) @docker compose up -d redis ml-inference worker down: ## Stop all services @docker compose down logs: ## Tail all service logs @docker compose logs -f ps: ## Show service status @docker compose ps rebuild: ## Rebuild + restart all services (no cache) @docker compose build --no-cache && docker compose up -d ## ── Service Profiles ───────────────────────────────────────────────────────── lift: up ## Alias for 'up' lift.minio: ## Start core services + MinIO object storage @docker compose --profile minio up -d @echo "MinIO console: http://localhost:9901 (minioadmin/minioadmin)" lift.tensorboard: ## Start TensorBoard @docker compose --profile tensorboard up -d @echo "TensorBoard: http://localhost:6006" lift.mlflow: ## Start optional MLflow tracking server @docker compose --profile mlflow up -d @echo "MLflow: http://localhost:5000" lift.logging: ## Start Loki + Grafana logging stack @docker compose --profile logging up -d @if [ -f .env ]; then . ./.env 2>/dev/null; fi; \ echo "Grafana: http://localhost:$${GRAFANA_PORT:-3000} (admin/admin)"; \ echo "Loki: http://localhost:$${LOKI_PORT:-3100}" lift.database: ## Start database services (postgres/mongodb) @docker compose --profile database up -d ## ── Run Applications ───────────────────────────────────────────────────────── run.webapp: ## Start Next.js webapp with bun (dev + turbopack) @echo "Starting webapp at http://localhost:3000" @$(NX) run webapp:dev run.webapp.simple: ## Start Streamlit minimal webapp @$(NX) run webapp-minimal:dev run.backend: ## Start API backend (BACKEND_MODE=fastapi|flask, default: fastapi) @if [ -f .env ]; then . ./.env; fi; \ MODE=$${BACKEND_MODE:-fastapi}; \ if [ "$$MODE" = "fastapi" ]; then \ $(NX) run backend-fastapi:dev; \ elif [ "$$MODE" = "flask" ]; then \ $(NX) run backend-flask:dev; \ else \ echo "Unknown BACKEND_MODE=$$MODE (fastapi|flask)"; exit 1; \ fi run.worker: ## Start Celery worker (requires redis) @$(NX) run worker:dev run.ml: ## Start ML inference server (FastAPI) @$(NX) run ml:dev ## ── ML Workflow ────────────────────────────────────────────────────────────── etl: venv ## Run ETL pipeline @$(NX) run ml:etl train: venv ## Run model training @$(NX) run ml:train nx.graph: ## Open Nx project graph @$(NX) graph nx.projects: ## List Nx projects in workspace @$(NX) show projects nx.affected: ## Run lint/test/build only for affected projects @$(NX) affected -t lint,test,build infer: run.ml ## Alias for run.ml ## ── Utilities ──────────────────────────────────────────────────────────────── seed: venv ## Seed development data @$(PYTHON) scripts/seed.py 2>/dev/null || echo "Create scripts/seed.py for seeding" clean: ## Remove caches, build artifacts, and compiled files @find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true @find . -type f -name "*.pyc" -delete 2>/dev/null || true @find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true @find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true @find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true @find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true @rm -rf build/ dist/ 2>/dev/null || true help: ## Show this help @echo "Ultiplate - make targets" @echo "" @echo " Quick start:" @echo " make init - First-time setup" @echo " make dev - Start Next.js webapp" @echo " make up - Start Docker services" @echo "" @grep -E '^[a-zA-Z_.%-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " %-22s %s\n", $$1, $$2}'