chore: import upstream snapshot with attribution
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# E2E entrypoint for the multi-stage Dockerfile's `e2e` target.
|
||||
#
|
||||
# Dispatches on SERVICE_ROLE to the test-only entrypoints under tests/e2e/.
|
||||
# Those scripts apply sys.modules hijacks and LLM/embedding patches BEFORE
|
||||
# importing production app code (see tests/e2e/run_backend.py for rationale).
|
||||
#
|
||||
# Production never sees this file: tests/ is excluded from the production
|
||||
# stage, and the production stage uses scripts/docker/entrypoint.sh.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SERVICE_ROLE="${SERVICE_ROLE:-api}"
|
||||
echo "[e2e-entrypoint] starting role=${SERVICE_ROLE}"
|
||||
|
||||
wait_for_db() {
|
||||
# Block until the database is reachable. We don't loop forever — Compose
|
||||
# depends_on/healthchecks already gate on db readiness, this is just
|
||||
# belt-and-suspenders so a slow first connection doesn't race migrations.
|
||||
for i in {1..60}; do
|
||||
echo "[e2e-entrypoint] db check attempt ${i}/60"
|
||||
if python -c "from app.db import engine; import asyncio; asyncio.run(engine.dispose())"; then
|
||||
echo "[e2e-entrypoint] db reachable after ${i} attempts"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "[e2e-entrypoint] ERROR: db not reachable after 60s" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
case "${SERVICE_ROLE}" in
|
||||
api)
|
||||
wait_for_db
|
||||
echo "[e2e-entrypoint] running alembic upgrade head"
|
||||
alembic upgrade head
|
||||
# `exec` so SIGTERM from `docker stop` reaches Python directly,
|
||||
# without a shell wrapper interposing.
|
||||
exec python tests/e2e/run_backend.py
|
||||
;;
|
||||
worker)
|
||||
# Worker doesn't run migrations — the api role does that exactly once.
|
||||
# We still wait for db so Celery's broker connection check doesn't
|
||||
# race against an unready Postgres on cold start.
|
||||
wait_for_db
|
||||
exec python tests/e2e/run_celery.py
|
||||
;;
|
||||
*)
|
||||
echo "[e2e-entrypoint] ERROR: unknown SERVICE_ROLE='${SERVICE_ROLE}' (expected: api | worker)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# SERVICE_ROLE controls which process(es) this container runs.
|
||||
#
|
||||
# migrate – Run `alembic upgrade head`, verify zero_publication,
|
||||
# then exit 0. Used by the dedicated `migrations` service
|
||||
# in docker-compose.yml so downstream services can gate
|
||||
# on `condition: service_completed_successfully`.
|
||||
# api – FastAPI backend only (does NOT run migrations)
|
||||
# worker – Celery worker only
|
||||
# beat – Celery beat scheduler only
|
||||
# all – migrations + api + worker + beat in one container
|
||||
# (legacy / dev default; fails fast on migration error)
|
||||
#
|
||||
# Set SERVICE_ROLE as an environment variable in Coolify for
|
||||
# each service deployment.
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
SERVICE_ROLE="${SERVICE_ROLE:-all}"
|
||||
echo "Starting SurfSense with SERVICE_ROLE=${SERVICE_ROLE}"
|
||||
|
||||
# ── Autoscale defaults (override via env) ────────────────────
|
||||
# CELERY_MAX_WORKERS – max concurrent worker processes
|
||||
# CELERY_MIN_WORKERS – min workers kept warm
|
||||
# CELERY_QUEUES – comma-separated queues to consume
|
||||
# (empty = all queues for backward compat)
|
||||
CELERY_MAX_WORKERS="${CELERY_MAX_WORKERS:-10}"
|
||||
CELERY_MIN_WORKERS="${CELERY_MIN_WORKERS:-2}"
|
||||
CELERY_MAX_TASKS_PER_CHILD="${CELERY_MAX_TASKS_PER_CHILD:-50}"
|
||||
CELERY_QUEUES="${CELERY_QUEUES:-}"
|
||||
|
||||
# ── Graceful shutdown ────────────────────────────────────────
|
||||
PIDS=()
|
||||
|
||||
cleanup() {
|
||||
echo "Shutting down services..."
|
||||
for pid in "${PIDS[@]}"; do
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
done
|
||||
for pid in "${PIDS[@]}"; do
|
||||
wait "$pid" 2>/dev/null || true
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup SIGTERM SIGINT
|
||||
|
||||
# ── Database migrations (only for migrate / all) ─────────────
|
||||
# Fail-fast contract:
|
||||
# - alembic upgrade head must succeed within ${MIGRATION_TIMEOUT:-900}s
|
||||
# - zero_publication must match the canonical app.zero_publication shape
|
||||
# Either failure exits non-zero so the dedicated `migrations` compose
|
||||
# service exits non-zero, halting the rest of the stack instead of
|
||||
# silently producing a drifted Zero publication.
|
||||
run_migrations() {
|
||||
echo "Running database migrations..."
|
||||
for i in {1..30}; do
|
||||
if python -c "from app.db import engine; import asyncio; asyncio.run(engine.dispose())" 2>/dev/null; then
|
||||
echo "Database is ready."
|
||||
break
|
||||
fi
|
||||
echo "Waiting for database... ($i/30)"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
local timeout_secs="${MIGRATION_TIMEOUT:-900}"
|
||||
echo "Running alembic upgrade head (timeout=${timeout_secs}s)..."
|
||||
if ! timeout "${timeout_secs}" alembic upgrade head; then
|
||||
echo "ERROR: alembic upgrade head failed (or exceeded ${timeout_secs}s timeout)." >&2
|
||||
echo "Refusing to start. Inspect the error above and re-run." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Migrations completed successfully."
|
||||
|
||||
echo "Verifying zero_publication matches the canonical shape..."
|
||||
if ! python -m app.zero_publication --verify; then
|
||||
echo "ERROR: zero_publication does not match the canonical shape." >&2
|
||||
echo "Inspect alembic state with:" >&2
|
||||
echo " docker compose exec db psql -U \"\$DB_USER\" -d \"\$DB_NAME\" -c 'SELECT * FROM alembic_version;'" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Service starters ─────────────────────────────────────────
|
||||
start_api() {
|
||||
echo "Starting FastAPI Backend..."
|
||||
python main.py &
|
||||
PIDS+=($!)
|
||||
echo " FastAPI PID=${PIDS[-1]}"
|
||||
}
|
||||
|
||||
start_worker() {
|
||||
QUEUE_ARGS=""
|
||||
if [ -n "${CELERY_QUEUES}" ]; then
|
||||
QUEUE_ARGS="--queues=${CELERY_QUEUES}"
|
||||
else
|
||||
# When no queues specified, consume from the default, connectors, and
|
||||
# gateway maintenance queues. Without --queues, Celery only consumes
|
||||
# from the default queue, leaving connector/gateway maintenance tasks stuck.
|
||||
DEFAULT_Q="${CELERY_TASK_DEFAULT_QUEUE:-surfsense}"
|
||||
QUEUE_ARGS="--queues=${DEFAULT_Q},${DEFAULT_Q}.connectors,${DEFAULT_Q}.gateway"
|
||||
fi
|
||||
|
||||
echo "Starting Celery Worker (autoscale=${CELERY_MAX_WORKERS},${CELERY_MIN_WORKERS}, max-tasks-per-child=${CELERY_MAX_TASKS_PER_CHILD}, queues=${CELERY_QUEUES:-all})..."
|
||||
celery -A app.celery_app worker \
|
||||
--loglevel=info \
|
||||
--autoscale="${CELERY_MAX_WORKERS},${CELERY_MIN_WORKERS}" \
|
||||
--max-tasks-per-child="${CELERY_MAX_TASKS_PER_CHILD}" \
|
||||
--prefetch-multiplier=1 \
|
||||
-Ofair \
|
||||
${QUEUE_ARGS} &
|
||||
PIDS+=($!)
|
||||
echo " Celery Worker PID=${PIDS[-1]}"
|
||||
}
|
||||
|
||||
start_beat() {
|
||||
echo "Starting Celery Beat..."
|
||||
celery -A app.celery_app beat --loglevel=info &
|
||||
PIDS+=($!)
|
||||
echo " Celery Beat PID=${PIDS[-1]}"
|
||||
}
|
||||
|
||||
# ── Main: run based on role ──────────────────────────────────
|
||||
case "${SERVICE_ROLE}" in
|
||||
migrate)
|
||||
run_migrations
|
||||
echo "Migrations complete; exiting cleanly."
|
||||
exit 0
|
||||
;;
|
||||
api)
|
||||
start_api
|
||||
;;
|
||||
worker)
|
||||
start_worker
|
||||
;;
|
||||
beat)
|
||||
start_beat
|
||||
;;
|
||||
all)
|
||||
run_migrations
|
||||
start_api
|
||||
sleep 5
|
||||
start_worker
|
||||
sleep 3
|
||||
start_beat
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown SERVICE_ROLE '${SERVICE_ROLE}'. Use: migrate, api, worker, beat, or all"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "All requested services started. PIDs: ${PIDS[*]}"
|
||||
|
||||
# Wait for any process to exit
|
||||
wait -n
|
||||
|
||||
# If we get here, one process exited unexpectedly
|
||||
exit $?
|
||||
Reference in New Issue
Block a user