#!/usr/bin/env bash set -euo pipefail cd /app/site wait_for_database() { if [[ -z "${DATABASE_URL:-}" && -z "${DB_HOST:-}" ]]; then return 0 fi echo "Waiting for database..." for _ in $(seq 1 30); do if uv run python - <<'PY' import os import sys os.environ.setdefault("DJANGO_SETTINGS_MODULE", "client_site.settings") import django from django.db import connections from django.db.utils import OperationalError django.setup() try: connections["default"].ensure_connection() except OperationalError: sys.exit(1) PY then echo "Database is ready." return 0 fi sleep 2 done echo "Database did not become ready in time." >&2 exit 1 } wait_for_database uv run python manage.py migrate --noinput # Email branding (and anything else that resolves static URLs) needs the # ManifestStaticFilesStorage map. Web entrypoint already collectstatics; # worker must too or hashed lookups raise ValueError. uv run python manage.py collectstatic --noinput interval="${DISPATCH_DUE_INTERVAL_SECONDS:-15}" echo "Starting dispatch_due poller (every ${interval}s)." uv run python manage.py dispatch_due --loop --interval "${interval}" & dispatch_pid=$! cleanup() { echo "Stopping dispatch_due poller..." kill "${dispatch_pid}" 2>/dev/null || true wait "${dispatch_pid}" 2>/dev/null || true } trap cleanup EXIT INT TERM # Dev uses ImmediateBackend: enqueue runs inline in dispatch_due. No dj-queue. if [[ "${DJANGO_ENV:-}" == "dev" ]]; then echo "Dev worker: dispatch_due loop publishes scheduled work." wait "${dispatch_pid}" exit $? fi # dj-queue supervisor (workers + dispatcher + scheduler). Run on ONE host only. uv run python manage.py dj_queue