-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathMakefile
More file actions
575 lines (493 loc) · 20.8 KB
/
Copy pathMakefile
File metadata and controls
575 lines (493 loc) · 20.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
# Omni v2 Makefile
# Universal Event-Driven Omnichannel Platform
.PHONY: help install dev dev-api dev-ui dev-services dev-stop build build-ui clean version \
test test-sweep test-watch test-api test-db test-pg-gate test-pg-gate-warm pg-gate-warm-stop \
typecheck typecheck-ui lint lint-fix lint-ui format check check-all dead-code verify-migrations verify-versions \
db-push db-migrate db-studio db-reset \
ensure-nats ensure-ffmpeg check-ffmpeg check-deps start stop restart logs status \
restart-api restart-nats restart-pgserve logs-api \
kill-ghosts kill-stale-test-daemons reset sdk-generate \
cli cli-build cli-build-full cli-link \
migrate-messages migrate-messages-dry \
_init-db-wait _sync-db
# Default target
help:
@echo "Omni v2 - Development Commands"
@echo ""
@echo "Quick Start:"
@echo " make setup One-command setup: install + services + dev (RECOMMENDED)"
@echo ""
@echo "Development:"
@echo " make install Install deps + .env + init database"
@echo " make dev Start services + API in watch mode"
@echo " make dev-api Start just the API (services must be running)"
@echo " make dev-ui Start UI dev server (Vite on :5173)"
@echo " make dev-services Start infrastructure (pgserve + NATS) via PM2"
@echo " make dev-stop Stop PM2 dev services"
@echo ""
@echo "Quality:"
@echo " make check Run all quality checks (typecheck + lint + dead-code + migrations + test)"
@echo " make check-all Run check AND the pg-gate concurrently (fastest full validation)"
@echo " make verify-migrations Static migration contract gate (journal/immutability/lint)"
@echo " make verify-versions Version sync gate (BASE=<ref> exempts files absent on ref)"
@echo " make typecheck TypeScript type checking"
@echo " make lint Run Biome linter"
@echo " make lint-fix Fix auto-fixable lint issues"
@echo " make lint-api Lint API package only"
@echo " make format Format code with Biome"
@echo " make dead-code Run knip dead code detection"
@echo " make test Run all tests (per-package, turbo-cached)"
@echo " make test-sweep Run all tests in one process (CI parity, leak hunting)"
@echo " make test-watch Run tests in watch mode"
@echo " make test-api Run API package tests only"
@echo " make test-db Run DB package tests only"
@echo " make test-pg-gate Run every real-PostgreSQL suite (fails loudly, never skips)"
@echo " make test-pg-gate-warm Same gate against a kept-warm cluster (fast dev loop)"
@echo " make pg-gate-warm-stop Destroy the kept-warm pg-gate cluster"
@echo " make test-file F=<path> Run a specific test file"
@echo " make kill-stale-test-daemons Sweep leaked PM2 god daemons from CLI tests (#413)"
@echo ""
@echo "Database:"
@echo " make db-push Push schema changes (dev-only; requires I_KNOW_WHAT_IM_DOING=1)"
@echo " make db-migrate Run migrations (prod)"
@echo " make db-studio Open Drizzle Studio"
@echo " make db-reset Reset database (DESTRUCTIVE)"
@echo ""
@echo "Migrations:"
@echo " make migrate-messages-dry Dry run: events → unified messages"
@echo " make migrate-messages Run migration: events → unified messages"
@echo ""
@echo "Building:"
@echo " make build Build all packages"
@echo " make build-ui Build UI for production"
@echo " make clean Clean build artifacts"
@echo ""
@echo "Production:"
@echo " make start Start production (PM2)"
@echo " make stop Stop all services"
@echo " make restart Restart all services"
@echo " make logs View logs"
@echo " make status Check service status"
@echo ""
@echo "Individual Services:"
@echo " make restart-api Restart API only"
@echo " make restart-nats Restart NATS only"
@echo " make restart-pgserve Restart pgserve (embedded in API)"
@echo " make logs-api View API logs"
@echo ""
@echo "CLI:"
@echo " make cli ARGS=\"...\" Run CLI from source"
@echo " make cli-build Build CLI package"
@echo " make cli-build-full Build CLI client + server bundles"
@echo " make cli-link Build + link globally (omni command)"
@echo ""
@echo "SDK:"
@echo " make sdk-generate Generate SDK from OpenAPI spec"
@echo ""
@echo "Setup:"
@echo " make check-deps Check for required dependencies"
@echo " make ensure-nats Download NATS binary if missing"
@echo " make ensure-ffmpeg Install ffmpeg (for WhatsApp voice note conversion)"
@echo " make check-ffmpeg Check if ffmpeg is installed"
@echo " make reset Full clean and reinstall"
# ============================================================================
# Development
# ============================================================================
install: _install-deps _setup-env _init-db
@echo ""
@echo "✓ Installation complete!"
@echo ""
@echo "Next steps:"
@echo " 1. Start services: make dev-services"
@echo " 2. Start API: make dev-api"
@echo " 3. In another terminal:"
@echo " make dev"
_install-deps:
@echo "Installing dependencies..."
bun install
_setup-env:
@if [ ! -f .env ]; then \
echo "Creating .env from .env.example..."; \
cp .env.example .env; \
echo "✓ .env created"; \
else \
echo "✓ .env already exists"; \
fi
_init-db: _setup-env
@echo "Initializing database schema..."
@set -a && . ./.env && set +a && \
cd packages/db && \
bun x drizzle-kit push --force 2>/dev/null && \
echo "✓ Database schema initialized" || \
echo "⚠️ Database initialization skipped (PostgreSQL may not be running yet)"
# Retry-based DB init: waits for PostgreSQL to be ready (used by setup/dev after dev-services)
_init-db-wait: _setup-env
@echo "Initializing database schema (waiting for PostgreSQL)..."
@set -a && . ./.env && set +a && \
for i in $$(seq 1 15); do \
(cd packages/db && bun x drizzle-kit push --force) >/dev/null 2>&1 && \
echo "✓ Database schema initialized" && exit 0 || \
sleep 1; \
done; \
echo "⚠️ Could not initialize database (is PostgreSQL running?)" && exit 1
# Quick schema sync — assumes DB is already running (used by test targets)
_sync-db:
@set -a && . ./.env && set +a && \
cd packages/db && bun x drizzle-kit push --force </dev/null >/dev/null 2>&1 && \
echo "✓ Schema synced" || \
echo "⚠️ Schema sync skipped (is PostgreSQL running?)"
# Check dependencies
check-deps:
@./scripts/check-dependencies.sh
# Complete setup: install + services + init DB + build SDK + start all
# Order matters: dev-services starts PostgreSQL, _init-db-wait retries until DB is ready,
# _build-dist builds SDK before UI needs it, then turbo dev starts API + UI.
setup: check-deps install dev-services _init-db-wait _build-dist
@echo ""
@echo "✓ Setup complete! Starting development..."
bun run dev
# Start dev services (PM2), init DB if needed, build SDK, then run turbo dev
dev: dev-services _init-db-wait _build-dist
bun run dev
# Start just the API (assumes services already running)
# OMNI_PACKAGES_DIR is passed explicitly to ensure correct plugin directory
dev-api:
@set -a && . ./.env && set +a && OMNI_PACKAGES_DIR=/home/cezar/dev/omni-v2/packages bun --watch packages/api/src/index.ts
# Start infrastructure services via PM2 (pgserve + NATS only)
# API is NOT started here — turbo dev handles it (avoids port conflicts, see GH #14)
dev-services: ensure-nats
@if [ ! -f .env ]; then \
echo "Creating .env from .env.example..."; \
cp .env.example .env; \
fi
@echo "Starting infrastructure services (pgserve + NATS)..."
@set -a && . ./.env && set +a && API_MANAGED=false pm2 start ecosystem.config.cjs || true
@sleep 2
@$(MAKE) status
# Stop PM2 dev services
dev-stop:
-pm2 stop ecosystem.config.cjs 2>/dev/null || true
-pm2 delete ecosystem.config.cjs 2>/dev/null || true
@echo "Dev services stopped"
# Start UI dev server (Vite)
dev-ui:
cd apps/ui && bun run dev
# ============================================================================
# Quality Checks
# ============================================================================
typecheck: _build-dist
bun run typecheck
lint:
bunx biome check .
lint-fix:
bunx biome check --write .
# Lint specific packages (faster during development)
lint-api:
bunx biome check packages/api
lint-db:
bunx biome check packages/db
lint-ui:
cd apps/ui && bun run lint
typecheck-ui:
cd apps/ui && bun run typecheck
lint-core:
bunx biome check packages/core
format:
bunx biome format --write .
# Per-package tests via turbo (#967): unchanged packages replay their cached
# green run instead of re-executing, so an incremental `make check` pays only
# for the packages the change touched. The turbo `test` task hashes
# DATABASE_URL / TEST_DATABASE_URL / ENABLE_DB_TESTS / OMNI_* on top of each
# package's files, so pointing at a different database invalidates the cache.
# Env is loaded into the environment here because per-package `bun test` runs
# have no --env-file. Workspaces only — apps/khal-ui stays out (private
# @khal-os deps, own test run).
# LOG_LEVEL is forced to 'silent' AFTER .env loads (which sets debug): suites
# intentionally exercise thousands of warn/error paths and the JSON flood is
# pure I/O + noise (#967). Override: make test TEST_LOG_LEVEL=debug
test: _build-dist _sync-db
@set -a && . ./.env && set +a && LOG_LEVEL=$(or $(TEST_LOG_LEVEL),silent) bun run test
# The pre-#967 single-process sweep, kept for CI parity and for hunting
# cross-file state leaks (e.g. globalThis.fetch pollution) that only reproduce
# when every suite shares one process.
test-sweep: _build-dist _sync-db
bun test --env-file=.env packages apps/ui
test-watch: _build-dist
bun test --env-file=.env --watch
# Build SDK and CLI dist (required for CLI tests)
_build-dist:
@if [ ! -d packages/sdk/dist ]; then \
echo "Building SDK..."; \
cd packages/sdk && bun run build; \
fi
@if [ ! -d packages/cli/dist ]; then \
echo "Building CLI..."; \
cd packages/cli && bun run build; \
fi
# Run tests for specific packages (load .env from root)
test-api: _sync-db
bun test --env-file=.env packages/api/src
test-db:
bun test --env-file=.env packages/db/src
# Real-PostgreSQL gate (wish: omni-full-multitenancy, G3).
#
# Runs EVERY *-postgres.test.ts suite against a disposable cluster it creates
# and destroys, and FAILS LOUDLY when no server is available — unlike a plain
# `bun test`, where those suites opt out silently and a green run proves nothing
# about tenant isolation. No .env is loaded: the gate never touches an ambient
# DATABASE_URL or a shared cluster.
test-pg-gate:
bun scripts/pg-gate.ts
# Keep-warm variant (#967): first run creates the disposable cluster and
# remembers it in .pg-gate-warm.json; later runs reuse it via --url, skipping
# the ~15-20s initdb + role setup per iteration. Same gate, same zero-skip
# contract. `make pg-gate-warm-stop` destroys the kept cluster.
test-pg-gate-warm:
bun scripts/pg-gate-warm.ts run
pg-gate-warm-stop:
bun scripts/pg-gate-warm.ts stop
# Run a specific test file (usage: make test-file F=packages/api/src/__tests__/foo.test.ts)
test-file:
@if [ -z "$(F)" ]; then echo "Usage: make test-file F=<path-to-test-file>"; exit 1; fi
bun test $(F)
dead-code:
bunx knip
# Static migration contract gate (journal ↔ files, immutability, additive-only
# lint, headers, schema.ts pairing). No DB — see scripts/verify-migration-contract.ts.
# Base ref defaults to origin/dev; override: make verify-migrations BASE=origin/main
verify-migrations:
bun test scripts/verify-migration-contract.test.ts
bun scripts/verify-migration-contract.ts $(if $(BASE),--base $(BASE),)
# Version sync gate: every tracked version field matches root package.json.
# Pass BASE=<ref> to exempt files absent on that ref (what CI does on PRs, #1020).
verify-versions:
bun test scripts/verify-versions.test.ts
bun scripts/verify-versions.ts $(if $(BASE),--base $(BASE),)
# Run all quality checks
check: typecheck lint dead-code verify-migrations verify-versions test
@echo ""
@echo "All checks passed!"
# Run `make check` and the real-PostgreSQL gate CONCURRENTLY (#967). Safe
# because the pg-gate stands up its own disposable cluster on a random
# loopback port and never reads .env, so it cannot collide with check's
# database. pg-gate output is spooled to a temp file and replayed after
# check's, so the two streams never interleave.
check-all:
@pg_out=$$(mktemp -t omni-pg-gate-out); \
( $(MAKE) test-pg-gate >"$$pg_out" 2>&1 ) & pg_pid=$$!; \
check_status=0; $(MAKE) check || check_status=$$?; \
pg_status=0; wait $$pg_pid || pg_status=$$?; \
echo ""; echo "===== pg-gate (ran concurrently) ====="; \
cat "$$pg_out"; rm -f "$$pg_out"; \
if [ $$check_status -ne 0 ] || [ $$pg_status -ne 0 ]; then \
echo ""; echo "check-all: FAILED (check=$$check_status, pg-gate=$$pg_status)"; \
exit 1; \
fi; \
echo ""; echo "check-all: PASSED — check and pg-gate both green"
# ============================================================================
# Database (Drizzle)
# ============================================================================
db-push:
@if [ "$$I_KNOW_WHAT_IM_DOING" != "1" ]; then \
echo "drizzle-kit push is DISABLED by default (issue #407)."; \
echo ""; \
echo "drizzle-kit push and migrateDb() are INCOMPATIBLE. Push creates"; \
echo "tables without migration journal entries; the API's auto-migrate"; \
echo "then crashes with 'relation already exists' or leaves columns in"; \
echo "a drifted state (see CLAUDE.md: 'Database & Migrations')."; \
echo ""; \
echo "For dev-only schema exploration, rerun with I_KNOW_WHAT_IM_DOING=1:"; \
echo " I_KNOW_WHAT_IM_DOING=1 make db-push"; \
echo ""; \
echo "For any shared / staging / production DB, use migrations instead:"; \
echo " cd packages/db && bunx drizzle-kit generate"; \
exit 1; \
fi
@set -a && . ./.env && set +a && cd packages/db && bunx drizzle-kit push --force
db-migrate:
@set -a && . ./.env && set +a && bun run --filter @omni/db db:migrate
db-studio:
@set -a && . ./.env && set +a && bun run --filter @omni/db db:studio
db-reset:
@echo "WARNING: This will delete all data!"
@read -p "Are you sure? [y/N] " confirm && [ "$$confirm" = "y" ] || exit 1
@echo "Resetting database..."
@# TODO: Implement reset logic
# ============================================================================
# Building
# ============================================================================
version:
bun run scripts/generate-version.ts
sync-versions: version
@VERSION=$$(cat version.json | bun -e 'const v=await Bun.stdin.json();console.log(v.version)') && \
bun scripts/sync-versions.ts $$VERSION
build: version
bun run build
build-ui:
cd apps/ui && bun run build
clean:
rm -rf node_modules/.cache
rm -rf packages/*/dist
rm -rf apps/*/dist
rm -rf .turbo
@echo "Clean complete"
# ============================================================================
# NATS Setup
# ============================================================================
ensure-nats:
@./scripts/ensure-nats.sh
# ============================================================================
# Production (PM2)
# ============================================================================
start: ensure-nats
@set -a && . ./.env && set +a && pm2 start ecosystem.config.cjs
stop:
-pm2 stop all 2>/dev/null || true
-pm2 delete all 2>/dev/null || true
@$(MAKE) kill-ghosts
restart:
pm2 restart omni-v2-api omni-v2-nats --update-env
logs:
pm2 logs
status:
@pm2 list 2>/dev/null || echo "PM2 not running"
@echo ""
@echo "Service URLs:"
@echo " API: http://localhost:$${API_PORT:-8882}"
@echo " Swagger: http://localhost:$${API_PORT:-8882}/api/v2/docs"
@echo " PostgreSQL: localhost:$${PGSERVE_PORT:-8432}"
@echo " NATS: localhost:$${NATS_PORT:-4222}"
# Individual service control
restart-api:
pm2 restart omni-v2-api --update-env
restart-nats:
pm2 restart omni-v2-nats --update-env
restart-pgserve:
@echo "pgserve is embedded in the API server — restarting API..."
$(MAKE) restart-api
logs-api:
pm2 logs omni-v2-api --lines 100
# Kill ghost processes that might block ports
kill-ghosts:
@echo "Cleaning up ghost processes..."
-pkill -f "bun run" 2>/dev/null || true
-pkill -f "bunx pgserve" 2>/dev/null || true
-pkill -f "nats-server" 2>/dev/null || true
-lsof -ti :3000 | xargs kill -9 2>/dev/null || true
-lsof -ti :4222 | xargs kill -9 2>/dev/null || true
-lsof -ti :8432 | xargs kill -9 2>/dev/null || true
@echo "Ghost cleanup complete"
# Sweep up leaked PM2 god daemons spawned by CLI tests (issue #413).
# Stale daemons have a PM2_HOME under /tmp/.omni-test-* that no longer exists.
kill-stale-test-daemons:
@bun scripts/kill-stale-test-daemons.ts
# ============================================================================
# Utilities
# ============================================================================
# Generate SDK from OpenAPI spec
sdk-generate:
bun run generate:sdk
# CLI commands
cli:
@bun packages/cli/src/index.ts $(ARGS)
cli-build:
bun run --cwd packages/cli build
cli-build-full: ## Build CLI client + server bundles
cd packages/cli && bun run build && bun run build:server
cli-link: cli-build
cd packages/cli && bun link
@echo "✓ 'omni' command now available globally"
# Full clean and reinstall
reset: clean stop
rm -rf node_modules
rm -rf packages/*/node_modules
rm -rf .pgserve-data
bun install
@echo "Reset complete"
# ============================================================================
# Data Migrations
# ============================================================================
# Migrate events to unified messages (dry run - no changes)
migrate-messages-dry:
@echo "Running migration dry-run (no changes will be made)..."
cd packages/api && bun run scripts/migrate-events-to-messages.ts --dry-run
# Migrate events to unified messages (LIVE - makes changes)
migrate-messages:
@echo "WARNING: This will migrate events to the unified messages schema."
@read -p "Are you sure? [y/N] " confirm && [ "$$confirm" = "y" ] || exit 1
cd packages/api && bun run scripts/migrate-events-to-messages.ts
# ============================================================================
# FFmpeg Setup (for WhatsApp voice note conversion)
# ============================================================================
# Check if ffmpeg is installed
check-ffmpeg:
@echo "Checking ffmpeg installation..."
@if command -v ffmpeg >/dev/null 2>&1; then \
echo "✓ ffmpeg is installed"; \
ffmpeg -version 2>&1 | head -1; \
echo ""; \
echo "Checking for libopus codec support..."; \
if ffmpeg -encoders 2>/dev/null | grep -q libopus; then \
echo "✓ libopus codec available"; \
else \
echo "⚠️ libopus codec not available (voice note conversion may not work)"; \
fi; \
else \
echo "✗ ffmpeg is NOT installed"; \
echo ""; \
echo "Run 'make ensure-ffmpeg' to install it"; \
fi
# Install ffmpeg with opus support (auto-detects OS)
ensure-ffmpeg:
@echo "Installing ffmpeg..."
@if command -v ffmpeg >/dev/null 2>&1; then \
echo "✓ ffmpeg is already installed"; \
$(MAKE) check-ffmpeg; \
elif [ "$$(uname)" = "Darwin" ]; then \
echo "Detected macOS - using Homebrew..."; \
if command -v brew >/dev/null 2>&1; then \
brew install ffmpeg; \
else \
echo "✗ Homebrew not found. Please install it first:"; \
echo " /bin/bash -c \"\$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""; \
exit 1; \
fi; \
elif [ -f /etc/debian_version ]; then \
echo "Detected Debian/Ubuntu - using apt..."; \
sudo apt-get update && sudo apt-get install -y ffmpeg; \
elif [ -f /etc/fedora-release ]; then \
echo "Detected Fedora - using dnf..."; \
sudo dnf install -y ffmpeg; \
elif [ -f /etc/arch-release ]; then \
echo "Detected Arch Linux - using pacman..."; \
sudo pacman -S --noconfirm ffmpeg; \
elif [ -f /etc/alpine-release ]; then \
echo "Detected Alpine - using apk..."; \
sudo apk add --no-cache ffmpeg; \
elif grep -qi microsoft /proc/version 2>/dev/null; then \
echo "Detected WSL - using apt..."; \
sudo apt-get update && sudo apt-get install -y ffmpeg; \
else \
echo "✗ Could not detect OS. Please install ffmpeg manually:"; \
echo ""; \
echo " macOS: brew install ffmpeg"; \
echo " Ubuntu/Debian: sudo apt install ffmpeg"; \
echo " Fedora: sudo dnf install ffmpeg"; \
echo " Arch: sudo pacman -S ffmpeg"; \
echo " Alpine: apk add ffmpeg"; \
echo " Windows: choco install ffmpeg"; \
exit 1; \
fi
@echo ""
@echo "✓ ffmpeg installation complete!"
@$(MAKE) check-ffmpeg
# Integration test for messaging
test-messaging:
@echo "Running messaging integration tests..."
bun --env-file=.env scripts/test-messaging.ts
test-messaging-whatsapp:
@echo "Running WhatsApp integration tests..."
bun --env-file=.env scripts/test-messaging.ts --channel=whatsapp
test-messaging-discord:
@echo "Running Discord integration tests..."
bun --env-file=.env scripts/test-messaging.ts --channel=discord