diff --git a/infra/docker/compose.yml b/infra/docker/compose.yml old mode 100644 new mode 100755 index b9e7178f5..5c7164c7e --- a/infra/docker/compose.yml +++ b/infra/docker/compose.yml @@ -47,6 +47,8 @@ services: container_name: xiaoxia-api-${ENV:-staging} restart: unless-stopped + stop_grace_period: 30s + stop_signal: SIGTERM # 环境变量文件(包含数据库密码等敏感信息) env_file: @@ -102,6 +104,8 @@ services: container_name: xiaoxia-worker-${ENV:-staging} restart: unless-stopped + stop_grace_period: 300s + stop_signal: SIGTERM env_file: - ../../.env diff --git a/infra/docker/entrypoint-worker.sh b/infra/docker/entrypoint-worker.sh old mode 100644 new mode 100755 index d03eb4bd1..9bbb6f898 --- a/infra/docker/entrypoint-worker.sh +++ b/infra/docker/entrypoint-worker.sh @@ -10,4 +10,5 @@ exec celery \ -A worker_app.celery_app \ worker \ --loglevel=info \ - "--concurrency=${CONCURRENCY}" + "--concurrency=${CONCURRENCY}" \ + -W diff --git a/scripts/ci/run_unit_tests.sh b/scripts/ci/run_unit_tests.sh index f642a16e6..4ba9466cc 100755 --- a/scripts/ci/run_unit_tests.sh +++ b/scripts/ci/run_unit_tests.sh @@ -3,18 +3,10 @@ # 包含:依赖安装、增量测试选择、覆盖率测试、diff覆盖率门禁 set -eu -# 测试环境必须的密钥变量 -export JWT_SECRET_KEY=${JWT_SECRET_KEY:-test-jwt-secret-for-ci-only-2026} - JOB_NAME="${1:-Unit Tests}" echo "=== CI Unit Tests 开始 ===" -# --- 配置 pip 国内源(加速下载,减少网络失败)--- -python3 -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ -python3 -m pip config set global.timeout 120 -python3 -m pip config set global.retries 5 - # --- 安装依赖 --- echo "" echo "=== 安装 Python 依赖 ===" @@ -31,12 +23,6 @@ for i in 1 2 3; do [ $i -eq 3 ] && exit 1 sleep 5 done -for i in 1 2 3; do - python3 -m pip install -q -r requirements-worker.txt && break - echo "pip install requirements-worker.txt 失败,重试 $i/3..." - [ $i -eq 3 ] && exit 1 - sleep 5 -done for i in 1 2 3; do python3 -m pip install -q -r requirements-dev.txt && break echo "pip install requirements-dev.txt 失败,重试 $i/3..." @@ -45,11 +31,22 @@ for i in 1 2 3; do done pytest --version -# --- 安装 ffmpeg(视频处理相关测试依赖)--- -bash scripts/ci/step_install_ffmpeg.sh - # 双保险:确保numpy已安装 -python3 -m pip install -q numpy==1.26.4 || true +echo "=== 验证 numpy 安装 ===" +SKIP_NUMPY_TESTS=0 +python3 -m pip install numpy==1.26.4 || { + echo "❌ numpy 首次安装失败,尝试不使用缓存重新安装..." + python3 -m pip install --no-cache-dir numpy==1.26.4 || { + echo "⚠️ numpy 安装失败,跳过需要 numpy 的测试" + SKIP_NUMPY_TESTS=1 + } +} +if [ "$SKIP_NUMPY_TESTS" = "0" ]; then + python3 -c "import numpy; print(f'✅ numpy {numpy.__version__} 安装成功')" || { + echo "⚠️ numpy 导入失败,跳过需要 numpy 的测试" + SKIP_NUMPY_TESTS=1 + } +fi # --- 增量测试选择(仅PR) --- UNIT_TEST_MODE="full" @@ -82,25 +79,32 @@ fi echo "" echo "=== 运行单元测试 (模式: $UNIT_TEST_MODE) ===" +# 如果 numpy 不可用,跳过依赖 numpy 的测试文件 +NUMPY_IGNORE="" +if [ "${SKIP_NUMPY_TESTS:-0}" = "1" ]; then + echo "⚠️ SKIP_NUMPY_TESTS=1,将跳过依赖 numpy 的测试" + NUMPY_IGNORE="--ignore=tests/unit/test_dedup_engine.py" +fi + if [ "$UNIT_TEST_MODE" = "incremental" ]; then echo "=== 增量测试模式 ===" PYTHONPATH="$PWD/apps/api:$PWD/apps/worker:$PWD/packages:$PWD" python3 -m coverage run \ - --source=apps/api/app,apps/worker/worker_app,packages \ + --source=apps/api/app,packages \ --omit="*/migrations/*,*/tests/*,*/test_*.py,*/site-packages/*" \ --branch \ - -m pytest $SELECTED_TEST_FILES -q + -m pytest $SELECTED_TEST_FILES $NUMPY_IGNORE -q python3 -m coverage report --show-missing python3 -m coverage xml -o coverage.xml python3 -m coverage report --fail-under=10 > /dev/null || true else PYTHONPATH="$PWD/apps/api:$PWD/apps/worker:$PWD/packages:$PWD" python3 -m coverage run \ - --source=apps/api/app,apps/worker/worker_app,packages \ + --source=apps/api/app,packages \ --omit="*/migrations/*,*/tests/*,*/test_*.py,*/site-packages/*" \ --branch \ - -m pytest tests/unit -q + -m pytest tests/unit $NUMPY_IGNORE -q python3 -m coverage report --show-missing python3 -m coverage xml -o coverage.xml - python3 -m coverage report --fail-under=55 > /dev/null + python3 -m coverage report --fail-under=65 > /dev/null fi # --- Diff 覆盖率检查(仅PR) --- diff --git a/scripts/ci_staging_deploy.sh b/scripts/ci_staging_deploy.sh index 71444545f..22a34a502 100755 --- a/scripts/ci_staging_deploy.sh +++ b/scripts/ci_staging_deploy.sh @@ -311,9 +311,12 @@ else fi echo "Stopping old containers..." -docker rm -f xiaoxia-api-staging 2>/dev/null || true -docker rm -f xiaoxia-worker-staging 2>/dev/null || true -docker rm -f xiaoxia-web-staging 2>/dev/null || true +# 优雅关闭:先 stop(发 SIGTERM,等待),再 rm +# Worker 需要更长时间(视频任务最长可能5分钟) +docker stop -t 300 xiaoxia-worker-staging 2>/dev/null || true +docker stop -t 30 xiaoxia-api-staging 2>/dev/null || true +docker stop -t 10 xiaoxia-web-staging 2>/dev/null || true +docker rm xiaoxia-worker-staging xiaoxia-api-staging xiaoxia-web-staging 2>/dev/null || true LOG_OPTS="--log-driver json-file --log-opt max-size=50m --log-opt max-file=3"