#!/bin/bash # PR构建专用:只构建不输出,验证Dockerfile能否正常构建 # 缓存:复用宿主机持久 builder (ci-builder-persist) 的层缓存 + ACR registry 缓存兜底 # 无状态:build-only 不推送,job 结束无需清理(builder 为共享持久资源) set -eu NO_CACHE_FLAG="" if [ "$1" = "--no-cache" ]; then NO_CACHE_FLAG="--no-cache" shift fi DOCKERFILE="$1" IMAGE_TAG="$2" CACHE_REF="$3" shift 3 BUILD_ARGS="" for arg in "$@"; do BUILD_ARGS="$BUILD_ARGS --build-arg $arg" done # Web 镜像 cache bust:计算 apps/web/ 的 git tree hash # 当源码变化时 hash 变化,buildx 的 ARG 缓存键失效 → vite build 必定重新执行 if [ "${DOCKERFILE##*/}" = "web.Dockerfile" ]; then SOURCE_HASH=$(git rev-parse HEAD:apps/web 2>/dev/null || echo "") if [ -n "$SOURCE_HASH" ]; then echo "Web cache bust: SOURCE_HASH=${SOURCE_HASH}" BUILD_ARGS="$BUILD_ARGS --build-arg SOURCE_HASH=${SOURCE_HASH}" else echo "⚠️ 无法计算 apps/web tree hash,跳过 cache bust" fi fi BUILDER_NAME="ci-builder-persist" if ! docker buildx inspect "$BUILDER_NAME" > /dev/null 2>&1; then echo "持久 builder 不存在,创建中..." docker buildx create --name "$BUILDER_NAME" --driver docker-container \ --driver-opt network=host \ --buildkitd-flags "--allow-insecure-entitlement network.host" \ --platform linux/amd64 fi docker buildx use "$BUILDER_NAME" docker buildx inspect "$BUILDER_NAME" --bootstrap echo "=== PR Build: build only, no push (persistent builder cache) ===" echo "Dockerfile: ${DOCKERFILE}" echo "Image tag: ${IMAGE_TAG}" echo "Builder: ${BUILDER_NAME}" echo "" run_build() { docker buildx build \ $NO_CACHE_FLAG \ $BUILD_ARGS \ --cache-from "type=registry,ref=${CACHE_REF}" \ -f "${DOCKERFILE}" \ -t "${IMAGE_TAG}" \ . } # PR 构建同样容错:检测到 builder 缓存损坏时,用 flock 串行重建共享 builder 后重试一次 if ! build_output=$(run_build 2>&1); then if echo "$build_output" | grep -qE "parent snapshot.*not found|snapshot.*does not exist|cache.*corrupt|failed to compute cache key|no such file or directory.*cache"; then echo "$build_output" echo "⚠️ builder 缓存异常,串行重建持久 builder 后重试..." LOCK_FILE="/tmp/ci-builder-persist-rebuild.lock" exec 9>"$LOCK_FILE" flock -w 120 9 || echo "⚠️ 等待重建锁超时,直接重试 build" if ! docker buildx inspect "$BUILDER_NAME" --bootstrap >/dev/null 2>&1; then echo "🔨 锁内重建持久 builder..." docker buildx rm "$BUILDER_NAME" >/dev/null 2>&1 || true docker buildx create --name "$BUILDER_NAME" --driver docker-container \ --driver-opt network=host \ --buildkitd-flags "--allow-insecure-entitlement network.host" \ --platform linux/amd64 docker buildx use "$BUILDER_NAME" docker buildx inspect "$BUILDER_NAME" --bootstrap else echo "✅ builder 已被其他并发 job 重建/恢复,直接复用" fi run_build else echo "$build_output" exit 1 fi else echo "$build_output" fi echo "" echo "PR build OK (build only, no output): ${IMAGE_TAG}"