#!/bin/sh set -eu # The container is always non-interactive. This also makes pnpm replace a # modules directory created with a different store without waiting for a TTY. export CI="${CI:-true}" require_env() { eval "value=\${$1:-}" if [ -z "$value" ]; then echo "$1 is required" >&2 exit 64 fi } for key in POSTGRES_PASSWORD REDIS_PASSWORD GAME_TOKEN_SECRET KAKAO_REST_KEY KAKAO_REDIRECT_URI; do require_env "$key" done case "${CORE_SOURCE_MODE:-clone}" in clone) require_env CORE_REPOSITORY_URL core_root=/srv/core/repository if [ ! -d "$core_root/.git" ]; then mkdir -p /srv/core git clone --no-single-branch "$CORE_REPOSITORY_URL" "$core_root" git -C "$core_root" checkout "${CORE_BOOTSTRAP_REF:-main}" fi ;; bind) core_root=${CORE_BIND_ROOT:-/workspace/core2026} if [ ! -e "$core_root/.git" ] || [ ! -f "$core_root/pnpm-lock.yaml" ]; then echo "CORE_BIND_ROOT must be a Core2026 Git checkout" >&2 exit 66 fi ;; *) echo 'CORE_SOURCE_MODE must be clone or bind' >&2 exit 64 ;; esac git config --global --add safe.directory "$core_root" export GATEWAY_WORKSPACE_ROOT="$core_root" export RELEASE_CONTROLLER_WORKSPACE_ROOT="$core_root" export DATABASE_URL="$(node -e 'const u=new URL("postgresql://localhost");u.username=process.env.POSTGRES_USER||"sammo";u.password=process.env.POSTGRES_PASSWORD;u.hostname=process.env.POSTGRES_HOST||"postgres";u.port=process.env.POSTGRES_PORT||"5432";u.pathname="/"+(process.env.POSTGRES_DB||"sammo");u.searchParams.set("schema","public");process.stdout.write(u.href)')" export GATEWAY_DATABASE_URL="$DATABASE_URL" export REDIS_URL="$(node -e 'const u=new URL("redis://localhost/0");u.password=process.env.REDIS_PASSWORD;u.hostname=process.env.REDIS_HOST||"redis";u.port=process.env.REDIS_PORT||"6379";process.stdout.write(u.href)')" cd "$core_root" pnpm install --frozen-lockfile if [ "${RUNTIME_MODE:-production}" = development ]; then pnpm --filter @sammo-ts/infra prisma:generate echo 'Development container is ready. Start the desired pnpm dev processes with docker compose exec runtime.' if [ "$#" -gt 0 ]; then exec "$@" fi exec sleep infinity fi pnpm --filter @sammo-ts/common build pnpm --filter @sammo-ts/infra prisma:generate pnpm --filter @sammo-ts/infra build pnpm --filter @sammo-ts/logic build pnpm --filter @sammo-ts/game-engine build pnpm --filter @sammo-ts/gateway-api build pnpm --filter @sammo-ts/gateway-frontend build pnpm --filter @sammo-ts/release-controller build pnpm --filter @sammo-ts/infra prisma:migrate:deploy:gateway GATEWAY_ROLE=api node app/gateway-api/dist/index.js & bootstrap_pid=$! cleanup_bootstrap() { kill "$bootstrap_pid" 2>/dev/null || true wait "$bootstrap_pid" 2>/dev/null || true } trap cleanup_bootstrap EXIT INT TERM attempt=0 until curl --fail --silent http://127.0.0.1:15001/healthz >/dev/null; do attempt=$((attempt + 1)) if [ "$attempt" -ge 120 ]; then echo 'Gateway API did not become ready for bootstrap' >&2 exit 1 fi sleep 1 done node /opt/sammo/bootstrap.mjs cleanup_bootstrap trap - EXIT INT TERM pm2_bin="$core_root/app/gateway-api/node_modules/.bin/pm2" "$pm2_bin" start /opt/sammo/ecosystem.config.cjs pm2_pid=$(cat "$PM2_HOME/pm2.pid") tail -n 0 -F "$PM2_HOME/pm2.log" "$PM2_HOME"/logs/*.log & logs_pid=$! shutdown_pm2() { kill "$logs_pid" 2>/dev/null || true "$pm2_bin" kill >/dev/null 2>&1 || true wait "$logs_pid" 2>/dev/null || true } trap 'shutdown_pm2; exit 0' INT TERM while kill -0 "$pm2_pid" 2>/dev/null; do sleep 5 done shutdown_pm2 echo 'PM2 daemon exited unexpectedly' >&2 exit 1