From 91eb4307bda8eb7d8320629345ec011b536f606d Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Tue, 26 May 2026 03:42:48 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E7=BD=B2=E5=8A=A0=E5=9B=BA=EF=BC=9A?= =?UTF-8?q?=E5=8E=BB=E9=87=8D=E6=9E=84=E5=BB=BA=20+=20SHA=20tag=20?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=20+=20smoke=20test=20+=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - frontend-quality 移除重复 npm run build - docker-compose 镜像标签改用 ${IMAGE_TAG:-latest} - deploy.sh:存旧 tag → SHA 部署 → smoke test → 失败回滚 - CI deploy job 改为 SCP deploy.sh 远程执行 --- .github/workflows/ci.yml | 15 ++++-------- deploy.sh | 51 +++++++++++++++++++++++++++++++++------- docker-compose.yml | 8 +++---- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 946b2442..6443a160 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,6 @@ jobs: - name: Business state tests run: npm run test:business - - name: Build - run: npm run build - build-and-push: needs: [python-quality, frontend-quality] if: github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -105,17 +102,15 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Deploy to VPS run: | mkdir -p ~/.ssh echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa + scp -o StrictHostKeyChecking=accept-new deploy.sh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:/tmp/deploy.sh ssh -o StrictHostKeyChecking=accept-new ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} " - echo '${{ secrets.GHCR_PAT }}' | docker login ghcr.io -u yangyuan-zhen --password-stdin - cd /root/PolyWeather - git fetch origin main && git reset --hard origin/main - docker compose pull - docker compose up -d - sleep 10 - curl -s http://localhost:8000/healthz + bash /tmp/deploy.sh '${{ secrets.GHCR_PAT }}' '${{ github.sha }}' " diff --git a/deploy.sh b/deploy.sh index 9ea5238d..7b8c7753 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,12 +1,45 @@ -#!/bin/bash -set -e -VPS="root@38.54.27.70" -PROJECT="/root/PolyWeather" +#!/usr/bin/env bash +set -euo pipefail -echo "🚀 Deploying to $VPS..." +GHCR_PAT="$1" +NEW_TAG="${2:-latest}" +TAG_FILE="/var/lib/polyweather/.current_tag" +COMPOSE_DIR="/root/PolyWeather" -ssh "$VPS" "cd $PROJECT && git pull && docker compose up -d --build" +echo "$GHCR_PAT" | docker login ghcr.io -u yangyuan-zhen --password-stdin -echo "✅ Deploy complete. Checking health..." -sleep 8 -ssh "$VPS" "curl -s http://localhost:8000/healthz" +cd "$COMPOSE_DIR" +git fetch origin main && git reset --hard origin/main + +PREVIOUS_TAG="" +if [ -f "$TAG_FILE" ]; then + PREVIOUS_TAG=$(cat "$TAG_FILE") + echo "Previous tag: $PREVIOUS_TAG" +fi + +export IMAGE_TAG="$NEW_TAG" +docker compose pull +docker compose up -d +sleep 5 + +FAILED=0 +curl -fsSo /dev/null --max-time 10 "https://api.polyweather.top/healthz" && echo "✅ healthz" || { echo "❌ healthz"; FAILED=1; } +curl -fsSo /dev/null --max-time 10 "https://api.polyweather.top/api/scan/terminal?limit=1" && echo "✅ scan" || { echo "❌ scan"; FAILED=1; } +curl -fsSo /dev/null --max-time 10 "https://www.polyweather.top/" && echo "✅ frontend" || { echo "❌ frontend"; FAILED=1; } + +if [ "$FAILED" = "1" ]; then + echo "❌ Smoke tests failed. Rolling back..." + if [ -n "$PREVIOUS_TAG" ]; then + export IMAGE_TAG="$PREVIOUS_TAG" + docker compose pull + docker compose up -d + echo "✅ Rolled back to $PREVIOUS_TAG" + else + echo "⚠️ No previous tag to rollback to" + fi + exit 1 +fi + +mkdir -p "$(dirname "$TAG_FILE")" +echo "$NEW_TAG" > "$TAG_FILE" +echo "✅ Deployed $NEW_TAG" diff --git a/docker-compose.yml b/docker-compose.yml index 49bde8e4..855b7ecf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - import sqlite3; c=sqlite3.connect('/var/lib/polyweather/polyweather.db'); c.execute('SELECT 1'); c.close() timeout: 10s - image: ghcr.io/yangyuan-zhen/polyweather-backend:latest + image: ghcr.io/yangyuan-zhen/polyweather-backend:${IMAGE_TAG:-latest} mem_limit: ${POLYWEATHER_BOT_MEM_LIMIT:-768m} memswap_limit: ${POLYWEATHER_BOT_MEMSWAP_LIMIT:-1g} pids_limit: 256 @@ -47,7 +47,7 @@ services: - -qO- - http://localhost:3000 timeout: 5s - image: ghcr.io/yangyuan-zhen/polyweather-frontend:latest + image: ghcr.io/yangyuan-zhen/polyweather-frontend:${IMAGE_TAG:-latest} ports: - 3001:3000 restart: unless-stopped @@ -64,7 +64,7 @@ services: - -c - from urllib.request import urlopen; urlopen('http://localhost:8000/healthz') timeout: 5s - image: ghcr.io/yangyuan-zhen/polyweather-backend:latest + image: ghcr.io/yangyuan-zhen/polyweather-backend:${IMAGE_TAG:-latest} ports: - 8000:8000 restart: unless-stopped @@ -74,4 +74,4 @@ services: - ${POLYWEATHER_RUNTIME_DATA_DIR:-/var/lib/polyweather}:/app/data x-polyweather-base: env_file: *id001 - image: ghcr.io/yangyuan-zhen/polyweather-backend:latest + image: ghcr.io/yangyuan-zhen/polyweather-backend:${IMAGE_TAG:-latest}