A standalone harness, never part of the test run, that drives a real stack through the durable path — the webhook, which journals every trigger — and then stops Redis, kills the engine mid-cascade and restarts the broker under a live subscription. Latest-value-wins with concurrent cascades means counter equality is not a promise, so what it asserts is that the queue drains, that state ends on the last value sent, and that nothing reached the dead-letter stream. Every docker verb goes through one helper that checks the compose label before it acts and refuses anything outside this project, because the machines this runs on host unrelated services. Two invariants are deliberately looser than they look. The queue belongs to the whole stack, so "nothing pending" would be measuring other people's traffic; the harness waits on the age of the oldest unacknowledged entry instead, which a stuck item always dominates. And the observability tables are cleared only after the collector's flush interval has passed, since deleting a flow publishes an audit event of its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017MeiWk3Yq12n2pTvnQWYvt
106 lines
4.9 KiB
Makefile
106 lines
4.9 KiB
Makefile
# ─── Fluksio app Makefile ───
|
|
# Convenience targets for development, testing, linting, and deployment.
|
|
# The workspace root delegates to these (see ../Makefile).
|
|
|
|
.PHONY: dev-utils dev dev-local up down update install dev-backend dev-frontend \
|
|
generate-client test test-backend test-frontend soak lint lint-backend \
|
|
lint-frontend clean help
|
|
|
|
COMPOSE_ROOT := $(CURDIR)
|
|
# Explicit project name keeps this stack isolated from the sibling website
|
|
# stack (otherwise both default to "docker", the directory their compose
|
|
# files live in).
|
|
COMPOSE_PROJECT := fluksio-app
|
|
# Compose interpolation needs the project-local .env before reading compose.yml.
|
|
COMPOSE := docker compose -p $(COMPOSE_PROJECT) --env-file $(COMPOSE_ROOT)/.env
|
|
COMPOSE_PROD := $(COMPOSE) -f docker/compose.yml
|
|
# Production also runs autoheal, which restarts the backend when its deep
|
|
# health check fails. It is profile-gated because it mounts the Docker socket.
|
|
COMPOSE_PROD_RUN := $(COMPOSE_PROD) --profile autoheal
|
|
COMPOSE_DEV := $(COMPOSE_PROD) -f docker/compose.dev.yml
|
|
# Integrated local stack: dev stack wired onto the shared `proxy` network.
|
|
COMPOSE_LOCAL := $(COMPOSE_DEV) -f docker/compose.local.yml
|
|
|
|
help: ## Show available targets
|
|
@awk 'BEGIN{FS=":.*?## "} /^[a-zA-Z_-]+:.*?##/ {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
|
|
# ── Development (Docker) ──────────────────────────────────────────
|
|
|
|
dev-utils: ## Start only the utility containers (db, adminer, proxy, mailcatcher, prestart)
|
|
$(COMPOSE_DEV) up --build db adminer proxy mailcatcher prestart
|
|
|
|
dev: ## Start the full dev stack (includes a local Traefik proxy)
|
|
$(COMPOSE_DEV) up --build
|
|
|
|
dev-local: ## Start the integrated local stack (called by the root `make dev`)
|
|
DOMAIN=$${DOMAIN:-localhost} ENVIRONMENT=$${ENVIRONMENT:-local} \
|
|
$(COMPOSE_LOCAL) up --build -d proxy db adminer prestart backend frontend mailcatcher
|
|
|
|
up: ## Start the production stack
|
|
$(COMPOSE_PROD_RUN) up --build -d
|
|
|
|
update: ## Pull, rebuild using the layer cache, and recreate changed containers
|
|
git pull
|
|
$(COMPOSE_PROD_RUN) build
|
|
$(COMPOSE_PROD_RUN) up -d --remove-orphans
|
|
docker image prune -f
|
|
|
|
down: ## Stop all running containers
|
|
-$(COMPOSE_LOCAL) down
|
|
-$(COMPOSE_PROD_RUN) down
|
|
|
|
# ── Development (local, no Docker) ───────────────────────────────
|
|
# Run `make dev-backend` and `make dev-frontend` in two separate terminals.
|
|
|
|
install: ## Install all dependencies (backend + frontend)
|
|
cd backend && uv sync
|
|
cd frontend && bun install
|
|
|
|
dev-backend: ## Start the FastAPI backend with hot-reload (local)
|
|
cd backend && uv run fastapi dev app/main.py
|
|
|
|
dev-frontend: ## Start the Vite dev server (local)
|
|
cd frontend && bun dev
|
|
|
|
generate-client: ## Regenerate the frontend SDK from the backend's OpenAPI schema
|
|
bash scripts/generate-client.sh
|
|
|
|
# ── Testing ───────────────────────────────────────────────────────
|
|
|
|
test: test-backend test-frontend ## Run all tests (backend + frontend)
|
|
|
|
# The integrated stack publishes no host port for Postgres (docker/compose.local.yml
|
|
# drops them all), so the suite reaches the container on the compose network
|
|
# instead. Empty when the stack is down — then .env's localhost:5432 stands.
|
|
DB_HOST = $(shell docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' fluksio-db 2>/dev/null | awk '{print $$1}')
|
|
|
|
test-backend: ## Run backend tests (pytest + coverage)
|
|
cd backend && $(if $(DB_HOST),POSTGRES_SERVER=$(DB_HOST)) uv run bash scripts/tests-start.sh
|
|
|
|
test-frontend: ## Run frontend tests (Playwright e2e)
|
|
cd frontend && bunx playwright test
|
|
|
|
# Load and chaos against a *running* stack, never part of `make test`: it
|
|
# restarts this stack's containers. `SOAK_ARGS="--dry-run"` only looks.
|
|
soak: ## Run the soak/chaos harness (SOAK_ARGS="--minutes 30 --scenario redis")
|
|
cd backend && uv run python scripts/soak.py $(SOAK_ARGS)
|
|
|
|
# ── Linting ───────────────────────────────────────────────────────
|
|
|
|
lint: lint-backend lint-frontend ## Run all linters
|
|
|
|
lint-backend: ## Lint backend with ruff + mypy
|
|
cd backend && uv run ruff check .
|
|
cd backend && uv run ruff format --check .
|
|
cd backend && uv run mypy app
|
|
|
|
lint-frontend: ## Lint frontend with biome
|
|
cd frontend && bun run lint
|
|
|
|
# ── Cleanup ───────────────────────────────────────────────────────
|
|
|
|
clean: ## Remove build artifacts and caches
|
|
rm -rf frontend/dist frontend/blob-report frontend/test-results
|
|
rm -rf backend/.pytest_cache backend/htmlcov
|
|
find backend -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|