최초 기동에만 Core 메뉴 기본값을 복사하고 이후 운영 편집은 배포와 컨테이너 재생성에서 유지한다. Compose 환경과 안전 검증 문서를 함께 갱신한다.
150 lines
5.1 KiB
Bash
Executable File
150 lines
5.1 KiB
Bash
Executable File
#!/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
|
|
|
|
node /opt/sammo/validate-env.mjs
|
|
|
|
git_auth_dir=/srv/data/git-auth
|
|
mkdir -p "$git_auth_dir"
|
|
umask 077
|
|
|
|
if [ -n "${CORE_REPOSITORY_USERNAME:-}" ] || [ -n "${CORE_REPOSITORY_TOKEN:-}" ]; then
|
|
printf '%s' "$CORE_REPOSITORY_USERNAME" >"$git_auth_dir/https-username"
|
|
printf '%s' "$CORE_REPOSITORY_TOKEN" >"$git_auth_dir/https-token"
|
|
chmod 600 "$git_auth_dir/https-username" "$git_auth_dir/https-token"
|
|
export SAMMO_GIT_AUTH_DIR="$git_auth_dir"
|
|
export GIT_ASKPASS=/opt/sammo/git-askpass.sh
|
|
export GIT_TERMINAL_PROMPT=0
|
|
unset CORE_REPOSITORY_USERNAME CORE_REPOSITORY_TOKEN
|
|
else
|
|
rm -f "$git_auth_dir/https-username" "$git_auth_dir/https-token"
|
|
fi
|
|
|
|
if [ -n "${CORE_SSH_PRIVATE_KEY_BASE64:-}" ] || [ -n "${CORE_SSH_KNOWN_HOSTS_BASE64:-}" ]; then
|
|
printf '%s' "$CORE_SSH_PRIVATE_KEY_BASE64" | base64 -d >"$git_auth_dir/id_deploy"
|
|
printf '%s' "$CORE_SSH_KNOWN_HOSTS_BASE64" | base64 -d >"$git_auth_dir/known_hosts"
|
|
chmod 600 "$git_auth_dir/id_deploy" "$git_auth_dir/known_hosts"
|
|
export GIT_SSH_COMMAND="ssh -i $git_auth_dir/id_deploy -o IdentitiesOnly=yes -o UserKnownHostsFile=$git_auth_dir/known_hosts -o StrictHostKeyChecking=yes"
|
|
unset CORE_SSH_PRIVATE_KEY_BASE64 CORE_SSH_KNOWN_HOSTS_BASE64
|
|
else
|
|
rm -f "$git_auth_dir/id_deploy" "$git_auth_dir/known_hosts"
|
|
fi
|
|
|
|
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"
|
|
|
|
navigation_config_file=${CORE_NAVIGATION_CONFIG_FILE:-/srv/data/navigation.json}
|
|
if [ ! -e "$navigation_config_file" ]; then
|
|
navigation_config_dir=$(dirname "$navigation_config_file")
|
|
mkdir -p "$navigation_config_dir"
|
|
cp "$core_root/resources/navigation.json" "$navigation_config_file"
|
|
chmod 644 "$navigation_config_file"
|
|
fi
|
|
|
|
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
|