a64037881a
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2m20s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m28s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m16s
AI Code Review / AI Code Review (pull_request) Successful in 1m59s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m13s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m59s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m8s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m45s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m8s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 9m25s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m16s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 16m45s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 28s
1. build-staging 路径过滤:纯前端改动只构建Web,纯后端只构建API/Worker; 未重建镜像通过 retag-staging-skipped job 将分支tag retag为新SHA推送,Watchtower链路不变 2. deploy-staging、acr-cleanup runs-on 从 runtime-builder 改为 ci-l2,释放构建槽 3. buildx 改为宿主机持久 builder(ci-builder-persist)+buildkit命名卷缓存, job结束不再prune,Worker镜像同样接入buildx缓存 4. validate/unit-test/frontend-test 的 pip/npm 依赖通过宿主机命名卷持久化缓存 (runner config 挂载 ci-pip-cache/xiaoxia-npm-cache/act-toolcache) 5. 不影响生产构建/发版/canary 相关 job
119 lines
3.9 KiB
Bash
Executable File
119 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
||
# 通用Docker镜像构建+推送脚本
|
||
# 缓存策略(2026-08 起):
|
||
# - buildx 使用宿主机持久 builder (ci-builder-persist),层缓存保存在
|
||
# buildkit 容器/命名卷中,跨 job 共享、job 结束不清理
|
||
# - registry cache 仅作为冷启动兜底读取
|
||
# - 额外 tag(如分支 tag :develop)通过 EXTRA_TAGS 环境变量传入,随构建一并推送
|
||
# 用法: docker_build_push.sh [--no-cache] <Dockerfile> <image_tag> <cache_ref> [build_arg...]
|
||
# 环境变量:
|
||
# EXTRA_TAGS 空格分隔的额外 tag(完整 image:tag 引用),可选
|
||
set -eu
|
||
|
||
# 单次 build 超时时间(秒),防止 docker buildx build 无限挂起
|
||
BUILD_TIMEOUT=1500
|
||
# 持久 builder 名(宿主机级,所有 CI job 共享;由 ensure_persistent_builder.sh 维护)
|
||
BUILDER_NAME="ci-builder-persist"
|
||
|
||
NO_CACHE_FLAG=""
|
||
if [ "$1" = "--no-cache" ]; then
|
||
NO_CACHE_FLAG="--no-cache"
|
||
shift
|
||
echo "模式: --no-cache (不使用缓存,全新构建)"
|
||
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
|
||
|
||
# 确保持久 builder 存在并使用(幂等)
|
||
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
|
||
|
||
# 组装额外 tag 参数
|
||
EXTRA_TAG_FLAGS=""
|
||
EXTRA_TAG_LIST=""
|
||
if [ -n "${EXTRA_TAGS:-}" ]; then
|
||
for t in $EXTRA_TAGS; do
|
||
EXTRA_TAG_FLAGS="$EXTRA_TAG_FLAGS -t $t"
|
||
EXTRA_TAG_LIST="$EXTRA_TAG_LIST $t"
|
||
done
|
||
fi
|
||
|
||
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=$(timeout ${BUILD_TIMEOUT} docker buildx build \
|
||
$NO_CACHE_FLAG \
|
||
$BUILD_ARGS \
|
||
--cache-from "type=registry,ref=${CACHE_REF}" \
|
||
-f "${DOCKERFILE}" \
|
||
-t "${IMAGE_TAG}" \
|
||
$EXTRA_TAG_FLAGS \
|
||
--push \
|
||
. 2>&1)
|
||
exit_code=$?
|
||
set -e
|
||
if [ $exit_code -eq 0 ]; then
|
||
echo "$build_output"
|
||
return 0
|
||
fi
|
||
# 超时退出(exit code 124)
|
||
if [ $exit_code -eq 124 ]; then
|
||
echo "❌ Docker build TIMEOUT after ${BUILD_TIMEOUT}s - build hung and was killed"
|
||
echo "$build_output" | tail -20
|
||
return $exit_code
|
||
fi
|
||
# 检测到缓存/快照损坏类错误,重建 builder 后重试
|
||
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 ""
|
||
echo "⚠️ builder 缓存异常,重建持久 builder 后重试 (attempt $attempt/$max_attempts)..."
|
||
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
|
||
attempt=$((attempt + 1))
|
||
else
|
||
echo "$build_output"
|
||
return $exit_code
|
||
fi
|
||
done
|
||
return 1
|
||
}
|
||
|
||
echo "=== Build & push image (persistent builder cache) ==="
|
||
echo "Builder: ${BUILDER_NAME} (persistent)"
|
||
echo "Registry cache(from): ${CACHE_REF}"
|
||
echo "Image tag: ${IMAGE_TAG}"
|
||
[ -n "$EXTRA_TAG_LIST" ] && echo "Extra tags: ${EXTRA_TAG_LIST}"
|
||
echo "Timeout: ${BUILD_TIMEOUT}s"
|
||
echo ""
|
||
|
||
build_with_cache_retry
|
||
|
||
echo ""
|
||
echo "Image pushed: ${IMAGE_TAG}"
|
||
[ -n "$EXTRA_TAG_LIST" ] && echo "Also pushed: ${EXTRA_TAG_LIST}"
|
||
echo ""
|
||
echo "Build completed: ${IMAGE_TAG}"
|