#!/bin/bash # ============================================= # Vue前端应用自动部署脚本 # 功能:从Git拉取最新代码 → 打包编译 → 备份当前部署 → 部署新版本到nginx # 支持多环境:dev/test/prod # ============================================= # 设置颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color # 日志函数 log_info() { echo -e "${GREEN}[INFO] $1${NC}" } log_warn() { echo -e "${YELLOW}[WARN] $1${NC}" } log_error() { echo -e "${RED}[ERROR] $1${NC}" } log_step() { echo -e "${BLUE}[STEP] $1${NC}" } log_env() { echo -e "${PURPLE}[ENV] $1${NC}" } log_debug() { echo -e "${CYAN}[DEBUG] $1${NC}" } # ================= 配置区域 ================= # 环境配置 (可通过命令行参数覆盖) ENVIRONMENT="dev" # 默认环境: dev/test/prod # Git配置 GIT_USER="WangXuMing" GIT_PASS="123456" # 环境相关配置 declare -A ENV_CONFIG # dev环境配置 ENV_CONFIG["dev,branch"]="dev" ENV_CONFIG["dev,npm_build_cmd"]="build:dev" ENV_CONFIG["dev,dist_subdir"]="dist-dev" ENV_CONFIG["dev,backup_prefix"]="frontend_dev" # test环境配置 ENV_CONFIG["test,branch"]="test" ENV_CONFIG["test,npm_build_cmd"]="build:test" ENV_CONFIG["test,dist_subdir"]="dist-test" ENV_CONFIG["test,backup_prefix"]="frontend_test" # prod环境配置 ENV_CONFIG["prod,branch"]="master" ENV_CONFIG["prod,npm_build_cmd"]="build:prod" ENV_CONFIG["prod,dist_subdir"]="dist" ENV_CONFIG["prod,backup_prefix"]="frontend_prod" # 公共配置 SOURCE_DIR="/home/lq/lq_workspace/LQAdminFrontServer/source/LQAdminFront" NGINX_HTML_DIR="/home/lq/nginx/html" # nginx部署目录 BACKUP_DIR="/home/lq/lq_workspace/deploy_workspace/deploy_bak/LQAdminFrontServer" # 备份目录 TEMP_DEPLOY_DIR="/home/lq/lq_workspace/LQAdminFrontServer/deploy_tmp" # 临时部署目录 # Node.js环境检查 NODE_VERSION=">=14.0.0" NPM_VERSION=">=6.0.0" # ================= 功能函数 ================= # 显示使用帮助 show_help() { echo "==================================================" echo " Vue前端应用自动部署脚本" echo "==================================================" echo "" echo "使用方法:" echo " $0 [环境参数]" echo "" echo "环境参数:" echo " dev 开发环境 (默认)" echo " test 测试环境" echo " prod 生产环境" echo " -h, --help 显示帮助信息" echo "" echo "示例:" echo " $0 # 部署开发环境" echo " $0 test # 部署测试环境" echo " $0 prod # 部署生产环境" echo "" echo "环境配置:" echo " 开发环境: 分支=dev, 构建命令=npm run build:dev" echo " 测试环境: 分支=test, 构建命令=npm run build:test" echo " 生产环境: 分支=master, 构建命令=npm run build:prod" echo "" exit 0 } # 解析命令行参数 parse_arguments() { if [ $# -gt 0 ]; then case "$1" in -h|--help) show_help ;; dev|test|prod) ENVIRONMENT="$1" ;; *) log_error "未知的环境参数: $1" show_help ;; esac fi } # 获取环境配置 get_env_config() { local key="$1" local full_key="${ENVIRONMENT},${key}" if [ -n "${ENV_CONFIG[$full_key]}" ]; then echo "${ENV_CONFIG[$full_key]}" else log_error "未找到环境配置: $full_key" exit 1 fi } # 初始化环境变量 init_environment() { log_env "初始化 $ENVIRONMENT 环境配置..." # 获取环境配置 GIT_BRANCH=$(get_env_config "branch") NPM_BUILD_CMD=$(get_env_config "npm_build_cmd") DIST_SUBDIR=$(get_env_config "dist_subdir") BACKUP_PREFIX=$(get_env_config "backup_prefix") # 构建输出目录 DIST_DIR="$SOURCE_DIR/$DIST_SUBDIR" # 显示环境配置 log_info "==================================================" log_info "环境: $ENVIRONMENT" log_info "Git分支: $GIT_BRANCH" log_info "构建命令: npm run $NPM_BUILD_CMD" log_info "打包目录: $DIST_DIR" log_info "备份前缀: $BACKUP_PREFIX" log_info "==================================================" } # 检查命令是否存在 check_command() { if ! command -v "$1" &> /dev/null; then log_error "命令 $1 未安装,请先安装" return 1 fi return 0 } # 检查目录是否存在,不存在则创建 check_and_create_dir() { if [ ! -d "$1" ]; then log_warn "目录不存在,正在创建: $1" mkdir -p "$1" if [ $? -eq 0 ]; then log_info "目录创建成功: $1" else log_error "目录创建失败: $1" exit 1 fi fi } # 检查环境 check_environment() { log_step "检查部署环境..." # 检查必要命令 local required_commands=("git" "node" "npm" "unzip") for cmd in "${required_commands[@]}"; do if ! check_command "$cmd"; then exit 1 fi done # 检查Node.js版本 local node_version=$(node --version | sed 's/v//') local npm_version=$(npm --version) log_info "Node.js版本: $node_version" log_info "npm版本: $npm_version" # 检查package.json中的构建脚本 if [ -f "$SOURCE_DIR/package.json" ]; then log_info "检查package.json构建脚本..." # 检查当前环境对应的构建命令是否存在 if ! grep -q "\"$NPM_BUILD_CMD\"" "$SOURCE_DIR/package.json"; then log_warn "未在package.json中找到构建脚本: $NPM_BUILD_CMD" log_info "可用的构建脚本:" grep '"build' "$SOURCE_DIR/package.json" | sed 's/^/ /' # 尝试使用默认的build命令 if grep -q '"build"' "$SOURCE_DIR/package.json"; then log_warn "将使用默认的 build 命令" NPM_BUILD_CMD="build" else log_error "未找到可用的构建脚本" exit 1 fi fi else log_warn "未找到package.json文件,将在后续步骤中检查" fi # 检查目录 check_and_create_dir "$SOURCE_DIR" check_and_create_dir "$BACKUP_DIR" check_and_create_dir "$TEMP_DEPLOY_DIR" check_and_create_dir "$NGINX_HTML_DIR" log_info "环境检查完成" } # Git拉取最新代码 git_pull_latest() { log_step "步骤 1: 从Git拉取最新代码 ($GIT_BRANCH 分支)..." if [ ! -d "$SOURCE_DIR" ]; then log_error "源码目录不存在: $SOURCE_DIR" exit 1 fi cd "$SOURCE_DIR" || { log_error "进入源码目录失败: $SOURCE_DIR" exit 1 } # 检查是否为Git仓库 if [ ! -d ".git" ]; then log_error "当前目录不是Git仓库: $SOURCE_DIR" exit 1 fi # 获取当前分支 local current_branch=$(git branch --show-current 2>/dev/null) log_info "当前分支: ${current_branch:-未设置}" # 检查本地修改 log_info "检查本地修改..." local has_changes=$(git status --porcelain) if [ -n "$has_changes" ]; then log_warn "发现本地修改:" echo "$has_changes" | while read line; do log_info " - $line" done # 询问是否暂存修改 if [ "$ENVIRONMENT" == "prod" ]; then # 生产环境必须清理修改 log_info "生产环境:强制清理本地修改..." git checkout -- . git clean -fd else read -p "是否暂存这些修改?(y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then git stash log_info "本地修改已暂存" else log_warn "跳过暂存,继续执行..." fi fi fi # 获取远程URL local origin_url=$(git remote get-url origin 2>/dev/null) if [ $? -ne 0 ]; then log_error "获取Git远程地址失败" exit 1 fi # 构造认证URL local clean_url=${origin_url#*://} local auth_url="http://${GIT_USER}:${GIT_PASS}@${clean_url}" # 拉取最新代码 log_info "正在切换到分支: $GIT_BRANCH" git checkout "$GIT_BRANCH" 2>/dev/null || { log_warn "分支 $GIT_BRANCH 不存在,正在创建并跟踪远程分支..." git fetch origin "$GIT_BRANCH" git checkout -b "$GIT_BRANCH" "origin/$GIT_BRANCH" || { log_error "无法切换到分支 $GIT_BRANCH" exit 1 } } local max_retries=3 local retry_count=0 local pull_success=0 while [ $retry_count -lt $max_retries ]; do log_info "尝试拉取代码 (第 $((retry_count+1)) 次)..." git pull "$auth_url" "$GIT_BRANCH" --force --allow-unrelated-histories if [ $? -eq 0 ]; then pull_success=1 break fi retry_count=$((retry_count+1)) log_warn "拉取失败,3秒后重试..." sleep 3 done if [ $pull_success -eq 1 ]; then # 显示最新提交信息 local latest_commit=$(git log -1 --format="%h - %an - %ad - %s" --date=format:"%Y-%m-%d %H:%M:%S") log_info "✓ Git拉取成功" log_info "最新提交: $latest_commit" # 显示最近3次提交 log_info "最近3次提交记录:" git log -3 --format=" %h - %ad - %s" --date=format:"%m-%d %H:%M" | while read line; do log_info "$line" done # 显示分支信息 log_info "当前分支状态:" git log --oneline -5 --graph --decorate else log_error "Git拉取失败,已达到最大重试次数" exit 1 fi } # 安装依赖和打包 build_project() { log_step "步骤 2: 安装依赖并打包项目 ($NPM_BUILD_CMD)..." cd "$SOURCE_DIR" || { log_error "进入源码目录失败" exit 1 } # 检查package.json是否存在 if [ ! -f "package.json" ]; then log_error "未找到package.json文件" exit 1 fi # 显示当前环境变量(可选) if [ "$ENVIRONMENT" == "prod" ]; then log_info "生产环境构建,将使用生产配置" # 可以在这里设置生产环境的环境变量 export NODE_ENV=production elif [ "$ENVIRONMENT" == "test" ]; then log_info "测试环境构建" export NODE_ENV=test else log_info "开发环境构建" export NODE_ENV=development fi # 显示构建配置 log_info "当前NODE_ENV: ${NODE_ENV:-未设置}" # 安装依赖 log_info "正在安装依赖..." # 检查是否有package-lock.json或yarn.lock if [ -f "package-lock.json" ]; then log_info "使用 package-lock.json 安装依赖" npm ci --registry=https://registry.npm.taobao.org else log_info "使用 package.json 安装依赖" npm install --registry=https://registry.npm.taobao.org fi if [ $? -ne 0 ]; then log_error "依赖安装失败" exit 1 fi log_info "✓ 依赖安装成功" # 执行打包 log_info "正在执行构建命令: npm run $NPM_BUILD_CMD" # 记录构建开始时间 local build_start=$(date +%s) # 执行构建命令 npm run "$NPM_BUILD_CMD" local build_exit_code=$? # 记录构建结束时间 local build_end=$(date +%s) local build_duration=$((build_end - build_start)) if [ $build_exit_code -eq 0 ]; then log_info "✓ 项目构建成功 (耗时: ${build_duration}秒)" # 检查打包结果 if [ -d "$DIST_DIR" ]; then local file_count=$(find "$DIST_DIR" -type f | wc -l) local dir_size=$(du -sh "$DIST_DIR" | cut -f1) log_info "打包目录: $DIST_DIR" log_info "文件数量: $file_count 个" log_info "目录大小: $dir_size" # 显示关键文件 log_info "关键文件检查:" if [ -f "$DIST_DIR/index.html" ]; then local html_size=$(du -h "$DIST_DIR/index.html" | cut -f1) log_info "✓ index.html (${html_size})" else log_error "✗ 未找到index.html" exit 1 fi if [ -d "$DIST_DIR/assets" ]; then local assets_count=$(find "$DIST_DIR/assets" -type f | wc -l) local assets_size=$(du -sh "$DIST_DIR/assets" | cut -f1) log_info "✓ assets目录 (${assets_count}个文件, ${assets_size})" else log_warn "⚠ 未找到assets目录" fi # 显示主要文件类型统计 log_info "文件类型统计:" local js_count=$(find "$DIST_DIR" -name "*.js" -type f | wc -l) local css_count=$(find "$DIST_DIR" -name "*.css" -type f | wc -l) local img_count=$(find "$DIST_DIR" \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" -o -name "*.webp" \) -type f | wc -l) log_info " JS文件: $js_count 个" log_info " CSS文件: $css_count 个" log_info " 图片文件: $img_count 个" else log_error "打包目录不存在: $DIST_DIR" exit 1 fi else log_error "项目构建失败 (退出码: $build_exit_code)" exit 1 fi } # 备份当前部署 backup_current_deployment() { log_step "步骤 3: 备份当前部署..." # 检查nginx目录是否为空 if [ ! -d "$NGINX_HTML_DIR" ] || [ -z "$(ls -A $NGINX_HTML_DIR 2>/dev/null)" ]; then log_warn "nginx部署目录为空,跳过备份" return 0 fi # 创建备份 local current_time=$(date "+%Y%m%d_%H%M%S") local backup_filename="${BACKUP_PREFIX}_${current_time}.zip" local backup_path="$BACKUP_DIR/$backup_filename" cd "$NGINX_HTML_DIR" || { log_error "进入nginx目录失败" exit 1 } # 检查是否有需要备份的文件 if [ ! -f "index.html" ] && [ ! -d "assets" ]; then log_warn "未找到index.html和assets目录,跳过备份" return 0 fi log_info "正在创建备份文件: $backup_filename" # 记录备份开始时间 local backup_start=$(date +%s) # 创建备份压缩包(排除隐藏文件和临时文件) find . -maxdepth 1 -type f -name "*.html" -o -name "*.js" -o -name "*.css" | zip -q "$backup_path" -@ if [ -d "assets" ]; then zip -qr "$backup_path" assets fi local backup_exit_code=$? # 记录备份结束时间 local backup_end=$(date +%s) local backup_duration=$((backup_end - backup_start)) if [ $backup_exit_code -eq 0 ]; then local backup_size=$(du -h "$backup_path" | cut -f1) log_info "✓ 备份创建成功 (耗时: ${backup_duration}秒)" log_info "备份文件: $backup_path" log_info "文件大小: $backup_size" # 显示备份目录信息 local backup_files=$(ls -1 "$BACKUP_DIR/${BACKUP_PREFIX}_"*.zip 2>/dev/null | wc -l) log_info "当前环境备份文件数: $backup_files 个" # 如果备份文件超过10个,删除最旧的5个 if [ $backup_files -gt 10 ]; then log_info "清理旧备份文件..." ls -1t "$BACKUP_DIR/${BACKUP_PREFIX}_"*.zip | tail -n 5 | xargs -I {} rm -f {} log_info "已清理5个旧备份" fi # 显示所有环境的备份统计 log_info "所有环境备份统计:" local total_backups=$(ls -1 "$BACKUP_DIR/"*.zip 2>/dev/null | wc -l) log_info " 总备份数: $total_backups 个" for env in dev test prod; do local env_backups=$(ls -1 "$BACKUP_DIR/frontend_${env}_"*.zip 2>/dev/null | wc -l) if [ $env_backups -gt 0 ]; then log_info " ${env}: $env_backups 个" fi done else log_error "备份创建失败" exit 1 fi } # 准备部署文件 prepare_deployment() { log_step "步骤 4: 准备部署文件..." # 清理临时部署目录 log_info "清理临时部署目录..." rm -rf "$TEMP_DEPLOY_DIR"/* # 复制打包文件到临时目录 log_info "从 $DIST_DIR 复制文件到 $TEMP_DEPLOY_DIR..." cp -r "$DIST_DIR"/* "$TEMP_DEPLOY_DIR/" if [ $? -eq 0 ]; then local temp_file_count=$(find "$TEMP_DEPLOY_DIR" -type f | wc -l) local temp_dir_size=$(du -sh "$TEMP_DEPLOY_DIR" | cut -f1) log_info "✓ 部署文件准备完成" log_info "临时目录文件数量: $temp_file_count 个" log_info "临时目录大小: $temp_dir_size" log_info "临时目录: $TEMP_DEPLOY_DIR" # 显示临时目录结构 if [ "$ENVIRONMENT" == "dev" ]; then log_debug "临时目录结构:" ls -la "$TEMP_DEPLOY_DIR" | head -20 fi else log_error "部署文件准备失败" exit 1 fi } # 部署到Nginx deploy_to_nginx() { log_step "步骤 5: 部署到Nginx ($ENVIRONMENT 环境)..." # 检查Nginx目录权限 if [ ! -w "$NGINX_HTML_DIR" ]; then log_error "Nginx目录无写权限: $NGINX_HTML_DIR" exit 1 fi # 清理nginx目录(先备份到临时目录) log_info "准备部署到Nginx目录: $NGINX_HTML_DIR" # 创建临时备份目录 local nginx_backup_dir="/tmp/nginx_${ENVIRONMENT}_backup_$(date +%Y%m%d_%H%M%S)" mkdir -p "$nginx_backup_dir" # 备份当前文件(如果有) if [ -n "$(ls -A $NGINX_HTML_DIR 2>/dev/null)" ]; then log_info "备份当前Nginx文件到: $nginx_backup_dir" mv "$NGINX_HTML_DIR"/* "$nginx_backup_dir/" 2>/dev/null || true fi # 复制新文件 log_info "复制新版本文件到Nginx..." cp -r "$TEMP_DEPLOY_DIR"/* "$NGINX_HTML_DIR/" if [ $? -eq 0 ]; then log_info "✓ 文件复制完成" # 设置文件权限 log_info "设置文件权限..." chmod -R 755 "$NGINX_HTML_DIR" chown -R lq:lq "$NGINX_HTML_DIR" 2>/dev/null || { log_warn "无法更改文件所有者,跳过此步骤" } # 验证部署 log_info "验证部署结果..." local verification_passed=1 if [ -f "$NGINX_HTML_DIR/index.html" ]; then local index_size=$(du -h "$NGINX_HTML_DIR/index.html" | cut -f1) log_info "✓ index.html 存在 (${index_size})" else log_error "✗ index.html 不存在" verification_passed=0 fi if [ -d "$NGINX_HTML_DIR/assets" ]; then local asset_count=$(find "$NGINX_HTML_DIR/assets" -type f | wc -l) log_info "✓ assets 目录存在,包含 $asset_count 个文件" else log_warn "⚠ assets 目录不存在" fi # 检查静态文件 local static_files=("favicon.ico" "manifest.json" "robots.txt") for file in "${static_files[@]}"; do if [ -f "$NGINX_HTML_DIR/$file" ]; then log_info "✓ $file 存在" fi done if [ $verification_passed -eq 0 ]; then log_error "部署验证失败" # 恢复备份 log_info "正在恢复备份..." rm -rf "$NGINX_HTML_DIR"/* if [ -n "$(ls -A $nginx_backup_dir 2>/dev/null)" ]; then cp -r "$nginx_backup_dir"/* "$NGINX_HTML_DIR/" log_info "已恢复原文件" fi exit 1 fi log_info "✓ 部署验证通过" # 清理临时备份 rm -rf "$nginx_backup_dir" log_info "临时备份已清理" else log_error "文件复制失败" # 恢复备份 log_info "正在恢复备份..." if [ -n "$(ls -A $nginx_backup_dir 2>/dev/null)" ]; then rm -rf "$NGINX_HTML_DIR"/* cp -r "$nginx_backup_dir"/* "$NGINX_HTML_DIR/" log_info "已恢复原文件" fi exit 1 fi } # 清理临时文件 cleanup_temp_files() { log_step "步骤 6: 清理临时文件..." # 清理临时部署目录 log_info "清理临时部署目录..." rm -rf "$TEMP_DEPLOY_DIR"/* log_info "临时部署目录已清理" # 可选:清理node_modules缓存(谨慎使用) if [ "$ENVIRONMENT" == "prod" ]; then log_info "生产环境:清理npm缓存..." npm cache clean --force fi log_info "✓ 清理完成" } # 部署后验证 post_deployment_validation() { log_step "步骤 7: 部署后验证..." log_info "==================== 部署详情 ====================" log_info "部署环境: $ENVIRONMENT" log_info "部署时间: $(date)" log_info "Git分支: $GIT_BRANCH" log_info "构建命令: npm run $NPM_BUILD_CMD" log_info "源码目录: $SOURCE_DIR" log_info "Nginx目录: $NGINX_HTML_DIR" log_info "备份目录: $BACKUP_DIR" # 显示部署文件统计 log_info "部署文件统计:" local html_count=$(find "$NGINX_HTML_DIR" -name "*.html" -type f | wc -l) local js_count=$(find "$NGINX_HTML_DIR" -name "*.js" -type f | wc -l) local css_count=$(find "$NGINX_HTML_DIR" -name "*.css" -type f | wc -l) local img_count=$(find "$NGINX_HTML_DIR" \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" -o -name "*.webp" \) -type f | wc -l) local total_files=$(find "$NGINX_HTML_DIR" -type f | wc -l) log_info " HTML文件: $html_count 个" log_info " JS文件: $js_count 个" log_info " CSS文件: $css_count 个" log_info " 图片文件: $img_count 个" log_info " 总文件数: $total_files 个" # 显示目录大小 local dir_size=$(du -sh "$NGINX_HTML_DIR" | cut -f1) log_info " 目录大小: $dir_size" # 显示关键文件信息 log_info "关键文件:" for file in index.html main.js app.css; do if [ -f "$NGINX_HTML_DIR/$file" ]; then local file_size=$(du -h "$NGINX_HTML_DIR/$file" | cut -f1) log_info " ✓ $file (${file_size})" fi done log_info "==================================================" # 显示最新的Git提交 cd "$SOURCE_DIR" && git log -1 --format="最新提交: %h - %an - %ad - %s" --date=format:"%Y-%m-%d %H:%M:%S" log_info "${GREEN}✅ Vue前端应用 ($ENVIRONMENT 环境) 部署完成!${NC}" # 输出环境特定的建议 echo "" log_info "后续操作建议:" log_info "1. 重新加载Nginx配置: sudo nginx -s reload" case "$ENVIRONMENT" in "prod") log_info "2. 生产环境建议执行: sudo systemctl restart nginx" log_info "3. 访问地址: https://your-production-domain.com" ;; "test") log_info "2. 访问测试地址: http://test.your-domain.com" log_info "3. 测试完成后可执行: $0 prod 部署到生产环境" ;; "dev") log_info "2. 访问开发地址: http://dev.your-domain.com" log_info "3. 查看构建日志: tail -f $SOURCE_DIR/build.log" ;; esac log_info "4. 查看Nginx日志: sudo tail -f /var/log/nginx/error.log" log_info "5. 查看访问日志: sudo tail -f /var/log/nginx/access.log" } # ================= 主程序 ================= main() { # 解析命令行参数 parse_arguments "$@" # 显示部署信息 echo "==================================================" echo " Vue前端应用自动部署脚本" echo " 环境: $ENVIRONMENT" echo "==================================================" # 记录开始时间 local start_time=$(date +%s) log_info "部署开始时间: $(date)" # 初始化环境配置 init_environment # 执行部署流程 check_environment git_pull_latest build_project backup_current_deployment prepare_deployment deploy_to_nginx cleanup_temp_files post_deployment_validation # 计算执行时间 local end_time=$(date +%s) local duration=$((end_time - start_time)) local minutes=$((duration / 60)) local seconds=$((duration % 60)) log_info "总执行时间: ${duration}秒 (${minutes}分${seconds}秒)" echo "" log_info "${GREEN}🎉 $ENVIRONMENT 环境所有部署步骤已完成!${NC}" # 添加完成时间戳 log_info "部署完成时间: $(date)" } # 脚本入口 main "$@"