Skip to content

Makefile targets

Every command is a make target; make on its own lists them. The tools run through uv, at the versions in uv.lock.

Commands

Command What it does
make help List the available commands
make env Create or update .env, generating local secrets
make up Start the chosen PROFILES (default: all of them), with the database adapter they need
make down Stop the stack, keeping its data
make reset Stop the stack and delete its data volumes, local Supabase's included
make ps Show the stack's containers
make logs Follow the logs of the running services
make tenant Create a tenant's team and key on the gateway: make tenant NAME=acme [TENANT_FLAGS="--max-budget 20"]
make bump-libraries Pin the template's libraries to their main commits: make bump-libraries [BUMP_FLAGS="--check" or "artifactr=REV"]
make validate Validate every configuration without starting containers, as CI does
make smoke Send test telemetry through the running stack and find it (PROFILES as for up)
make smoke-app Run an application from the template beside the running stack, and trace its agents
make docs-reference Regenerate the reference pages from the files they describe
make clean Remove tool caches

Variables

Set a variable on the command line (make up PROFILES=observability) or in the environment. STACKR_DATABASE is read from .env unless you set it.

Variable Default
UV uv
COMPOSE docker compose
PROFILES $(ALL_PROFILES)
STACKR_DATABASE $(or $(shell sed -n 's/^STACKR_DATABASE=//p' .env 2>/dev/null),supabase)
SUPABASE $(shell command -v supabase 2>/dev/null || echo npx --yes supabase@$(SUPABASE_CLI_VERSION))
SUPABASE_START_FLAGS empty

make tenant also reads NAME, the tenant, and TENANT_FLAGS, the options scripts/create-tenant takes (The LLM gateway). make bump-libraries reads BUMP_FLAGS, the option and LIBRARY=REV pins scripts/bump-libraries takes (The application template). make smoke-app reads SMOKE_APP_PORT, and both smoke targets SMOKE_TIMEOUT (The smoke tests).

The file

The whole file: Makefile
# stackr: everyday commands. `make` lists them.
#
# The tools (linters and Copier) run through uv, so their versions are the ones in
# lattice's uv.lock. The stack itself needs Docker with Compose v2, the
# Supabase CLI for local Supabase, and jq for `validate` and `smoke`.

.DEFAULT_GOAL := help
UV ?= uv
COMPOSE ?= docker compose

include versions.env

# The stack's images are multi-architecture and run natively. A
# DOCKER_DEFAULT_PLATFORM that forces another architecture would run them
# under emulation, where Supabase's Realtime fails to start.
unexport DOCKER_DEFAULT_PLATFORM

# The profiles `make up` starts: all of them unless you choose, for example
#   make up PROFILES=observability
ALL_PROFILES := observability langfuse gateway
PROFILES ?= $(ALL_PROFILES)

# --- the database adapter (ADR-0009) -----------------------------------------
# STACKR_DATABASE in .env chooses it: supabase (default) or postgres. Profiles
# that keep data in PostgreSQL bring it up with them.
STACKR_DATABASE ?= $(or $(shell sed -n 's/^STACKR_DATABASE=//p' .env 2>/dev/null),supabase)
DATABASE_USERS := langfuse gateway
NEEDS_DATABASE = $(filter $(DATABASE_USERS),$(PROFILES))

# Local Supabase, run by its CLI: an installed `supabase`, or the pinned
# version through npx. SUPABASE_START_FLAGS can skip services, for example
#   make up SUPABASE_START_FLAGS="-x studio,imgproxy"
SUPABASE_PROJECT := stackr-supabase
SUPABASE_NETWORK := supabase_network_$(SUPABASE_PROJECT)
SUPABASE ?= $(shell command -v supabase 2>/dev/null || echo npx --yes supabase@$(SUPABASE_CLI_VERSION))
SUPABASE_START_FLAGS ?=
SUPABASE_RUNNING = $(shell docker ps -q --filter label=com.supabase.cli.project=$(SUPABASE_PROJECT))

ifeq ($(STACKR_DATABASE),supabase)
DATABASE_PROFILES :=
# Compose's defaults are local Supabase's host and fixed admin password.
DATABASE_ENV :=
START_SUPABASE = $(NEEDS_DATABASE)
else ifeq ($(STACKR_DATABASE),postgres)
DATABASE_PROFILES = $(if $(NEEDS_DATABASE),postgres)
# The admin password is read from .env by the shell, so it never appears on a
# command line.
DATABASE_ENV = STACKR_DB_HOST=postgres STACKR_DB_ADMIN_PASSWORD="$$(sed -n 's/^POSTGRES_ADMIN_PASSWORD=//p' .env)"
START_SUPABASE :=
else
$(error STACKR_DATABASE must be supabase or postgres, not '$(STACKR_DATABASE)')
endif

PROFILE_FLAGS = $(foreach profile,$(PROFILES) $(DATABASE_PROFILES),--profile $(profile))

# The Collector sends traces to Langfuse only when the langfuse profile runs,
# and the gateway sends telemetry only when the Collector runs.
comma := ,
TRACES_EXPORTERS = [otlp_grpc/tempo$(if $(filter langfuse,$(PROFILES)),$(comma) otlp_http/langfuse)]
GATEWAY_TELEMETRY = $(if $(filter observability,$(PROFILES)),true,false)
COMPOSE_ENV = STACKR_TRACES_EXPORTERS='$(TRACES_EXPORTERS)' STACKR_GATEWAY_TELEMETRY=$(GATEWAY_TELEMETRY) $(DATABASE_ENV)

# scripts/smoke runs Compose as `make up` does, and the Supabase CLI as make runs it.
SMOKE = $(COMPOSE_ENV) STACKR_DATABASE=$(STACKR_DATABASE) SUPABASE="$(SUPABASE)" scripts/smoke

.PHONY: help env up down reset ps logs tenant bump-libraries validate smoke smoke-app docs-reference clean

help: ## List the available commands
    @awk 'BEGIN {FS = ":.*## "} /^[a-zA-Z_-]+:.*## / {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

env: ## Create or update .env, generating local secrets
    $(UV) run scripts/setup-env

.env: .env.example
    $(UV) run scripts/setup-env

up: .env ## Start the chosen PROFILES (default: all of them), with the database adapter they need
    @docker network inspect $(SUPABASE_NETWORK) >/dev/null 2>&1 || docker network create $(SUPABASE_NETWORK) >/dev/null
ifneq ($(strip $(START_SUPABASE)),)
    $(SUPABASE) start $(SUPABASE_START_FLAGS)
endif
    $(COMPOSE_ENV) $(COMPOSE) $(PROFILE_FLAGS) up --detach --wait

down: ## Stop the stack, keeping its data
    $(COMPOSE) --profile '*' down
    $(if $(SUPABASE_RUNNING),$(SUPABASE) stop)

reset: ## Stop the stack and delete its data volumes, local Supabase's included
    $(COMPOSE) --profile '*' down --volumes
    $(if $(or $(SUPABASE_RUNNING),$(shell docker volume ls -q --filter label=com.supabase.cli.project=$(SUPABASE_PROJECT))),$(SUPABASE) stop --no-backup)
    @docker network rm $(SUPABASE_NETWORK) >/dev/null 2>&1 || true

ps: ## Show the stack's containers
    $(COMPOSE) --profile '*' ps

logs: ## Follow the logs of the running services
    $(COMPOSE) --profile '*' logs --follow --tail=100

tenant: ## Create a tenant's team and key on the gateway: make tenant NAME=acme [TENANT_FLAGS="--max-budget 20"]
    @if [ -z "$(NAME)" ]; then echo "usage: make tenant NAME=<tenant> [TENANT_FLAGS=...]"; exit 2; fi
    $(UV) run scripts/create-tenant $(NAME) $(TENANT_FLAGS)

bump-libraries: ## Pin the template's libraries to their main commits: make bump-libraries [BUMP_FLAGS="--check" or "artifactr=REV"]
    $(UV) run scripts/bump-libraries $(BUMP_FLAGS)

validate: .env ## Validate every configuration without starting containers, as CI does
    scripts/validate

smoke: ## Send test telemetry through the running stack and find it (PROFILES as for up)
    $(SMOKE) $(PROFILES) $(if $(NEEDS_DATABASE),database)

smoke-app: ## Run an application from the template beside the running stack, and trace its agents
    $(SMOKE) app

docs-reference: ## Regenerate the reference pages from the files they describe
    $(UV) run scripts/docs-reference

clean: ## Remove tool caches
    rm -rf .ruff_cache .cache