|
|
@@ -0,0 +1,807 @@
|
|
|
+#!/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 "$@"
|