fix(ci): buildx本地缓存损坏自动检测+清理重试,根治snapshot not found连锁失败 #523

Merged
auto-approve-bot merged 2 commits from fix/buildx-local-cache-corruption into develop 2026-07-18 16:15:40 +08:00
2 changed files with 57 additions and 21 deletions
+4 -5
View File
@@ -8,10 +8,9 @@ ENV VITE_API_URL=$VITE_API_URL
COPY apps/web/package.json apps/web/package-lock.json ./apps/web/
WORKDIR /app/apps/web
# 安装依赖:用BuildKit cache mount缓存npm下载和node_modules
# sharing=locked 防止并发构建竞争写缓存
# 安装依赖:node_modules写入镜像layer,走ACR缓存保证完整性
# /root/.npm 保留cache mount加速下载(不影响构建正确性)
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
--mount=type=cache,target=/app/apps/web/node_modules,sharing=locked \
npm config set registry https://registry.npmmirror.com \
&& npm ci
@@ -19,8 +18,8 @@ RUN --mount=type=cache,target=/root/.npm,sharing=locked \
COPY apps/web/ ./
# 构建:TS增量编译 + Vite构建,tsbuildinfo用cache mount持久化
RUN --mount=type=cache,target=/app/apps/web/node_modules,sharing=locked \
--mount=type=cache,target=/app/apps/web/.tscache,sharing=locked \
# node_modules直接使用镜像中已安装的(layer缓存保证完整性)
RUN --mount=type=cache,target=/app/apps/web/.tscache,sharing=locked \
mkdir -p .tscache \
&& npx tsc --incremental --tsBuildInfoFile .tscache/tsconfig.tsbuildinfo \
&& npx vite build
+53 -16
View File
@@ -28,27 +28,64 @@ LOCAL_CACHE_DIR="/tmp/buildx-cache/${CACHE_NAME}"
mkdir -p "$LOCAL_CACHE_DIR"
# 缓存源:local优先,registry兜底
CACHE_FROM_LOCAL="type=local,src=${LOCAL_CACHE_DIR}"
CACHE_FROM_REGISTRY="type=registry,ref=${CACHE_REF},ignore-error=true"
# 缓存源:local优先(带自动修复)registry兜底
# 本地缓存损坏时自动清理后重试,避免snapshot not found导致构建全挂
build_with_cache_retry() {
local attempt=1
local max_attempts=2
while [ $attempt -le $max_attempts ]; do
local build_output
local exit_code
set +e
build_output=$(docker buildx build \
$BUILD_ARGS \
--cache-from "type=local,src=${LOCAL_CACHE_DIR}" \
--cache-from "type=registry,ref=${CACHE_REF},ignore-error=true" \
--cache-to "type=local,dest=${LOCAL_CACHE_DIR},mode=max" \
-f "${DOCKERFILE}" \
-t "${IMAGE_TAG}" \
--push \
. 2>&1)
exit_code=$?
set -e
if [ $exit_code -eq 0 ]; then
echo "$build_output"
return 0
fi
# 检测到缓存损坏类错误,清掉本地缓存重试
if echo "$build_output" | grep -qE "parent snapshot.*not found|snapshot.*does not exist|cache.*corrupt|failed to compute cache key"; then
echo "$build_output"
echo ""
echo "⚠️ Local cache appears corrupted, cleaning up and retrying (attempt $attempt/$max_attempts)..."
rm -rf "${LOCAL_CACHE_DIR}"
mkdir -p "${LOCAL_CACHE_DIR}"
# 清理buildx builder的内部snapshot状态
docker buildx prune -f -a >/dev/null 2>&1 || true
attempt=$((attempt + 1))
else
# 非缓存类错误,直接输出并返回
echo "$build_output"
return $exit_code
fi
done
# 重试完还是失败,不用本地缓存最后试一次(只从registry读)
echo "⚠️ All cached attempts failed, building without local cache..."
docker buildx build \
$BUILD_ARGS \
--cache-from "type=registry,ref=${CACHE_REF},ignore-error=true" \
--cache-to "type=local,dest=${LOCAL_CACHE_DIR},mode=max" \
-f "${DOCKERFILE}" \
-t "${IMAGE_TAG}" \
--push \
.
}
# 本地缓存目标(必选,mode=max最大化命中率)
CACHE_TO_LOCAL="type=local,dest=${LOCAL_CACHE_DIR},mode=max"
echo "=== Step 1: Build & push image (local cache read-write + registry read) ==="
echo "=== Step 1: Build & push image (local cache + registry read, with auto-repair) ==="
echo "Local cache: ${LOCAL_CACHE_DIR}"
echo "Registry cache: ${CACHE_REF}"
echo ""
docker buildx build \
$BUILD_ARGS \
--cache-from "${CACHE_FROM_LOCAL}" \
--cache-from "${CACHE_FROM_REGISTRY}" \
--cache-to "${CACHE_TO_LOCAL}" \
-f "${DOCKERFILE}" \
-t "${IMAGE_TAG}" \
--push \
.
build_with_cache_retry
echo ""
echo "Image pushed: ${IMAGE_TAG}"