From 628fc6c4e6d6aaeddc2e1c86ba345ee7a8927e2f Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 14 Sep 2026 14:26:05 +0800 Subject: [PATCH] fix(ci): push :dev tag to registry for watchtower auto-update (#1888) Co-authored-by: xiaoxia Co-committed-by: xiaoxia --- scripts/build_release_images.sh | 66 ++++++++++++++++++++++++--------- scripts/ci_staging_deploy.sh | 3 ++ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/scripts/build_release_images.sh b/scripts/build_release_images.sh index d56881297..78f057749 100755 --- a/scripts/build_release_images.sh +++ b/scripts/build_release_images.sh @@ -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 diff --git a/scripts/ci_staging_deploy.sh b/scripts/ci_staging_deploy.sh index f66e70873..60950c81f 100755 --- a/scripts/ci_staging_deploy.sh +++ b/scripts/ci_staging_deploy.sh @@ -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}"