Add local dev setup, fix BRouter Dockerfile, archive change (#12)

This commit is contained in:
Ullrich Schäfer 2026-03-23 00:11:43 +01:00 committed by GitHub
parent 2d02ae3f9e
commit 52b2baaf58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 329 additions and 9 deletions

View file

@ -62,6 +62,10 @@ pnpm test # Run unit tests (vitest)
pnpm test:watch # Run unit tests in watch mode
pnpm test:e2e # Run E2E tests (playwright, requires dev servers)
pnpm test:e2e:ui # Run E2E tests with Playwright UI
pnpm dev:full # Start full stack (Docker + DB + BRouter + apps)
pnpm dev:services # Start Docker services only (PostgreSQL + BRouter)
pnpm db:push # Push Drizzle schema to local PostgreSQL
pnpm db:studio # Open Drizzle Studio (DB browser)
```
## Testing Strategy

View file

@ -35,7 +35,7 @@ packages/
## Getting Started
Prerequisites: Node.js 20+, pnpm
Prerequisites: Node.js 20+, pnpm, Docker
```bash
# Clone
@ -45,8 +45,29 @@ cd trails
# Install dependencies
pnpm install
# Start development
# Start development (apps only, no database or routing)
pnpm dev
# Start full stack (PostgreSQL + BRouter + apps)
pnpm dev:full
```
### Full Local Dev Setup
`pnpm dev:full` starts everything needed to test the Planner end-to-end:
1. **PostgreSQL + PostGIS** on port 5432 (via Docker)
2. **BRouter** routing engine on port 17777 (via Docker)
3. **Database schema** pushed automatically via Drizzle
4. **BRouter segment** downloaded for Berlin area (~124MB, cached)
5. **Journal** on http://localhost:3000
6. **Planner** on http://localhost:3001
Other useful commands:
```bash
pnpm dev:services # Start Docker services only (DB + BRouter)
pnpm db:push # Push database schema changes
pnpm db:studio # Open Drizzle Studio (DB browser)
```
## Development Tools

View file

@ -1,6 +1,6 @@
services:
postgres:
image: postgis/postgis:16-3.4
image: imresamu/postgis:16-3.4 # multi-arch (amd64 + arm64)
ports:
- "5432:5432"
environment:
@ -15,5 +15,13 @@ services:
timeout: 5s
retries: 5
brouter:
build: docker/brouter
ports:
- "17777:17777"
volumes:
- brouter_segments:/data/segments
volumes:
pgdata:
brouter_segments:

View file

@ -4,17 +4,20 @@ WORKDIR /brouter
# Download latest BRouter release
ARG BROUTER_VERSION=1.7.8
RUN apt-get update && apt-get install -y --no-install-recommends wget unzip \
RUN apt-get update && apt-get install -y --no-install-recommends wget unzip curl \
&& wget -q "https://github.com/abrensch/brouter/releases/download/v${BROUTER_VERSION}/brouter-${BROUTER_VERSION}.zip" \
&& unzip "brouter-${BROUTER_VERSION}.zip" \
&& mv "brouter-${BROUTER_VERSION}"/* . \
&& rmdir "brouter-${BROUTER_VERSION}" \
&& rm "brouter-${BROUTER_VERSION}.zip" \
&& apt-get purge -y wget unzip && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
# Create directories for segments and profiles
RUN mkdir -p /data/segments /data/profiles
# Copy default profiles from release
RUN cp -r profiles2/* /data/profiles/ 2>/dev/null || true
# Copy default profiles from release and rename JAR for simpler CMD
RUN cp -r profiles2/* /data/profiles/ 2>/dev/null || true \
&& mv brouter-*-all.jar brouter.jar
EXPOSE 17777

View file

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-22

View file

@ -0,0 +1,63 @@
## Context
The Planner app has server-side dependencies (PostgreSQL for sessions, BRouter
for routing) that aren't available when running `pnpm dev`. The app starts but
session creation and route computation fail. We need a local dev environment
that mirrors production dependencies.
## Goals / Non-Goals
**Goals:**
- One command to start the full local stack (DB + BRouter + apps)
- Database migrations run automatically
- BRouter has at least one segment for local route testing
- Works on macOS (primary dev platform)
**Non-Goals:**
- Full Germany segment coverage (too large for dev, one tile is enough)
- Production-like Caddy/Garage setup locally
- CI integration (CI doesn't need BRouter for current tests)
## Decisions
### D1: Extend docker-compose.dev.yml with BRouter
Add BRouter to the existing dev compose file. Use a named volume for segments
so they persist across restarts.
**Alternative**: Run BRouter natively (requires JVM). Rejected — Docker is
simpler and consistent.
### D2: Use a small test segment
Download one BRouter segment covering Berlin area (E10_N50.rd5, ~124MB) for
local testing. This is small enough to download quickly but covers a useful
area for route testing.
**Alternative**: Download all Germany (~750MB). Rejected — too slow for first
dev setup. Can always download more tiles manually.
### D3: Drizzle push for dev, migrations for production
Use `drizzle-kit push` in development (applies schema directly, no migration
files needed). Use `drizzle-kit migrate` in production.
**Alternative**: Always use migrations. Rejected — adds friction during rapid
schema iteration in dev.
### D4: Script-based orchestration
A shell script (`scripts/dev.sh`) starts Docker services, waits for health
checks, runs migrations, and starts the apps. The `pnpm dev:full` command
wraps this script.
**Alternative**: Use Docker Compose for everything including apps. Rejected —
we want Vite HMR for the apps, which works better running natively.
## Risks / Trade-offs
**[Docker required]** → Developers need Docker Desktop or OrbStack running.
Mitigation: Document in README prerequisites.
**[Segment download time]** → First run downloads ~124MB. Mitigation: Only
one tile, and it's cached in a Docker volume.

View file

@ -0,0 +1,31 @@
## Why
We can't test the Planner end-to-end locally. The Planner needs PostgreSQL
for session persistence and BRouter for route computation. Without a one-command
local dev setup, developers must manually run Docker containers and download
BRouter segments. This blocks iteration on Groups 5-6.
## What Changes
- Add a `pnpm dev:full` command that starts PostgreSQL, BRouter, and both apps
- Download a minimal BRouter segment for local testing (one tile)
- Run database migrations automatically on dev startup
- Seed data for quick testing (sample session with waypoints)
- Document local dev setup in README
## Capabilities
### New Capabilities
- `local-dev-environment`: One-command local development setup with PostgreSQL, BRouter, and both apps running together
### Modified Capabilities
(None)
## Impact
- **docker-compose.dev.yml**: Add BRouter service with segment volume
- **package.json**: New `dev:full` script
- **packages/db**: Add Drizzle migration runner
- **README.md**: Document local dev prerequisites and setup

View file

@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: One-command dev startup
The project SHALL provide a single command that starts all services needed for local development.
#### Scenario: Start full dev stack
- **WHEN** a developer runs `pnpm dev:full`
- **THEN** PostgreSQL, BRouter, and both apps start and are reachable at their respective ports
#### Scenario: Services are healthy before apps start
- **WHEN** the dev script starts
- **THEN** it waits for PostgreSQL health check and BRouter readiness before starting the apps
### Requirement: Automatic database setup
The dev environment SHALL automatically create schemas and tables on startup.
#### Scenario: First run database setup
- **WHEN** `pnpm dev:full` runs for the first time
- **THEN** the planner and journal PostgreSQL schemas and tables are created automatically
### Requirement: BRouter segment for local testing
The dev environment SHALL include at least one BRouter segment for route computation testing.
#### Scenario: Download segment on first run
- **WHEN** the BRouter Docker volume has no segments
- **THEN** the dev script downloads E10_N50.rd5 (Berlin area, ~124MB)
#### Scenario: Skip download if segment exists
- **WHEN** the BRouter Docker volume already has segments
- **THEN** the dev script skips the download
### Requirement: Local route computation
The Planner SHALL be able to compute routes locally in the dev environment.
#### Scenario: Compute a route in Berlin
- **WHEN** a developer sends a route request with two Berlin waypoints to the local Planner
- **THEN** the Planner proxies to local BRouter and returns a valid GeoJSON route

View file

@ -0,0 +1,26 @@
## 1. Docker Compose Dev Services
- [x] 1.1 Add BRouter service to docker-compose.dev.yml with segment volume
- [x] 1.2 Add BRouter profiles volume (included in BRouter Dockerfile)
- [x] 1.3 Verify PostgreSQL + BRouter start with `docker compose -f docker-compose.dev.yml up -d`
## 2. Segment Download
- [x] 2.1 Create scripts/download-dev-segments.sh that downloads E10_N50.rd5 to BRouter volume
- [x] 2.2 Skip download if segment already exists in volume
## 3. Database Setup
- [x] 3.1 Add `pnpm db:push` script that runs `drizzle-kit push` against local PostgreSQL
- [x] 3.2 Verify planner and journal schemas are created
## 4. Dev Orchestration
- [x] 4.1 Create scripts/dev.sh that starts Docker services, waits for health, pushes schema, starts apps
- [x] 4.2 Add `pnpm dev:full` script to root package.json
- [x] 4.3 Add `pnpm dev:services` script to start only Docker services (for running apps separately)
## 5. Documentation & Verification
- [x] 5.1 Update README.md with local dev prerequisites (Docker, pnpm) and setup instructions
- [x] 5.2 Test full flow: start dev stack → create session → compute route with Berlin waypoints

View file

@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: One-command dev startup
The project SHALL provide a single command that starts all services needed for local development.
#### Scenario: Start full dev stack
- **WHEN** a developer runs `pnpm dev:full`
- **THEN** PostgreSQL, BRouter, and both apps start and are reachable at their respective ports
#### Scenario: Services are healthy before apps start
- **WHEN** the dev script starts
- **THEN** it waits for PostgreSQL health check and BRouter readiness before starting the apps
### Requirement: Automatic database setup
The dev environment SHALL automatically create schemas and tables on startup.
#### Scenario: First run database setup
- **WHEN** `pnpm dev:full` runs for the first time
- **THEN** the planner and journal PostgreSQL schemas and tables are created automatically
### Requirement: BRouter segment for local testing
The dev environment SHALL include at least one BRouter segment for route computation testing.
#### Scenario: Download segment on first run
- **WHEN** the BRouter Docker volume has no segments
- **THEN** the dev script downloads E10_N50.rd5 (Berlin area, ~124MB)
#### Scenario: Skip download if segment exists
- **WHEN** the BRouter Docker volume already has segments
- **THEN** the dev script skips the download
### Requirement: Local route computation
The Planner SHALL be able to compute routes locally in the dev environment.
#### Scenario: Compute a route in Berlin
- **WHEN** a developer sends a route request with two Berlin waypoints to the local Planner
- **THEN** the Planner proxies to local BRouter and returns a valid GeoJSON route

View file

@ -16,7 +16,11 @@
"test:watch": "vitest",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"typecheck": "turbo typecheck"
"typecheck": "turbo typecheck",
"db:push": "drizzle-kit push --config packages/db/drizzle.config.ts --force",
"db:studio": "drizzle-kit studio --config packages/db/drizzle.config.ts",
"dev:services": "docker compose -f docker-compose.dev.yml up -d",
"dev:full": "./scripts/dev.sh"
},
"devDependencies": {
"@eslint/js": "^10.0.1",

View file

@ -1,9 +1,17 @@
import { defineConfig } from "drizzle-kit";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
schema: ["./src/schema/planner.ts", "./src/schema/journal.ts"],
out: "./migrations",
schema: [
path.resolve(__dirname, "src/schema/planner.ts"),
path.resolve(__dirname, "src/schema/journal.ts"),
],
out: path.resolve(__dirname, "migrations"),
dialect: "postgresql",
schemaFilter: ["planner", "journal"],
dbCredentials: {
url: process.env.DATABASE_URL ?? "postgres://trails:trails@localhost:5432/trails",
},

55
scripts/dev.sh Executable file
View file

@ -0,0 +1,55 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_DIR"
echo "=== trails.cool dev environment ==="
echo ""
# 1. Start Docker services
echo "Starting Docker services..."
docker compose -f docker-compose.dev.yml up -d
# 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
echo "Pushing database schema..."
pnpm db:push 2>&1 | tail -3
echo "✓ Database schema up to date"
# 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"
echo ""
# 6. Start both apps with turbo
exec pnpm dev

View file

@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
# Download a minimal BRouter segment for local development.
# E10_N50 covers the Berlin area (~124MB) — enough for route testing.
VOLUME_NAME="trails-cool_brouter_segments"
SEGMENT="E10_N50.rd5"
URL="https://brouter.de/brouter/segments4/${SEGMENT}"
# Check if segment already exists in the Docker volume
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f "/data/${SEGMENT}" 2>/dev/null; then
echo "✓ Segment ${SEGMENT} already exists, skipping download"
exit 0
fi
echo "Downloading ${SEGMENT} (~124MB) for local development..."
docker run --rm -v "${VOLUME_NAME}:/data" alpine/curl \
-L -o "/data/${SEGMENT}" "${URL}"
echo "✓ Segment downloaded to Docker volume ${VOLUME_NAME}"