Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
511 lines (444 loc) · 22.6 KB
/
Copy pathMakefile
File metadata and controls
511 lines (444 loc) · 22.6 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
.PHONY: docker-build build build-backend build-dash0 build-status0 build-docs copy-dash0 copy-status0 copy-docs embed-placeholders cloud-setup check-clean-clone \
build-cli install-cli clean clean-all run run-test dev dev-test dev-saas dev-dash0 dev-status0 dev-docs dev-backend \
test test-cover test-postgres test-slow test-scenario test-dash0 test-docs lint lint-agent-docs lint-back lint-dash0 lint-status0 fmt deps migrate help sync-brand-assets build-favicons \
showcase showcase-terminal showcase-cut \
build-loadgen bench-checks bench-checks-sqlite bench-checks-postgres \
build-scenario scenario-test
.DEFAULT_GOAL := build
APP_NAME := solidping
# SaaS mode (make dev-saas): the separate solidping-billing service (next door
# at ../solidping-billing, served on :4050) drives plan upgrades. These three
# values wire the two services together for local development:
# SAAS_BILLING_TOKEN — LEGACY shared secret; the billing service presents it
# as a bearer token when PUTting resolved entitlements. Must match the
# billing service's BILLING_SOLIDPING_TOKEN. Superseded by the signing key
# sets below, but still accepted (see SAAS_ALLOW_LEGACY_TOKEN) until the
# billing service has stopped sending it.
# SAAS_UPGRADE_URL — the dashboard's "Upgrade" link. {org} is replaced with
# the org slug. Points at the billing service's customer upgrade page.
# SAAS_SIGNING_KEYS_IN / _OUT — ordered [{"id","secret"}] key sets (newest
# first) for the signed service channels: _IN verifies the billing service's
# entitlements push (its BILLING_SIGNING_KEYS_OUTBOUND), _OUT signs our own
# calls to billing's /api/v1/* (its BILLING_SIGNING_KEYS_INBOUND). One set
# per direction so a leak of one cannot forge the other.
# SAAS_ALLOW_LEGACY_TOKEN — whether the legacy bearer above is still accepted.
# Stays true until billing signs everything; flipping it is a parameter
# change, not a deploy.
SAAS_BILLING_TOKEN ?= dev-billing-service-token
SAAS_UPGRADE_URL ?= http://localhost:4050/app/upgrade?org={org}
SAAS_SIGNING_KEYS_IN ?= [{"id":"dev-in","secret":"dev-billing-to-solidping-key"}]
SAAS_SIGNING_KEYS_OUT ?= [{"id":"dev-out","secret":"dev-solidping-to-billing-key"}]
SAAS_ALLOW_LEGACY_TOKEN ?= true
# Version information
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null | sed 's/^v//' || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
GIT_TIME ?= $(shell TZ=UTC git log -1 --format=%cd --date=format-local:%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
# Build flags
LDFLAGS := -ldflags "\
-X 'github.com/fclairamb/solidping/server/internal/version.Version=$(VERSION)' \
-X 'github.com/fclairamb/solidping/server/internal/version.Commit=$(COMMIT)' \
-X 'github.com/fclairamb/solidping/server/internal/version.GitTime=$(GIT_TIME)'"
# Directories
DASH0_DIR := web/dash0
DASH0_DIST := $(DASH0_DIR)/dist
STATUS0_DIR := web/status0
STATUS0_DIST := $(STATUS0_DIR)/dist
DOCS_DIR := web/docs
DOCS_DIST := $(DOCS_DIR)/build
BACK_DIR := server
BACK_DASH0_RES := $(BACK_DIR)/internal/app/dash0res/
BACK_STATUS0_RES := $(BACK_DIR)/internal/app/status0res/
BACK_DOCS_RES := $(BACK_DIR)/internal/app/docsres/
LOG_DIR := logs
# Detect current OS
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)
help: ## Show this help message
@echo 'Usage: make [target]'
@echo ''
@echo 'Available targets:'
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
kill:
lsof -ti :4000 | xargs kill
lsof -ti :5174 | xargs kill
lsof -ti :5175 | xargs kill
build: sync-brand-assets build-dash0 copy-dash0 build-status0 copy-status0 build-docs copy-docs build-backend ## Build complete application
sync-brand-assets: ## Copy res/logo.svg + favicon set into web/{dash0,status0}/public/ (favicons under public/assets/)
@mkdir -p web/dash0/public/assets web/status0/public/assets
@cp res/logo.svg web/dash0/public/logo.svg
@cp res/logo.svg web/status0/public/logo.svg
@cp res/logo.svg web/dash0/public/assets/favicon.svg
@cp res/logo.svg web/status0/public/assets/favicon.svg
@cp res/logo.png web/dash0/public/logo.png
@cp res/logo.png web/status0/public/logo.png
@if [ -d res/favicons ]; then \
cp res/favicons/*.png web/dash0/public/assets/; \
cp res/favicons/*.png web/status0/public/assets/; \
fi
@mkdir -p web/docs/static/img
@cp res/logo.png web/docs/static/img/logo.png
@echo "Brand assets synced to web/dash0/public/ (favicons in assets/), web/status0/public/ (favicons in assets/), and web/docs/static/"
build-favicons: ## Generate favicon PNG set from res/logo.svg into res/favicons/
@./scripts/build-favicons.sh
docker-build: ## Build Docker image
@echo "Building Docker image..."
@docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
--build-arg GIT_TIME=$(GIT_TIME) \
-t $(APP_NAME):$(VERSION) \
-t $(APP_NAME):latest .
@echo "Docker image built: $(APP_NAME):$(VERSION) and $(APP_NAME):latest"
build-dash0: ## Build dash0 status page with bun
@echo "Building dash0..."
@cd $(DASH0_DIR) && bun install && bun run build
@echo "Dash0 build complete"
copy-dash0: ## Copy dash0 dist to backend dash0res directory
@echo "Copying dash0 dist to backend resources..."
@mkdir -p $(BACK_DASH0_RES)
@find $(BACK_DASH0_RES) -mindepth 1 ! -name .gitkeep -delete
@cp -r $(DASH0_DIST)/* $(BACK_DASH0_RES)/
@echo "Dash0 resources copied to $(BACK_DASH0_RES)"
build-status0: ## Build status0 public status page with bun
@echo "Building status0..."
@cd $(STATUS0_DIR) && bun install && bun run build
@echo "Status0 build complete"
copy-status0: ## Copy status0 dist to backend status0res directory
@echo "Copying status0 dist to backend resources..."
@mkdir -p $(BACK_STATUS0_RES)
@find $(BACK_STATUS0_RES) -mindepth 1 ! -name .gitkeep -delete
@cp -r $(STATUS0_DIST)/* $(BACK_STATUS0_RES)/
@echo "Status0 resources copied to $(BACK_STATUS0_RES)"
build-docs: ## Build docs site (Docusaurus, incl. generated API ref) with bun
@echo "Building docs..."
@cd $(DOCS_DIR) && bun install && bun run build
@echo "Docs build complete"
copy-docs: ## Copy docs build to backend docsres directory
@echo "Copying docs build to backend resources..."
@mkdir -p $(BACK_DOCS_RES)
@find $(BACK_DOCS_RES) -mindepth 1 ! -name .gitkeep -delete
@cp -r $(DOCS_DIST)/* $(BACK_DOCS_RES)/
@echo "Docs resources copied to $(BACK_DOCS_RES)"
build-backend: ## Build backend Go binary
@echo "Building backend for $(GOOS)/$(GOARCH)..."
@cd $(BACK_DIR) && go build $(LDFLAGS) -o ../$(APP_NAME) .
@echo "Binary created: ./$(APP_NAME)"
build-cli: ## Build standalone CLI (sp) binary (also available as 'solidping client')
@echo "Building CLI for $(GOOS)/$(GOARCH)..."
@cd $(BACK_DIR) && go build $(LDFLAGS) -o ../bin/sp ./cmd/sp
@echo "Binary created: ./bin/sp"
@echo "Note: CLI commands are also available via './solidping client <command>'"
install-cli: ## Install standalone CLI to GOPATH
@echo "Installing CLI..."
@cd $(BACK_DIR) && go install $(LDFLAGS) ./cmd/sp
@echo "CLI installed to GOPATH"
@echo "Note: CLI commands are also available via 'solidping client <command>'"
build-loadgen: ## Build the loadgen benchmark client
@echo "Building loadgen..."
@cd $(BACK_DIR) && go build -o ../bin/loadgen ./cmd/loadgen
@echo "Binary created: ./bin/loadgen"
build-scenario: ## Build the scenario driver CLI
@echo "Building scenario driver..."
@mkdir -p bin
@cd $(BACK_DIR) && go build -o ../bin/solidping-scenario ./cmd/scenariodriver
@echo "Binary created: ./bin/solidping-scenario"
scenario-test: build-scenario ## Run scenario smoke test against a local dev server (make dev-test first)
@echo "Running scenario test against local dev server..."
./bin/solidping-scenario run \
--server http://localhost:4000 \
--org test \
--token pat_test \
--listen :9876 \
--public-url http://localhost:9876 \
--scenario server/cmd/scenariodriver/scenarios/incident-open-close.yaml \
--junit /tmp/scenario-results.xml
# Bench knobs (override per invocation, e.g. `make bench-checks BENCH_CHECKS=500`).
BENCH_CHECKS ?= 200
BENCH_DURATION ?= 2m
BENCH_PERIOD ?= 10s
BENCH_PORT ?= 4001
BENCH_DATA := bench-data
BENCH_OUT := bench-results
# /metrics requires a bearer scrape token by default (spec 2026-09-25-25); the
# loadgen binary scrapes it for its before/after report, so the bench server
# and loadgen must share this value. A fixed local-only placeholder is fine —
# it never leaves this machine.
BENCH_METRICS_TOKEN ?= loadgen-local-bench-token
bench-checks: bench-checks-sqlite bench-checks-postgres ## Run loadgen against SQLite and PostgreSQL backends (both)
@echo "Bench complete; reports under $(BENCH_OUT)/"
bench-checks-sqlite: build build-loadgen ## Run loadgen against a SQLite-backed test server
@echo "==> Bench: SQLite"
@mkdir -p $(BENCH_DATA)/sqlite $(BENCH_OUT)
@lsof -ti :$(BENCH_PORT) | xargs kill 2>/dev/null || true
@SP_RUNMODE=test \
SP_DB_TYPE=sqlite \
SP_DB_DIR=$(BENCH_DATA)/sqlite \
SP_DB_RESET=true \
SP_SERVER_LISTEN=127.0.0.1:$(BENCH_PORT) \
SP_METRICS_SCRAPE_TOKEN=$(BENCH_METRICS_TOKEN) \
LOG_LEVEL=warn \
./$(APP_NAME) serve > $(BENCH_OUT)/server-sqlite.log 2>&1 & echo $$! > $(BENCH_OUT)/server.pid; \
trap "kill `cat $(BENCH_OUT)/server.pid` 2>/dev/null || true; rm -f $(BENCH_OUT)/server.pid" EXIT; \
./bin/loadgen \
-api-url http://127.0.0.1:$(BENCH_PORT) \
-backend sqlite \
-checks $(BENCH_CHECKS) \
-duration $(BENCH_DURATION) \
-period $(BENCH_PERIOD) \
-scrape-token $(BENCH_METRICS_TOKEN) \
-output-dir $(BENCH_OUT)
# No BENCH_DATA directory here: embedded PostgreSQL owns its data directory (a
# temp dir created by internal/db/postgres/embeddedpg) and listens on a fixed
# port, so neither is configurable from the environment.
bench-checks-postgres: build build-loadgen ## Run loadgen against a PostgreSQL-backed test server (embedded PG)
@echo "==> Bench: PostgreSQL (embedded)"
@mkdir -p $(BENCH_OUT)
@lsof -ti :$(BENCH_PORT) | xargs kill 2>/dev/null || true
@SP_RUNMODE=test \
SP_DB_TYPE=postgres-embedded \
SP_DB_RESET=true \
SP_SERVER_LISTEN=127.0.0.1:$(BENCH_PORT) \
SP_METRICS_SCRAPE_TOKEN=$(BENCH_METRICS_TOKEN) \
LOG_LEVEL=warn \
./$(APP_NAME) serve > $(BENCH_OUT)/server-postgres.log 2>&1 & echo $$! > $(BENCH_OUT)/server.pid; \
trap "kill `cat $(BENCH_OUT)/server.pid` 2>/dev/null || true; rm -f $(BENCH_OUT)/server.pid" EXIT; \
./bin/loadgen \
-api-url http://127.0.0.1:$(BENCH_PORT) \
-backend postgres \
-checks $(BENCH_CHECKS) \
-duration $(BENCH_DURATION) \
-period $(BENCH_PERIOD) \
-scrape-token $(BENCH_METRICS_TOKEN) \
-output-dir $(BENCH_OUT)
# Memory bench knobs (override per invocation, e.g.
# `make bench-memory BENCH_MEM_MODE=docker BENCH_MEM_SCENARIOS=all`).
BENCH_MEM_MODE ?= local
BENCH_MEM_SCENARIOS ?= idle-all-sqlite,checks-500,docs-crawl
BENCH_MEM_LABEL ?= baseline
BENCH_MEM_WARMUP ?= 5m
BENCH_MEM_INTERVAL ?= 5s
BENCH_MEM_DURATION ?= 5m
BENCH_MEM_REPS ?= 3
BENCH_MEM_IMAGE ?= solidping-bench:local
BENCH_MEM_MEMORY ?= 1g
BENCH_MEM_CPUS ?= 1
BENCH_MEM_PORT ?= 4009
BENCH_MEM_COMPARE ?=
build-membench: ## Build the memory bench harness
@echo "Building membench..."
@cd $(BACK_DIR) && go build -o ../bin/membench ./cmd/membench
@echo "Binary created: ./bin/membench"
bench-memory-image: ## Build a Linux bench image from the CURRENT tree (needs `make build` first for the embedded assets)
@echo "==> Building $(BENCH_MEM_IMAGE) from the working tree"
@docker build -f Dockerfile.bench -t $(BENCH_MEM_IMAGE) $(BACK_DIR)
bench-memory: build-backend build-membench ## Measure memory (see wiki/runbooks/memory-profiling.md); BENCH_MEM_MODE=docker for the authoritative run
@echo "==> Memory bench: mode=$(BENCH_MEM_MODE) scenarios=$(BENCH_MEM_SCENARIOS) reps=$(BENCH_MEM_REPS)"
@mkdir -p $(BENCH_OUT)
@lsof -ti :$(BENCH_MEM_PORT) | xargs kill 2>/dev/null || true
./bin/membench \
-mode $(BENCH_MEM_MODE) \
-image $(BENCH_MEM_IMAGE) \
-binary ./$(APP_NAME) \
-scenarios $(BENCH_MEM_SCENARIOS) \
-label $(BENCH_MEM_LABEL) \
-warmup $(BENCH_MEM_WARMUP) \
-interval $(BENCH_MEM_INTERVAL) \
-duration $(BENCH_MEM_DURATION) \
-reps $(BENCH_MEM_REPS) \
-memory $(BENCH_MEM_MEMORY) \
-cpus $(BENCH_MEM_CPUS) \
-port $(BENCH_MEM_PORT) \
-out-dir $(BENCH_OUT) \
$(if $(BENCH_MEM_COMPARE),-compare $(BENCH_MEM_COMPARE),)
run: build ## Build and run the application
@echo "Running application..."
@./$(APP_NAME) serve
run-test: build ## Build and run the application in test mode
@echo "Running application in test mode..."
@SP_RUNMODE=test ./$(APP_NAME) serve
# devloop supervises all dev processes (backend rebuild loop + the two bun dev
# servers) as one foreground tree, and size-rotates each stream into
# $(LOG_DIR)/<name>.log (.1/.2 backups). No tee, no backgrounded pipelines.
DEVLOOP_LOG_FLAGS := -log-dir $(CURDIR)/$(LOG_DIR)
DEVLOOP_PROCS := -proc "dash0:$(CURDIR)/$(DASH0_DIR):bun run dev" -proc "status0:$(CURDIR)/$(STATUS0_DIR):bun run dev"
# The live demo (demo org, demo user, seeded catalog, cleanup sweep) is ON by
# default in development, so `make dev` shows the same login page and banner a
# visitor to the public demo gets, instead of a surface nobody sees locally
# until it breaks in production. Turn it off for a run with `make dev DEMO=false`
# (or export DEMO=false).
#
# Safe against local data: the cleanup sweep only ever deletes checks created by
# the demo USER inside an org carrying the demo.enabled flag, so a `default` org
# is never swept (server/internal/jobs/jobtypes/job_demo_cleanup.go). dev-test
# is not listed below because test run mode already forces the demo on.
DEMO ?= true
# Optional, git-ignored (*.priv*) shell file of KEY=value lines sourced by `make dev`
# when present, e.g. SP_AI_PROVIDER=openai, or SP_AI_API_KEY="$(gopass show -o <path>)"
# to keep the secret out of the file. Values set by the recipe itself (SP_DEMO_ENABLED...) win.
DEV_ENV_FILE ?= dev.priv.env
# A git worktree has no copy of the git-ignored file: it falls back to the one
# in the main checkout (the parent of the shared git dir).
DEV_ENV_PATH := $(firstword $(wildcard $(CURDIR)/$(DEV_ENV_FILE) \
$(abspath $(shell git rev-parse --git-common-dir 2>/dev/null)/../$(DEV_ENV_FILE))))
dev: kill ## Run backend, dash0 and status0 in development mode (demo on; DEMO=false to disable; loads dev.priv.env if present)
@echo "Running application in development mode (demo: $(DEMO))..."
@if [ -n "$(DEV_ENV_PATH)" ]; then echo "Loading env from $(DEV_ENV_PATH)"; fi
@set -a; if [ -n "$(DEV_ENV_PATH)" ]; then . "$(DEV_ENV_PATH)"; fi; set +a; \
cd $(BACK_DIR) && SP_REDIRECTS="/d:localhost:5174/d,/s:localhost:5175/s" SP_PROFILER_ENABLED=true \
SP_DB_MIGRATION_GUARD_MODE=warn \
SP_DEMO_ENABLED=$(DEMO) \
go run ./cmd/devloop $(DEVLOOP_LOG_FLAGS) $(DEVLOOP_PROCS)
dev-test: kill ## Run backend, dash0 and status0 in development test mode
@echo "Running application in development test mode..."
@cd $(BACK_DIR) && SP_RUNMODE=test SP_REDIRECTS="/d:localhost:5174/d,/s:localhost:5175/s" \
SP_DB_MIGRATION_GUARD_MODE=warn \
SP_SERVER_RATE_LIMITING_REQUESTS_PER_MINUTE=0 \
SP_SERVER_RATE_LIMITING_MAX_CONCURRENT=0 \
SP_AUTH_REGISTRATION_EMAIL_PATTERN='.*' \
go run ./cmd/devloop $(DEVLOOP_LOG_FLAGS) $(DEVLOOP_PROCS)
dev-saas: kill ## Run backend (SaaS mode) + dash0 + status0 — pairs with ../solidping-billing `make dev`
@echo "Running application in SaaS mode (billing via ../solidping-billing on :4050)..."
@echo " upgrade URL template: $(SAAS_UPGRADE_URL)"
@cd $(BACK_DIR) && \
SP_DB_MIGRATION_GUARD_MODE=warn \
SP_DEPLOYMENT_MODE=saas \
SP_ENTITLEMENTS_SERVICE_TOKEN="$(SAAS_BILLING_TOKEN)" \
SP_ENTITLEMENTS_UPGRADE_URL_TEMPLATE="$(SAAS_UPGRADE_URL)" \
SP_ENTITLEMENTS_ADMIN_WRITES_ENABLED=true \
SP_ENTITLEMENTS_SERVICE_SIGNING_KEYS='$(SAAS_SIGNING_KEYS_IN)' \
SP_ENTITLEMENTS_OUTBOUND_SIGNING_KEYS='$(SAAS_SIGNING_KEYS_OUT)' \
SP_ENTITLEMENTS_ALLOW_LEGACY_SERVICE_TOKEN=$(SAAS_ALLOW_LEGACY_TOKEN) \
SP_REDIRECTS="/d:localhost:5174/d,/s:localhost:5175/s" \
go run ./cmd/devloop $(DEVLOOP_LOG_FLAGS) $(DEVLOOP_PROCS)
clean: ## Remove built binaries and SPA artifacts
@echo "Cleaning build artifacts..."
@rm -f $(APP_NAME)
@rm -rf bin/
@rm -rf $(DASH0_DIST)
@rm -rf $(BACK_DASH0_RES)
@rm -rf $(STATUS0_DIST)
@rm -rf $(BACK_STATUS0_RES)
@rm $(shell find . -name "*.db*")
@echo "Clean complete"
clean-all: clean ## Remove all generated files including node_modules
@echo "Cleaning all generated files..."
@rm -rf $(DASH0_DIR)/node_modules $(DASH0_DIR)/.bun
@rm -rf $(STATUS0_DIR)/node_modules $(STATUS0_DIR)/.bun
@echo "Deep clean complete"
embed-placeholders: ## Create missing placeholder files in the embedded frontend dirs (backend-only work)
@scripts/embed-placeholders.sh
check-clean-clone: ## Clone HEAD and run go build + make test with no frontend build and no Docker (nightly guard)
@scripts/check-clean-clone.sh
cloud-setup: ## Bootstrap a fresh Linux sandbox (verify Go, install bun + golangci-lint, deps, frontend)
@scripts/cloud-setup.sh
test: embed-placeholders ## Run all tests (SQLite only — `-short` skips every Postgres suite)
@echo "Running backend tests..."
@cd $(BACK_DIR) && go test ./... -short
@echo "Tests complete"
# Backend coverage (spec 2026-09-29-06). -short by default (SQLite only, ~8 min);
# COVER_FULL=1 runs the Postgres layer as CI does. Generated and vendored code
# (pkg/client, third_party, mocks) is filtered out of the profile so the number
# reflects hand-written code. Output: server/coverage.out (filtered) plus a total.
test-cover: ## Backend coverage profile + total (COVER_FULL=1 for the Postgres layer)
@echo "Running backend tests with coverage..."
@cd $(BACK_DIR) && \
if [ -n "$(COVER_FULL)" ]; then \
SP_TEST_REQUIRE_POSTGRES=1 go test -count=1 -p 1 -coverprofile=coverage.raw.out -coverpkg=./... ./... ; \
else \
go test -count=1 -short -coverprofile=coverage.raw.out -coverpkg=./... ./... ; \
fi
@cd $(BACK_DIR) && ../scripts/coverage.sh filter coverage.raw.out coverage.out && rm -f coverage.raw.out
@cd $(BACK_DIR) && go tool cover -func=coverage.out | tail -1
@echo "Coverage profile: $(BACK_DIR)/coverage.out"
# `make test` above is -short, which means it has NEVER exercised Postgres.
# That is deliberate (it keeps the inner loop fast) and it is exactly how spec
# 2026-09-11-03 shipped a 500: a key pattern SQLite accepted and Postgres
# refused. This target is the other half — the same invocation the
# `backend-postgres` CI job runs, character for character, so "green locally"
# and "green in CI" mean the same thing. See wiki/testing/test-layers.md.
#
# -p 1 embedded-postgres shares ~/.embedded-postgres-go/extracted (pwfile
# included) across instances, so concurrent packages race and the loser
# fails to start.
# -v the CI guard step parses --- PASS / --- SKIP out of this.
# SP_TEST_REQUIRE_POSTGRES=1 turns "embedded postgres unavailable" from a
# silent skip into a failure (internal/testsupport).
test-postgres: ## Run the backend tests against Postgres (non-short, ~10-15 min)
@echo "Running backend tests against embedded PostgreSQL (this takes ~10-15 minutes)..."
@cd $(BACK_DIR) && SP_TEST_REQUIRE_POSTGRES=1 go test -count=1 -p 1 -v ./...
@echo "Postgres test layer complete"
test-slow: ## Run the slowtests layer (live network + Docker; nightly in CI)
@echo "Running the slowtests layer (needs the public internet and a Docker daemon)..."
@cd $(BACK_DIR) && go test -count=1 -p 1 -tags slowtests -v \
./internal/checkers/... ./internal/tlsedge/...
@echo "Slow test layer complete"
test-scenario: ## Run full-pipeline scenario tests (requires Docker / embedded Postgres)
@echo "Running scenario integration tests..."
@cd $(BACK_DIR) && go test -v -timeout 120s ./test/integration/scenario/...
@echo "Scenario tests complete"
test-dash0: ## Run dash0 unit tests (mirrors the CI step)
@echo "Running dash0 unit tests..."
@cd $(DASH0_DIR) && bun run test:unit
@echo "Dash0 unit tests complete"
test-docs: ## Run docs site unit tests (mirrors the CI step)
@echo "Running docs unit tests..."
@cd $(DOCS_DIR) && bun run test:unit
@echo "Docs unit tests complete"
showcase: ## Regenerate the showcase media (terminal + dashboard cut, stills, docs video)
@echo "Filming the terminal segment (needs docker and vhs)..."
@cd $(DASH0_DIR) && bun run showcase/terminal.ts
@echo "Recording showcase media (needs a running SolidPing server)..."
@cd $(DASH0_DIR) && bunx playwright test --config=showcase/playwright.config.ts
@echo "Post-processing (stitch + label + AV1/H.264)..."
@cd $(DASH0_DIR) && bun run showcase/postprocess.ts
@echo "Showcase media written to web/docs/static/showcase/ and res/screenshots/ — commit the changed assets."
showcase-terminal: ## Re-film only the terminal segment of the showcase cut
@cd $(DASH0_DIR) && bun run showcase/terminal.ts
showcase-cut: ## Re-cut the showcase media from the takes already in showcase/output/
@cd $(DASH0_DIR) && bun run showcase/postprocess.ts
lint-back: ## Run backend linter
@echo "Running backend linter..."
@cd $(BACK_DIR) && golangci-lint run ./...
@echo "Backend linting complete"
lint-dash0: ## Run dash0 linter
@echo "Running dash0 linter..."
@cd $(DASH0_DIR) && bun run lint
@echo "Dash0 linting complete"
lint-status0: ## Run status0 linter
@echo "Running status0 linter..."
@cd $(STATUS0_DIR) && bun run lint
@echo "Status0 linting complete"
lint-agent-docs: ## Check AGENTS.md size limits and CLAUDE.md stubs
@scripts/check-agent-docs_test.sh
@scripts/check-agent-docs.sh
lint: lint-agent-docs lint-back lint-dash0 lint-status0 ## Run all linters
fmt: ## Format code
@echo "Formatting backend code..."
@cd $(BACK_DIR) && go fmt ./...
@echo "Formatting dash0 code..."
@cd $(DASH0_DIR) && bun run lint --fix || true
@echo "Code formatting complete"
dev-dash0: ## Start dash0 development server
@echo "Starting dash0 dev server..."
@cd $(DASH0_DIR) && bun run dev
dev-status0: ## Start status0 development server
@echo "Starting status0 dev server..."
@cd $(STATUS0_DIR) && bun run dev
dev-docs: ## Start docs (Docusaurus) dev server on :3000
@echo "Starting docs dev server..."
@cd $(DOCS_DIR) && bun run gen-api-docs && bun run start
dev-backend: ## Start backend development server (hot reload via cmd/devloop, rotating log; demo on)
@echo "Starting backend dev server (demo: $(DEMO))..."
@cd $(BACK_DIR) && SP_DEMO_ENABLED=$(DEMO) go run ./cmd/devloop $(DEVLOOP_LOG_FLAGS)
deps: ## Install all dependencies
@echo "Installing backend dependencies..."
@cd $(BACK_DIR) && go mod download
@echo "Installing dash0 dependencies..."
@cd $(DASH0_DIR) && bun install
@echo "Installing status0 dependencies..."
@cd $(STATUS0_DIR) && bun install
@echo "Dependencies installed"
migrate: ## Run database migrations
@echo "Running database migrations..."
@./$(APP_NAME) migrate
@echo "Migrations complete"