From 2da7c385d0017ad7c4ca311d0b2714fdd960fbb7 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 31 Aug 2026 14:46:09 +0100 Subject: [PATCH] Put the compose services behind dev, full, test and integration profiles docker-compose.test.yml is absorbed into docker-compose.yml so --profile test works against the default file. Every service now names a profile, so plain docker compose up starts nothing. Two things the merge exposed: api-test sat on stellar-test-net while postgres-test sat on the default network, so it could never resolve its own database. Both are on one network now. Dockerfile.test ran npm ci before copying the prisma directory, and the postinstall hook is prisma generate, so the image could not build. --- .dockerignore | 46 ++--- README.md | 50 +++++- docker-compose.test.yml | 64 ------- docker-compose.yml | 210 ++++++++++++++++------- stellar-payment-platform/Dockerfile.test | 6 +- tests/integration/README.md | 4 +- tests/integration/deploy.sh | 4 +- tests/integration/run.sh | 5 +- 8 files changed, 230 insertions(+), 159 deletions(-) delete mode 100644 docker-compose.test.yml diff --git a/.dockerignore b/.dockerignore index 966e0fa7..41041fa9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,24 +1,24 @@ -**/node_modules -.git -.github -.husky -payment_router -*.md -.env -**/.env -*.db - -# Backend test files & artifacts — never needed inside an image -# (CI runs tests on the runner; docker-compose.test.yml builds from the -# stellar-payment-platform context, which has its own .dockerignore) -stellar-payment-platform/tests -stellar-payment-platform/*.test.js -stellar-payment-platform/test_*.txt -stellar-payment-platform/test_output.txt -stellar-payment-platform/data -stellar-payment-platform/artillery.yml - -# Root-level files not referenced by any image build -docs -tmp_server.js +**/node_modules +.git +.github +.husky +payment_router +*.md +.env +**/.env +*.db + +# Backend test files & artifacts — never needed inside an image +# (CI runs tests on the runner; the api-test service builds from the +# stellar-payment-platform context, which has its own .dockerignore) +stellar-payment-platform/tests +stellar-payment-platform/*.test.js +stellar-payment-platform/test_*.txt +stellar-payment-platform/test_output.txt +stellar-payment-platform/data +stellar-payment-platform/artillery.yml + +# Root-level files not referenced by any image build +docs +tmp_server.js artillery.yml \ No newline at end of file diff --git a/README.md b/README.md index 758122f6..817c187c 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,47 @@ The following diagram maps exactly how data flows between the user, Render, and > These steps are split by module so you can run only what you need. +### Docker Compose profiles + +Every service in `docker-compose.yml` belongs to a profile, so +`docker compose up` on its own starts nothing and you always say which stack +you want: + +| Command | Starts | +| --- | --- | +| `docker compose --profile dev up` | backend, postgres, redis | +| `docker compose --profile full up` | the same, plus the built frontend on :3000 | +| `docker compose --profile test up` | the API under test on :5001 and its own database | +| `docker compose --profile integration up` | a local standalone Stellar network on :8000 | + +`dev` is the everyday one. `full` adds the frontend, which is the slowest +thing in the file to build and is not needed for backend work. + +The dev and test stacks use separate databases on separate host ports (5432 +and 5433), so you can run both at once: + +```bash +docker compose --profile dev --profile test up -d +``` + +`COMPOSE_PROFILES` works too, if you would rather not repeat the flag: + +```bash +export COMPOSE_PROFILES=dev +docker compose up -d +``` + +Stop a stack with the same profile you started it with, otherwise Compose +will not know which services it is meant to remove: + +```bash +docker compose --profile dev down +``` + +The `integration` profile pulls `stellar/quickstart`, a multi-gigabyte image +used only by the Soroban contract tests in `tests/integration/`. It is kept +out of `test` so the API tests do not drag it in. + ### Frontend dashboard ```bash @@ -111,7 +152,14 @@ For a typical local install that becomes, for example: DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_tags?schema=public" ``` -The quickest way to get a local database is Docker: +The quickest way to get a local database is the dev profile, which also +brings up Redis: + +```bash +docker compose --profile dev up -d postgres redis +``` + +Or a single container, if you want nothing else: ```bash docker run --name stellar-postgres -e POSTGRES_PASSWORD=postgres \ diff --git a/docker-compose.test.yml b/docker-compose.test.yml deleted file mode 100644 index 2b86e8c3..00000000 --- a/docker-compose.test.yml +++ /dev/null @@ -1,64 +0,0 @@ -services: - postgres-test: - image: postgres:16-alpine - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: stellar_tags_test - ports: - - "5433:5432" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s - timeout: 3s - retries: 5 - - api-test: - build: - context: ./stellar-payment-platform - dockerfile: Dockerfile.test - environment: - PORT: "5001" - DATABASE_URL: postgresql://postgres:postgres@postgres-test:5432/stellar_tags_test?schema=public - ports: - - "5001:5001" - depends_on: - postgres-test: - condition: service_healthy - networks: - - stellar-test-net - healthcheck: - test: ["CMD", "wget", "-qO-", "http://localhost:5001/health"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - - # Standalone Stellar network (Soroban-enabled) used by the contract - # integration tests. Exposes both the Horizon REST API (8000) and the - # Soroban RPC endpoint (8000/rpc) so the Rust SDK can submit real - # transactions against a local network. - stellar-standalone: - image: stellar/quickstart:latest - command: [ - "--standalone", - "--enable-soroban-rpc", - "--enable-core-artifacts", - "--local", - ] - environment: - ENABLE_LOGS: "true" - ports: - - "8000:8000" - networks: - - stellar-test-net - healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:8000/"] - interval: 5s - timeout: 5s - retries: 20 - start_period: 30s - -networks: - stellar-test-net: - driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index 104fb311..04b7c3a5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,64 +1,146 @@ -services: - postgres: - image: postgres:16-alpine - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: stellar_tags - ports: - - "5432:5432" - volumes: - - pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s - timeout: 3s - retries: 5 - - redis: - image: redis:7-alpine - ports: - - "6379:6379" - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 3s - retries: 5 - - backend: - build: - context: . - dockerfile: Dockerfile - target: backend - ports: - - "5000:5000" - environment: - DATABASE_URL: postgresql://postgres:postgres@postgres:5432/stellar_tags?schema=public - REDIS_URL: redis://redis:6379 - PORT: "5000" - LOG_DIR: /app/logs - volumes: - # Keep rotated logs across container restarts. The transports cap the - # volume's growth by rotating daily / at 20MB and pruning old archives. - - backendlogs:/app/logs - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - - frontend: - build: - context: . - dockerfile: Dockerfile - target: frontend - args: - VITE_API_BASE: http://localhost:5000 - ports: - - "3000:80" - depends_on: - - backend - -volumes: - pgdata: - backendlogs: +# Every service belongs to a profile, so `docker compose up` on its own starts +# nothing and you always say which stack you want: +# +# docker compose --profile dev up backend + postgres + redis +# docker compose --profile full up the same, plus the built frontend +# docker compose --profile test up API under test + its own database +# docker compose --profile integration up local standalone Stellar network +# +# dev and test use separate databases on separate host ports, so both can run +# at once without colliding. + +services: + # ── dev ──────────────────────────────────────────────────────────────────── + postgres: + profiles: ["dev", "full"] + image: postgres:16-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: stellar_tags + ports: + - "5432:5432" + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 3s + retries: 5 + + redis: + profiles: ["dev", "full"] + image: redis:7-alpine + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + + backend: + profiles: ["dev", "full"] + build: + context: . + dockerfile: Dockerfile + target: backend + ports: + - "5000:5000" + environment: + DATABASE_URL: postgresql://postgres:postgres@postgres:5432/stellar_tags?schema=public + REDIS_URL: redis://redis:6379 + PORT: "5000" + LOG_DIR: /app/logs + volumes: + # Keep rotated logs across container restarts. The transports cap the + # volume's growth by rotating daily / at 20MB and pruning old archives. + - backendlogs:/app/logs + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # Only in full, not dev: the Vite build is the slowest thing here and backend + # work does not need it. full is dev plus this. + frontend: + profiles: ["full"] + build: + context: . + dockerfile: Dockerfile + target: frontend + args: + VITE_API_BASE: http://localhost:5000 + ports: + - "3000:80" + depends_on: + - backend + + # ── test ─────────────────────────────────────────────────────────────────── + postgres-test: + profiles: ["test"] + image: postgres:16-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: stellar_tags_test + # 5433 on the host so a dev postgres can stay up on 5432. + ports: + - "5433:5432" + # No volume: a test database should start empty every time. + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 3s + retries: 5 + + api-test: + profiles: ["test"] + build: + context: ./stellar-payment-platform + dockerfile: Dockerfile.test + environment: + PORT: "5001" + DATABASE_URL: postgresql://postgres:postgres@postgres-test:5432/stellar_tags_test?schema=public + ports: + - "5001:5001" + depends_on: + postgres-test: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:5001/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + + # ── integration ──────────────────────────────────────────────────────────── + # Standalone Stellar network (Soroban-enabled) used by the contract + # integration tests. Exposes both the Horizon REST API (8000) and the + # Soroban RPC endpoint (8000/rpc) so the Rust SDK can submit real + # transactions against a local network. Kept out of the test profile + # because it pulls a multi-gigabyte image the API tests never touch. + stellar-standalone: + profiles: ["integration"] + image: stellar/quickstart:latest + command: [ + "--standalone", + "--enable-soroban-rpc", + "--enable-core-artifacts", + "--local", + ] + environment: + ENABLE_LOGS: "true" + ports: + - "8000:8000" + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8000/"] + interval: 5s + timeout: 5s + retries: 20 + start_period: 30s + +volumes: + pgdata: + backendlogs: diff --git a/stellar-payment-platform/Dockerfile.test b/stellar-payment-platform/Dockerfile.test index b5d6b349..de514bcd 100644 --- a/stellar-payment-platform/Dockerfile.test +++ b/stellar-payment-platform/Dockerfile.test @@ -3,10 +3,14 @@ FROM node:20-alpine WORKDIR /app COPY package*.json ./ +# Before npm ci: the postinstall hook runs `prisma generate`, which fails +# unless the schema is already present. +COPY prisma ./prisma RUN npm ci --omit=dev COPY . . -EXPOSE 5000 +# Matches PORT in the api-test service and its healthcheck. +EXPOSE 5001 CMD ["node", "server.js"] diff --git a/tests/integration/README.md b/tests/integration/README.md index 9e092fac..6584101d 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -10,7 +10,7 @@ observe the resulting network state. ``` ┌─────────────────────────────────────────────────────────────┐ -│ docker-compose.test.yml │ +│ docker compose --profile integration │ │ │ │ stellar-standalone (stellar/quickstart) │ │ ├── Horizon REST API ──► http://localhost:8000 │ @@ -36,7 +36,7 @@ smart contracts out of the box. ```bash # 1. Start the standalone Stellar network -docker compose -f docker-compose.test.yml up -d stellar-standalone +docker compose --profile integration up -d stellar-standalone # 2. Wait for Horizon to be ready (healthcheck handles this automatically) diff --git a/tests/integration/deploy.sh b/tests/integration/deploy.sh index e578751c..f83566fa 100644 --- a/tests/integration/deploy.sh +++ b/tests/integration/deploy.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash # # Deploys the payment_router Soroban contract to the local standalone -# Stellar network started by docker-compose.test.yml. +# Stellar network started by docker-compose.yml's integration profile. # # Prerequisites: # - Docker running with the standalone network up: -# docker compose -f docker-compose.test.yml up -d stellar-standalone +# docker compose --profile integration up -d stellar-standalone # - The `soroban` CLI installed (https://soroban.stellar.org/docs/reference/cli) # - Rust toolchain with the `wasm32-unknown-unknown` target: # rustup target add wasm32-unknown-unknown diff --git a/tests/integration/run.sh b/tests/integration/run.sh index 402e8d55..19b5cfc7 100644 --- a/tests/integration/run.sh +++ b/tests/integration/run.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # # End-to-end integration test for the payment_router Soroban contract -# against a local standalone Stellar network (docker-compose.test.yml). +# against a local standalone Stellar network (the integration profile in +# docker-compose.yml). # # Verifies: # 1. The contract can be deployed to the local network. @@ -10,7 +11,7 @@ # fee and crediting the recipient, as observed through local Horizon. # # Prerequisites: -# - docker compose -f docker-compose.test.yml up -d stellar-standalone +# - docker compose --profile integration up -d stellar-standalone # - soroban CLI installed # - rustup target add wasm32-unknown-unknown #