fix(ci): push :dev tag to registry for watchtower auto-update (#1888)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 19s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 50s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 56s
CI/CD Pipeline / Integration Tests (push) Successful in 3m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m44s
CI/CD Pipeline / Validate - Style (push) Successful in 4m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m4s
CI/CD Pipeline / Validate - Security (push) Successful in 10m34s
CI/CD Pipeline / Unit Tests (push) Successful in 12m4s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 48h45m22s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 48h55m35s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 48h57m39s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 48h56m9s
CI/CD Pipeline / PR Build Web Image (push) Failing after 48h57m12s
CI/CD Pipeline / PR Build API Image (push) Failing after 48h57m13s
CI/CD Pipeline / Deploy Production (push) Failing after 48h44m51s
CI/CD Pipeline / Build Production Web Image (push) Failing after 48h44m55s
CI/CD Pipeline / CI Gate (push) Failing after 48h44m55s
CI/CD Pipeline / Canary Release to Production (push) Failing after 48h44m51s
CI/CD Pipeline / Build Production API Image (push) Failing after 48h44m55s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 48h55m8s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 48h55m8s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 48h56m9s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 48h56m9s
CI/CD Pipeline / Frontend Lint (push) Failing after 48h57m7s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 48h57m14s

Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1888.
This commit is contained in:
2026-09-14 14:26:05 +08:00
committed by auto-approve-bot
parent f651422582
commit 628fc6c4e6
2 changed files with 52 additions and 17 deletions
+49 -17
View File
@@ -36,14 +36,20 @@ CACHE_TAG_PRIMARY="${CACHE_TAG:-develop}"
API_IMAGE="xiaoxia-saas-api:$VERSION"
WORKER_IMAGE="xiaoxia-saas-worker:$VERSION"
WEB_IMAGE="xiaoxia-saas-web:$VERSION"
API_LATEST="xiaoxia-saas-api:dev"
WORKER_LATEST="xiaoxia-saas-worker:dev"
API_DEV="xiaoxia-saas-api:dev"
WORKER_DEV="xiaoxia-saas-worker:dev"
WEB_DEV="xiaoxia-saas-web:dev"
# Registry 上的完整镜像名
# Registry 上的完整镜像名(SHA/版本 tag)
REGISTRY_API="${REGISTRY}/xiaoxia-saas-api:$VERSION"
REGISTRY_WORKER="${REGISTRY}/xiaoxia-saas-worker:$VERSION"
REGISTRY_WEB="${REGISTRY}/xiaoxia-saas-web:$VERSION"
# Registry 上的 dev floating tag(仅 staging/develop 构建时推送,供 Watchtower 监听自动更新)
REGISTRY_API_DEV="${REGISTRY}/xiaoxia-saas-api:dev"
REGISTRY_WORKER_DEV="${REGISTRY}/xiaoxia-saas-worker:dev"
REGISTRY_WEB_DEV="${REGISTRY}/xiaoxia-saas-web:dev"
USE_CACHE=0
USE_PUSH=0
CACHE_WRITE=0
@@ -56,23 +62,34 @@ if docker buildx version >/dev/null 2>&1; then
docker buildx use default 2>/dev/null || true
fi
# ---- 是否需要推送 dev tag ----
# staging 构建 或 develop 分支构建时才推送 :dev tag,供 Watchtower 自动更新;
# production / release tag 构建不推送 dev tag,避免污染生产 tag 指向。
PUSH_DEV_TAG=0
BRANCH_NAME="${GITHUB_REF_NAME:-${CI_COMMIT_BRANCH:-unknown}}"
if [ "$BUILD_ENV" = "staging" ] || [ "$BRANCH_NAME" = "develop" ]; then
PUSH_DEV_TAG=1
echo "Dev tag push: ENABLED (BUILD_ENV=$BUILD_ENV, BRANCH=$BRANCH_NAME) — Watchtower will pick up new :dev"
else
echo "Dev tag push: disabled (BUILD_ENV=$BUILD_ENV, BRANCH=$BRANCH_NAME)"
fi
# ---- 缓存读写策略(按分支隔离)----
# 默认只读不写,防止 feature 分支污染主缓存
# 只有 develop/main 分支才写回缓存
BRANCH_NAME="${GITHUB_REF_NAME:-${CI_COMMIT_BRANCH:-unknown}}"
# 清理本地旧镜像
docker rmi -f "$API_IMAGE" "$API_LATEST" 2>/dev/null || true
docker rmi -f "$API_IMAGE" "$API_DEV" 2>/dev/null || true
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--build-arg APP_VERSION="$VERSION" \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/api-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/api.Dockerfile \
-t "$API_IMAGE" -t "$API_LATEST" \
-t "$API_IMAGE" -t "$API_DEV" \
--load \
.
else
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/api.Dockerfile -t "$API_IMAGE" -t "$API_LATEST" .
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/api.Dockerfile -t "$API_IMAGE" -t "$API_DEV" .
fi
build_with_cache() {
@@ -122,22 +139,22 @@ build_with_cache() {
echo "=== Building API image ==="
build_with_cache "api" "infra/docker/api.Dockerfile" \
"--build-arg APP_VERSION=$VERSION"
docker tag "$API_IMAGE" "$API_LATEST"
docker tag "$API_IMAGE" "$API_DEV"
echo "=== Building Worker image ==="
# 清理本地旧镜像
docker rmi -f "$WORKER_IMAGE" "$WORKER_LATEST" 2>/dev/null || true
docker rmi -f "$WORKER_IMAGE" "$WORKER_DEV" 2>/dev/null || true
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--build-arg APP_VERSION="$VERSION" \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/worker-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/worker.Dockerfile \
-t "$WORKER_IMAGE" -t "$WORKER_LATEST" \
-t "$WORKER_IMAGE" -t "$WORKER_DEV" \
--load \
.
else
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/worker.Dockerfile -t "$WORKER_IMAGE" -t "$WORKER_LATEST" .
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/worker.Dockerfile -t "$WORKER_IMAGE" -t "$WORKER_DEV" .
fi
echo "=== Building Web image (with buildx cache) ==="
@@ -158,37 +175,52 @@ docker run --rm \
test -f apps/web/dist/index.html
# 清理本地旧镜像
docker rmi -f "$WEB_IMAGE" 2>/dev/null || true
docker rmi -f "$WEB_IMAGE" "$WEB_DEV" 2>/dev/null || true
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/web-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/web-artifact.Dockerfile \
--build-arg "NGINX_CONF=$NGINX_CONF_FILE" \
-t "$WEB_IMAGE" \
-t "$WEB_IMAGE" -t "$WEB_DEV" \
--load \
.
else
docker build --pull=false \
-f infra/docker/web-artifact.Dockerfile \
--build-arg "NGINX_CONF=$NGINX_CONF_FILE" \
-t "$WEB_IMAGE" \
-t "$WEB_IMAGE" -t "$WEB_DEV" \
.
fi
# Push 到 Registry
if [ "$USE_PUSH" -eq 1 ]; then
echo "=== Pushing images to Registry ==="
echo "=== Pushing SHA/version-tagged images to Registry ==="
docker tag "$API_IMAGE" "$REGISTRY_API"
docker tag "$WORKER_IMAGE" "$REGISTRY_WORKER"
docker tag "$WEB_IMAGE" "$REGISTRY_WEB"
docker push "$REGISTRY_API"
docker push "$REGISTRY_WORKER"
docker push "$REGISTRY_WEB"
echo "All images pushed to $REGISTRY"
echo "SHA-tagged images pushed to $REGISTRY"
# ---- 额外推送 :dev floating tag(仅 staging/develop)----
# :dev tag 指向最新一次 develop/staging 构建,供 Watchtower 通过 WATCHTOWER_ROLLING_RESTART 等方式
# 监听镜像更新并自动 pull + restart;部署脚本 (ci_staging_deploy.sh) 仍使用 SHA tag 做确定性部署,
# dev tag 仅作为"最新 develop 成功构建"的可滚动标识,避免并发构建互相覆盖导致的不可重现问题。
if [ "$PUSH_DEV_TAG" -eq 1 ]; then
echo "=== Pushing :dev floating tags to Registry (for Watchtower auto-update) ==="
docker tag "$API_IMAGE" "$REGISTRY_API_DEV"
docker tag "$WORKER_IMAGE" "$REGISTRY_WORKER_DEV"
docker tag "$WEB_IMAGE" "$REGISTRY_WEB_DEV"
docker push "$REGISTRY_API_DEV"
docker push "$REGISTRY_WORKER_DEV"
docker push "$REGISTRY_WEB_DEV"
echo ":dev tags pushed to $REGISTRY"
fi
else
echo "Registry push skipped (no auth token available)"
fi
echo "=== Build complete ==="
docker images | grep "xiaoxia-saas" | grep "$VERSION" || true
docker images | grep "xiaoxia-saas" | grep -E "($VERSION|dev)" || true
+3
View File
@@ -264,6 +264,9 @@ if [ -n "$REGISTRY_TOKEN" ]; then
fi
# ---- 并行 Pull 三个镜像 ----
# 注意:这里必须使用 IMAGE_TAG(commit SHA)做确定性部署,不要改成 :dev。
# :dev 是 floating tag,可能被并发构建覆盖,导致部署版本不可重现、回滚混乱。
# Watchtower 可监听 :dev 做非关键路径的自动同步;正式部署/回滚一律锚定 SHA。
REGISTRY_API="${REGISTRY}/xiaoxia-saas-api:${IMAGE_TAG}"
REGISTRY_WORKER="${REGISTRY}/xiaoxia-saas-worker:${IMAGE_TAG}"
REGISTRY_WEB="${REGISTRY}/xiaoxia-saas-web:${IMAGE_TAG}"