#!/bin/bash # ============================================================================ # MOMO Pro System - 多環境同步腳本 # 支援開發 (DEV)、UAT、正式 (PROD) 三環境同步 # ============================================================================ set -e # 顏色定義 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # 環境配置 DEV_PATH="/Users/ogt/momo_pro_system" UAT_HOST="wooo@192.168.0.110" UAT_PATH="/home/wooo/momo_pro_system" PROD_HOST="momo-server" PROD_ZONE="asia-east1-a" PROD_PATH="/home/ogt/momo_pro_system" # 密碼檔案(避免硬編碼) UAT_PASS_FILE="$HOME/.momo_uat_pass" # 排除的檔案和目錄 EXCLUDES="--exclude='venv' --exclude='__pycache__' --exclude='*.pyc' --exclude='.git' --exclude='logs/*.log' --exclude='backups' --exclude='.DS_Store'" # ============================================================================ # 工具函式 # ============================================================================ print_header() { echo -e "\n${CYAN}========================================${NC}" echo -e "${CYAN} $1${NC}" echo -e "${CYAN}========================================${NC}\n" } print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } print_success() { echo -e "${GREEN}[OK]${NC} $1"; } print_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; } print_error() { echo -e "${RED}[ERROR]${NC} $1"; } get_uat_pass() { if [ -f "$UAT_PASS_FILE" ]; then # V-Fix: 使用 xargs 去除可能存在的換行符號,避免 SSH 失敗 cat "$UAT_PASS_FILE" | xargs else read -sp "請輸入 UAT 密碼: " pass echo "$pass" fi } ssh_uat() { local pass=$(get_uat_pass) sshpass -p "$pass" ssh -o StrictHostKeyChecking=accept-new $UAT_HOST "$1" } scp_to_uat() { local pass=$(get_uat_pass) sshpass -p "$pass" scp -r "$1" "$UAT_HOST:$2" } scp_to_uat_2() { local pass=$(get_uat_pass) sshpass -p "$pass" scp -r "$1" "$UAT_HOST_2:$2" } # ============================================================================ # 環境狀態檢查 # ============================================================================ check_all_status() { print_header "檢查所有環境狀態" echo -e "${YELLOW}📍 開發環境 (DEV) - 本機 Mac${NC}" echo " 路徑: $DEV_PATH" local dev_app_mtime=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$DEV_PATH/app.py" 2>/dev/null || echo "N/A") local dev_db_size=$(ls -lh "$DEV_PATH/data/momo_database.db" 2>/dev/null | awk '{print $5}' || echo "N/A") echo " app.py 修改時間: $dev_app_mtime" echo " 資料庫大小: $dev_db_size" echo "" echo -e "${YELLOW}📍 UAT 環境 - VMWare VM${NC}" echo " 主機: $UAT_HOST" echo " 網址: https://mo.wooo.work" local uat_status=$(ssh_uat "systemctl is-active momo 2>/dev/null" 2>/dev/null || echo "N/A") local uat_app_mtime=$(ssh_uat "stat -c '%y' $UAT_PATH/app.py 2>/dev/null | cut -d'.' -f1" 2>/dev/null || echo "N/A") local uat_db_size=$(ssh_uat "ls -lh $UAT_PATH/data/momo_database.db 2>/dev/null | awk '{print \$5}'" 2>/dev/null || echo "N/A") echo " 服務狀態: $uat_status" echo " app.py 修改時間: $uat_app_mtime" echo " 資料庫大小: $uat_db_size" echo "" echo -e "${YELLOW}📍 正式環境 (PROD) - GCP VM${NC}" echo " 主機: $PROD_HOST ($PROD_ZONE)" echo " 網址: https://mo.wooo.work" local prod_status=$(gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="systemctl is-active momo 2>/dev/null" 2>/dev/null || echo "N/A") local prod_app_mtime=$(gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="stat -c '%y' $PROD_PATH/app.py 2>/dev/null | cut -d'.' -f1" 2>/dev/null || echo "N/A") local prod_db_size=$(gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="ls -lh $PROD_PATH/data/momo_database.db 2>/dev/null | awk '{print \$5}'" 2>/dev/null || echo "N/A") echo " 服務狀態: $prod_status" echo " app.py 修改時間: $prod_app_mtime" echo " 資料庫大小: $prod_db_size" } # ============================================================================ # 同步程式碼 # ============================================================================ sync_code_to_uat() { print_header "同步程式碼: DEV → UAT" print_info "打包程式碼..." cd "$DEV_PATH" tar czf /tmp/momo_code_sync.tar.gz \ --exclude='venv' --exclude='__pycache__' --exclude='*.pyc' \ --exclude='.git' --exclude='logs/*.log' --exclude='backups' \ --exclude='.DS_Store' --exclude='data/*.db' \ *.py *.html *.md database/ static/ services/ config/ 2>/dev/null || true print_info "上傳到 UAT..." scp_to_uat /tmp/momo_code_sync.tar.gz "$UAT_PATH/" print_info "在 UAT 解壓並重啟服務..." local pass=$(get_uat_pass) ssh_uat "cd $UAT_PATH && tar xzf momo_code_sync.tar.gz && rm momo_code_sync.tar.gz && echo \"$pass\" | sudo -S systemctl restart momo" print_success "程式碼同步到 UAT 完成!" } sync_code_to_prod() { print_header "同步程式碼: DEV → PROD" print_info "打包程式碼..." cd "$DEV_PATH" tar czf /tmp/momo_code_sync.tar.gz \ --exclude='venv' --exclude='__pycache__' --exclude='*.pyc' \ --exclude='.git' --exclude='logs/*.log' --exclude='backups' \ --exclude='.DS_Store' --exclude='data/*.db' \ *.py *.html *.md database/ static/ services/ config/ 2>/dev/null || true print_info "上傳到 PROD..." gcloud compute scp /tmp/momo_code_sync.tar.gz $PROD_HOST:$PROD_PATH/ --zone=$PROD_ZONE print_info "在 PROD 解壓並重啟服務..." gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="cd $PROD_PATH && tar xzf momo_code_sync.tar.gz && rm momo_code_sync.tar.gz && sudo systemctl restart momo" print_success "程式碼同步到 PROD 完成!" } sync_code_uat_to_prod() { print_header "同步程式碼: UAT → PROD" print_info "從 UAT 打包程式碼..." ssh_uat "cd $UAT_PATH && tar czf /tmp/momo_uat_code.tar.gz --exclude='venv' --exclude='__pycache__' --exclude='*.pyc' --exclude='data/*.db' *.py *.html *.md database/ static/ services/ config/ 2>/dev/null || true" print_info "下載到本機..." local pass=$(get_uat_pass) sshpass -p "$pass" scp "$UAT_HOST:/tmp/momo_uat_code.tar.gz" /tmp/ print_info "上傳到 PROD..." gcloud compute scp /tmp/momo_uat_code.tar.gz $PROD_HOST:$PROD_PATH/ --zone=$PROD_ZONE print_info "在 PROD 解壓並重啟服務..." gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="cd $PROD_PATH && tar xzf momo_uat_code.tar.gz && rm momo_uat_code.tar.gz && sudo systemctl restart momo" print_success "UAT 程式碼同步到 PROD 完成!" } # ============================================================================ # 同步資料庫 # ============================================================================ sync_db_to_uat() { print_header "同步資料庫: DEV → UAT" print_info "上傳資料庫到 UAT..." scp_to_uat "$DEV_PATH/data/momo_database.db" "$UAT_PATH/data/" print_info "重啟 UAT 服務..." local pass=$(get_uat_pass) ssh_uat "echo \"$pass\" | sudo -S systemctl restart momo" print_success "資料庫同步到 UAT 完成!" } sync_db_to_prod() { print_header "同步資料庫: DEV → PROD" print_info "上傳資料庫到 PROD..." gcloud compute scp "$DEV_PATH/data/momo_database.db" $PROD_HOST:$PROD_PATH/data/ --zone=$PROD_ZONE print_info "重啟 PROD 服務..." gcloud compute ssh $PROD_HOST --zone=$PROD_ZONE --command="sudo systemctl restart momo" print_success "資料庫同步到 PROD 完成!" } sync_db_prod_to_dev() { print_header "同步資料庫: PROD → DEV" print_info "備份本機資料庫..." cp "$DEV_PATH/data/momo_database.db" "$DEV_PATH/data/momo_database_backup_$(date +%Y%m%d_%H%M%S).db" print_info "從 PROD 下載資料庫..." gcloud compute scp $PROD_HOST:$PROD_PATH/data/momo_database.db "$DEV_PATH/data/" --zone=$PROD_ZONE print_success "PROD 資料庫同步到 DEV 完成!" } sync_db_prod_to_uat() { print_header "同步資料庫: PROD → UAT" print_info "從 PROD 下載資料庫..." gcloud compute scp $PROD_HOST:$PROD_PATH/data/momo_database.db /tmp/ --zone=$PROD_ZONE print_info "上傳到 UAT..." scp_to_uat /tmp/momo_database.db "$UAT_PATH/data/" print_info "重啟 UAT 服務..." local pass=$(get_uat_pass) ssh_uat "echo \"$pass\" | sudo -S systemctl restart momo" print_success "PROD 資料庫同步到 UAT 完成!" } # ============================================================================ # 完整同步 # ============================================================================ sync_all_to_uat() { print_header "完整同步: DEV → UAT (程式碼 + 資料庫)" sync_code_to_uat sync_db_to_uat print_success "完整同步到 UAT 完成!" } sync_all_to_prod() { print_header "完整同步: DEV → PROD (程式碼 + 資料庫)" sync_code_to_prod sync_db_to_prod print_success "完整同步到 PROD 完成!" } sync_all_environments() { print_header "同步所有環境 (DEV → UAT → PROD)" sync_all_to_uat sync_code_uat_to_prod sync_db_to_prod print_success "所有環境同步完成!" } # ============================================================================ # 主選單 # ============================================================================ show_menu() { print_header "MOMO Pro System - 多環境同步工具" echo "請選擇操作:" echo "" echo -e "${CYAN}狀態檢查${NC}" echo " 1) 檢查所有環境狀態" echo "" echo -e "${CYAN}程式碼同步${NC}" echo " 2) DEV → UAT (程式碼)" echo " 3) DEV → PROD (程式碼)" echo " 4) UAT → PROD (程式碼)" echo "" echo -e "${CYAN}資料庫同步${NC}" echo " 5) DEV → UAT (資料庫)" echo " 6) DEV → PROD (資料庫)" echo " 7) PROD → DEV (資料庫)" echo " 8) PROD → UAT (資料庫)" echo "" echo -e "${CYAN}完整同步${NC}" echo " 9) DEV → UAT (完整)" echo " 10) DEV → PROD (完整)" echo " 11) 同步所有環境" echo "" echo " 0) 離開" echo "" read -p "請輸入選項 [0-11]: " choice case $choice in 1) check_all_status ;; 2) sync_code_to_uat ;; 3) sync_code_to_prod ;; 4) sync_code_uat_to_prod ;; 5) sync_db_to_uat ;; 6) sync_db_to_prod ;; 7) sync_db_prod_to_dev ;; 8) sync_db_prod_to_uat ;; 9) sync_all_to_uat ;; 10) sync_all_to_prod ;; 11) sync_all_environments ;; 0) echo "再見!"; exit 0 ;; *) print_error "無效選項"; show_menu ;; esac echo "" read -p "按 Enter 返回選單..." show_menu } # ============================================================================ # 命令列參數處理 # ============================================================================ case "$1" in --status) check_all_status ;; --code-to-uat) sync_code_to_uat ;; --code-to-prod) sync_code_to_prod ;; --db-to-uat) sync_db_to_uat ;; --db-to-prod) sync_db_to_prod ;; --db-from-prod) sync_db_prod_to_dev ;; --full-uat) sync_all_to_uat ;; --full-prod) sync_all_to_prod ;; --sync-all) sync_all_environments ;; --help|-h) echo "使用方式: $0 [選項]" echo "" echo "選項:" echo " --status 檢查所有環境狀態" echo " --code-to-uat 同步程式碼到 UAT" echo " --code-to-prod 同步程式碼到 PROD" echo " --db-to-uat 同步資料庫到 UAT" echo " --db-to-prod 同步資料庫到 PROD" echo " --db-from-prod 從 PROD 下載資料庫到 DEV" echo " --full-uat 完整同步到 UAT" echo " --full-prod 完整同步到 PROD" echo " --sync-all 同步所有環境" echo " --help, -h 顯示此說明" echo "" echo "不帶參數執行會顯示互動式選單" ;; *) show_menu ;; esac