ci(retag/deploy): 增加镜像内容校验,防止静默部署旧/损坏镜像
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
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 / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (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
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 3m58s
CI/CD Pipeline / Check push changed paths (push) Successful in 4m19s
CI/CD Pipeline / Build Staging Web Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4m22s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 4m43s
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
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m5s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 5m15s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 5m18s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m51s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m7s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m57s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 8m18s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 8m31s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 9m23s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2m28s
AI Code Review / AI Code Review (pull_request) Successful in 11m10s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Successful in 2m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Integration Tests (push) Successful in 4m31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m23s
CI/CD Pipeline / Unit Tests (push) Successful in 17m40s
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 / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m1s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
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 / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (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
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 3m58s
CI/CD Pipeline / Check push changed paths (push) Successful in 4m19s
CI/CD Pipeline / Build Staging Web Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4m22s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 4m43s
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
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m5s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 5m15s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 5m18s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m51s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m7s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m57s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 8m18s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 8m31s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 9m23s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2m28s
AI Code Review / AI Code Review (pull_request) Successful in 11m10s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Successful in 2m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Integration Tests (push) Successful in 4m31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m23s
CI/CD Pipeline / Unit Tests (push) Successful in 17m40s
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 / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m1s
retag_skipped_image.sh 加固: - 源镜像 layers 有效性检查(layers=0 拒绝 retag) - 镜像创建时间审计(>72h 输出警告) - digest 审计记录,方便溯源 ci_staging_deploy.sh 加固: - pull 后逐服务校验镜像 layers 有效性 - 记录每个镜像的 digest/size/created - 对比上次部署 manifest,标注变化状态 - 写入 deploy-manifest.json 供下次对比 - 校验失败直接阻断部署 concurrency 保持 cancel-in-progress: true(不变) 生产发版链路不动
This commit was merged in pull request #1569.
This commit is contained in:
@@ -50,8 +50,59 @@ if [ -z "$SRC_TAG" ]; then
|
||||
fi
|
||||
|
||||
echo "✅ 源镜像: ${IMAGE}:${SRC_TAG}"
|
||||
|
||||
# ====== 镜像内容校验(CI 加固 - 防止静默部署旧/损坏镜像) ======
|
||||
echo ""
|
||||
echo "--- 镜像内容校验 ---"
|
||||
|
||||
# 1. 检查镜像 layers 有效性
|
||||
LAYERS=$(docker inspect --format='{{len .RootFS.Layers}}' "${IMAGE}:${SRC_TAG}" 2>/dev/null || echo "0")
|
||||
echo "源镜像 layers 数量: $LAYERS"
|
||||
if [ "$LAYERS" -eq 0 ]; then
|
||||
echo "❌ 源镜像无有效 layers,可能为损坏镜像,拒绝 retag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. 镜像创建时间检查
|
||||
CREATED=$(docker inspect --format='{{.Created}}' "${IMAGE}:${SRC_TAG}")
|
||||
echo "源镜像创建时间: $CREATED"
|
||||
|
||||
# 解析创建时间距现在多少小时
|
||||
CREATED_EPOCH=$(date -d "$CREATED" +%s 2>/dev/null || echo "0")
|
||||
NOW_EPOCH=$(date +%s)
|
||||
AGE_HOURS=$(( (NOW_EPOCH - CREATED_EPOCH) / 3600 ))
|
||||
echo "源镜像年龄: ${AGE_HOURS}小时"
|
||||
if [ "$AGE_HOURS" -gt 72 ]; then
|
||||
echo "⚠️ 警告: 源镜像已超过 72 小时,可能存在分支 tag 未更新的风险"
|
||||
echo " 请检查最近几次 CI 是否对该服务成功构建过"
|
||||
fi
|
||||
|
||||
# 3. Digest 审计记录
|
||||
SRC_DIGEST=$(docker inspect --format='{{.Id}}' "${IMAGE}:${SRC_TAG}")
|
||||
echo "源镜像 ID: $SRC_DIGEST"
|
||||
|
||||
echo "--- 镜像内容校验通过 ---"
|
||||
echo ""
|
||||
|
||||
docker tag "${IMAGE}:${SRC_TAG}" "${NEW_REF}"
|
||||
|
||||
# 推新 SHA tag;分支 tag 若指向的就是源 digest 则无需重复,失败可忽略
|
||||
docker push "${NEW_REF}"
|
||||
|
||||
NEW_DIGEST=$(docker inspect --format='{{.Id}}' "${NEW_REF}")
|
||||
echo "✅ retag 推送完成: ${NEW_REF} (from ${SRC_TAG})"
|
||||
echo " 源 digest: ${SRC_DIGEST}"
|
||||
echo " 新 tag digest: ${NEW_DIGEST}"
|
||||
|
||||
# 写入 retag 审计记录(供 deploy 步骤参考)
|
||||
RETAG_LOG="/tmp/retag-audit-$(date +%s).txt"
|
||||
cat > "$RETAG_LOG" <<EOF
|
||||
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
image=${IMAGE}
|
||||
src_tag=${SRC_TAG}
|
||||
new_tag=${NEW_TAG}
|
||||
src_digest=${SRC_DIGEST}
|
||||
new_digest=${NEW_DIGEST}
|
||||
age_hours=${AGE_HOURS}
|
||||
EOF
|
||||
echo "审计记录: $RETAG_LOG"
|
||||
|
||||
@@ -260,6 +260,90 @@ fi
|
||||
|
||||
echo "All images pulled."
|
||||
|
||||
# ====== 镜像内容校验(CI 加固 - 防止静默部署损坏/过期镜像) ======
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " 镜像内容校验"
|
||||
echo "=========================================="
|
||||
|
||||
VERIFY_FAILED=0
|
||||
DEPLOY_MANIFEST="${GENERATED_DIR}/deploy-manifest.json"
|
||||
|
||||
# 读取上次部署的 manifest(用于对比)
|
||||
PREV_MANIFEST=""
|
||||
if [ -f "$DEPLOY_MANIFEST" ]; then
|
||||
PREV_MANIFEST=$(cat "$DEPLOY_MANIFEST")
|
||||
echo "上次部署 manifest 存在"
|
||||
fi
|
||||
|
||||
NEW_MANIFEST_LINES=""
|
||||
for svc in api worker web; do
|
||||
img_var="REGISTRY_$(echo $svc | tr '[:lower:]' '[:upper:]')"
|
||||
img_val=$(eval echo "\$$img_var")
|
||||
|
||||
# 1. 检查镜像是否存在
|
||||
if ! docker image inspect "$img_val" >/dev/null 2>&1; then
|
||||
echo " ❌ $svc: 镜像不存在 ($img_val)"
|
||||
VERIFY_FAILED=$((VERIFY_FAILED + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# 2. 检查 layers 有效性
|
||||
LAYER_COUNT=$(docker inspect --format='{{len .RootFS.Layers}}' "$img_val" 2>/dev/null || echo "0")
|
||||
if [ "$LAYER_COUNT" -eq 0 ]; then
|
||||
echo " ❌ $svc: 镜像无有效 layers ($img_val)"
|
||||
VERIFY_FAILED=$((VERIFY_FAILED + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# 3. 获取 digest 和创建时间
|
||||
IMG_ID=$(docker inspect --format='{{.Id}}' "$img_val")
|
||||
IMG_CREATED=$(docker inspect --format='{{.Created}}' "$img_val")
|
||||
IMG_SIZE=$(docker inspect --format='{{.Size}}' "$img_val")
|
||||
echo " ✅ $svc: ${LAYER_COUNT} layers, size=${IMG_SIZE}, created=${IMG_CREATED}"
|
||||
echo " id: $IMG_ID"
|
||||
|
||||
# 4. 对比上次部署
|
||||
CHANGED="unchanged"
|
||||
if [ -n "$PREV_MANIFEST" ]; then
|
||||
PREV_ID=$(echo "$PREV_MANIFEST" | grep "\"${svc}_id\"" | sed 's/.*: *"\(.*\)".*/\1/' 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_ID" ] && [ "$PREV_ID" != "$IMG_ID" ]; then
|
||||
CHANGED="changed"
|
||||
elif [ -n "$PREV_ID" ] && [ "$PREV_ID" = "$IMG_ID" ]; then
|
||||
CHANGED="unchanged"
|
||||
else
|
||||
CHANGED="unknown"
|
||||
fi
|
||||
else
|
||||
CHANGED="first-deploy"
|
||||
fi
|
||||
echo " vs last deploy: $CHANGED"
|
||||
|
||||
NEW_MANIFEST_LINES="${NEW_MANIFEST_LINES} \"${svc}_id\": \"${IMG_ID}\",
|
||||
\"${svc}_created\": \"${IMG_CREATED}\",
|
||||
\"${svc}_layers\": ${LAYER_COUNT},
|
||||
"
|
||||
done
|
||||
|
||||
if [ "$VERIFY_FAILED" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "ERROR: $VERIFY_FAILED 个镜像校验失败,拒绝部署"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 写入新 manifest
|
||||
cat > "$DEPLOY_MANIFEST" <<MANIFEST_EOF
|
||||
{
|
||||
"deployed_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"image_tag": "$IMAGE_TAG",
|
||||
${NEW_MANIFEST_LINES} "verified": true
|
||||
}
|
||||
MANIFEST_EOF
|
||||
echo ""
|
||||
echo "部署 manifest 已更新: $DEPLOY_MANIFEST"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo "Backing up legacy assets from current web container..."
|
||||
if docker inspect xiaoxia-web-staging >/dev/null 2>&1; then
|
||||
_tmpdir="/tmp/legacy-assets-$$"
|
||||
|
||||
Reference in New Issue
Block a user