-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
691 lines (586 loc) Β· 27.8 KB
/
Copy pathMakefile
File metadata and controls
691 lines (586 loc) Β· 27.8 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
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
.PHONY: run dev stop restart setup install test-stack migrate migrate-host migrate-compose seed-demo seed-demo-host seed-demo-compose services services-compose test test-backend test-integration test-all test-postgres test-redis test-artifact test-coverage test-unit test-unit-fast test-unit-file test-with-timing clean clean-all pre-commit help check-poetry check-python check-deps install-poetry install-deps up down build logs shell db-shell redis-shell test-docker migrate-docker restart-api ps clean-docker check-docker ensure-test-deps prepare-test-data ensure-test-env
export SKIP_TEST_DB_FIXTURES ?= false
.PHONY: doctor services serve compose-up require-docker test-web test-contract test-e2e test-mobile test-mobile-generated test-performance test-load test-recovery test-security test-staging test-docs mobile-codegen-preflight mobile-codegen mobile-codegen-check capture-screenshots validate-screenshots
FLUTTER ?= flutter
DART ?= dart
JAVA_BIN ?=
NPX ?= npx
TEST_SERVER_HOST ?= 0.0.0.0
TEST_SERVER_PORT ?= 8000
TEST_APP_URL ?= http://localhost:$(TEST_SERVER_PORT)
TEST_API_BASE ?= $(TEST_APP_URL)/api
# Share one disposable database across this invocation's test tiers.
# Independent make/pytest runs must never reset another checkout's database.
TEST_DB_PATH := $(shell mktemp -d /tmp/signupflow-tests.XXXXXX)/signupflow_test.db
TEST_DB_PATH_STRIPPED := $(patsubst /%,%,$(TEST_DB_PATH))
TEST_DB_URL := sqlite:////$(TEST_DB_PATH_STRIPPED)
ifneq ($(filter test% pre-commit prepare-test-data ensure-test-env capture-screenshots validate-screenshots,$(MAKECMDGOALS)),)
export SIGNUPFLOW_TEST_DATABASE_URL := $(TEST_DB_URL)
export DATABASE_URL := $(TEST_DB_URL)
endif
# Detect available Docker Compose command (v1 `docker-compose` or v2 `docker compose`)
DOCKER_COMPOSE := $(shell \
if command -v docker-compose >/dev/null 2>&1; then \
echo docker-compose; \
elif docker compose version >/dev/null 2>&1; then \
echo docker compose; \
else \
echo missing; \
fi)
# Ensure Docker CLI and daemon are available before running docker-compose targets
check-docker:
@command -v docker >/dev/null 2>&1 || { \
echo "β Docker CLI not found. Install Docker Desktop or Docker Engine first."; \
exit 1; \
}
@docker info >/dev/null 2>&1 || { \
echo "β Cannot connect to the Docker daemon. Start Docker (Docker Desktop, colima, or 'systemctl start docker') and ensure your user can access /var/run/docker.sock."; \
exit 1; \
}
DOCKER_TARGETS := compose-up down build rebuild logs logs-api logs-db logs-redis shell db-shell redis-shell \
test-docker test-docker-quick test-docker-summary test-docker-file \
test-docker-unit test-docker-unit-fast test-docker-integration \
test-docker-coverage \
migrate-docker restart-api ps
$(DOCKER_TARGETS): check-docker
ensure-test-deps: check-python
@poetry check --lock
@poetry install --no-interaction
@poetry run python -c "import playwright, pytest"
prepare-test-data: ensure-test-deps
@echo "π§ͺ Preparing baseline test data..."
@poetry run python -m tests.setup_test_data
ensure-test-env: prepare-test-data
install-poetry:
@if ! command -v poetry >/dev/null 2>&1; then \
echo "π¦ Installing Poetry..."; \
echo " Trying official installer..."; \
if curl -sSL https://install.python-poetry.org | python3 - 2>/dev/null; then \
echo "β
Poetry installed successfully!"; \
echo "β οΈ Add Poetry to your PATH by running:"; \
echo " export PATH=\"\$$HOME/.local/bin:\$$PATH\""; \
echo " Or restart your terminal."; \
else \
echo "β οΈ Official installer failed. Trying pip installation..."; \
python3 -m pip install --user poetry && \
echo "β
Poetry installed via pip!" && \
echo "β οΈ You may need to add Poetry to your PATH:"; \
echo " export PATH=\"\$$HOME/Library/Python/3.9/bin:\$$PATH\""; \
fi \
else \
echo "β
Poetry is already installed: $$(poetry --version)"; \
fi
install-deps: install-poetry
@echo ""
@echo "β
All system dependencies installed!"
@echo ""
check-poetry:
@command -v poetry >/dev/null 2>&1 || { \
echo "β Poetry is not installed or not in PATH"; \
echo " Run 'make install-poetry' to install it automatically"; \
echo " Or install manually: curl -sSL https://install.python-poetry.org | python3 -"; \
exit 1; \
}
check-python: check-poetry
@PY_VERSION=$$(poetry run python --version 2>&1 | sed 's/Python //'); \
PY_MAJOR=$$(echo $$PY_VERSION | cut -d. -f1); \
PY_MINOR=$$(echo $$PY_VERSION | cut -d. -f2); \
if [ "$$PY_MAJOR" -ne 3 ] || [ "$$PY_MINOR" -lt 11 ] || [ "$$PY_MINOR" -gt 13 ]; then \
echo "β Python 3.11 through 3.13 required (you have: Python $$PY_VERSION)"; \
echo " Install with: brew install python@3.11"; \
exit 1; \
else \
echo "β
Python version OK: Python $$PY_VERSION"; \
fi
check-deps:
@echo "π Checking development dependencies..."
@echo ""
@echo "Python:"
@if python3 --version >/dev/null 2>&1; then \
PY_VERSION=$$(python3 --version 2>&1 | sed 's/Python //'); \
PY_MAJOR=$$(echo $$PY_VERSION | cut -d. -f1); \
PY_MINOR=$$(echo $$PY_VERSION | cut -d. -f2); \
echo " β
Python installed: $$PY_VERSION"; \
if [ "$$PY_MAJOR" -lt 3 ] || ([ "$$PY_MAJOR" -eq 3 ] && [ "$$PY_MINOR" -lt 10 ]); then \
echo " β οΈ Python 3.10+ required (upgrade recommended)"; \
else \
echo " β
Python 3.10+ detected"; \
fi; \
else \
echo " β Python not found"; \
fi
@echo ""
@echo "Poetry:"
@command -v poetry >/dev/null 2>&1 && echo " β
Poetry installed: $$(poetry --version)" || echo " β Poetry not installed"
@echo ""
@if ! command -v poetry >/dev/null 2>&1; then \
echo "β οΈ Missing dependencies detected. Run 'make help' for installation instructions."; \
else \
echo "β
All dependencies installed! You can run 'make setup' to install project packages."; \
fi
# Bring the app up. Two commands cover the whole lifecycle: 'make setup'
# prepares the environment, 'make up' serves the app. Which way it is served
# follows DATABASE_URL, so the same command works on either path.
up:
@set -e; \
DB_URL="$$($(DB_URL_CMD))"; \
case "$$DB_URL" in \
$(COMPOSE_DB_PATTERNS)) \
echo "π³ DATABASE_URL names the compose database, so the app runs there."; \
$(MAKE) compose-up; \
;; \
*) \
$(MAKE) serve; \
;; \
esac
# Run the development server on the host.
serve: check-poetry
@echo "π Starting SignUpFlow development server..."
@poetry run uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
run: up
# Run Celery worker
celery: check-poetry
@echo "π Starting Celery worker..."
@poetry run celery -A api.celery_app worker --loglevel=info
dev: run
stop:
@echo "Retired: stop the development server from the terminal that ran 'make up'."
@exit 2
restart:
@echo "Retired: stop the owned 'make up' process, then run 'make up' again."
@exit 2
# Deliberately does not depend on check-poetry or an installed virtualenv:
# this is what you run when setup itself fails, so it must work before setup.
doctor:
@python3 scripts/doctor.py
# Resolve DATABASE_URL the way the app does: an exported variable wins, because
# python-dotenv will not override one, and .env is only consulted when it does
# not. Shared by every target that needs to know which database is configured.
DB_URL_CMD = if [ -n "$$DATABASE_URL" ]; then printf '%s' "$$DATABASE_URL"; \
elif [ -f .env ]; then \
sed -n 's/^[[:space:]]*\(export[[:space:]][[:space:]]*\)\{0,1\}DATABASE_URL[[:space:]]*=[[:space:]]*//p' .env \
| tail -n 1 | sed -e 's/^"//' -e 's/"$$//' -e "s/^'//" -e "s/'$$//"; \
fi
# The shapes a URL takes when its host is the compose service name 'db', which
# resolves only inside the compose network. Kept in one place so every target
# agrees on what "the compose database" means.
COMPOSE_DB_PATTERNS = *@db:*|*@db/*|*@db
# Prepare everything the app needs to run: language dependencies, any backing
# services the configuration asks for, and the schema. It does not serve the
# app; 'make up' does that. Whether the services are containers is decided by
# DATABASE_URL, not by which target you typed, so the configuration and the
# commands cannot disagree.
setup:
@echo "π Starting SignUpFlow setup..."
@echo ""
@$(MAKE) check-python
@$(MAKE) install-deps
@$(MAKE) install
@$(MAKE) services
@$(MAKE) migrate
@if [ "$(SEED_DEMO)" != "false" ]; then echo ""; $(MAKE) --no-print-directory seed-demo; fi
@echo ""
@echo "β
Setup complete! Run 'make up' to start the app."
@echo " Visit http://localhost:8000/docs"
@echo ""
# Start the backing services the configuration points at, and nothing else.
#
# A compose hostname is only resolvable inside the compose network, so a
# compose-backed database brings its containers up here; the app itself waits
# for 'make up'. A SQLite or directly reachable database needs nothing, so the
# common case stays free of Docker entirely.
services:
@set -e; \
if [ -f /.dockerenv ]; then \
echo "βΉοΈ Inside a container; backing services are managed by compose."; \
else \
DB_URL="$$($(DB_URL_CMD))"; \
case "$$DB_URL" in \
$(COMPOSE_DB_PATTERNS)) \
echo "π³ DATABASE_URL names the compose database, which only resolves"; \
echo " inside docker compose, so the database runs there."; \
$(MAKE) --no-print-directory services-compose; \
;; \
*) \
echo "β
No backing services needed (using the configured database directly)."; \
;; \
esac; \
fi
# The branches the lifecycle targets choose between. A $(MAKE) line runs even
# under 'make -n', so the choosing lines above do nothing but choose; the work
# lives here, where a dry run only prints it.
services-compose: require-docker
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml up -d db redis
@echo "β
Backing services are up."
# One explanation of an unusable Docker, shared by every target that needs it,
# so the remedy never drifts between them. Callers that know why they wanted
# Docker say so first; this only reports that it is not there.
require-docker:
@if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then \
echo "β Docker is unavailable, so the compose stack cannot be reached."; \
echo " Start Docker and retry, or switch to SQLite for a host-only setup:"; \
echo " DATABASE_URL=sqlite:///./roster.db"; \
echo " Run 'make doctor' for the full environment report."; \
exit 1; \
fi
install: check-poetry
@echo "π¦ Installing project packages..."
@poetry install
@echo "β
Project packages installed"
# Migrations run wherever the configured database actually lives. A compose
# hostname resolves only inside that network, so running alembic on the host
# could never reach it; those migrations go through a one-off api container,
# which works whether or not the app is already serving.
migrate:
@set -e; \
DB_URL="$$($(DB_URL_CMD))"; \
case "$$DB_URL" in \
$(COMPOSE_DB_PATTERNS)) \
if [ -f /.dockerenv ]; then \
$(MAKE) --no-print-directory migrate-host; \
else \
echo "π³ DATABASE_URL names the compose database, which only resolves"; \
echo " inside docker compose, so migrations run there too."; \
$(MAKE) --no-print-directory migrate-compose; \
fi; \
;; \
*) \
$(MAKE) --no-print-directory migrate-host; \
;; \
esac
migrate-host: check-poetry
@echo "π Running database migrations..."
@poetry run alembic upgrade head
@echo "β
Migrations complete"
migrate-compose: require-docker
@echo "π Running database migrations inside compose..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml run --rm api alembic upgrade head
@echo "β
Migrations complete"
# Load the demo organization and print its sample logins. 'make setup' runs
# this last; set SEED_DEMO=false to skip it, and RESET=1 rebuilds it with
# fresh dates. It is routed like 'migrate',
# because it writes to the same database, and it refuses ENVIRONMENT=production.
SEED_DEMO_FLAGS = $(if $(filter 1 true yes,$(RESET)),--reset,)
seed-demo:
@set -e; \
DB_URL="$$($(DB_URL_CMD))"; \
case "$$DB_URL" in \
$(COMPOSE_DB_PATTERNS)) \
if [ -f /.dockerenv ]; then \
$(MAKE) --no-print-directory seed-demo-host; \
else \
$(MAKE) --no-print-directory seed-demo-compose; \
fi; \
;; \
*) \
$(MAKE) --no-print-directory seed-demo-host; \
;; \
esac
seed-demo-host: check-poetry
@poetry run python -m api.cli.main seed-demo $(SEED_DEMO_FLAGS)
seed-demo-compose: require-docker
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml run --rm api python -m api.cli.main seed-demo $(SEED_DEMO_FLAGS)
# Walk the demo in Chromium, WebKit and Firefox against the app you are running
# after 'make setup' and 'make up', including the Docker path. make test-all
# starts its own server and never exercises that path, so run this whenever
# setup, compose, headers or pages change. STACK_URL defaults to localhost:8000.
STACK_URL ?= http://localhost:8000
test-stack: check-poetry
@curl -sf $(STACK_URL)/health >/dev/null 2>&1 || { \
echo "β Nothing is serving $(STACK_URL). Run 'make setup' and 'make up' first."; \
exit 1; \
}
@SIGNUPFLOW_STACK_URL=$(STACK_URL) poetry run pytest tests/e2e/test_demo_tour.py -v --tb=short -p no:cacheprovider
# Run all backend tests
test: test-all
test-backend: test-all
test-integration: check-poetry
@echo "π§ͺ Running integration tests..."
@poetry run pytest tests/integration/ -v --tb=short
test-all: ensure-test-env
@echo "π Running complete test suite..."
@rm -f $(TEST_DB_PATH) $(TEST_DB_PATH)-shm $(TEST_DB_PATH)-wal
@echo "π Rebuilding fresh SQLite test database..."
@poetry run python -m tests.setup_test_data >/dev/null
@poetry run python scripts/run_local_validation.py
test-postgres: check-poetry check-docker
@echo "π§ͺ Running owned PostgreSQL acceptance..."
@poetry run python scripts/run_postgres_validation.py
test-redis: check-poetry check-docker
@echo "π§ͺ Running owned Redis quota, event-bus, and broker acceptance..."
@poetry run python scripts/run_redis_validation.py
test-artifact: check-poetry check-docker
@echo "π§ͺ Building and exercising an owned production artifact through loopback TLS..."
@poetry run python scripts/validate_production_artifact.py
test-staging: check-poetry
@test -n "$$STAGING_BASE_URL" || { echo "STAGING_BASE_URL is required"; exit 2; }
@test -n "$$STAGING_EXPECTED_RELEASE_SHA" || { echo "STAGING_EXPECTED_RELEASE_SHA is required"; exit 2; }
@test -n "$$STAGING_APPROVAL_REFERENCE" || { echo "STAGING_APPROVAL_REFERENCE is required"; exit 2; }
@echo "Running explicitly authorized staging acceptance against $$STAGING_BASE_URL..."
@poetry run python scripts/run_staging_acceptance.py \
--base-url "$$STAGING_BASE_URL" \
--expected-release-sha "$$STAGING_EXPECTED_RELEASE_SHA" \
--approval-reference "$$STAGING_APPROVAL_REFERENCE" \
--allow-authorized-remote
test-recovery: check-poetry
@echo "π§ͺ Running owned SQLite backup and restore acceptance..."
@poetry run python scripts/run_sqlite_recovery_drill.py
test-security: check-poetry check-docker
@echo "π§ͺ Scanning the committed source and exact retained production image..."
@poetry run python scripts/run_security_validation.py
test-docs: check-poetry
@echo "π§ͺ Validating the tracked documentation inventory and current local links..."
@poetry run python scripts/validate_documentation.py
test-web: check-poetry
@poetry run pytest tests/web/ -v --tb=short
test-contract: check-poetry
@poetry run pytest tests/contract/ -v --tb=short
test-e2e: check-poetry
@poetry run pytest tests/e2e/ -v --tb=short
capture-screenshots: ensure-test-env
@poetry run python scripts/capture_playbook_screenshots.py --source-ref "$${SCREENSHOT_SOURCE_REF:-$$(git rev-parse HEAD)}"
validate-screenshots: check-poetry
@poetry run python scripts/capture_playbook_screenshots.py --source-ref "$$(git rev-parse HEAD)" --validate-only
test-mobile:
@cd mobile && $(FLUTTER) pub get && $(FLUTTER) test
test-mobile-generated:
@cd mobile/api_client && $(DART) analyze --no-fatal-warnings && $(DART) test
test-performance: ensure-test-env
@poetry run pytest tests/performance/ -v --tb=short
test-load: check-poetry
@echo "Running bounded source-identified load validation..."
@poetry run python scripts/run_load_validation.py \
--start-local \
--profile tests/performance/profiles/local-smoke.json
test-coverage: check-poetry
@echo "π Generating test coverage reports..."
@poetry run pytest tests/ --cov=api --cov-report=html --cov-report=term
test-unit: check-poetry
@echo "π§ͺ Running unit tests..."
@poetry run pytest tests/unit/ -v --tb=short
test-unit-file: check-poetry
@echo "π§ͺ Running specific unit test file..."
@if [ -z "$(FILE)" ]; then \
echo "β Usage: make test-unit-file FILE=tests/unit/test_name.py"; \
exit 1; \
fi
@timeout 60 poetry run pytest $(FILE) -v --tb=short -s
clean:
@echo "Retired: each command cleans only the unique artifacts it owns."
@exit 2
clean-weekly:
@echo "Retired: no repository-wide scheduled deletion is supported."
@exit 2
clean-all:
@echo "Retired: remove dependencies or data only with an explicit path-specific action."
@exit 2
pre-commit: check-poetry
@echo "β‘ Running fast pre-commit tests..."
@poetry run pytest tests/unit/ -x --tb=short
@echo "β
Pre-commit tests passed!"
test-unit-fast: check-poetry
@echo "β‘ Running fast unit tests (skipping slow tests)..."
@poetry run pytest tests/unit/ -v --tb=short -m "not slow"
test-with-timing: check-poetry
@echo "β±οΈ Running tests with timing information..."
@poetry run pytest tests/unit/ --durations=20 -v --tb=short
update-openapi-snapshot: check-poetry
@echo "π Refreshing OpenAPI contract snapshot..."
@poetry run python -m tests.contract.test_openapi_snapshot --update
@echo "β
Snapshot updated. Review the diff, run 'make mobile-codegen' to refresh the Flutter client, then commit."
# ============================================================================
# Mobile (Flutter) β see specs/022-flutter-mobile-app/spec.md
# ============================================================================
# Validate the pinned Java/Node/Flutter/Dart toolchain and canonical schema.
# Override tool discovery with JAVA_BIN, NPX, FLUTTER, or DART when needed.
mobile-codegen-preflight: check-poetry
@JAVA_BIN="$(JAVA_BIN)" NPX="$(NPX)" FLUTTER="$(FLUTTER)" DART="$(DART)" \
poetry run python scripts/mobile_codegen.py --preflight
# Generate twice in temporary directories, require identical output, then sync
# the complete generated package. The strict canonical snapshot is never modified.
mobile-codegen: check-poetry
@JAVA_BIN="$(JAVA_BIN)" NPX="$(NPX)" FLUTTER="$(FLUTTER)" DART="$(DART)" \
poetry run python scripts/mobile_codegen.py --write
# Prove deterministic generation and fail on checked-in client drift without
# modifying mobile/api_client or the maintained Flutter application.
mobile-codegen-check: check-poetry
@JAVA_BIN="$(JAVA_BIN)" NPX="$(NPX)" FLUTTER="$(FLUTTER)" DART="$(DART)" \
poetry run python scripts/mobile_codegen.py --check
# ============================================================================
# Docker Compose Commands (Development Environment)
# ============================================================================
# The unconditional compose path. 'make up' routes here when DATABASE_URL names
# the compose database; call it directly to bring the stack up regardless.
compose-up:
@echo "π³ Starting SignUpFlow development environment..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml up -d
@echo ""
@echo "β
Services started!"
@echo " API: http://localhost:8000"
@echo " PostgreSQL: localhost:5433 (user: signupflow, db: signupflow_dev)"
@echo " Redis: localhost:6380"
@echo ""
@echo "View logs: make logs"
@echo "Stop services: make down"
@echo ""
down:
@echo "π Stopping SignUpFlow development environment..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml down
@echo "β
Services stopped"
build:
@echo "π¨ Building Docker images..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml build --no-cache
@echo "β
Build complete"
rebuild: down build compose-up
logs:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f
logs-api:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f api
logs-db:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f db
logs-redis:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f redis
logs-worker:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f worker
shell:
@echo "π Opening shell in API container..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec api bash
db-shell:
@echo "π Opening PostgreSQL shell..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec db psql -U signupflow -d signupflow_dev
redis-shell:
@echo "π΄ Opening Redis CLI..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec redis redis-cli -a dev_redis_password
# ============================================================================
# Docker-Based Testing
# ============================================================================
test-docker:
@echo "π§ͺ Running unit tests in Docker container..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/unit/ -v --tb=short
test-docker-quick:
@echo "β‘ Running unit tests in Docker (quick mode)..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/unit/ -v --tb=no
test-docker-summary:
@echo "π Running unit tests in Docker (summary only)..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/unit/ -v --tb=no 2>&1 | grep -E "(PASSED|FAILED|SKIPPED|ERROR|=====|passed|failed|warning)"
test-docker-file:
@echo "π― Running specific test file in Docker..."
@if [ -z "$(FILE)" ]; then \
echo "β Usage: make test-docker-file FILE=tests/unit/test_name.py"; \
exit 1; \
fi
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest $(FILE) -v --tb=short
test-docker-unit:
@echo "π§ͺ Running unit tests in Docker container..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/unit/ -v --tb=short
test-docker-unit-fast:
@echo "β‘ Running fast unit tests in Docker..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/unit/ -v --tb=short -m "not slow"
test-docker-integration:
@echo "π Running integration tests in Docker..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/integration/ -v --tb=short
test-docker-comprehensive:
@echo "Docker comprehensive validation is retired; run 'make test-all' locally."
@exit 2
test-docker-all:
@echo "Docker full validation is retired; run 'make test-all' locally."
@exit 2
test-docker-coverage:
@echo "π Running tests with coverage in Docker..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec -T api pytest tests/ --cov=api --cov-report=html --cov-report=term -v --tb=short
migrate-docker:
@echo "π Running migrations in Docker container..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml exec api alembic upgrade head
@echo "β
Migrations complete"
restart-api:
@echo "π Restarting API service..."
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml restart api
@echo "β
API restarted"
ps:
@$(DOCKER_COMPOSE) -f docker-compose.dev.yml ps
clean-docker:
@echo "Retired: use 'make down' for the owned Compose project; volume deletion is separate."
@exit 2
clean-docker-all:
@echo "Retired: global image and volume deletion is not a repository helper operation."
@exit 2
.DEFAULT_GOAL := help
help:
@echo "SignUpFlow Commands:"
@echo ""
@echo "π Quick Start:"
@echo " make doctor - Report what this machine will start the app with"
@echo " make setup - Prepare the environment: deps, services, schema"
@echo " make up - Start the app (follows DATABASE_URL)"
@echo ""
@echo "π³ Docker Development:"
@echo " make compose-up - Start all services (PostgreSQL + Redis + API)"
@echo " make down - Stop all services"
@echo " make logs - View logs from all services"
@echo " make logs-api - View API logs only"
@echo " make shell - Open bash shell in API container"
@echo " make db-shell - Open PostgreSQL shell"
@echo " make redis-shell - Open Redis CLI"
@echo " make test-docker - Run tests in Docker container"
@echo " make migrate-docker - Run migrations in Docker container"
@echo " make restart-api - Restart API service only"
@echo " make build - Build Docker images"
@echo " make rebuild - Rebuild and restart all services"
@echo " make ps - Show running services"
@echo " make clean-docker - Retired; does not delete volumes"
@echo " make clean-docker-all - Retired; does not delete images or volumes"
@echo ""
@echo "π» Local Development (Without Docker):"
@echo " make check-deps - Check which dependencies are installed"
@echo " make install-deps - Auto-install Poetry (if missing)"
@echo " make install-poetry - Auto-install Poetry only"
@echo " make install - Install project packages (requires Poetry)"
@echo " make serve - Start development server on the host (localhost:8000)"
@echo ""
@echo "Development:"
@echo " make run / make dev - Aliases for 'make up'"
@echo " make stop - Retired; stop the owning 'make up' terminal"
@echo " make restart - Retired; restart from the owning terminal"
@echo " make migrate - Run database migrations"
@echo " make seed-demo - Load the demo organization and print its logins (RESET=1 rebuilds)"
@echo " make test-stack - Browse the running app as the demo users in three browsers"
@echo ""
@echo "Testing:"
@echo " make test - Run backend tests"
@echo " make test-backend - Run backend Python tests only"
@echo " make test-integration - Run integration tests only"
@echo " make test-all - Run all Python tiers, including web/contract/Playwright"
@echo " make test-postgres - Run owned PostgreSQL migration/business/race acceptance"
@echo " make test-redis - Run owned Redis quota, event-bus, and broker acceptance"
@echo " make test-artifact - Exercise an owned production image and loopback TLS"
@echo " make test-staging - Run approval-gated Church/Basketball staging acceptance"
@echo " make test-security - Scan the exact committed source and retained image locally"
@echo " make test-recovery - Run the owned encrypted SQLite restore drill"
@echo " make test-docs - Validate documentation dispositions and current local links"
@echo " make test-performance - Run load tests against an explicit owned loopback server"
@echo " make test-load - Run bounded source-identified load validation locally"
@echo " make test-mobile - Run Flutter tests locally (requires Flutter SDK)"
@echo " make test-mobile-generated - Analyze and test the generated Dart client"
@echo " make mobile-codegen-preflight - Validate pinned tools and the OpenAPI snapshot"
@echo " make mobile-codegen - Reproducibly update the generated Dart API client"
@echo " make mobile-codegen-check - Detect generated-client drift without writing"
@echo " make test-e2e - Run Playwright browser tests locally"
@echo " make test-web - Run in-process web tests locally"
@echo " make test-contract - Run OpenAPI contract tests locally"
@echo " make test-coverage - Run tests with coverage reports"
@echo " make test-unit - Run unit tests only"
@echo " make test-unit-fast - Run fast unit tests (skip slow password tests)"
@echo " make test-unit-file - Run specific unit file (FILE=path/to/test.py)"
@echo " make test-with-timing - Run tests with timing information"
@echo " make pre-commit - Run fast tests for pre-commit hook"
@echo ""
@echo "Maintenance:"
@echo " make clean - Retired; commands clean only owned artifacts"
@echo " make clean-weekly - Retired; no broad scheduled deletion"
@echo " make clean-all - Retired; no broad dependency/data deletion"
@echo " make help - Show this help message"
@echo ""
@echo "Manual Dependency Installation (if auto-install fails):"
@echo " Poetry: curl -sSL https://install.python-poetry.org | python3 -"
@echo " Python: brew install python@3.11"
@echo ""