Files
ewoooc/deploy_to_uat.sh
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml)
- 部署模式: rsync Python 檔案至 188 → docker restart (volume mount)
- Dockerfile/requirements 變動時自動重建 Docker image
- 部署通知: Telegram (開始/成功/失敗)
- 健康檢查: https://mo.wooo.work/health (最多 5 次重試)
- 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 01:21:13 +08:00

305 lines
8.4 KiB
Bash
Executable File

#!/bin/bash
# ============================================================
# MOMO Pro System - UAT 一鍵部署腳本
# ============================================================
# 用法: ./deploy_to_uat.sh [選項]
# -f, --full 完整同步 (包含所有檔案)
# -q, --quick 快速更新 (僅 Python/HTML)
# -r, --restart 僅重啟容器
# -h, --help 顯示說明
#
# 預設行為: 快速更新 + 重啟容器
# ============================================================
set -e # 發生錯誤時停止執行
# === 配置 ===
UAT_HOST="wooo@192.168.0.110"
UAT_PATH="/home/wooo/momo_pro_system"
LOCAL_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
CONTAINER_NAME="momo-pro-system"
# === 顏色輸出 ===
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# === 函數定義 ===
print_header() {
echo ""
echo -e "${BLUE}============================================================${NC}"
echo -e "${BLUE} MOMO Pro System - UAT 部署工具${NC}"
echo -e "${BLUE}============================================================${NC}"
echo -e " 目標主機: ${GREEN}${UAT_HOST}${NC}"
echo -e " 目標路徑: ${GREEN}${UAT_PATH}${NC}"
echo ""
}
print_step() {
echo -e "${YELLOW}[步驟]${NC} $1"
}
print_success() {
echo -e "${GREEN}[成功]${NC} $1"
}
print_error() {
echo -e "${RED}[錯誤]${NC} $1"
}
show_help() {
echo "用法: $0 [選項]"
echo ""
echo "選項:"
echo " -f, --full 完整同步 (rsync 整個專案,排除 data/logs/venv)"
echo " -q, --quick 快速更新 (僅同步 Python 和 HTML 檔案)"
echo " -r, --restart 僅重啟容器 (不同步檔案)"
echo " -m, --monitor 啟動監控服務 (Grafana/Prometheus/Loki)"
echo " -v, --verify 驗證部署狀態"
echo " -h, --help 顯示此說明"
echo ""
echo "範例:"
echo " $0 # 預設: 快速更新 + 重啟"
echo " $0 -f # 完整同步 + 重啟"
echo " $0 -r # 僅重啟容器"
echo " $0 -v # 驗證部署狀態"
}
# 檢查 SSH 連線
check_ssh() {
print_step "檢查 SSH 連線..."
if ssh -o ConnectTimeout=5 ${UAT_HOST} "echo 'SSH OK'" > /dev/null 2>&1; then
print_success "SSH 連線正常"
return 0
else
print_error "無法連線到 ${UAT_HOST}"
exit 1
fi
}
# 快速更新 (僅 Python/HTML)
quick_sync() {
print_step "快速同步 Python 和 HTML 檔案..."
# 同步 routes 目錄
echo " - 同步 routes/*.py"
scp -q ${LOCAL_PATH}/routes/*.py ${UAT_HOST}:${UAT_PATH}/routes/
# 同步根目錄的 Python 檔案
echo " - 同步根目錄 Python 檔案"
scp -q ${LOCAL_PATH}/app.py ${UAT_HOST}:${UAT_PATH}/
scp -q ${LOCAL_PATH}/auth.py ${UAT_HOST}:${UAT_PATH}/
scp -q ${LOCAL_PATH}/config.py ${UAT_HOST}:${UAT_PATH}/
scp -q ${LOCAL_PATH}/auto_import_routes.py ${UAT_HOST}:${UAT_PATH}/ 2>/dev/null || true
scp -q ${LOCAL_PATH}/vendor_routes.py ${UAT_HOST}:${UAT_PATH}/ 2>/dev/null || true
scp -q ${LOCAL_PATH}/crawler_management_routes.py ${UAT_HOST}:${UAT_PATH}/ 2>/dev/null || true
# 同步 services 目錄
echo " - 同步 services/*.py"
scp -q ${LOCAL_PATH}/services/*.py ${UAT_HOST}:${UAT_PATH}/services/
# 同步 database 目錄
echo " - 同步 database/*.py"
scp -q ${LOCAL_PATH}/database/*.py ${UAT_HOST}:${UAT_PATH}/database/
# 同步 templates 目錄
echo " - 同步 templates/"
rsync -az --delete -e ssh \
${LOCAL_PATH}/templates/ \
${UAT_HOST}:${UAT_PATH}/templates/
# 同步根目錄的 HTML 檔案
echo " - 同步根目錄 HTML 檔案"
scp -q ${LOCAL_PATH}/*.html ${UAT_HOST}:${UAT_PATH}/ 2>/dev/null || true
print_success "快速同步完成"
}
# 完整同步 (rsync)
full_sync() {
print_step "完整同步專案檔案..."
rsync -avz --progress -e ssh \
--exclude='venv' \
--exclude='__pycache__' \
--exclude='*.pyc' \
--exclude='.git' \
--exclude='backups' \
--exclude='logs/*.log' \
--exclude='data/*.db' \
--exclude='data/*.db-*' \
--exclude='.env' \
--exclude='*.tar.gz' \
${LOCAL_PATH}/ \
${UAT_HOST}:${UAT_PATH}/
print_success "完整同步完成"
}
# 重啟容器
restart_container() {
print_step "重啟 Docker 容器 (${CONTAINER_NAME})..."
ssh ${UAT_HOST} "docker restart ${CONTAINER_NAME}"
# 等待容器啟動
echo " - 等待容器啟動..."
sleep 5
# 檢查容器狀態
local status=$(ssh ${UAT_HOST} "docker ps --filter name=${CONTAINER_NAME} --format '{{.Status}}' | head -1")
if [[ $status == *"healthy"* ]] || [[ $status == *"Up"* ]]; then
print_success "容器已啟動: ${status}"
else
print_error "容器狀態異常: ${status}"
echo " 查看日誌: ssh ${UAT_HOST} 'docker logs ${CONTAINER_NAME} --tail 50'"
fi
}
# 重建容器 (當 docker-compose.yml 有變更時)
recreate_container() {
print_step "重建 Docker 容器..."
ssh ${UAT_HOST} "cd ${UAT_PATH} && \
docker rm -f ${CONTAINER_NAME} 2>/dev/null || true && \
docker-compose up -d momo-app"
# 等待容器啟動
echo " - 等待容器啟動..."
sleep 8
local status=$(ssh ${UAT_HOST} "docker ps --filter name=${CONTAINER_NAME} --format '{{.Status}}' | head -1")
print_success "容器已重建: ${status}"
}
# 啟動監控服務
start_monitoring() {
print_step "啟動監控服務..."
ssh ${UAT_HOST} "cd ${UAT_PATH} && docker-compose --profile monitoring up -d"
print_success "監控服務已啟動"
echo " - Grafana: http://192.168.0.110:3000"
echo " - Prometheus: http://192.168.0.110:9090"
}
# 驗證部署
verify_deployment() {
print_step "驗證部署狀態..."
echo ""
echo " [容器狀態]"
ssh ${UAT_HOST} "docker ps --filter name=momo --format 'table {{.Names}}\t{{.Status}}' | head -10"
echo ""
echo " [健康檢查]"
local health=$(curl -s -o /dev/null -w "%{http_code}" "https://mo.wooo.work/health" 2>/dev/null || echo "000")
if [ "$health" = "200" ]; then
echo -e " /health: ${GREEN}200 OK${NC}"
else
echo -e " /health: ${RED}${health}${NC}"
fi
echo ""
echo " [登入保護驗證] (應為 302)"
for path in "/" "/auto_import" "/daily_sales" "/sales_analysis"; do
local code=$(curl -s -o /dev/null -w "%{http_code}" "https://mo.wooo.work${path}" 2>/dev/null || echo "000")
if [ "$code" = "302" ]; then
echo -e " ${path}: ${GREEN}${code}${NC}"
else
echo -e " ${path}: ${RED}${code}${NC}"
fi
done
echo ""
echo " [公開端點] (應為 200)"
for path in "/health" "/metrics"; do
local code=$(curl -s -o /dev/null -w "%{http_code}" "https://mo.wooo.work${path}" 2>/dev/null || echo "000")
if [ "$code" = "200" ]; then
echo -e " ${path}: ${GREEN}${code}${NC}"
else
echo -e " ${path}: ${RED}${code}${NC}"
fi
done
echo ""
print_success "驗證完成"
}
# === 主程式 ===
print_header
# 解析參數
MODE="quick" # 預設模式
RESTART=true
VERIFY=false
while [[ $# -gt 0 ]]; do
case $1 in
-f|--full)
MODE="full"
shift
;;
-q|--quick)
MODE="quick"
shift
;;
-r|--restart)
MODE="restart_only"
shift
;;
-m|--monitor)
MODE="monitor"
shift
;;
-v|--verify)
VERIFY=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知選項: $1"
show_help
exit 1
;;
esac
done
# 執行
check_ssh
case $MODE in
"quick")
quick_sync
restart_container
;;
"full")
full_sync
recreate_container
;;
"restart_only")
restart_container
;;
"monitor")
start_monitoring
;;
esac
if [ "$VERIFY" = true ] || [ "$MODE" != "monitor" ]; then
verify_deployment
fi
echo ""
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} 部署完成!${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""