e6dfeafb85
CI/CD Pipeline / Deploy Staging (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 60h28m37s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 60h30m7s
117 lines
4.3 KiB
Bash
Executable File
117 lines
4.3 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
HOST_PREFIX="${HOST_PREFIX-/host}"
|
|
ROOT_DIR="$HOST_PREFIX/var/lib/xiaoxia-saas-production/repo"
|
|
COMPOSE_DIR="$ROOT_DIR/infra/docker"
|
|
ENV_FILE="$HOST_PREFIX/var/lib/xiaoxia-saas-production/.env"
|
|
RELEASE_VERSION="${RELEASE_VERSION:-}"
|
|
|
|
# Registry 配置
|
|
REGISTRY="${REGISTRY:-git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas}"
|
|
REGISTRY_USER="${REGISTRY_USER:-xiaoxia}"
|
|
REGISTRY_TOKEN="${REGISTRY_TOKEN:-}"
|
|
|
|
ensure_container_running() {
|
|
name="$1"
|
|
if ! docker inspect "$name" >/dev/null 2>&1; then
|
|
echo "Required infrastructure container not found: $name"
|
|
exit 1
|
|
fi
|
|
state="$(docker inspect -f "{{.State.Status}}" "$name")"
|
|
if [ "$state" != "running" ]; then
|
|
echo "Required infrastructure container is not running: $name ($state)"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
if [ ! -d "$ROOT_DIR" ] && [ -n "${PRODUCTION_REPO_FALLBACK:-}" ] && [ -d "$PRODUCTION_REPO_FALLBACK" ]; then
|
|
ROOT_DIR="$PRODUCTION_REPO_FALLBACK"
|
|
COMPOSE_DIR="$ROOT_DIR/infra/docker"
|
|
fi
|
|
|
|
cd "$ROOT_DIR"
|
|
cp "$ENV_FILE" "$ROOT_DIR/.env"
|
|
mkdir -p "$ROOT_DIR/apps/web/public" "$HOST_PREFIX/var/lib/xiaoxia-saas-production/generated"
|
|
[ -f "$ROOT_DIR/apps/web/public/.keep" ] || printf "placeholder" > "$ROOT_DIR/apps/web/public/.keep"
|
|
if [ ! -f "$ROOT_DIR/apps/web/dist/index.html" ]; then
|
|
echo "Missing prebuilt web artifact: $ROOT_DIR/apps/web/dist/index.html"
|
|
echo "Production deploy must not build frontend assets on the server."
|
|
exit 1
|
|
fi
|
|
|
|
ensure_container_running xiaoxia-postgres-production
|
|
ensure_container_running xiaoxia-redis-production
|
|
|
|
cd "$COMPOSE_DIR"
|
|
|
|
if [ -n "$RELEASE_VERSION" ]; then
|
|
export API_IMAGE="xiaoxia-saas-api:$RELEASE_VERSION"
|
|
export WORKER_IMAGE="xiaoxia-saas-worker:$RELEASE_VERSION"
|
|
export WEB_IMAGE="xiaoxia-saas-web:$RELEASE_VERSION"
|
|
export APP_VERSION="$RELEASE_VERSION"
|
|
|
|
# ---- 从 Registry Pull 镜像 ----
|
|
echo "Pulling images from registry: $REGISTRY"
|
|
if [ -n "$REGISTRY_TOKEN" ]; then
|
|
printf "%s" "$REGISTRY_TOKEN" | docker login git.xiaoxiajianji.com -u "$REGISTRY_USER" --password-stdin 2>/dev/null || true
|
|
fi
|
|
|
|
REGISTRY_API="${REGISTRY}/xiaoxia-saas-api:$RELEASE_VERSION"
|
|
REGISTRY_WORKER="${REGISTRY}/xiaoxia-saas-worker:$RELEASE_VERSION"
|
|
REGISTRY_WEB="${REGISTRY}/xiaoxia-saas-web:$RELEASE_VERSION"
|
|
|
|
docker pull "$REGISTRY_API"
|
|
docker pull "$REGISTRY_WORKER"
|
|
docker pull "$REGISTRY_WEB"
|
|
|
|
# Re-tag 为本地镜像名,保持 compose 兼容
|
|
docker tag "$REGISTRY_API" "$API_IMAGE"
|
|
docker tag "$REGISTRY_WORKER" "$WORKER_IMAGE"
|
|
docker tag "$REGISTRY_WEB" "$WEB_IMAGE"
|
|
|
|
echo "All images pulled and tagged."
|
|
fi
|
|
|
|
export DOCKER_BUILDKIT=0
|
|
export COMPOSE_DOCKER_CLI_BUILD=0
|
|
export COMPOSE_PROJECT_NAME=xiaoxia-production-app
|
|
export ENV=production
|
|
export WEB_DOCKERFILE=infra/docker/web-artifact.Dockerfile
|
|
export WEB_NGINX_CONF=infra/docker/nginx-production.conf
|
|
|
|
docker network create xiaoxia-net-production 2>/dev/null || true
|
|
export WORKER_CONCURRENCY="${WORKER_CONCURRENCY:-1}"
|
|
export WORKER_MAX_TASKS_PER_CHILD="${WORKER_MAX_TASKS_PER_CHILD:-100}"
|
|
|
|
if [ "${ALLOW_PRODUCTION_BUILDS:-false}" = "true" ]; then
|
|
docker compose --env-file "$ENV_FILE" build --pull=false api
|
|
docker compose --env-file "$ENV_FILE" build --pull=false worker
|
|
docker compose --env-file "$ENV_FILE" build --pull=false web
|
|
else
|
|
echo "Skipping production image builds (using prebuilt images from registry)."
|
|
docker image inspect "${API_IMAGE:-xiaoxia-saas-api:dev}" >/dev/null
|
|
docker image inspect "${WORKER_IMAGE:-xiaoxia-saas-worker:dev}" >/dev/null
|
|
docker image inspect "${WEB_IMAGE:-xiaoxia-saas-web:dev}" >/dev/null
|
|
fi
|
|
|
|
docker compose --env-file "$ENV_FILE" run --rm --no-deps api sh -c "
|
|
cd /app &&
|
|
python /app/scripts/validate_release_env.py --from-environ --strict-external &&
|
|
alembic upgrade head
|
|
"
|
|
docker compose --env-file "$ENV_FILE" up -d api worker
|
|
docker compose --env-file "$ENV_FILE" up -d --force-recreate web
|
|
docker compose --env-file "$ENV_FILE" ps
|
|
|
|
if [ -n "$RELEASE_VERSION" ] && [ -d "$HOST_PREFIX/etc/cron.d" ]; then
|
|
cat > "$HOST_PREFIX/etc/cron.d/xiaoxia-production-resource-check" << EOF
|
|
*/5 * * * * root cd /var/lib/xiaoxia-saas-production/repo && EXPECTED_VERSION=$RELEASE_VERSION sh scripts/production_resource_check.sh >/var/log/xiaoxia-resource-check.log 2>&1
|
|
EOF
|
|
fi
|
|
|
|
if [ "${PRUNE_UNUSED_DOCKER_AFTER_DEPLOY:-true}" = "true" ]; then
|
|
docker image prune -af --filter "until=168h" || true
|
|
docker builder prune -af --filter "until=168h" || true
|
|
fi
|