#!/bin/bash # =========================================== # Rust Backend Deployment Script # =========================================== # Rust Backend Deployment Script # =========================================== # Usage: ./deploy.sh [development|production] [options] # # Options: # --dry-run 预览模式 # --yes, -y 跳过确认 # --skip-tests 跳过部署后测试 # --rollback 回滚到指定备份 # --backup-list 列出可用备份 # --logs [N] 查看后端日志(默认50行) # --tail [N] 实时跟踪日志 # --status 查看服务状态 # --post-logs 部署后显示日志 # --init-env 初始化 systemd service 模板 # --deploy-service 上传 systemd service 文件到服务器 # --target blue|green 蓝绿部署目标(默认单实例) # --remote-host IP 部署目标服务器(默认 1panel-server) # =========================================== set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/lib/common.sh" source "${SCRIPT_DIR}/lib/test.sh" # ---------- 默认值 ---------- PROJECT_NAME="rust-backend" REMOTE_USER="root" REMOTE_HOST="aliyun-server" APP_ENV="production" DRY_RUN=false; SKIP_CONFIRM=false; SKIP_TESTS=false ROLLBACK=false; LIST_BACKUPS=false; VIEW_LOGS=false; TAIL_LOGS=false LOG_LINES=50; VIEW_STATUS=false; POST_DEPLOY_LOGS=false; INIT_ENV=false DEPLOY_SERVICE=false; BACKUP_PATH="" # ---------- 选项解析 ---------- BG_TARGET="" # blue 或 green(仅 production 有效) while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=true ;; --yes|-y) SKIP_CONFIRM=true ;; --skip-tests) SKIP_TESTS=true ;; --rollback) ROLLBACK=true ;; --backup-list) LIST_BACKUPS=true ;; --logs) VIEW_LOGS=true ;; --tail) TAIL_LOGS=true ;; --logs=*) VIEW_LOGS=true; LOG_LINES="${1#*=}" ;; --tail=*) TAIL_LOGS=true; LOG_LINES="${1#*=}" ;; --status) VIEW_STATUS=true ;; --post-logs) POST_DEPLOY_LOGS=true ;; --init-env) INIT_ENV=true ;; --deploy-service) DEPLOY_SERVICE=true ;; --target) BG_TARGET="${2:-}"; shift ;; --remote-host) REMOTE_HOST="${2:-}"; shift ;; --help|-h) echo "用法: $0 [development|production] [options]" echo "" echo "选项:" echo " --dry-run 预览模式" echo " --yes, -y 跳过确认" echo " --skip-tests 跳过部署后测试" echo " --target blue|green 蓝绿部署目标" echo " --remote-host IP 部署目标服务器(默认 aliyun-server)" echo " --rollback 回滚到指定备份" echo " --backup-list 列出可用备份" echo " --logs [N] 查看后端日志(默认50行)" echo " --tail [N] 实时跟踪日志" echo " --status 查看服务状态" echo " --post-logs 部署后显示日志" echo " --init-env 初始化 systemd service 模板" echo " --deploy-service 上传 systemd service 文件到服务器" exit 0 ;; *) if [[ "$1" == "development" || "$1" == "production" ]]; then APP_ENV="$1" else echo "未知选项: $1"; exit 1 fi ;; esac; shift done # 提前建立 SSH 连接(蓝绿检测需要 remote()) ssh_connect # ---------- 蓝绿目标自动检测 + 配置 ---------- # 优先级: 手动 --target > 自动检测 detect_blue_green() { local proxy_conf; proxy_conf=$(get_proxy_conf) local active active=$(remote "sudo cat ${proxy_conf} 2>/dev/null" 2>/dev/null | grep -oP '127\.0\.0\.1:\d+' | head -1 || echo "") case "${APP_ENV}:${active}" in *:4433) echo "blue" ;; *:4434) echo "green" ;; *:8083) echo "blue" ;; *:8084) echo "green" ;; *) echo "" ;; esac } if [ -z "$BG_TARGET" ] && [ "${APP_ENV}" = "production" ]; then # 自动检测:部署到待命环境(仅生产环境支持蓝绿) active_target=$(detect_blue_green) if [ -n "$active_target" ]; then BG_TARGET=$([ "$active_target" = "blue" ] && echo "green" || echo "blue") log_info "自动检测: 当前活动 ${active_target},部署到 ${BG_TARGET}" fi fi if [ -n "$BG_TARGET" ]; then case "${APP_ENV}:${BG_TARGET}" in production:blue) BG_PORT="4433"; BG_DIR="rust_backend_blue"; BG_SVC="rust-backend-blue.service" ;; production:green) BG_PORT="4434"; BG_DIR="rust_backend_green"; BG_SVC="rust-backend-green.service" ;; development:blue) BG_PORT="8083"; BG_DIR="rust_backend_dev_blue"; BG_SVC="rust-backend-dev-blue.service" ;; development:green)BG_PORT="8084"; BG_DIR="rust_backend_dev_green";BG_SVC="rust-backend-dev-green.service" ;; *) echo "错误: --target 只能是 blue 或 green"; exit 1 ;; esac fi # ---------- 环境配置 ---------- case "${APP_ENV}" in development) if [ -n "$BG_TARGET" ]; then REMOTE_DIR="/root/rust/${BG_DIR}" SERVICE_NAME="${BG_SVC}" BACKEND_PORT="${BG_PORT}" else REMOTE_DIR="/root/rust/rust_backend_dev" SERVICE_NAME="rust-backend-dev.service" BACKEND_PORT="8080" fi TEST_DOMAIN="https://dev.xmclassmate.top" DB_CONTAINER="postgres" DB_NAME="milkydata_dev" DB_USER="milkydata" ;; production) if [ -n "$BG_TARGET" ]; then REMOTE_DIR="/root/rust/${BG_DIR}" SERVICE_NAME="${BG_SVC}" BACKEND_PORT="${BG_PORT}" else REMOTE_DIR="/root/rust/rust_backend" SERVICE_NAME="rust-backend.service" BACKEND_PORT="4433" fi TEST_DOMAIN="https://xmclassmate.top" DB_CONTAINER="postgres" DB_NAME="milkydata" DB_USER="milkydata" ;; esac # ---------- 快捷命令 ---------- GIT_VERSION=$(get_git_info) if [ "$LIST_BACKUPS" = true ]; then list_backups; exit 0; fi if [ "$INIT_ENV" = true ]; then init_env; exit 0; fi if [ "$DEPLOY_SERVICE" = true ]; then deploy_service_file; exit 0; fi if [ "$VIEW_LOGS" = true ]; then view_logs; exit 0; fi if [ "$TAIL_LOGS" = true ]; then tail_logs; exit 0; fi if [ "$VIEW_STATUS" = true ]; then status; exit 0; fi if [ "$ROLLBACK" = true ]; then rollback; exit 0; fi # ---------- 主部署流程 ---------- show_deploy_info confirm_deploy check_not_active check_dependencies check_service_file check_ssl_expiry build_project backup_old_version cleanup_old_backups 5 upload_binary upload_dir "迁移文件" migrations upload_script "测试脚本" test_deployment.sh test_deployment.sh upload_script "清理脚本" scripts/cleanup_refresh_tokens.sh scripts/cleanup_refresh_tokens.sh upload_script "备份脚本" scripts/backup-db.sh scripts/backup-db.sh deploy_service_file restart_service run_migrations if run_tests; then log_to_file "DEPLOY success" send_webhook "success" "部署完成" echo ""; log_info "========== 部署完成!==========" echo -e " Git: ${GIT_VERSION}"; echo -e " 环境: ${APP_ENV}" if [ -n "$BG_TARGET" ]; then echo -e " 目标: ${BG_TARGET} (:${BG_PORT})" echo -e "" echo -e " ${YELLOW}自动切换代理...${NC}" if bash "${SCRIPT_DIR}/scripts/switch-env.sh" --switch "${BG_TARGET}" 2>&1; then log_info "代理已切换至 ${BG_TARGET} (:${BG_PORT})" else log_warn "代理切换失败,请手动执行: ./scripts/switch-env.sh --switch ${BG_TARGET}" fi fi echo "" [ "$POST_DEPLOY_LOGS" = true ] && { echo ""; view_logs; } else log_error "========== 部署失败,已自动回滚 ==========" exit 1 fi exit 0