diff --git a/scripts/ci/run_integration_tests.sh b/scripts/ci/run_integration_tests.sh index 87b4b3388..12df41d4e 100755 --- a/scripts/ci/run_integration_tests.sh +++ b/scripts/ci/run_integration_tests.sh @@ -19,127 +19,163 @@ echo "" echo "=== 安装 ffmpeg ===" bash scripts/ci/step_install_ffmpeg.sh -# --- 安装并启动 Redis --- +# --- 启动 Redis --- echo "" -echo "=== 安装并启动 Redis ===" -# 兼容多种包管理器 -install_redis() { - if command -v apk > /dev/null 2>&1; then - apk add --no-cache redis > /dev/null 2>&1 - elif command -v apt-get > /dev/null 2>&1; then - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq > /dev/null 2>&1 - apt-get install -y -qq redis-server > /dev/null 2>&1 - elif command -v yum > /dev/null 2>&1; then - yum install -y -q redis > /dev/null 2>&1 - elif command -v dnf > /dev/null 2>&1; then - dnf install -y -q redis > /dev/null 2>&1 - else - return 1 - fi -} -# 如果redis-cli不存在才安装 -if ! command -v redis-cli > /dev/null 2>&1; then - install_redis || echo "Warning: Redis installation failed, trying Docker..." -fi -# 优先用本地redis-server,失败再fallback -if command -v redis-server > /dev/null 2>&1; then - redis-server --daemonize yes --port 6379 2>/dev/null || true - sleep 1 - for i in $(seq 1 10); do - if redis-cli ping 2>/dev/null | grep -q PONG; then - echo "Redis is ready on 127.0.0.1:6379" - break - fi - sleep 1 - done -fi -# 如果本地redis连不上,fallback到Docker -if ! redis-cli ping 2>/dev/null | grep -q PONG; then - echo "Local Redis not available, falling back to Docker..." - REDIS_CONTAINER="ci-redis-${GITHUB_RUN_ID:-$$}" - docker rm -f "$REDIS_CONTAINER" 2>/dev/null || true - docker run -d --name "$REDIS_CONTAINER" -P --health-cmd "redis-cli ping" --health-interval 2s --health-timeout 2s --health-retries 10 redis:7-alpine - for i in $(seq 1 20); do - if docker inspect --format='{{.State.Health.Status}}' "$REDIS_CONTAINER" 2>/dev/null | grep -q healthy; then - break - fi - sleep 2 - done - REDIS_PORT=$(docker port "$REDIS_CONTAINER" 6379/tcp | cut -d: -f2) - export REDIS_URL="redis://127.0.0.1:$REDIS_PORT/0" - echo "Redis (Docker) ready on 127.0.0.1:$REDIS_PORT" -else - export REDIS_URL="redis://127.0.0.1:6379/0" -fi +echo "=== 启动 Redis ===" -# --- 安装并启动 PostgreSQL --- -echo "" -echo "=== 安装并启动 PostgreSQL ===" - -install_pg_local() { - if command -v pg_isready > /dev/null 2>&1; then - return 0 - fi - if command -v apk > /dev/null 2>&1; then - apk add --no-cache postgresql postgresql-client > /dev/null 2>&1 - # Alpine需要手动初始化 - mkdir -p /var/lib/postgresql/data - chown postgres:postgres /var/lib/postgresql/data - su - postgres -c "initdb -D /var/lib/postgresql/data" > /dev/null 2>&1 - su - postgres -c "pg_ctl -D /var/lib/postgresql/data -l /var/lib/postgresql/logfile start" > /dev/null 2>&1 - sleep 2 - su - postgres -c "psql -c "CREATE USER postgres WITH SUPERUSER PASSWORD 'postgres';"" > /dev/null 2>&1 - su - postgres -c "psql -c "CREATE DATABASE xiaoxia_saas OWNER postgres;"" > /dev/null 2>&1 - elif command -v apt-get > /dev/null 2>&1; then - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq > /dev/null 2>&1 - apt-get install -y -qq postgresql postgresql-client > /dev/null 2>&1 - PG_VERSION=$(pg_lsclusters -h | head -1 | awk '{print $1}') - PG_CLUSTER=$(pg_lsclusters -h | head -1 | awk '{print $2}') - PG_HBA="/etc/postgresql/$PG_VERSION/$PG_CLUSTER/pg_hba.conf" - sed -i "s/local.*all.*all.*peer/local all all trust/" "$PG_HBA" 2>/dev/null || true - sed -i "s/host.*all.*all.*127.0.0.1.*scram-sha-256/host all all 127.0.0.1/32 trust/" "$PG_HBA" 2>/dev/null || true - pg_ctlcluster "$PG_VERSION" "$PG_CLUSTER" start 2>/dev/null || true - sleep 2 - su - postgres -c "psql -c "CREATE USER postgres WITH SUPERUSER PASSWORD 'postgres';"" 2>/dev/null || true - su - postgres -c "psql -c "CREATE DATABASE xiaoxia_saas OWNER postgres;"" 2>/dev/null || true +start_redis() { + local name="ci-redis-${GITHUB_RUN_ID:-$$}" + + docker rm -f "$name" 2>/dev/null || true + docker run -d --name "$name" \ + -p 0:6379 \ + --health-cmd "redis-cli ping" \ + --health-interval 2s \ + --health-timeout 2s \ + --health-retries 15 \ + redis:7-alpine > /dev/null 2>&1 + + # 等几秒让容器起来 + sleep 3 + + # 方式1: 端口映射 + local port="" + port=$(docker inspect --format='{{if (index .NetworkSettings.Ports "6379/tcp")}}{{(index (index .NetworkSettings.Ports "6379/tcp") 0).HostPort}}{{end}}' "$name" 2>/dev/null || true) + + if [ -n "$port" ]; then + echo " 端口映射: 127.0.0.1:$port" + # 等健康 + for i in $(seq 1 20); do + if docker inspect --format='{{.State.Health.Status}}' "$name" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + # TCP验证 + for i in $(seq 1 15); do + if (echo > /dev/tcp/127.0.0.1/$port) 2>/dev/null; then + echo "✅ Redis端口映射模式可用" + export REDIS_URL="redis://127.0.0.1:$port/0" + return 0 + fi + sleep 1 + done + echo " ⚠️ 端口映射TCP不通,尝试容器IP" else - return 1 + echo " ⚠️ 无映射端口,尝试容器IP" fi + + # 方式2: 容器IP + local ip="" + ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$name" 2>/dev/null || true) + + if [ -n "$ip" ] && [ "$ip" != "null" ]; then + echo " 容器IP: $ip" + for i in $(seq 1 20); do + if docker inspect --format='{{.State.Health.Status}}' "$name" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + for i in $(seq 1 15); do + if (echo > /dev/tcp/$ip/6379) 2>/dev/null; then + echo "✅ Redis容器IP模式可用" + export REDIS_URL="redis://$ip:6379/0" + return 0 + fi + sleep 1 + done + fi + + echo "❌ Redis启动失败" + docker ps -a | head -5 + docker logs --tail=10 "$name" 2>/dev/null || true + return 1 } -PG_LOCAL_OK=0 -if install_pg_local; then - for i in $(seq 1 15); do - if pg_isready -U postgres -h 127.0.0.1 -p 5432 2>/dev/null | grep -q "accepting connections"; then - echo "PostgreSQL is ready on 127.0.0.1:5432 (local install)" - PG_LOCAL_OK=1 - break - fi - sleep 2 - done -fi +start_redis -# 本地安装失败则fallback到Docker -if [ "$PG_LOCAL_OK" != "1" ]; then - echo "Local PostgreSQL not available, falling back to Docker..." - PG_CONTAINER="ci-pg-${GITHUB_RUN_ID:-$$}" - docker rm -f "$PG_CONTAINER" 2>/dev/null || true - docker run -d --name "$PG_CONTAINER" -P --shm-size=256m -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=xiaoxia_saas --health-cmd "pg_isready -U postgres" --health-interval 3s --health-timeout 3s --health-retries 20 postgres:16-alpine - for i in $(seq 1 30); do - if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then - break - fi - sleep 2 - done - PG_PORT=$(docker port "$PG_CONTAINER" 5432/tcp | cut -d: -f2) - echo "PostgreSQL (Docker) ready on 127.0.0.1:$PG_PORT" - export DATABASE_URL="postgresql+psycopg://postgres:postgres@127.0.0.1:$PG_PORT/xiaoxia_saas" -else - export DATABASE_URL="postgresql+psycopg://postgres:postgres@127.0.0.1:5432/xiaoxia_saas" -fi +# --- 启动 PostgreSQL --- +echo "" +echo "=== 启动 PostgreSQL ===" +start_pg() { + local name="ci-pg-${GITHUB_RUN_ID:-$$}" + + docker rm -f "$name" 2>/dev/null || true + docker run -d --name "$name" \ + -p 0:5432 \ + --shm-size=256m \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=xiaoxia_saas \ + --health-cmd "pg_isready -U postgres" \ + --health-interval 3s \ + --health-timeout 3s \ + --health-retries 20 \ + postgres:16-alpine > /dev/null 2>&1 + + sleep 3 + + # 方式1: 端口映射 + local port="" + port=$(docker inspect --format='{{if (index .NetworkSettings.Ports "5432/tcp")}}{{(index (index .NetworkSettings.Ports "5432/tcp") 0).HostPort}}{{end}}' "$name" 2>/dev/null || true) + + if [ -n "$port" ]; then + echo " 端口映射: 127.0.0.1:$port" + # 等健康 + for i in $(seq 1 30); do + if docker inspect --format='{{.State.Health.Status}}' "$name" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + # TCP验证 + for i in $(seq 1 20); do + if (echo > /dev/tcp/127.0.0.1/$port) 2>/dev/null; then + echo "✅ PG端口映射模式可用" + export DATABASE_URL="postgresql+psycopg://postgres:postgres@127.0.0.1:$port/xiaoxia_saas" + return 0 + fi + sleep 1 + done + echo " ⚠️ 端口映射TCP不通,尝试容器IP" + else + echo " ⚠️ 无映射端口,尝试容器IP" + fi + + # 方式2: 容器IP + local ip="" + ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$name" 2>/dev/null || true) + + if [ -n "$ip" ] && [ "$ip" != "null" ]; then + echo " 容器IP: $ip" + for i in $(seq 1 30); do + if docker inspect --format='{{.State.Health.Status}}' "$name" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + for i in $(seq 1 20); do + if (echo > /dev/tcp/$ip/5432) 2>/dev/null; then + echo "✅ PG容器IP模式可用" + export DATABASE_URL="postgresql+psycopg://postgres:postgres@$ip:5432/xiaoxia_saas" + return 0 + fi + sleep 1 + done + fi + + echo "❌ PostgreSQL启动失败" + echo " 容器状态:" + docker inspect --format='状态:{{.State.Status}} 健康:{{.State.Health.Status}} 退出码:{{.State.ExitCode}}' "$name" 2>/dev/null || true + docker logs --tail=20 "$name" 2>/dev/null || true + return 1 +} + +start_pg + +echo "" echo "=== 执行 Alembic 迁移 ===" PYTHONPATH="$PWD/apps/api:$PWD" python3 -m alembic upgrade head echo "✅ 迁移完成" @@ -180,13 +216,6 @@ fi rm -f "$PERF_OUTPUT" set -e -# --- 清理 --- -echo "" -echo "=== 停止服务 ===" -pg_ctlcluster $PG_VERSION $PG_CLUSTER stop 2>/dev/null || true -redis-cli shutdown 2>/dev/null || true -echo "✅ 清理完成" - # --- 覆盖率汇总 --- echo "" echo "=== 覆盖率汇总 ===" diff --git a/scripts/ci/run_validate.sh b/scripts/ci/run_validate.sh index ae986b89a..0eba04df0 100755 --- a/scripts/ci/run_validate.sh +++ b/scripts/ci/run_validate.sh @@ -219,17 +219,83 @@ fi if [ "$PG_LOCAL_OK" != "1" ]; then echo "Local PG not available, falling back to Docker..." PG_CONTAINER="ci-pg-validate-${GITHUB_RUN_ID:-$$}" + docker rm -f "$PG_CONTAINER" 2>/dev/null || true - docker run -d --name "$PG_CONTAINER" -P --shm-size=256m -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=xiaoxia_saas --health-cmd "pg_isready -U postgres" --health-interval 3s --health-timeout 3s --health-retries 20 postgres:16-alpine - for i in $(seq 1 30); do - if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then - break + docker run -d --name "$PG_CONTAINER" \ + -p 0:5432 \ + --shm-size=256m \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=xiaoxia_saas \ + --health-cmd "pg_isready -U postgres" \ + --health-interval 3s \ + --health-timeout 3s \ + --health-retries 20 \ + postgres:16-alpine > /dev/null 2>&1 + + sleep 3 + + # 方式1: 端口映射 + PG_PORT=$(docker inspect --format='{{if (index .NetworkSettings.Ports "5432/tcp")}}{{(index (index .NetworkSettings.Ports "5432/tcp") 0).HostPort}}{{end}}' "$PG_CONTAINER" 2>/dev/null || true) + + PG_HOST="127.0.0.1" + PG_OK=0 + if [ -n "$PG_PORT" ]; then + echo " PG端口映射: 127.0.0.1:$PG_PORT" + for i in $(seq 1 30); do + if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + for i in $(seq 1 20); do + if (echo > /dev/tcp/127.0.0.1/$PG_PORT) 2>/dev/null; then + echo "✅ PG端口映射模式可用" + PG_OK=1 + break + fi + sleep 1 + done + if [ "$PG_OK" != "1" ]; then + echo " ⚠️ 端口映射TCP不通,尝试容器IP" fi - sleep 2 - done - PG_PORT=$(docker port "$PG_CONTAINER" 5432/tcp | cut -d: -f2) - echo "PostgreSQL (Docker) ready on port $PG_PORT" - export DATABASE_URL="postgresql+psycopg://postgres:postgres@127.0.0.1:$PG_PORT/xiaoxia_saas" + else + echo " ⚠️ 无映射端口,尝试容器IP" + fi + + # 方式2: 容器IP直连 + if [ "$PG_OK" != "1" ]; then + PG_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_CONTAINER" 2>/dev/null || true) + if [ -n "$PG_IP" ] && [ "$PG_IP" != "null" ]; then + echo " PG容器IP: $PG_IP" + for i in $(seq 1 30); do + if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then + break + fi + sleep 2 + done + for i in $(seq 1 20); do + if (echo > /dev/tcp/$PG_IP/5432) 2>/dev/null; then + echo "✅ PG容器IP模式可用" + PG_HOST="$PG_IP" + PG_PORT=5432 + PG_OK=1 + break + fi + sleep 1 + done + fi + fi + + if [ "$PG_OK" != "1" ]; then + echo "❌ PG启动失败,打印诊断信息:" + docker inspect --format='状态:{{.State.Status}} 健康:{{.State.Health.Status}} 退出码:{{.State.ExitCode}}' "$PG_CONTAINER" 2>/dev/null || true + docker logs --tail=20 "$PG_CONTAINER" 2>/dev/null || true + exit 1 + fi + + echo "PostgreSQL (Docker) ready on $PG_HOST:$PG_PORT" + export DATABASE_URL="postgresql+psycopg://postgres:postgres@$PG_HOST:$PG_PORT/xiaoxia_saas" else export DATABASE_URL="postgresql+psycopg://postgres:postgres@127.0.0.1:5432/xiaoxia_saas" fi