#!/usr/bin/env sh set -eu API_LOG="/tmp/projectbrain-api.log" PG_LOG="/tmp/projectbrain-postgres.log" ANALYSIS_WORKER_LOG="/tmp/projectbrain-analysis-worker.log" PROJECTBRAIN_DATA_DIR="${PROJECTBRAIN_DATA_DIR:-/var/lib/projectbrain}" PGDATA="${PROJECTBRAIN_POSTGRES_DATA_DIR:-${PROJECTBRAIN_DATA_DIR}/postgres}" POSTGRES_DB="${POSTGRES_DB:-projectbrain}" POSTGRES_USER="${POSTGRES_USER:-projectbrain}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}" if [ "${PROJECTBRAIN_USE_EXTERNAL_DATABASE:-false}" != "true" ]; then : "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required for embedded PostgreSQL}" export DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:5432/${POSTGRES_DB}?schema=public" else export DATABASE_URL="${DATABASE_URL:?DATABASE_URL is required when PROJECTBRAIN_USE_EXTERNAL_DATABASE=true}" fi mkdir -p "${PGDATA}" mkdir -p /run/postgresql chown -R postgres:postgres "${PROJECTBRAIN_DATA_DIR}" chown postgres:postgres /run/postgresql if [ ! -s "${PGDATA}/PG_VERSION" ]; then echo "[ProjectBrain] Initializing embedded PostgreSQL data directory..." pwfile="/tmp/projectbrain-pgpass" printf '%s' "${POSTGRES_PASSWORD}" > "${pwfile}" chown postgres:postgres "${pwfile}" su postgres -c "initdb -D '${PGDATA}' -U '${POSTGRES_USER}' --pwfile='${pwfile}' --auth-host=md5 --auth-local=trust" >/tmp/projectbrain-initdb.log 2>&1 || { cat /tmp/projectbrain-initdb.log >&2 || true exit 1 } rm -f "${pwfile}" fi echo "[ProjectBrain] Starting embedded PostgreSQL..." su postgres -c "pg_ctl -D '${PGDATA}' -o \"-c listen_addresses='127.0.0.1'\" -w start" > "${PG_LOG}" 2>&1 if ! su postgres -c "psql -U '${POSTGRES_USER}' -d postgres -tAc \"SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DB}'\"" | grep -q 1; then echo "[ProjectBrain] Creating database ${POSTGRES_DB}..." su postgres -c "createdb -U '${POSTGRES_USER}' '${POSTGRES_DB}'" fi cd /app/backend echo "[ProjectBrain] Starting API inside single-container runtime..." NODE_OPTIONS="--max-old-space-size=${PROJECTBRAIN_API_HEAP_MB:-512}" sh scripts/docker-entrypoint.sh > "${API_LOG}" 2>&1 & API_PID="$!" ANALYSIS_WORKER_SUPERVISOR_PID="" NGINX_PID="" cleanup() { echo "[ProjectBrain] Stopping runtime..." if [ -n "${ANALYSIS_WORKER_SUPERVISOR_PID}" ]; then kill "${ANALYSIS_WORKER_SUPERVISOR_PID}" 2>/dev/null || true fi if [ -n "${NGINX_PID}" ]; then kill "${NGINX_PID}" 2>/dev/null || true fi kill "${API_PID}" 2>/dev/null || true su postgres -c "pg_ctl -D '${PGDATA}' -m fast stop" >/dev/null 2>&1 || true } trap cleanup INT TERM echo "[ProjectBrain] Waiting for API readiness..." attempt=0 until curl -fsS "http://127.0.0.1:4000/api/system/readiness" >/dev/null 2>&1; do attempt=$((attempt + 1)) if ! kill -0 "${API_PID}" 2>/dev/null; then echo "[ProjectBrain] API exited before readiness." >&2 cat "${API_LOG}" >&2 || true exit 1 fi if [ "${attempt}" -ge 60 ]; then echo "[ProjectBrain] API did not become ready in time." >&2 cat "${API_LOG}" >&2 || true exit 1 fi sleep 1 done run_analysis_worker() ( child_pid="" stop_worker() { if [ -n "${child_pid}" ]; then kill "${child_pid}" 2>/dev/null || true wait "${child_pid}" 2>/dev/null || true fi exit 0 } trap stop_worker INT TERM while true; do echo "[ProjectBrain] Starting bounded repository-analysis worker..." >> "${ANALYSIS_WORKER_LOG}" NODE_OPTIONS="--max-old-space-size=${PROJECTBRAIN_ANALYSIS_WORKER_HEAP_MB:-384}" node dist/workers/analysisWorker.js >> "${ANALYSIS_WORKER_LOG}" 2>&1 & child_pid="$!" echo 500 > "/proc/${child_pid}/oom_score_adj" 2>/dev/null || true wait "${child_pid}" || true child_pid="" sleep 5 done ) run_analysis_worker & ANALYSIS_WORKER_SUPERVISOR_PID="$!" echo "[ProjectBrain] API and analysis worker are ready. Starting nginx..." nginx -g "daemon off;" & NGINX_PID="$!" wait "${NGINX_PID}"