mirror of
https://github.com/suitenumerique/messages.git
synced 2026-08-17 21:25:41 +02:00
636 lines
19 KiB
YAML
636 lines
19 KiB
YAML
name: st-messages
|
|
|
|
# Raise the open-file limit for the Django dev server + import workers. The
|
|
# Docker default (1024) is too low: the dev ``runserver`` keeps inbound HTTP
|
|
# keep-alive connections open with no idle timeout, so a long UI session polling
|
|
# the API (plus large imports opening S3 connections) exhausts FDs and wedges
|
|
# the backend with "Too many open files".
|
|
x-nofile-ulimits: &nofile-ulimits
|
|
nofile:
|
|
soft: 262144
|
|
hard: 262144
|
|
|
|
# Shared config for the Celery workers. worker-dev (full stack) and
|
|
# worker-dev-light (light stack) differ only in their profiles / depends_on /
|
|
# environment; everything else lives here. Note that ``environment`` is a list,
|
|
# so the ``<<`` merge does NOT combine it — each service repeats the full list.
|
|
x-worker-base: &worker-base
|
|
build:
|
|
context: src/backend
|
|
target: runtime-dev
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-1000}
|
|
user: ${DOCKER_USER:-1000}
|
|
ulimits: *nofile-ulimits
|
|
command: ["python", "worker.py", "--loglevel=DEBUG"]
|
|
env_file:
|
|
- deploy/env/backend.defaults
|
|
- deploy/env/backend.local
|
|
volumes:
|
|
- ./src/backend:/app
|
|
- ./data/static:/data/static
|
|
mem_limit: 2G
|
|
|
|
services:
|
|
postgresql:
|
|
image: postgres:16.6@sha256:557fea37a744d5f4c8faab304b0a90858b53ab119735a88c131fd19dab802f36
|
|
ports:
|
|
- "8912:5432"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
|
interval: 1s
|
|
timeout: 2s
|
|
retries: 300
|
|
env_file:
|
|
- deploy/env/postgresql.defaults
|
|
- deploy/env/postgresql.local
|
|
|
|
redis:
|
|
image: redis:5
|
|
ports:
|
|
- "8913:6379"
|
|
|
|
opensearch:
|
|
image: opensearchproject/opensearch:2.19.2@sha256:69588c664014fa3d3260ed5dd337f8538fec94790dad00bfbe92301eaaaac240
|
|
environment:
|
|
- discovery.type=single-node
|
|
- bootstrap.memory_lock=true
|
|
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
- "DISABLE_INSTALL_DEMO_CONFIG=true"
|
|
- "DISABLE_SECURITY_PLUGIN=true"
|
|
# - http.cors.enabled=true
|
|
# - "http.cors.allow-origin=/.*/"
|
|
ports:
|
|
- "8914:9200" # REST API
|
|
- "8915:9600" # Performance Analyzer
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:9200"]
|
|
interval: 1s
|
|
timeout: 5s
|
|
retries: 60
|
|
ulimits:
|
|
memlock:
|
|
soft: -1 # Set memlock to unlimited (no soft or hard limit)
|
|
hard: -1
|
|
nofile:
|
|
soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536
|
|
hard: 65536
|
|
|
|
mailcatcher:
|
|
image: maildev/maildev:2.2.1@sha256:180ef51f65eefebb0e7122d8308813c1fd7bff164bc440ce5a3c2feee167a810
|
|
ports:
|
|
- "8904:1080"
|
|
- "8917:1025"
|
|
|
|
objectstorage:
|
|
# user: ${DOCKER_USER:-1000}
|
|
image: rustfs/rustfs:1.0.0-alpha.83@sha256:1cfa82fb394a7c6a4ffc24f5333b52afee608d258503f94b37fcce5cc965c896
|
|
environment:
|
|
- RUSTFS_ACCESS_KEY=st-messages
|
|
- RUSTFS_SECRET_KEY=password
|
|
- RUSTFS_CONSOLE_ENABLE=true
|
|
- RUSTFS_ADDRESS=0.0.0.0:9000
|
|
- RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
|
|
- RUSTFS_CORS_ALLOWED_ORIGINS=*
|
|
- RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=*
|
|
ports:
|
|
- "8906:9000"
|
|
- "8907:9001"
|
|
healthcheck:
|
|
test:
|
|
[
|
|
"CMD",
|
|
"sh",
|
|
"-c",
|
|
"curl -f http://127.0.0.1:9000/health && curl -f http://127.0.0.1:9001/rustfs/console/health",
|
|
]
|
|
interval: 1s
|
|
timeout: 5s
|
|
retries: 60
|
|
start_period: 0s
|
|
volumes:
|
|
- objectstorage-data:/data
|
|
|
|
keycloak:
|
|
image: quay.io/keycloak/keycloak:26.7.1@sha256:f1f1f01e472c8a78df40d8f2a49a925274eda4d3d80d5f6edbb5c880ee3c01c6
|
|
volumes:
|
|
- ./src/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro
|
|
- ./src/keycloak/themes/dsfr-2.3.4.jar:/opt/keycloak/providers/keycloak-theme.jar:ro
|
|
- ./src/keycloak/bulk-role-membership/bulk-role-membership.jar:/opt/keycloak/providers/bulk-role-membership.jar:ro
|
|
environment:
|
|
- HOST=http://localhost:8902
|
|
- ADMIN_HOST=http://localhost:8902
|
|
command:
|
|
- start-dev
|
|
- --features=preview
|
|
- --import-realm
|
|
- --proxy-headers=xforwarded
|
|
- --http-enabled=true
|
|
- --hostname=$${HOST}
|
|
- --hostname-admin=$${ADMIN_HOST}
|
|
- --http-port=8802
|
|
# Expose the management health endpoints (:9000/health/*) so the
|
|
# healthcheck below can tell when Keycloak is actually serving.
|
|
- --health-enabled=true
|
|
env_file:
|
|
- deploy/env/keycloak.defaults
|
|
- deploy/env/keycloak.local
|
|
ports:
|
|
- "8902:8802"
|
|
# Without a healthcheck, `--wait` / `depends_on: service_started` consider
|
|
# Keycloak ready the instant the container process launches — but the JVM is
|
|
# still booting + importing the realm for several seconds, so :8902 500s.
|
|
# The image ships no curl/wget, so probe the management readiness endpoint
|
|
# (:9000/health/ready, 200 only once the realm import + DB are up) with
|
|
# bash's /dev/tcp.
|
|
healthcheck:
|
|
test:
|
|
- CMD
|
|
- bash
|
|
- -c
|
|
- "exec 3<>/dev/tcp/localhost/9000; printf 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3; head -n1 <&3 | grep -q ' 200'"
|
|
interval: 3s
|
|
timeout: 5s
|
|
retries: 40
|
|
# During start-up, probe every second (instead of `interval`) so Keycloak
|
|
# is detected healthy within ~1s of actually serving. start_period is set
|
|
# to comfortably cover a cold JVM boot + realm import so those early
|
|
# failures stay in the grace window.
|
|
start_period: 25s
|
|
start_interval: 1s
|
|
depends_on:
|
|
- postgresql
|
|
|
|
backend-base:
|
|
build:
|
|
context: src/backend
|
|
target: runtime-dev
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-1000}
|
|
user: ${DOCKER_USER:-1000}
|
|
volumes:
|
|
- ./src/backend:/app
|
|
- ./data/static:/data/static
|
|
# Dev-only override: live-mount the jmap-email working tree over the
|
|
# package installed from PyPI so local source edits propagate without a
|
|
# rebuild. The wheel installed at
|
|
# ``/venv/lib/${PYTHON_VERSION}/site-packages/jmap_email`` is overlaid
|
|
# with the working-tree source. Override ``PYTHON_VERSION`` in the
|
|
# environment when the backend's Python floor moves. Comment this mount
|
|
# out to run exactly what CI/prod install from PyPI.
|
|
- ./src/jmap-email/jmap_email:/venv/lib/${PYTHON_VERSION:-python3.14}/site-packages/jmap_email
|
|
ulimits: *nofile-ulimits
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request as u; u.urlopen('http://localhost:8000/__heartbeat__/', timeout=1)"]
|
|
interval: 3s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
backend-dev:
|
|
extends: backend-base
|
|
environment:
|
|
- PYLINTHOME=/app/.pylint.d
|
|
- DJANGO_CONFIGURATION=Development
|
|
env_file:
|
|
- deploy/env/backend.defaults
|
|
- deploy/env/backend.local
|
|
ports:
|
|
- "8901:8000"
|
|
depends_on:
|
|
postgresql:
|
|
condition: service_healthy
|
|
# restart: true
|
|
objectstorage:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
opensearch:
|
|
condition: service_healthy
|
|
keycloak:
|
|
condition: service_healthy
|
|
mailcatcher:
|
|
condition: service_started
|
|
|
|
# Lean backend for the default `make start`: same Development config (Redis
|
|
# cache + Celery broker, real worker) but WITHOUT OpenSearch, object storage
|
|
# or the MTAs — so a plain dev loop boots a handful of containers. Under the
|
|
# `light` profile so a bare `docker compose up` (make start-full) skips it;
|
|
# `make start` names it explicitly. `backend-dev` is left untouched because
|
|
# `make test-back` (docker compose run backend-dev) relies on its OpenSearch /
|
|
# object-storage deps to provision those for the search/storage test suites.
|
|
backend-dev-light:
|
|
extends: backend-base
|
|
profiles:
|
|
- light
|
|
environment:
|
|
- PYLINTHOME=/app/.pylint.d
|
|
- DJANGO_CONFIGURATION=Development
|
|
# No OpenSearch container in this stack: skip the per-delivery reindex
|
|
# enqueue so the worker isn't logging connection errors (search is a
|
|
# start-full feature).
|
|
- OPENSEARCH_INDEX_THREADS=False
|
|
env_file:
|
|
- deploy/env/backend.defaults
|
|
- deploy/env/backend.local
|
|
ports:
|
|
- "8901:8000"
|
|
depends_on:
|
|
postgresql:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
keycloak:
|
|
condition: service_healthy
|
|
|
|
backend-db:
|
|
extends: backend-base
|
|
profiles:
|
|
- tools
|
|
environment:
|
|
- DJANGO_CONFIGURATION=DevelopmentMinimal
|
|
env_file:
|
|
- deploy/env/backend.defaults
|
|
- deploy/env/backend.local
|
|
ports:
|
|
- "8901:8000"
|
|
depends_on:
|
|
postgresql:
|
|
condition: service_healthy
|
|
# restart: true
|
|
|
|
backend-uv:
|
|
profiles:
|
|
- tools
|
|
volumes:
|
|
- ./src/backend:/app
|
|
build:
|
|
context: src/backend
|
|
target: uv
|
|
pull_policy: build
|
|
|
|
worker-dev:
|
|
<<: *worker-base
|
|
environment:
|
|
- DJANGO_CONFIGURATION=Development
|
|
depends_on:
|
|
- backend-dev
|
|
|
|
# Worker for the light stack (see backend-dev-light). Needs only the
|
|
# broker (Redis) + DB — not the web backend, not OpenSearch. Defined inline
|
|
# rather than via ``extends: worker-dev`` because ``extends`` drags in that
|
|
# service's ``depends_on: [backend-dev]``, which would pull the full backend
|
|
# (and its OpenSearch / object-storage deps) back into the light stack.
|
|
worker-dev-light:
|
|
<<: *worker-base
|
|
profiles:
|
|
- light
|
|
environment:
|
|
- DJANGO_CONFIGURATION=Development
|
|
- OPENSEARCH_INDEX_THREADS=False
|
|
depends_on:
|
|
postgresql:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
|
|
worker-ui:
|
|
build:
|
|
context: src/backend
|
|
target: runtime-dev
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-1000}
|
|
user: ${DOCKER_USER:-1000}
|
|
depends_on:
|
|
- redis
|
|
environment:
|
|
- FLOWER_UNAUTHENTICATED_API=true
|
|
- DJANGO_CONFIGURATION=Development
|
|
env_file:
|
|
- deploy/env/backend.defaults
|
|
- deploy/env/backend.local
|
|
volumes:
|
|
- ./src/backend:/app
|
|
ports:
|
|
- "8903:8803"
|
|
command: celery -A messages.celery_app flower --port=8803
|
|
|
|
frontend-base:
|
|
user: "${DOCKER_USER:-1000}"
|
|
build:
|
|
context: ./src/frontend
|
|
dockerfile: Dockerfile
|
|
# Lean image with no node_modules baked in — deps live in the
|
|
# frontend-node-modules volume, installed by `make install-frozen-front`.
|
|
target: frontend-dev-base
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-1000}
|
|
|
|
frontend-dev:
|
|
extends: frontend-base
|
|
env_file:
|
|
- deploy/env/frontend.defaults
|
|
- deploy/env/frontend.local
|
|
command: ["npm", "run", "dev"]
|
|
volumes:
|
|
- ./src/frontend/:/home/frontend/
|
|
# Keep node_modules on a fast Docker volume instead of the (slow,
|
|
# virtualized) host bind mount above. Writing ~thousands of files to a
|
|
# bind mount is what made installs take minutes; a named volume lives on
|
|
# the container filesystem, so `npm ci` runs in ~20s. Populated by
|
|
# `make install-frozen-front`.
|
|
- frontend-node-modules:/home/frontend/node_modules
|
|
ports:
|
|
- "8900:3000"
|
|
|
|
frontend-tools:
|
|
extends: frontend-base
|
|
profiles:
|
|
- frontend-tools
|
|
volumes:
|
|
- ./src/backend/core/api/openapi.json:/home/backend/core/api/openapi.json
|
|
- ./src/frontend/:/home/frontend/
|
|
# Share the same fast node_modules volume as frontend-dev so that
|
|
# `make install-frozen-front` (which runs here) installs into the volume
|
|
# that frontend-dev consumes.
|
|
- frontend-node-modules:/home/frontend/node_modules
|
|
|
|
frontend-tools-amd64:
|
|
extends: frontend-tools
|
|
platform: linux/amd64
|
|
|
|
# Mobile (Capacitor) web build: like frontend-tools but with the env_file, so
|
|
# the NEXT_PUBLIC_* vars Vite inlines at build time are present (frontend-tools
|
|
# has none). The native projects (android/, ios/) are written back to the host
|
|
# through the mount; the native compile (gradle/xcode) stays a host step.
|
|
frontend-mobile:
|
|
extends: frontend-base
|
|
profiles:
|
|
- frontend-tools
|
|
# Run in the root group and register the (arbitrary) host uid in /etc/passwd
|
|
# before exec'ing the command, so the Capacitor CLI's os.userInfo() call does
|
|
# not throw ENOENT under musl. See the chmod in src/frontend/Dockerfile.
|
|
# Use DOCKER_UID (uid only) and not DOCKER_USER (uid:gid): appending ":0" to
|
|
# the latter yields a malformed "uid:gid:0" spec that drops the root group,
|
|
# leaving /etc/passwd read-only and breaking the registration above.
|
|
user: "${DOCKER_UID:-1000}:0"
|
|
entrypoint:
|
|
- /bin/sh
|
|
- -c
|
|
- 'grep -q ":x:$$(id -u):" /etc/passwd || echo "builder:x:$$(id -u):0::/home/frontend:/bin/sh" >> /etc/passwd; exec "$$@"'
|
|
- --
|
|
# The Capacitor CLI writes its config under $HOME/.config. HOME is unset for
|
|
# the arbitrary uid, so it falls back to "/" and fails with EACCES on
|
|
# mkdir /.config. Point it at a writable, non-mounted path (keeps the CLI's
|
|
# throwaway config out of the bind-mounted repo).
|
|
environment:
|
|
HOME: /tmp
|
|
env_file:
|
|
- deploy/env/frontend.defaults
|
|
- deploy/env/frontend.local
|
|
volumes:
|
|
- ./src/frontend/:/home/frontend/
|
|
# Share the same fast node_modules volume as frontend-dev so that
|
|
# `make install-frozen-front` (which runs here) installs into the volume
|
|
# that frontend-dev consumes.
|
|
- frontend-node-modules:/home/frontend/node_modules
|
|
|
|
crowdin:
|
|
image: crowdin/cli:4.11.0@sha256:9b32eafcfd8ba4b5183bd601bfeb8a65b0d338f2d2fc36df155f4845ee244eff
|
|
volumes:
|
|
- ".:/app"
|
|
env_file:
|
|
- deploy/env/crowdin.defaults
|
|
- deploy/env/crowdin.local
|
|
user: "${DOCKER_USER:-1000}"
|
|
working_dir: /app
|
|
|
|
mta-in:
|
|
build:
|
|
context: src/mta-in
|
|
target: runtime-prod
|
|
env_file:
|
|
- deploy/env/mta-in.defaults
|
|
- deploy/env/mta-in.local
|
|
ports:
|
|
- "8910:25"
|
|
depends_on:
|
|
- backend-dev
|
|
|
|
mta-in-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/mta-in
|
|
target: runtime-dev
|
|
env_file:
|
|
- deploy/env/mta-in.defaults
|
|
- deploy/env/mta-in.local
|
|
environment:
|
|
- EXEC_CMD=true
|
|
- MDA_API_BASE_URL=http://localhost:8000/api/mail/
|
|
- MTA_HOST=localhost
|
|
- MTA_PORT=25
|
|
- MTA_IMPL=postfix
|
|
command: pytest -vvs tests/
|
|
volumes:
|
|
- ./src/mta-in:/app
|
|
|
|
mta-in-uv:
|
|
profiles:
|
|
- tools
|
|
volumes:
|
|
- ./src/mta-in:/app
|
|
build:
|
|
context: src/mta-in
|
|
target: uv
|
|
pull_policy: build
|
|
|
|
# ---- Pure-Python (aiosmtpd) inbound MTA --------------------------------
|
|
# Runs side-by-side with the Postfix-based `mta-in` service on a different
|
|
# host port (8920 vs 8910). Both implementations share the same MDA
|
|
# contract, env vars, and test suite. Toggle which one is the public-facing
|
|
# MTA at the edge by switching the upstream pool.
|
|
|
|
mta-in-py:
|
|
build:
|
|
context: src/mta-in
|
|
dockerfile: Dockerfile.pymta
|
|
target: runtime-distroless-prod
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-65532}
|
|
user: ${DOCKER_USER:-65532}
|
|
env_file:
|
|
- deploy/env/mta-in.defaults
|
|
- deploy/env/mta-in.local
|
|
- deploy/env/mta-in-py.defaults
|
|
- deploy/env/mta-in-py.local
|
|
ports:
|
|
- "8920:25"
|
|
- "9120:9100" # Prometheus metrics
|
|
# Defence-in-depth: pymta needs no on-disk writes at runtime. Read-only
|
|
# rootfs + dropped capabilities + no-new-privileges mirror the posture
|
|
# a production k8s pod-spec should run with.
|
|
read_only: true
|
|
cap_drop:
|
|
- ALL
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
tmpfs:
|
|
- /tmp:rw,noexec,nosuid,size=16m
|
|
depends_on:
|
|
- backend-dev
|
|
|
|
mta-in-py-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/mta-in
|
|
dockerfile: Dockerfile.pymta
|
|
target: runtime-dev
|
|
args:
|
|
DOCKER_USER: ${DOCKER_USER:-65532}
|
|
user: ${DOCKER_USER:-65532}
|
|
cap_drop:
|
|
- ALL
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
env_file:
|
|
- deploy/env/mta-in.defaults
|
|
- deploy/env/mta-in.local
|
|
- deploy/env/mta-in-py.defaults
|
|
- deploy/env/mta-in-py.local
|
|
environment:
|
|
- EXEC_CMD=true
|
|
- MDA_API_BASE_URL=http://localhost:8000/api/mail/
|
|
- MTA_HOST=localhost
|
|
- MTA_PORT=25
|
|
- MTA_IMPL=pymta
|
|
- MTA_METRICS_URL=http://localhost:9100/metrics
|
|
command: pytest -vvs tests/
|
|
volumes:
|
|
- ./src/mta-in:/app
|
|
|
|
mta-out:
|
|
build:
|
|
context: src/mta-out
|
|
target: runtime-prod
|
|
env_file:
|
|
- deploy/env/mta-out.defaults
|
|
- deploy/env/mta-out.local
|
|
ports:
|
|
- "8911:587"
|
|
depends_on:
|
|
mailcatcher:
|
|
condition: service_started
|
|
|
|
mta-out-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/mta-out
|
|
target: runtime-dev
|
|
env_file:
|
|
- deploy/env/mta-out.defaults
|
|
- deploy/env/mta-out.local
|
|
environment:
|
|
- EXEC_CMD=true
|
|
- MTA_OUT_SMTP_HOST=localhost:587
|
|
- MTA_OUT_SMTP_USERNAME=user
|
|
- MTA_OUT_SMTP_PASSWORD=pass
|
|
- SMTP_RELAY_HOST=localhost:2525
|
|
command: pytest -vvs tests/
|
|
volumes:
|
|
- ./src/mta-out:/app
|
|
|
|
mta-out-uv:
|
|
profiles:
|
|
- tools
|
|
volumes:
|
|
- ./src/mta-out:/app
|
|
build:
|
|
context: src/mta-out
|
|
target: uv
|
|
pull_policy: build
|
|
|
|
socks-proxy:
|
|
build:
|
|
context: src/socks-proxy
|
|
target: runtime
|
|
env_file:
|
|
- deploy/env/socks-proxy.defaults
|
|
- deploy/env/socks-proxy.local
|
|
ports:
|
|
- "8916:1080"
|
|
|
|
socks-proxy-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/socks-proxy/tests
|
|
environment:
|
|
- SOCKS_PROXY1=user1:pwd1@socks-proxy:1080
|
|
- SOCKS_PROXY2=user2:pwd2@socks-proxy:1080
|
|
depends_on:
|
|
socks-proxy:
|
|
condition: service_started
|
|
|
|
mpa:
|
|
build:
|
|
context: src/mpa/rspamd
|
|
environment:
|
|
- RSPAMD_password=password
|
|
- PORT=8010
|
|
ports:
|
|
- "8918:8010"
|
|
depends_on:
|
|
redis:
|
|
condition: service_started
|
|
|
|
mpa-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/mpa/tests
|
|
environment:
|
|
- RSPAMD_URL=http://mpa:8010/_api
|
|
- RSPAMD_AUTH=Bearer password
|
|
command: pytest -vvs tests/
|
|
volumes:
|
|
- ./src/mpa/tests:/app/tests
|
|
depends_on:
|
|
mpa:
|
|
condition: service_started
|
|
redis:
|
|
condition: service_started
|
|
|
|
# Self-contained jmap-email package tests. Zero infrastructure
|
|
# dependencies (no DB, no opensearch, no redis) — the library has
|
|
# no runtime deps. Source is mounted for instant feedback during
|
|
# development.
|
|
jmap-email-test:
|
|
profiles:
|
|
- tools
|
|
build:
|
|
context: src/jmap-email
|
|
command: pytest -q tests/
|
|
volumes:
|
|
- ./src/jmap-email/jmap_email:/app/jmap_email
|
|
- ./src/jmap-email/tests:/app/tests
|
|
- ./src/jmap-email/pyproject.toml:/app/pyproject.toml
|
|
# Hypothesis' example database. Without it the container starts with
|
|
# no memory of past failures, so Phase.reuse has nothing to replay
|
|
# and an intermittent find stays intermittent.
|
|
#
|
|
# A named volume rather than a bind mount: a bind mount to a path
|
|
# that does not exist yet — and it never does on a fresh clone,
|
|
# since .hypothesis is gitignored — is created by Docker as *root*,
|
|
# which then blocks `git clean -fdx` and any pytest run against a
|
|
# local venv. This is a cache, so nothing needs to read it from the
|
|
# host.
|
|
- jmap-email-hypothesis:/app/.hypothesis
|
|
|
|
volumes:
|
|
jmap-email-hypothesis:
|
|
objectstorage-data:
|
|
frontend-node-modules:
|