fix(ci): Unit Tests添加worker依赖+修复PYTHONPATH+pip国内源 #757

Merged
auto-approve-bot merged 3 commits from fix/unit-tests-pythonpath-worker-deps into develop 2026-07-23 17:44:11 +08:00
3 changed files with 26 additions and 131 deletions
+15 -4
View File
@@ -7,6 +7,11 @@ 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 依赖 ==="
@@ -23,6 +28,12 @@ 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..."
@@ -64,8 +75,8 @@ echo "=== 运行单元测试 (模式: $UNIT_TEST_MODE) ==="
if [ "$UNIT_TEST_MODE" = "incremental" ]; then
echo "=== 增量测试模式 ==="
PYTHONPATH="$PWD/apps/api:$PWD" python3 -m coverage run \
--source=apps/api/app,packages \
PYTHONPATH="$PWD/apps/api:$PWD/apps/worker:$PWD" python3 -m coverage run \
--source=apps/api/app,apps/worker/worker_app,packages \
--omit="*/migrations/*,*/tests/*,*/test_*.py,*/site-packages/*" \
--branch \
-m pytest $SELECTED_TEST_FILES -q
@@ -73,8 +84,8 @@ if [ "$UNIT_TEST_MODE" = "incremental" ]; then
python3 -m coverage xml -o coverage.xml
python3 -m coverage report --fail-under=10 > /dev/null || true
else
PYTHONPATH="$PWD/apps/api:$PWD" python3 -m coverage run \
--source=apps/api/app,packages \
PYTHONPATH="$PWD/apps/api:$PWD/apps/worker:$PWD" python3 -m coverage run \
--source=apps/api/app,apps/worker/worker_app,packages \
--omit="*/migrations/*,*/tests/*,*/test_*.py,*/site-packages/*" \
--branch \
-m pytest tests/unit -q
+7 -106
View File
@@ -1,117 +1,18 @@
#!/bin/sh
# CI 公共步骤:前端依赖安装Docker Volume 持久化缓存方案)
# 通过 Docker named volume 缓存 node_modules,按 package-lock.json hash 命名
# 缓存命中时跳过 npm ci,直接复用已有 volume
# CI 公共步骤:前端依赖安装
# 直接在 CI 容器内运行(CI 镜像已包含 Node.js),无需 Docker 嵌套
set -eu
MODE="${1:-full}"
echo "=== 前端依赖安装开始 (模式: $MODE) ==="
# npm国内镜像源(加速下载,减少网络失败)
NPM_REGISTRY="https://registry.npmmirror.com"
cd apps/web
# 缓存配置 — 与 step_frontend_run.sh 保持一致
LOCK_FILE="apps/web/package-lock.json"
VOLUME_PREFIX="ci-web-nm-"
KEEP_CACHE_COUNT=5
# 配置国内镜像源加速
npm config set registry https://registry.npmmirror.com
# 计算 package-lock.json 的 md5 hash 作为缓存 key
VOLUME_NAME=""
if [ -f "$LOCK_FILE" ]; then
LOCK_HASH=$(md5sum "$LOCK_FILE" | cut -c1-12)
VOLUME_NAME="${VOLUME_PREFIX}${LOCK_HASH}"
echo "缓存 key: $LOCK_HASH (volume: $VOLUME_NAME)"
else
echo "警告: 未找到 $LOCK_FILE,将不使用持久化缓存"
fi
# 检查 volume 是否存在(缓存命中)
CACHE_HIT=0
if [ -n "$VOLUME_NAME" ]; then
if docker volume inspect "$VOLUME_NAME" >/dev/null 2>&1; then
CACHE_HIT=1
echo "缓存命中!复用 volume: $VOLUME_NAME"
else
echo "缓存未命中,创建 volume 并安装依赖..."
# 创建 volume(失败则降级为无缓存模式)
if ! docker volume create "$VOLUME_NAME" >/dev/null 2>&1; then
echo "警告: 创建 volume 失败,降级为无缓存模式"
VOLUME_NAME=""
fi
fi
fi
# 构建 docker run 的 volume 挂载参数(空时不挂载)
VOLUME_ARGS=""
if [ -n "$VOLUME_NAME" ]; then
VOLUME_ARGS="-v ${VOLUME_NAME}:/workspace/apps/web/node_modules"
fi
# 缓存未命中时执行 npm ci
if [ "$CACHE_HIT" -eq 0 ]; then
for i in 1 2 3; do
echo "npm ci 尝试 $i/3 (镜像: $NPM_REGISTRY)"
docker run --rm -v "$PWD:/workspace" $VOLUME_ARGS -w /workspace/apps/web docker.m.daocloud.io/library/node:20 sh -lc "npm config set registry $NPM_REGISTRY && npm ci --no-audit --no-fund" && break
echo "npm ci 失败,重试 $i/3..."
[ $i -eq 3 ] && exit 1
sleep 10
done
else
echo "缓存命中,验证依赖完整性..."
# 验证关键依赖是否存在(防止缓存损坏或版本漂移)
DEPS_OK=1
if ! docker run --rm $VOLUME_ARGS -v "$PWD:/workspace" -w /workspace/apps/web docker.m.daocloud.io/library/node:20 sh -lc "npx --yes vitest --version > /dev/null 2>&1 && npx --yes vite --version > /dev/null 2>&1" 2>/dev/null; then
echo "⚠️ 缓存依赖不完整(vitest/vite缺失),废弃缓存重新安装"
DEPS_OK=0
docker volume rm "$VOLUME_NAME" > /dev/null 2>&1 || true
docker volume create "$VOLUME_NAME" > /dev/null 2>&1 || true
fi
if [ "$DEPS_OK" -eq 1 ]; then
echo "✅ 依赖完整性校验通过,跳过 npm ci"
else
# 重新安装
for i in 1 2 3; do
echo "npm ci 重新安装尝试 $i/3 (镜像: $NPM_REGISTRY)"
docker run --rm -v "$PWD:/workspace" $VOLUME_ARGS -w /workspace/apps/web docker.m.daocloud.io/library/node:20 sh -lc "npm config set registry $NPM_REGISTRY && npm ci --no-audit --no-fund" && break
echo "npm ci 失败,重试 $i/3..."
[ $i -eq 3 ] && exit 1
sleep 10
done
fi
fi
# 清理旧缓存 volume(保留最近 N 个,防止磁盘占用无限增长)
if [ -n "$VOLUME_PREFIX" ]; then
echo "清理旧缓存 volume(保留最近 ${KEEP_CACHE_COUNT} 个)..."
ALL_VOLUMES=$(docker volume ls -q --filter "name=${VOLUME_PREFIX}" 2>/dev/null || true)
if [ -n "$ALL_VOLUMES" ]; then
TOTAL=$(echo "$ALL_VOLUMES" | wc -l)
if [ "$TOTAL" -gt "$KEEP_CACHE_COUNT" ]; then
# 按创建时间排序,保留最新的 N 个
SORTED_VOLUMES=$(for v in $ALL_VOLUMES; do
CREATED=$(docker volume inspect --format '{{.CreatedAt}}' "$v" 2>/dev/null || echo "0")
echo "$CREATED $v"
done | sort | awk '{print $2}')
# 删除超出保留数量的旧 volume
REMOVE_COUNT=$((TOTAL - KEEP_CACHE_COUNT))
TO_DELETE=$(echo "$SORTED_VOLUMES" | head -n "$REMOVE_COUNT")
REMOVED=0
for v in $TO_DELETE; do
# 跳过当前正在使用的 volume
if [ "$v" != "$VOLUME_NAME" ]; then
if docker volume rm "$v" >/dev/null 2>&1; then
REMOVED=$((REMOVED + 1))
fi
fi
done
echo "已清理 $REMOVED 个旧缓存 volume,当前共 $((TOTAL - REMOVED))"
else
echo "当前缓存 volume 数量: $TOTAL,无需清理"
fi
fi
fi
# 安装依赖
npm ci --no-audit --no-fund
echo "=== 前端依赖安装完成 ==="
+4 -21
View File
@@ -1,26 +1,9 @@
#!/bin/sh
# CI 公共步骤:前端命令执行(在 docker node 容器中运行)
# 用法:step_frontend_run.sh "要执行的命令"
# 支持 Docker Volume 持久化缓存的 node_modules
# CI 公共步骤:前端命令执行
# 直接在 CI 容器内运行(CI 镜像已包含 Node.js + pnpm),无需 Docker 嵌套
set -eu
CMD="${1:-echo 'no command'}"
# 缓存配置 — 与 step_frontend_install.sh 保持一致
LOCK_FILE="apps/web/package-lock.json"
VOLUME_PREFIX="ci-web-nm-"
# 计算 package-lock.json 的 hash,挂载对应的 volume
VOLUME_ARGS=""
if [ -f "$LOCK_FILE" ]; then
LOCK_HASH=$(md5sum "$LOCK_FILE" | cut -c1-12)
VOLUME_NAME="${VOLUME_PREFIX}${LOCK_HASH}"
if docker volume inspect "$VOLUME_NAME" >/dev/null 2>&1; then
VOLUME_ARGS="-v ${VOLUME_NAME}:/workspace/apps/web/node_modules"
echo "使用缓存 volume: $VOLUME_NAME"
else
echo "提示: 未找到缓存 volume $VOLUME_NAME,将使用源码目录 node_modules"
fi
fi
docker run --rm -v "$PWD:/workspace" $VOLUME_ARGS -w /workspace/apps/web docker.m.daocloud.io/library/node:20 sh -lc "$CMD"
cd apps/web
sh -lc "$CMD"