#!/bin/bash ############################################################################### # 服务器端部署脚本 # 此脚本在服务器上执行,由本地部署脚本远程调用 ############################################################################### set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' ############################################################################### # 配置变量(通过环境变量传入) ############################################################################### PROJECT_PATH="${REMOTE_PROJECT_PATH:-/home/ubuntu/vf_react}" PRODUCTION_PATH="${PRODUCTION_PATH:-/var/www/valuefrontier.cn}" BACKUP_DIR="${BACKUP_DIR:-/home/ubuntu/deployments}" LOG_DIR="${LOG_DIR:-/home/ubuntu/deploy-logs}" DEPLOY_BRANCH="${DEPLOY_BRANCH:-feature}" KEEP_BACKUPS="${KEEP_BACKUPS:-5}" RUN_NPM_INSTALL="${RUN_NPM_INSTALL:-true}" ############################################################################### # 函数:打印带颜色的消息 ############################################################################### log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } ############################################################################### # 函数:创建必要的目录 ############################################################################### create_directories() { log_info "创建必要的目录..." mkdir -p "$BACKUP_DIR" mkdir -p "$LOG_DIR" mkdir -p "$PRODUCTION_PATH" log_success "目录创建完成" } ############################################################################### # 函数:检查 Git 仓库 ############################################################################### check_git_repo() { log_info "检查 Git 仓库..." if [ ! -d "$PROJECT_PATH/.git" ]; then log_error "Git 仓库不存在: $PROJECT_PATH" exit 1 fi cd "$PROJECT_PATH" log_success "Git 仓库检查通过" } ############################################################################### # 函数:切换到目标分支 ############################################################################### checkout_branch() { log_info "切换到 $DEPLOY_BRANCH 分支..." cd "$PROJECT_PATH" # 获取当前分支 current_branch=$(git branch --show-current) if [ "$current_branch" != "$DEPLOY_BRANCH" ]; then log_warning "当前分支是 $current_branch,正在切换到 $DEPLOY_BRANCH..." git checkout "$DEPLOY_BRANCH" || { log_error "切换分支失败" exit 1 } fi log_success "已在 $DEPLOY_BRANCH 分支" } ############################################################################### # 函数:拉取最新代码 ############################################################################### pull_latest_code() { log_info "拉取最新代码..." cd "$PROJECT_PATH" # 保存本地修改(如果有) if ! git diff-index --quiet HEAD --; then log_warning "检测到本地修改,正在暂存..." git stash fi # 拉取最新代码 git pull origin "$DEPLOY_BRANCH" || { log_error "拉取代码失败" exit 1 } log_success "代码更新完成" } ############################################################################### # 函数:获取当前提交信息 ############################################################################### get_commit_info() { cd "$PROJECT_PATH" COMMIT_HASH=$(git rev-parse --short HEAD) COMMIT_MESSAGE=$(git log -1 --pretty=%B | head -n 1) COMMIT_AUTHOR=$(git log -1 --pretty=%an) COMMIT_TIME=$(git log -1 --pretty=%cd --date=format:'%Y-%m-%d %H:%M:%S') echo "提交哈希: $COMMIT_HASH" echo "提交信息: $COMMIT_MESSAGE" echo "提交作者: $COMMIT_AUTHOR" echo "提交时间: $COMMIT_TIME" } ############################################################################### # 函数:安装依赖 ############################################################################### install_dependencies() { if [ "$RUN_NPM_INSTALL" = "true" ]; then log_info "安装依赖..." cd "$PROJECT_PATH" # 检查 package.json 是否变化 if git diff HEAD@{1} HEAD --name-only | grep -q "package.json"; then log_info "package.json 有变化,执行 npm install..." npm install || { log_error "依赖安装失败" exit 1 } else log_info "package.json 无变化,跳过 npm install" fi log_success "依赖检查完成" else log_info "跳过依赖安装 (RUN_NPM_INSTALL=false)" fi } ############################################################################### # 函数:构建项目 ############################################################################### build_project() { log_info "构建项目..." cd "$PROJECT_PATH" # 执行构建 npm run build || { log_error "构建失败" exit 1 } # 检查构建产物 if [ ! -d "$PROJECT_PATH/build" ]; then log_error "构建产物不存在" exit 1 fi log_success "构建完成" } ############################################################################### # 函数:备份当前版本 ############################################################################### backup_current_version() { log_info "备份当前版本..." local timestamp=$(date +%Y%m%d-%H%M%S) local backup_path="$BACKUP_DIR/backup-$timestamp" if [ -d "$PRODUCTION_PATH" ] && [ "$(ls -A $PRODUCTION_PATH)" ]; then mkdir -p "$backup_path" cp -r "$PRODUCTION_PATH"/* "$backup_path/" || { log_error "备份失败" exit 1 } # 创建符号链接指向当前版本 ln -snf "$backup_path" "$BACKUP_DIR/current" log_success "备份完成: $backup_path" echo "$backup_path" else log_warning "生产目录为空,跳过备份" echo "no-backup" fi } ############################################################################### # 函数:清理旧备份 ############################################################################### cleanup_old_backups() { log_info "清理旧备份..." cd "$BACKUP_DIR" # 获取所有备份目录(排除 current 符号链接) local backup_count=$(find . -maxdepth 1 -type d -name "backup-*" | wc -l) if [ "$backup_count" -gt "$KEEP_BACKUPS" ]; then local to_delete=$((backup_count - KEEP_BACKUPS)) log_info "当前有 $backup_count 个备份,保留 $KEEP_BACKUPS 个,删除 $to_delete 个" find . -maxdepth 1 -type d -name "backup-*" | sort | head -n "$to_delete" | while read dir; do log_info "删除旧备份: $dir" rm -rf "$dir" done log_success "旧备份清理完成" else log_info "当前有 $backup_count 个备份,无需清理" fi } ############################################################################### # 函数:部署到生产环境 ############################################################################### deploy_to_production() { log_info "部署到生产环境..." # 清空生产目录 log_info "清空生产目录: $PRODUCTION_PATH" rm -rf "$PRODUCTION_PATH"/* # 复制构建产物 log_info "复制构建产物..." cp -r "$PROJECT_PATH/build"/* "$PRODUCTION_PATH/" || { log_error "复制文件失败" exit 1 } # 设置权限 chmod -R 755 "$PRODUCTION_PATH" log_success "部署完成" } ############################################################################### # 主函数 ############################################################################### main() { local start_time=$(date +%s) echo "" echo "========================================" echo " 服务器端部署脚本" echo "========================================" echo "" # 创建目录 create_directories # 检查 Git 仓库 check_git_repo # 切换分支 checkout_branch # 拉取最新代码 pull_latest_code # 获取提交信息 get_commit_info # 安装依赖 install_dependencies # 构建项目 build_project # 备份当前版本 backup_path=$(backup_current_version) # 部署到生产环境 deploy_to_production # 清理旧备份 cleanup_old_backups # 计算耗时 local end_time=$(date +%s) local duration=$((end_time - start_time)) local minutes=$((duration / 60)) local seconds=$((duration % 60)) echo "" echo "========================================" echo " 部署成功!" echo "========================================" echo "提交: $COMMIT_HASH - $COMMIT_MESSAGE" echo "备份: $backup_path" echo "耗时: ${minutes}分${seconds}秒" echo "" # 输出结果供本地脚本解析 echo "DEPLOY_SUCCESS=true" echo "COMMIT_HASH=$COMMIT_HASH" echo "COMMIT_MESSAGE=$COMMIT_MESSAGE" echo "DEPLOY_DURATION=${minutes}分${seconds}秒" } # 执行主函数 main "$@"