Local dev stack improvements

- Extend docker-compose.dev.yml with optional monitoring profile
  (Prometheus, Grafana, Loki) via --profile monitoring
- Align dev PostgreSQL with production: pg_stat_statements + init scripts
- Add scripts/seed.ts with idempotent Berlin test data (user, route, activity)
- Add pnpm db:seed and pnpm dev:reset scripts
- Simplify CI e2e job: replace manual docker run + BRouter setup with
  docker compose up --wait; add db:seed step
- Improve scripts/dev.sh: Docker check, --wait health checks, --monitoring
  flag, seed step
- Add scripts/reset-dev.sh to wipe and restart the local stack
- Add .env.development.example with documented local defaults

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-05-17 20:49:06 +02:00
parent d7b3c7c1a5
commit 10e0a8d41a
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
10 changed files with 209 additions and 105 deletions

View file

@ -6,50 +6,59 @@ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_DIR"
# Parse flags
MONITORING=false
for arg in "$@"; do
case "$arg" in
--monitoring) MONITORING=true ;;
esac
done
# Check Docker is running
if ! docker info > /dev/null 2>&1; then
echo "Error: Docker is not running. Please start Docker and try again."
exit 1
fi
echo "=== trails.cool dev environment ==="
echo ""
# 1. Start Docker services
echo "Starting Docker services..."
docker compose -f docker-compose.dev.yml up -d
if [ "$MONITORING" = "true" ]; then
echo "Starting Docker services (with monitoring)..."
docker compose -f docker-compose.dev.yml --profile monitoring up -d --wait
else
echo "Starting Docker services..."
docker compose -f docker-compose.dev.yml up -d --wait
fi
echo "✓ Services are ready"
# 2. Wait for PostgreSQL
echo "Waiting for PostgreSQL..."
until docker compose -f docker-compose.dev.yml exec -T postgres pg_isready -U trails > /dev/null 2>&1; do
sleep 1
done
echo "✓ PostgreSQL is ready"
# 3. Push database schema
# 2. Push database schema
echo "Pushing database schema..."
pnpm db:push 2>&1 | tail -3
echo "✓ Database schema up to date"
# 3. Seed database
echo "Seeding database..."
pnpm db:seed
echo "✓ Database seeded"
# 4. Download BRouter segments if needed
echo "Checking BRouter segments..."
"$SCRIPT_DIR/download-dev-segments.sh"
# 5. Wait for BRouter (if segments are available)
if docker compose -f docker-compose.dev.yml ps brouter --format '{{.State}}' 2>/dev/null | grep -q "running"; then
echo "Waiting for BRouter..."
for i in $(seq 1 30); do
if curl -sf http://localhost:17777/brouter?lonlats=13.4,52.5\|13.5,52.5\&profile=trekking\&format=geojson > /dev/null 2>&1; then
echo "✓ BRouter is ready"
break
fi
if [ "$i" = "30" ]; then
echo "⚠ BRouter not responding (segments may still be loading). Continuing..."
fi
sleep 2
done
fi
echo ""
echo "=== Starting apps ==="
echo " Journal: http://localhost:3000"
echo " Planner: http://localhost:3001"
echo " BRouter: http://localhost:17777"
if [ "$MONITORING" = "true" ]; then
echo " Grafana: http://localhost:3002"
echo " Prometheus: http://localhost:9090"
fi
echo ""
echo " Tip: pnpm dev:reset to wipe all data and start fresh"
echo ""
# 6. Start both apps with turbo
# 5. Start both apps with turbo
exec pnpm dev

26
scripts/reset-dev.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_DIR"
if ! docker info > /dev/null 2>&1; then
echo "Error: Docker is not running. Please start Docker and try again."
exit 1
fi
echo "=== Resetting dev environment ==="
echo "This will stop all containers and wipe all local data volumes."
echo ""
read -r -p "Are you sure? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Aborted."
exit 0
fi
echo "Stopping containers and removing volumes..."
docker compose -f docker-compose.dev.yml --profile monitoring down -v 2>/dev/null || \
docker compose -f docker-compose.dev.yml down -v
echo "✓ Done. Run 'pnpm dev:full' to start fresh."

72
scripts/seed.ts Normal file
View file

@ -0,0 +1,72 @@
import { createDb } from "../packages/db/src/index.ts";
import { users, routes, activities } from "../packages/db/src/schema/journal.ts";
const db = createDb();
const SEED_USER_ID = "seed-user-dev";
const SEED_ROUTE_ID = "seed-route-berlin";
const SEED_ACTIVITY_ID = "seed-activity-berlin";
// Minimal GPX for a short Berlin route (Tiergarten area)
const BERLIN_GPX = `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="trails.cool" xmlns="http://www.topografix.com/GPX/1/1">
<trk>
<name>Tiergarten Loop</name>
<trkseg>
<trkpt lat="52.5145" lon="13.3501"><ele>34</ele></trkpt>
<trkpt lat="52.5150" lon="13.3560"><ele>35</ele></trkpt>
<trkpt lat="52.5170" lon="13.3620"><ele>35</ele></trkpt>
<trkpt lat="52.5190" lon="13.3580"><ele>36</ele></trkpt>
<trkpt lat="52.5200" lon="13.3510"><ele>35</ele></trkpt>
<trkpt lat="52.5145" lon="13.3501"><ele>34</ele></trkpt>
</trkseg>
</trk>
</gpx>`;
await db
.insert(users)
.values({
id: SEED_USER_ID,
email: "dev@trails.cool",
username: "devuser",
displayName: "Dev User",
domain: "localhost:3000",
profileVisibility: "public",
})
.onConflictDoNothing();
await db
.insert(routes)
.values({
id: SEED_ROUTE_ID,
ownerId: SEED_USER_ID,
name: "Tiergarten Loop",
description: "A short loop through Tiergarten for local dev testing.",
gpx: BERLIN_GPX,
routingProfile: "trekking",
distance: 1200,
elevationGain: 5,
elevationLoss: 5,
visibility: "public",
})
.onConflictDoNothing();
await db
.insert(activities)
.values({
id: SEED_ACTIVITY_ID,
ownerId: SEED_USER_ID,
routeId: SEED_ROUTE_ID,
name: "Morning walk in Tiergarten",
gpx: BERLIN_GPX,
startedAt: new Date("2024-06-01T08:00:00Z"),
duration: 1800,
distance: 1200,
elevationGain: 5,
elevationLoss: 5,
visibility: "public",
})
.onConflictDoNothing();
console.log("✓ Seed complete");
process.exit(0);