Add local dev setup, fix BRouter Dockerfile, archive change (#12)
This commit is contained in:
parent
2d02ae3f9e
commit
52b2baaf58
14 changed files with 329 additions and 9 deletions
|
|
@ -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
|
||||
|
|
|
|||
25
README.md
25
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-03-22
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
26
openspec/changes/archive/2026-03-23-local-dev-setup/tasks.md
Normal file
26
openspec/changes/archive/2026-03-23-local-dev-setup/tasks.md
Normal 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
|
||||
37
openspec/specs/local-dev-environment/spec.md
Normal file
37
openspec/specs/local-dev-environment/spec.md
Normal 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
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
55
scripts/dev.sh
Executable 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
|
||||
21
scripts/download-dev-segments.sh
Executable file
21
scripts/download-dev-segments.sh
Executable 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}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue