Compare commits

...

8 Commits

Author SHA1 Message Date
XiaoXia Bot 2d66311c5b feat(ci): 纯前端PR自动跳过后端检查,节省CI时间
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m20s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 4m41s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 5m3s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m24s
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Failing after 1481h41m56s
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Failing after 1481h41m59s
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Failing after 1481h41m57s
CI Build & Deploy Pipeline / Production Browser E2E (push) Failing after 1481h42m0s
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Failing after 1481h42m1s
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Failing after 1481h42m1s
CI Build & Deploy Pipeline / Deploy Production (push) Failing after 1481h42m1s
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Failing after 1481h42m3s
CI Build & Deploy Pipeline / Build Production Web Image (push) Failing after 1481h42m5s
CI Build & Deploy Pipeline / Build Production API Image (push) Failing after 1481h42m5s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1481h42m6s
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Failing after 1481h42m7s
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Failing after 1481h42m9s
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Failing after 1481h42m9s
CI Build & Deploy Pipeline / Staging E2E Tests (push) Failing after 1481h42m9s
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Failing after 1481h42m9s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 1481h42m10s
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Failing after 1481h42m11s
CI Build & Deploy Pipeline / Build Staging API Image (push) Failing after 1481h42m11s
CI Build & Deploy Pipeline / Deploy Production (pull_request) Failing after 1482h13m53s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1482h14m3s
CI Build & Deploy Pipeline / Build Staging Web Image (push) Failing after 1482h14m6s
CI Build & Deploy Pipeline / Build Production Worker Image (push) Failing after 1482h13m58s
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been skipped
CI/CD Pipeline / Unit Tests (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
2026-07-16 21:44:35 +08:00
XiaoXia Bot 11b367ae00 feat(ci): 纯前端PR自动跳过后端检查,节省CI时间
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
2026-07-16 21:43:14 +08:00
xiaoxia 4c662cca32 feat(ci): staging部署脚本增加本地自动回滚能力
CI/CD Pipeline / Frontend Lint (push) Successful in 39s
CI/CD Pipeline / Unit Tests (push) Successful in 2m26s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 4m46s
CI/CD Pipeline / Integration Tests (push) Successful in 4m18s
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
PR #385: Staging本地自动回滚

- 部署前自动备份当前版本容器
- 健康检查失败自动回滚到上一版本
- 无需人工介入,提升部署安全性
2026-07-16 21:26:44 +08:00
xiaoxia 28e2bba5c3 feat(ci): Runner分层调度 - L1/L2/L3按任务类型路由
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
PR #401: Runner分层调度

- L1层(轻量): Validate/Frontend Lint,快速反馈
- L2层(标准): Unit Tests/Integration Tests
- L3层(重): Build/Deploy/E2E,独占资源
- 按任务类型路由到对应标签的runner池
2026-07-16 21:26:26 +08:00
xiaoxia c19e0d9387 feat(ci): staging部署脚本增加Registry登录和拉镜像重试机制
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
PR #390: Registry登录与拉镜像重试

- staging部署前自动登录本地Registry
- docker pull增加重试机制,应对网络抖动
- 提升部署稳定性
2026-07-16 21:26:11 +08:00
xiaoxia d9000fee54 feat(ci): deploy job增加独立concurrency,部署排队不打断
CI/CD Pipeline / Frontend Lint (push) Successful in 59s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 7m40s
CI/CD Pipeline / Unit Tests (push) Successful in 8m12s
CI/CD Pipeline / Integration Tests (push) Successful in 1m58s
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
PR #423: deploy job独立concurrency

- deploy-staging和deploy-production各加独立concurrency group
- cancel-in-progress: false,部署排队不打断
- 全局CI构建仍保持打断模式高效迭代
2026-07-16 21:04:06 +08:00
xiaoxia ffc9706a85 fix(ci): 修复concurrency group变量名,根治run堆积
CI Build & Deploy Pipeline / Build Staging API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production API Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been cancelled
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Production (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
PR #422: 修复concurrency group变量名

- 顶层concurrency group从github.workflow改为硬编码ci-cd前缀
- 使用gitea.event_name和gitea.ref确保跨workflow一致性
- 新push自动取消旧run,根治run堆积问题
2026-07-16 21:03:21 +08:00
xiaoxia bfce6f48ce Merge pull request 'fix(ci): Validate增加独立PG容器,修复alembic迁移检查全部失败' (#405) from fix/ci-validate-pg-container into develop
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI Build & Deploy Pipeline / Production Browser E2E (push) Failing after 1482h32m14s
CI Build & Deploy Pipeline / Deploy Production (push) Failing after 1482h32m14s
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Failing after 1482h32m15s
CI Build & Deploy Pipeline / Build Production Worker Image (push) Failing after 1482h32m15s
CI Build & Deploy Pipeline / Build Production Web Image (push) Failing after 1482h32m15s
CI Build & Deploy Pipeline / Build Production API Image (push) Failing after 1482h32m16s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 1482h32m16s
CI Build & Deploy Pipeline / Build Staging Web Image (push) Failing after 1482h32m16s
CI Build & Deploy Pipeline / Build Staging API Image (push) Failing after 1482h32m16s
CI Build & Deploy Pipeline / Staging E2E Tests (push) Failing after 1483h4m8s
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Failing after 1483h4m9s
2026-07-16 20:06:09 +08:00
3 changed files with 268 additions and 33 deletions
+13 -7
View File
@@ -35,7 +35,7 @@ concurrency:
jobs:
build-staging:
name: Build Staging ${{ matrix.service_display }} Image
runs-on: host
runs-on: ci-l2
timeout-minutes: ${{ matrix.timeout }}
if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')
strategy:
@@ -157,8 +157,11 @@ jobs:
'
deploy-staging:
name: Deploy Staging (Watchtower auto-deploy)
runs-on: saas
runs-on: ci-l3
timeout-minutes: 15
concurrency:
group: deploy-staging-${{ gitea.ref }}
cancel-in-progress: false
needs:
- build-staging
if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')
@@ -308,7 +311,7 @@ jobs:
'
staging-e2e:
name: Staging E2E Tests
runs-on: saas
runs-on: ci-l3
timeout-minutes: 15
if: github.ref_name == 'develop' || github.ref_name == 'main'
needs: deploy-staging
@@ -388,7 +391,7 @@ jobs:
'
staging-api-tests:
name: Staging API Integration Tests
runs-on: saas
runs-on: ci-l2
timeout-minutes: 10
if: github.ref_name == 'develop' || github.ref_name == 'main'
needs: deploy-staging
@@ -468,7 +471,7 @@ jobs:
'
build-production:
name: Build Production ${{ matrix.service_display }} Image
runs-on: host
runs-on: ci-l2
timeout-minutes: ${{ matrix.timeout }}
needs:
if: startsWith(github.ref, 'refs/tags/v')
@@ -592,8 +595,11 @@ jobs:
'
deploy-production:
name: Deploy Production
runs-on: saas
runs-on: ci-l3
timeout-minutes: 30
concurrency:
group: deploy-production-${{ gitea.ref }}
cancel-in-progress: false
if: startsWith(github.ref, 'refs/tags/v')
needs:
- build-production
@@ -735,7 +741,7 @@ jobs:
'
production-e2e:
name: Production Browser E2E
runs-on: saas
runs-on: ci-l3
timeout-minutes: 15
if: startsWith(github.ref, 'refs/tags/v')
needs: deploy-production
+47 -7
View File
@@ -30,12 +30,48 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
group: ci-cd-${{ gitea.event_name }}-${{ gitea.ref }}
cancel-in-progress: true
jobs:
check-frontend-only:
name: Check if frontend-only change
runs-on: ci-l1
if: github.event_name == 'pull_request'
outputs:
skip_backend: ${{ steps.check.outputs.skip_backend }}
steps:
- name: Checkout code
shell: sh
env:
GITHUB_TOKEN: ${{ github.token }}
run: "set -eu\npython3 - <<'PY'\nimport io, os, tarfile, time, urllib.request, urllib.error\nurl = f\"{os.environ['GITHUB_API_URL']}/repos/{os.environ['GITHUB_REPOSITORY']}/archive/{os.environ['GITHUB_SHA']}.tar.gz\"\nrequest = urllib.request.Request(url, headers={\"Authorization\": f\"token {os.environ['GITHUB_TOKEN']}\"})\nlast_err = None\nfor attempt in range(5):\n try:\n with urllib.request.urlopen(request, timeout=120) as response:\n archive = response.read()\n break\n except urllib.error.HTTPError as e:\n last_err = e\n if e.code >= 500 and attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout HTTP {e.code}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\n except Exception as e:\n last_err = e\n if attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout error: {e}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\nelse:\n raise last_err\nwith tarfile.open(fileobj=io.BytesIO(archive), mode='r:gz') as tar:\n root_prefix = tar.getmembers()[0].name.split('/', 1)[0] + '/'\n for member in tar.getmembers():\n name = member.name\n if name == root_prefix[:-1]:\n continue\n if name.startswith(root_prefix):\n member.name = name[len(root_prefix):]\n if member.name:\n tar.extract(member, '.')\nPY\n"
- name: Check changed files
id: check
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -eu
PR_NUMBER=$(echo "$GITHUB_REF" | sed 's|refs/pull/||; s|/.*||')
API_URL="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?limit=300"
FILES=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "$API_URL" | python3 -c "import sys,json; [print(f['filename']) for f in json.load(sys.stdin)]")
FRONTEND_COUNT=$(echo "$FILES" | grep -c '^apps/web/' || true)
BACKEND_COUNT=$(echo "$FILES" | grep -cv '^apps/web/' || true)
TOTAL=$(echo "$FILES" | grep -cv '^$' || true)
echo "变更文件: ${TOTAL} 个 (前端: ${FRONTEND_COUNT}, 后端/公共: ${BACKEND_COUNT})"
if [ "$BACKEND_COUNT" = "0" ] && [ "$FRONTEND_COUNT" -gt "0" ]; then
echo "skip_backend=true" >> $GITHUB_OUTPUT
echo "✅ 纯前端改动,跳过后端检查"
else
echo "skip_backend=false" >> $GITHUB_OUTPUT
echo "🔧 包含后端/公共变更,运行完整CI"
fi
validate:
needs: check-frontend-only
if: always() && needs.check-frontend-only.outputs.skip_backend != 'true'
name: Validate Code Quality And Tests
runs-on: host
runs-on: ci-l1
timeout-minutes: 10
env:
DATABASE_URL: postgresql+psycopg://postgres:postgres@127.0.0.1:5432/xiaoxia_saas
@@ -190,8 +226,10 @@ jobs:
'
unit-tests:
needs: check-frontend-only
if: always() && needs.check-frontend-only.outputs.skip_backend != 'true'
name: Unit Tests
runs-on: host
runs-on: ci-l2
timeout-minutes: 8
env:
USE_IN_MEMORY_DB: 'true'
@@ -262,10 +300,12 @@ jobs:
'
integration-tests:
name: Integration Tests
runs-on: host
runs-on: ci-l2
timeout-minutes: 30
if: always()
needs: validate
if: always() && needs.check-frontend-only.outputs.skip_backend != 'true'
needs:
- check-frontend-only
- validate
env:
DATABASE_URL: postgresql+psycopg://postgres:postgres@127.0.0.1:5432/xiaoxia_saas
USE_IN_MEMORY_DB: 'false'
@@ -379,7 +419,7 @@ jobs:
'
frontend-lint:
name: Frontend Lint
runs-on: host
runs-on: ci-l1
timeout-minutes: 10
steps:
- name: Checkout code
+208 -19
View File
@@ -1,6 +1,6 @@
#!/bin/sh
# ===========================================
# Staging 部署脚本(SSH 模式)
# Staging 部署脚本(SSH 模式,支持自动回滚)
# ===========================================
# 通过 SSH 在 staging 服务器上执行
#
@@ -12,9 +12,44 @@
# ENV_FILE - 环境变量文件路径
# GENERATED_DIR - 生成文件目录
# SKIP_MIGRATION - 跳过数据库迁移(true/false,默认 false)
# SKIP_ROLLBACK - 失败时跳过自动回滚(true/false,默认 false)
set -eu
# ---- 重试工具函数 ----
retry_cmd() {
local max_attempts=$1
local backoff=$2
shift 2
local attempt=1
while [ $attempt -le $max_attempts ]; do
if "$@"; then
return 0
fi
echo " attempt $attempt/$max_attempts failed, retrying in ${backoff}s..."
sleep $backoff
backoff=$((backoff * 2))
attempt=$((attempt + 1))
done
echo " ERROR: failed after $max_attempts retries"
return 1
}
retry_docker_login() {
echo "Logging in to registry (up to 3 retries)"
if retry_cmd 3 5 sh -c "printf %s "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USER" --password-stdin"; then
return 0
fi
echo "WARN: docker login failed after retries, will try pull anyway"
return 0
}
retry_docker_pull() {
local image=$1
echo "Pulling $image (up to 3 retries)"
retry_cmd 3 10 docker pull "$image"
}
IMAGE_TAG="${IMAGE_TAG:-}"
REGISTRY="${REGISTRY:-xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com/xiaoxiakeji}"
REGISTRY_USER="${ACR_USERNAME:-${REGISTRY_USER:-nick0415343655}}"
@@ -25,6 +60,7 @@ GENERATED_DIR="${GENERATED_DIR:-/var/lib/xiaoxia-saas-staging/generated}"
LEGACY_ASSETS_DIR="${LEGACY_ASSETS_DIR:-/var/lib/xiaoxia-saas-staging/legacy-assets}"
SKIP_MIGRATION="${SKIP_MIGRATION:-false}"
SKIP_ROLLBACK="${SKIP_ROLLBACK:-false}"
if [ -z "$IMAGE_TAG" ]; then
echo "ERROR: IMAGE_TAG is required"
@@ -35,17 +71,165 @@ test -f "$ENV_FILE"
mkdir -p "$GENERATED_DIR"
mkdir -p "$LEGACY_ASSETS_DIR"
echo "=========================================="
echo "==========================================="
echo " Staging 部署 - $IMAGE_TAG"
echo "=========================================="
echo "==========================================="
# ---- 记录当前运行的镜像版本(用于回滚) ----
echo "Recording current image versions for rollback..."
PREV_API_IMAGE=""
PREV_WORKER_IMAGE=""
PREV_WEB_IMAGE=""
for c in xiaoxia-api-staging xiaoxia-worker-staging xiaoxia-web-staging; do
if docker inspect "$c" >/dev/null 2>&1; then
img=$(docker inspect -f '{{.Config.Image}}' "$c")
case "$c" in
xiaoxia-api-staging) PREV_API_IMAGE="$img" ;;
xiaoxia-worker-staging) PREV_WORKER_IMAGE="$img" ;;
xiaoxia-web-staging) PREV_WEB_IMAGE="$img" ;;
esac
echo " $c -> $img"
else
echo " $c -> (not running)"
fi
done
# ---- 回滚函数 ----
rollback() {
echo ""
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo " 部署失败,正在自动回滚到上一版本..."
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo ""
if [ "$SKIP_ROLLBACK" = "true" ]; then
echo "SKIP_ROLLBACK=true,跳过自动回滚"
exit 1
fi
# 停止当前(失败的)新容器
echo "Stopping new containers..."
docker rm -f xiaoxia-api-staging 2>/dev/null || true
docker rm -f xiaoxia-worker-staging 2>/dev/null || true
docker rm -f xiaoxia-web-staging 2>/dev/null || true
LOG_OPTS="--log-driver json-file --log-opt max-size=50m --log-opt max-file=3"
# 恢复 API
if [ -n "$PREV_API_IMAGE" ]; then
echo "Rolling back API to: $PREV_API_IMAGE"
docker run -d \
--name xiaoxia-api-staging \
--env-file "$ENV_FILE" \
--network xiaoxia-net-staging \
-p 127.0.0.1:8000:8000 \
-e APP_ENV=staging \
-e APP_VERSION="$(echo $PREV_API_IMAGE | grep -oE '[^:]+$')" \
-e GENERATED_FILES_DIR=/app/generated \
-e GENERATED_FILES_URL_PREFIX=/generated-files \
-e PUBLIC_API_BASE_URL=https://staging-api.xiaoxiajianji.com \
-v "$GENERATED_DIR:/app/generated" \
--restart unless-stopped \
--health-cmd "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=5)\"" \
--health-interval 30s \
--health-timeout 10s \
--health-retries 3 \
--health-start-period 40s \
$LOG_OPTS \
"$PREV_API_IMAGE"
else
echo "No previous API image to roll back to"
fi
# 恢复 Worker
if [ -n "$PREV_WORKER_IMAGE" ]; then
echo "Rolling back Worker to: $PREV_WORKER_IMAGE"
docker run -d \
--name xiaoxia-worker-staging \
--env-file "$ENV_FILE" \
--network xiaoxia-net-staging \
-e APP_ENV=staging \
-e APP_VERSION="$(echo $PREV_WORKER_IMAGE | grep -oE '[^:]+$')" \
-e WORKER_CONCURRENCY=1 \
-e WORKER_MAX_TASKS_PER_CHILD=100 \
-e GENERATED_FILES_DIR=/app/generated \
-e GENERATED_FILES_URL_PREFIX=/generated-files \
-e PUBLIC_API_BASE_URL=https://staging-api.xiaoxiajianji.com \
-v "$GENERATED_DIR:/app/generated" \
--restart unless-stopped \
--health-cmd "sh -c \"grep -q celery /proc/1/cmdline || exit 1\"" \
--health-interval 30s \
--health-timeout 10s \
--health-retries 3 \
--health-start-period 30s \
$LOG_OPTS \
"$PREV_WORKER_IMAGE"
else
echo "No previous Worker image to roll back to"
fi
# 恢复 Web
if [ -n "$PREV_WEB_IMAGE" ]; then
echo "Rolling back Web to: $PREV_WEB_IMAGE"
LEGACY_VOLUME=""
if [ -d "$LEGACY_ASSETS_DIR" ] && [ "$(ls -A "$LEGACY_ASSETS_DIR" 2>/dev/null)" ]; then
LEGACY_VOLUME="-v ${LEGACY_ASSETS_DIR}:/usr/share/nginx/html/assets-legacy/assets:ro"
fi
docker run -d \
--name xiaoxia-web-staging \
--network xiaoxia-net-staging \
-p 127.0.0.1:3001:80 \
--restart unless-stopped \
$LEGACY_VOLUME \
--health-cmd "wget --spider -q http://127.0.0.1:80" \
--health-interval 30s \
--health-timeout 5s \
--health-retries 3 \
$LOG_OPTS \
"$PREV_WEB_IMAGE"
else
echo "No previous Web image to roll back to"
fi
# 等待 API 回滚后恢复健康
if [ -n "$PREV_API_IMAGE" ]; then
echo "Waiting for rolled-back API to become healthy..."
i=0
while [ "$i" -lt 40 ]; do
if curl -sf --max-time 5 http://127.0.0.1:8000/health >/dev/null 2>&1; then
echo "Rolled-back API is healthy!"
break
fi
i=$((i + 1))
echo " Waiting... ($i/40)"
sleep 3
done
if [ "$i" -ge 40 ]; then
echo "WARN: Rolled-back API did not become healthy within 120s"
docker logs --tail 30 xiaoxia-api-staging
fi
fi
echo ""
echo "==========================================="
echo " 回滚完成"
echo "==========================================="
echo "Previous API: ${PREV_API_IMAGE:-none}"
echo "Previous Worker: ${PREV_WORKER_IMAGE:-none}"
echo "Previous Web: ${PREV_WEB_IMAGE:-none}"
echo ""
echo "部署失败,已自动回滚到上一版本"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep staging
exit 1
}
# ---- 登录 Registry ----
if [ -n "$REGISTRY_TOKEN" ]; then
echo "Logging in to registry: $REGISTRY"
echo "=========================================="
echo " Login to Registry (with retries)"
echo "=========================================="
REGISTRY_HOST=$(echo "$REGISTRY" | cut -d/ -f1)
printf %s "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USER" --password-stdin 2>/dev/null || {
echo "WARN: docker login failed, will try to pull anyway"
}
retry_docker_login
fi
# ---- Pull 新版本镜像 ----
@@ -57,12 +241,12 @@ LOCAL_API="xiaoxia-saas-api:${IMAGE_TAG}"
LOCAL_WORKER="xiaoxia-saas-worker:${IMAGE_TAG}"
LOCAL_WEB="xiaoxia-saas-web:${IMAGE_TAG}"
echo "Pulling API image..."
docker pull "$REGISTRY_API"
echo "Pulling Worker image..."
docker pull "$REGISTRY_WORKER"
echo "Pulling Web image..."
docker pull "$REGISTRY_WEB"
echo "=========================================="
echo " Pull images (with retries)"
echo "=========================================="
retry_docker_pull "$REGISTRY_API"
retry_docker_pull "$REGISTRY_WORKER"
retry_docker_pull "$REGISTRY_WEB"
# Re-tag 成本地名
docker tag "$REGISTRY_API" "$LOCAL_API"
@@ -117,7 +301,12 @@ if [ "$SKIP_MIGRATION" != "true" ]; then
--env-file "$ENV_FILE" \
--network xiaoxia-net-staging \
-e APP_ENV=staging \
"$LOCAL_API" sh -c "cd /app && alembic upgrade head"
"$LOCAL_API" sh -c "cd /app && alembic upgrade head" || {
echo "ERROR: Database migration failed"
echo "Note: Migration failures are NOT automatically rolled back (data safety)"
echo "Please manually check and fix the migration, then redeploy"
exit 1
}
echo "Migrations completed."
else
echo "Skipping migrations (SKIP_MIGRATION=true)"
@@ -151,7 +340,7 @@ docker run -d \
--health-retries 3 \
--health-start-period 40s \
$LOG_OPTS \
"$LOCAL_API"
"$LOCAL_API" || rollback
# ---- 启动 Worker ----
echo "Starting Worker container..."
@@ -174,7 +363,7 @@ docker run -d \
--health-retries 3 \
--health-start-period 30s \
$LOG_OPTS \
"$LOCAL_WORKER"
"$LOCAL_WORKER" || rollback
# ---- 启动 Web ----
LEGACY_VOLUME=""
@@ -197,7 +386,7 @@ docker run -d \
--health-timeout 5s \
--health-retries 3 \
$LOG_OPTS \
"$LOCAL_WEB"
"$LOCAL_WEB" || rollback
# ---- 等待 API 健康 ----
echo "Waiting for API to become healthy..."
@@ -215,7 +404,7 @@ done
if [ "$i" -ge 40 ]; then
echo "ERROR: API did not become healthy within 120s"
docker logs --tail 50 xiaoxia-api-staging
exit 1
rollback
fi
# ---- 等待 Web 健康 ----
@@ -234,7 +423,7 @@ done
if [ "$i" -ge 15 ]; then
echo "ERROR: Web did not become healthy within 30s"
docker logs --tail 30 xiaoxia-web-staging
exit 1
rollback
fi
# ---- 清理旧镜像 ----