Caddy가 불변 아티팩트를 직접 제공하고 runtime과 builder의 자원·비밀 경계를 분리한다. Gateway active release ref를 재기동에 복원하고 production/development/smoke 모델 검증을 확장한다.
150 lines
5.2 KiB
Bash
Executable File
150 lines
5.2 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"
|
|
/opt/sammo/checkout-active-release.sh "$core_root" "${GATEWAY_ACTIVE_RELEASE_GIT_REF:-}"
|
|
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"
|
|
|
|
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
|
|
|
|
node /opt/sammo/release-builder-client.mjs
|
|
pnpm --filter @sammo-ts/infra prisma:migrate:deploy:gateway
|
|
|
|
core_commit_sha=$(git rev-parse HEAD)
|
|
node tools/build-scripts/publish-frontend-artifact.mjs \
|
|
--artifact-root "${FRONTEND_ARTIFACT_ROOT:-/srv/frontend-artifacts}" \
|
|
--frontend-key gateway \
|
|
--source-root "$core_root/app/gateway-frontend/dist" \
|
|
--commit-sha "$core_commit_sha"
|
|
|
|
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
|