614a8e95be
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 40s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 45s
CI/CD Pipeline / Frontend Lint (push) Successful in 55s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 57s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 16s
CI/CD Pipeline / Integration Tests (push) Successful in 59s
CI/CD Pipeline / Unit Tests (push) Successful in 2m42s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 3m25s
CI/CD Pipeline / Build Staging API Image (push) Successful in 5m22s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 5m22s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
118 lines
4.5 KiB
Bash
Executable File
118 lines
4.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# CI 公共步骤:前端依赖安装(Docker Volume 持久化缓存方案)
|
|
# 通过 Docker named volume 缓存 node_modules,按 package-lock.json hash 命名
|
|
# 缓存命中时跳过 npm ci,直接复用已有 volume
|
|
set -eu
|
|
|
|
MODE="${1:-full}"
|
|
|
|
echo "=== 前端依赖安装开始 (模式: $MODE) ==="
|
|
|
|
# npm国内镜像源(加速下载,减少网络失败)
|
|
NPM_REGISTRY="https://registry.npmmirror.com"
|
|
|
|
# 缓存配置 — 与 step_frontend_run.sh 保持一致
|
|
LOCK_FILE="apps/web/package-lock.json"
|
|
VOLUME_PREFIX="ci-web-nm-"
|
|
KEEP_CACHE_COUNT=5
|
|
|
|
# 计算 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
|
|
|
|
echo "=== 前端依赖安装完成 ==="
|