314 lines
9.2 KiB
Bash
Executable File
314 lines
9.2 KiB
Bash
Executable File
#!/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 "$@"
|