29ca51da9c
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 18s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 14s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m0s
CI/CD Pipeline / Integration Tests (push) Successful in 1m32s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 1m40s
CI/CD Pipeline / Validate - Style (push) Successful in 2m15s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m16s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m44s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 2m12s
CI/CD Pipeline / Validate - Security (push) Successful in 5m30s
CI/CD Pipeline / Unit Tests (push) Successful in 7m44s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 126h13m58s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 126h19m7s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 126h21m46s
CI/CD Pipeline / PR Build Web Image (push) Failing after 126h21m33s
CI/CD Pipeline / PR Build API Image (push) Failing after 126h21m34s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 126h18m54s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 126h18m54s
CI/CD Pipeline / CI Gate (push) Failing after 126h13m45s
CI/CD Pipeline / Build Production API Image (push) Failing after 126h13m45s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 126h21m35s
CI/CD Pipeline / Deploy Production (push) Failing after 126h13m42s
CI/CD Pipeline / Canary Release to Production (push) Failing after 126h13m42s
CI/CD Pipeline / Build Production Web Image (push) Failing after 126h13m45s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 126h16m37s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 126h16m37s
CI/CD Pipeline / Frontend Lint (push) Failing after 126h21m29s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 126h51m43s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
110 lines
3.4 KiB
Bash
Executable File
110 lines
3.4 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
HOST_PREFIX="${HOST_PREFIX-/host}"
|
|
ROOT_DIR="$HOST_PREFIX/var/lib/xiaoxia-saas-staging/repo"
|
|
COMPOSE_DIR="$ROOT_DIR/infra/docker"
|
|
ENV_FILE="$HOST_PREFIX/var/lib/xiaoxia-saas-staging/.env"
|
|
|
|
REGISTRY="${REGISTRY:-172.30.18.198:5000}"
|
|
|
|
ensure_container_running() {
|
|
name="$1"
|
|
if ! docker inspect "$name" >/dev/null 2>&1; then
|
|
echo "❌ Required infrastructure container not found: $name"
|
|
echo " Run infra/docker/infra.yml first before deploying applications."
|
|
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
|
|
}
|
|
|
|
cd "$ROOT_DIR"
|
|
cp "$ENV_FILE" "$ROOT_DIR/.env"
|
|
mkdir -p "$ROOT_DIR/apps/web/public" "$HOST_PREFIX/var/lib/xiaoxia-saas-staging/generated"
|
|
[ -f "$ROOT_DIR/apps/web/public/.keep" ] || printf 'placeholder' > "$ROOT_DIR/apps/web/public/.keep"
|
|
|
|
ensure_container_running xiaoxia-postgres-staging
|
|
ensure_container_running xiaoxia-redis-staging
|
|
|
|
cd "$COMPOSE_DIR"
|
|
WEB_PORT="${WEB_PORT:-3001}"
|
|
export WEB_PORT
|
|
export ENV=staging
|
|
export DOCKER_BUILDKIT=0
|
|
export COMPOSE_DOCKER_CLI_BUILD=0
|
|
|
|
# Ensure isolated staging network exists
|
|
docker network create xiaoxia-net-staging 2>/dev/null || true
|
|
|
|
# 登录内网 Registry(拉取 API/Worker 镜像需要认证)
|
|
REGISTRY_USER="${REGISTRY_USER:-admin}"
|
|
REGISTRY_PASS="${REGISTRY_PASS:-}"
|
|
if [ -n "$REGISTRY_PASS" ]; then
|
|
printf '%s' "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin 2>/dev/null || true
|
|
fi
|
|
|
|
# Set default image names with registry prefix if not provided
|
|
export API_IMAGE="${API_IMAGE:-${REGISTRY}/xiaoxia-saas-api:dev}"
|
|
export WORKER_IMAGE="${WORKER_IMAGE:-${REGISTRY}/xiaoxia-saas-worker:dev}"
|
|
|
|
# Use staging-specific nginx config (proxy_pass → xiaoxia-api-staging:8000)
|
|
|
|
if [ "${REBUILD_BACKEND:-0}" = "1" ] || [ "${BUILD_WEB:-0}" = "1" ]; then
|
|
if [ "${ALLOW_STAGING_BUILDS:-false}" != "true" ]; then
|
|
echo "❌ Staging deploy must not build images on the business server."
|
|
echo " Build artifacts/images on the dedicated CI/build server, then deploy with REBUILD_BACKEND=0 BUILD_WEB=0."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ "${REBUILD_BACKEND:-0}" = "1" ]; then
|
|
docker compose build --pull=false api
|
|
docker compose build --pull=false worker
|
|
fi
|
|
if [ "${BUILD_WEB:-0}" = "1" ]; then
|
|
docker compose build --pull=false web
|
|
fi
|
|
if [ "${RUN_MIGRATIONS:-0}" = "1" ]; then
|
|
docker compose run --rm --no-deps api sh -c '
|
|
cd /app
|
|
set +e
|
|
python - <<"PY"
|
|
import os
|
|
from sqlalchemy import create_engine, text
|
|
|
|
url = os.environ["DATABASE_URL"]
|
|
engine = create_engine(url)
|
|
with engine.begin() as conn:
|
|
has_version = conn.execute(
|
|
text("SELECT to_regclass(:table_name)"),
|
|
{"table_name": "public.alembic_version"},
|
|
).scalar() is not None
|
|
has_tables = conn.execute(
|
|
text(
|
|
"SELECT COUNT(*) FROM pg_tables "
|
|
"WHERE schemaname = :schema_name AND tablename != :version_table"
|
|
),
|
|
{"schema_name": "public", "version_table": "alembic_version"},
|
|
).scalar()
|
|
if has_tables and not has_version:
|
|
raise SystemExit(42)
|
|
PY
|
|
status=$?
|
|
set -e
|
|
if [ "$status" -eq 0 ]; then
|
|
alembic upgrade head
|
|
elif [ "$status" -eq 42 ]; then
|
|
alembic stamp head && alembic upgrade head
|
|
else
|
|
exit "$status"
|
|
fi
|
|
'
|
|
fi
|
|
docker compose up -d api worker web
|
|
docker compose ps
|