Initial Commit
This commit is contained in:
21
docker/.env.example
Normal file
21
docker/.env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# Set these to use your already-running local services instead of spinning containers.
|
||||
# A fixture only enters passthrough mode when ALL its required vars are set and non-empty.
|
||||
|
||||
SURREALDB_URL=ws://localhost:8001
|
||||
SURREALDB_USERNAME=root
|
||||
SURREALDB_PASSWORD=root
|
||||
|
||||
REDIS_URL=redis://localhost:8002
|
||||
|
||||
POSTGRES_URL=postgresql://postgres:postgres@localhost:8003/postgres
|
||||
|
||||
MAILPIT_SMTP_URL=smtp://localhost:8004
|
||||
MAILPIT_API_URL=http://localhost:8005
|
||||
|
||||
TYPESENSE_URL=http://localhost:8006
|
||||
TYPESENSE_API_KEY=test
|
||||
|
||||
GARAGE_S3_ENDPOINT=http://localhost:8900
|
||||
GARAGE_S3_ADMIN_ENDPOINT=http://localhost:8902
|
||||
GARAGE_S3_ACCESS_KEY_ID=
|
||||
GARAGE_S3_SECRET_ACCESS_KEY=
|
||||
3
docker/Caddyfile
Normal file
3
docker/Caddyfile
Normal file
@@ -0,0 +1,3 @@
|
||||
:80 {
|
||||
reverse_proxy testbed:3000
|
||||
}
|
||||
199
docker/README.md
Normal file
199
docker/README.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# @suaveplan/docker
|
||||
|
||||
Docker Compose infrastructure for the genesis monorepo. Three stacks cover every stage of the development lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## Stacks
|
||||
|
||||
### `docker-compose.dev.yml` — Development
|
||||
|
||||
Persistent local services with named volumes. Ports are offset into the `8000` range so they do not collide with app servers running on the typical `3000`/`4000` range. Intended to run continuously while developing; data survives container restarts.
|
||||
|
||||
### `docker-compose.test.yml` — Integration Tests
|
||||
|
||||
Ephemeral, no volumes. Ports are in the `7000` range, distinct from the dev stack so both can run simultaneously. Started automatically by CI and by the integration test suite's global setup; torn down after the run.
|
||||
|
||||
### `docker-compose.testbed.yml` — Testbed Staging
|
||||
|
||||
Fully isolated on a private Docker network (`testbed-net`). Services communicate by service name only — no host ports are exposed for the backing services. Includes a `testbed` app container (built from `apps/testbed`) and a Caddy reverse proxy on host ports `3200`/`3201`. Used for full E2E staging runs before graduation.
|
||||
|
||||
---
|
||||
|
||||
## Port Table
|
||||
|
||||
| Service | Dev host port | Test host port | Testbed |
|
||||
|---------|--------------|----------------|---------|
|
||||
| SurrealDB (WS) | 8001 | 7004 | internal only |
|
||||
| Redis | 8002 | 7005 | internal only |
|
||||
| PostgreSQL | 8003 | 7006 | internal only |
|
||||
| Mailpit SMTP | 8004 | 7007 | internal only |
|
||||
| Mailpit Web UI | 8005 | 7008 | internal only |
|
||||
| Typesense | 8006 | 7009 | internal only |
|
||||
| Garage S3 API | 8900 | 7010 | internal only |
|
||||
| Garage S3 Admin | 8902 | 7011 | internal only |
|
||||
| Caddy HTTP | — | — | 3200 |
|
||||
| Caddy HTTPS | — | — | 3201 |
|
||||
|
||||
---
|
||||
|
||||
## Starting and Stopping
|
||||
|
||||
### Development stack
|
||||
|
||||
```bash
|
||||
# From repo root
|
||||
bun run --filter=@suaveplan/docker dev:up
|
||||
bun run --filter=@suaveplan/docker dev:down
|
||||
|
||||
# Or directly from docker/
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
docker compose -f docker-compose.dev.yml down
|
||||
```
|
||||
|
||||
### Integration-test stack
|
||||
|
||||
```bash
|
||||
bun run --filter=@suaveplan/docker test:up
|
||||
bun run --filter=@suaveplan/docker test:down
|
||||
|
||||
# Or directly
|
||||
docker compose -f docker-compose.test.yml up -d
|
||||
docker compose -f docker-compose.test.yml down
|
||||
```
|
||||
|
||||
### Testbed staging stack
|
||||
|
||||
```bash
|
||||
bun run --filter=@suaveplan/docker testbed:up # builds apps/testbed first
|
||||
bun run --filter=@suaveplan/docker testbed:down
|
||||
|
||||
# Or directly
|
||||
docker compose -f docker-compose.testbed.yml up --build -d
|
||||
docker compose -f docker-compose.testbed.yml down
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Health Verification
|
||||
|
||||
After starting a stack, verify individual services are healthy:
|
||||
|
||||
```bash
|
||||
# SurrealDB
|
||||
curl -sf http://localhost:8001/health && echo "surreal OK"
|
||||
|
||||
# Redis
|
||||
redis-cli -p 8002 ping # expect PONG
|
||||
|
||||
# PostgreSQL
|
||||
pg_isready -h localhost -p 8003 -U postgres
|
||||
|
||||
# Mailpit Web UI
|
||||
curl -sf http://localhost:8005/api/v1/messages && echo "mailpit OK"
|
||||
|
||||
# Typesense
|
||||
curl -sf "http://localhost:8006/health" -H "X-TYPESENSE-API-KEY: test" && echo "typesense OK"
|
||||
|
||||
# Garage S3
|
||||
curl -sf http://localhost:8900 && echo "garage OK"
|
||||
```
|
||||
|
||||
For Docker-native health status:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variable Bridge
|
||||
|
||||
The env-bridge pattern lets you point test fixtures at services you are already running locally — for example, a managed SurrealDB instance — instead of starting a Docker container. A fixture **only** enters passthrough mode when **all** of its required variables are set and non-empty. If any variable in a group is missing or empty, the fixture spins up its own container.
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
cp docker/.env.example docker/.env
|
||||
# Edit docker/.env — fill in only the groups you want to bypass
|
||||
```
|
||||
|
||||
The `.env` file is gitignored. Never commit real credentials.
|
||||
|
||||
### Variable Reference
|
||||
|
||||
| Fixture | Required variables |
|
||||
|---------|--------------------|
|
||||
| SurrealDB | `SURREALDB_URL`, `SURREALDB_USERNAME`, `SURREALDB_PASSWORD` |
|
||||
| Redis | `REDIS_URL` |
|
||||
| PostgreSQL | `POSTGRES_URL` |
|
||||
| Mailpit | `MAILPIT_SMTP_URL`, `MAILPIT_API_URL` |
|
||||
| Typesense | `TYPESENSE_URL`, `TYPESENSE_API_KEY` |
|
||||
| Garage S3 | `GARAGE_S3_ENDPOINT`, `GARAGE_S3_ADMIN_ENDPOINT`, `GARAGE_S3_ACCESS_KEY_ID`, `GARAGE_S3_SECRET_ACCESS_KEY` |
|
||||
|
||||
### Default values (dev stack)
|
||||
|
||||
| Variable | Default |
|
||||
|----------|---------|
|
||||
| `SURREALDB_URL` | `ws://localhost:8001` |
|
||||
| `SURREALDB_USERNAME` | `root` |
|
||||
| `SURREALDB_PASSWORD` | `root` |
|
||||
| `REDIS_URL` | `redis://localhost:8002` |
|
||||
| `POSTGRES_URL` | `postgresql://postgres:postgres@localhost:8003/postgres` |
|
||||
| `MAILPIT_SMTP_URL` | `smtp://localhost:8004` |
|
||||
| `MAILPIT_API_URL` | `http://localhost:8005` |
|
||||
| `TYPESENSE_URL` | `http://localhost:8006` |
|
||||
| `TYPESENSE_API_KEY` | `test` |
|
||||
| `GARAGE_S3_ENDPOINT` | `http://localhost:8900` |
|
||||
| `GARAGE_S3_ADMIN_ENDPOINT` | `http://localhost:8902` |
|
||||
|
||||
---
|
||||
|
||||
## Garage S3 — Key Retrieval After First Run
|
||||
|
||||
Garage generates a root key on first startup. The access key ID and secret are **not** set via environment variables — you must retrieve them after the first run:
|
||||
|
||||
```bash
|
||||
# 1. Start the dev stack
|
||||
docker compose -f docker-compose.dev.yml up -d garage-s3
|
||||
|
||||
# 2. Create a layout (single-node, zone z1, 1 GB capacity)
|
||||
docker compose -f docker-compose.dev.yml exec garage-s3 \
|
||||
/garage layout assign -z z1 -c 1G $(
|
||||
/garage node id 2>/dev/null | awk '{print $1}'
|
||||
)
|
||||
docker compose -f docker-compose.dev.yml exec garage-s3 \
|
||||
/garage layout apply --version 1
|
||||
|
||||
# 3. Create a key
|
||||
docker compose -f docker-compose.dev.yml exec garage-s3 \
|
||||
/garage key create my-key
|
||||
|
||||
# 4. Copy the key ID and secret into docker/.env
|
||||
GARAGE_S3_ACCESS_KEY_ID=<key-id>
|
||||
GARAGE_S3_SECRET_ACCESS_KEY=<secret>
|
||||
```
|
||||
|
||||
Refer to the [Garage documentation](https://garagehq.deuxfleurs.fr/documentation/quick-start/) for full setup instructions.
|
||||
|
||||
---
|
||||
|
||||
## Testbed Staging Environment
|
||||
|
||||
The testbed stack builds `apps/testbed` locally and wires every service on the internal `testbed-net` network. Caddy proxies inbound traffic from `localhost:3200` to `testbed:3000`.
|
||||
|
||||
```bash
|
||||
# Build and start
|
||||
docker compose -f docker-compose.testbed.yml up --build -d
|
||||
|
||||
# Follow testbed app logs
|
||||
docker compose -f docker-compose.testbed.yml logs -f testbed
|
||||
|
||||
# Run Playwright E2E against the staging URL
|
||||
PLAYWRIGHT_BASE_URL=http://localhost:3200 bunx turbo test:e2e --filter=@suaveplan/testbed
|
||||
|
||||
# Tear down
|
||||
docker compose -f docker-compose.testbed.yml down
|
||||
```
|
||||
|
||||
The testbed service reads all connection strings from its `environment` block (defined in `docker-compose.testbed.yml`) and does not rely on `.env`. Service-to-service communication uses Docker DNS names (`surrealdb`, `redis`, etc.).
|
||||
69
docker/docker-compose.dev.yml
Normal file
69
docker/docker-compose.dev.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
services:
|
||||
surrealdb:
|
||||
image: surrealdb/surrealdb:v3.0.4
|
||||
command: start --auth --user root --pass root
|
||||
ports:
|
||||
- "8001:8000"
|
||||
volumes:
|
||||
- surrealdb-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "8002:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- "8003:5432"
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
mailpit:
|
||||
image: axllent/mailpit:latest
|
||||
ports:
|
||||
- "8004:1025"
|
||||
- "8005:8025"
|
||||
|
||||
typesense:
|
||||
image: typesense/typesense:27.1
|
||||
environment:
|
||||
TYPESENSE_DATA_DIR: /data
|
||||
TYPESENSE_API_KEY: test
|
||||
ports:
|
||||
- "8006:8108"
|
||||
volumes:
|
||||
- typesense-data:/data
|
||||
|
||||
garage-s3:
|
||||
image: ghcr.io/deuxfleurs/garage:v2.0.0
|
||||
ports:
|
||||
- "8900:3900"
|
||||
- "8902:3902"
|
||||
volumes:
|
||||
- garage-data:/data
|
||||
environment:
|
||||
GARAGE_ALLOW_WORLD_READABLE_CONFIG: "true"
|
||||
|
||||
volumes:
|
||||
surrealdb-data:
|
||||
redis-data:
|
||||
postgres-data:
|
||||
typesense-data:
|
||||
garage-data:
|
||||
62
docker/docker-compose.test.yml
Normal file
62
docker/docker-compose.test.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
services:
|
||||
surrealdb:
|
||||
image: surrealdb/surrealdb:v3.0.4
|
||||
# Flags ported from ../suaveplan/packages/universal/surrealdb/
|
||||
# docker-compose.test.yml. v3 dropped `--auth` (always on) and made
|
||||
# capabilities deny-by-default — `--allow-all` re-enables funcs/net/
|
||||
# scripting/guests. Trailing `memory` selects the in-memory datastore.
|
||||
command:
|
||||
- start
|
||||
- --log=info
|
||||
- --user=root
|
||||
- --pass=root
|
||||
- --allow-all
|
||||
- memory
|
||||
ports:
|
||||
- "7004:8000"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "7005:6379"
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- "7006:5432"
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
mailpit:
|
||||
image: axllent/mailpit:latest
|
||||
ports:
|
||||
- "7007:1025"
|
||||
- "7008:8025"
|
||||
|
||||
typesense:
|
||||
image: typesense/typesense:27.1
|
||||
environment:
|
||||
TYPESENSE_DATA_DIR: /data
|
||||
TYPESENSE_API_KEY: test
|
||||
ports:
|
||||
- "7009:8108"
|
||||
|
||||
garage-s3:
|
||||
image: ghcr.io/deuxfleurs/garage:v2.0.0
|
||||
ports:
|
||||
- "7010:3900"
|
||||
- "7011:3902"
|
||||
environment:
|
||||
GARAGE_ALLOW_WORLD_READABLE_CONFIG: "true"
|
||||
79
docker/docker-compose.testbed.yml
Normal file
79
docker/docker-compose.testbed.yml
Normal file
@@ -0,0 +1,79 @@
|
||||
networks:
|
||||
testbed-net:
|
||||
|
||||
services:
|
||||
surrealdb:
|
||||
image: surrealdb/surrealdb:v3.0.4
|
||||
command: start --auth --user root --pass root
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
mailpit:
|
||||
image: axllent/mailpit:latest
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
typesense:
|
||||
image: typesense/typesense:27.1
|
||||
environment:
|
||||
TYPESENSE_DATA_DIR: /data
|
||||
TYPESENSE_API_KEY: test
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
garage-s3:
|
||||
image: ghcr.io/deuxfleurs/garage:v2.0.0
|
||||
environment:
|
||||
GARAGE_ALLOW_WORLD_READABLE_CONFIG: "true"
|
||||
networks:
|
||||
- testbed-net
|
||||
|
||||
testbed:
|
||||
build:
|
||||
context: ../apps/testbed
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
SURREALDB_URL: ws://surrealdb:8000
|
||||
SURREALDB_USERNAME: root
|
||||
SURREALDB_PASSWORD: root
|
||||
GARAGE_S3_ENDPOINT: http://garage-s3:3900
|
||||
GARAGE_S3_ADMIN_ENDPOINT: http://garage-s3:3902
|
||||
REDIS_URL: redis://redis:6379
|
||||
POSTGRES_URL: postgresql://postgres:postgres@postgres:5432/postgres
|
||||
TYPESENSE_URL: http://typesense:8108
|
||||
TYPESENSE_API_KEY: test
|
||||
MAILPIT_SMTP_URL: smtp://mailpit:1025
|
||||
MAILPIT_API_URL: http://mailpit:8025
|
||||
networks:
|
||||
- testbed-net
|
||||
depends_on:
|
||||
- surrealdb
|
||||
- redis
|
||||
- postgres
|
||||
- garage-s3
|
||||
- typesense
|
||||
- mailpit
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
ports:
|
||||
- "3200:80"
|
||||
- "3201:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
networks:
|
||||
- testbed-net
|
||||
12
docker/package.json
Normal file
12
docker/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@suaveplan/docker",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev:up": "docker compose -f docker-compose.dev.yml up -d",
|
||||
"dev:down": "docker compose -f docker-compose.dev.yml down",
|
||||
"test:up": "docker compose -f docker-compose.test.yml up -d",
|
||||
"test:down": "docker compose -f docker-compose.test.yml down",
|
||||
"testbed:up": "docker compose -f docker-compose.testbed.yml up --build -d",
|
||||
"testbed:down": "docker compose -f docker-compose.testbed.yml down"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user