|
@@ -0,0 +1,242 @@
|
|
|
|
|
+#!/bin/bash
|
|
|
|
|
+
|
|
|
|
|
+# ================= 配置区域 =================
|
|
|
|
|
+# 源代码路径
|
|
|
|
|
+SOURCE_DIR="/home/lq/lq_workspace/LQAgentServer/source/LQAgentPlatform"
|
|
|
|
|
+# Docker Compose 运行路径
|
|
|
|
|
+DOCKER_APP_DIR="/home/lq/lq_workspace/LQAgentServer/app/docker"
|
|
|
|
|
+# 配置文件名称
|
|
|
|
|
+COMPOSE_FILE="docker-compose.yml"
|
|
|
|
|
+# 镜像名称 (Repository)
|
|
|
|
|
+IMAGE_NAME="lq_agent_platform_server_dev"
|
|
|
|
|
+# Git 凭证
|
|
|
|
|
+GIT_USER="WangXuMing"
|
|
|
|
|
+GIT_PASS="123456"
|
|
|
|
|
+
|
|
|
|
|
+# ================= 辅助函数 =================
|
|
|
|
|
+# 打印带时间戳的日志
|
|
|
|
|
+log_info() {
|
|
|
|
|
+ echo -e "\033[32m[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1\033[0m"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+log_error() {
|
|
|
|
|
+ echo -e "\033[31m[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1\033[0m"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# 检查命令执行状态,如果失败则退出
|
|
|
|
|
+check_status() {
|
|
|
|
|
+ if [ $? -ne 0 ]; then
|
|
|
|
|
+ log_error "$1 执行失败,脚本终止。"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ fi
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 1: Git 拉取代码 (带重试+强制拉取) =================
|
|
|
|
|
+log_info "步骤 1: 进入源码目录并拉取最新代码..."
|
|
|
|
|
+
|
|
|
|
|
+if [ ! -d "$SOURCE_DIR" ]; then
|
|
|
|
|
+ log_error "源码目录不存在: $SOURCE_DIR"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# 检查目录进入权限并修复
|
|
|
|
|
+if [ ! -x "$SOURCE_DIR" ]; then
|
|
|
|
|
+ log_error "源码目录无进入权限!正在修复..."
|
|
|
|
|
+ sudo chmod +x "$SOURCE_DIR"
|
|
|
|
|
+ sudo chown -R lq:lq "$SOURCE_DIR"
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+cd "$SOURCE_DIR" || {
|
|
|
|
|
+ log_error "进入源码目录失败!路径:$SOURCE_DIR"
|
|
|
|
|
+ log_error "可能原因:1. 目录权限不足 2. 路径含特殊字符 3. 目录被删除"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+}
|
|
|
|
|
+check_status "进入源码目录" # 双重保障
|
|
|
|
|
+
|
|
|
|
|
+# 检查是否为 Git 仓库
|
|
|
|
|
+if [ ! -d ".git" ]; then
|
|
|
|
|
+ log_error "当前目录不是 Git 仓库!路径:$SOURCE_DIR"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+log_info "检查本地是否存在可能与远程冲突的已修改文件..."
|
|
|
|
|
+
|
|
|
|
|
+HAS_CONFLICT_FILES=$(git status --porcelain | grep -v "^??")
|
|
|
|
|
+
|
|
|
|
|
+if [ -n "$HAS_CONFLICT_FILES" ]; then
|
|
|
|
|
+ log_info "发现以下文件存在本地修改(将被远程最新代码覆盖):"
|
|
|
|
|
+ echo "$HAS_CONFLICT_FILES" | awk '{print " - " $2}'
|
|
|
|
|
+ log_info "正在强制丢弃本地修改,确保同步远程最新代码..."
|
|
|
|
|
+
|
|
|
|
|
+ # 强制丢弃修改
|
|
|
|
|
+ git checkout -- . # 仅丢弃已跟踪文件的本地修改(冲突风险文件)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ log_info "本地冲突文件修改已丢弃,准备拉取远程最新代码..."
|
|
|
|
|
+else
|
|
|
|
|
+ log_info "本地无可能冲突的已修改文件,直接拉取远程最新代码..."
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# 组装 Git 认证 URL(保留原逻辑)
|
|
|
|
|
+ORIGIN_URL=$(git remote get-url origin 2>/dev/null)
|
|
|
|
|
+if [ $? -ne 0 ]; then
|
|
|
|
|
+ log_error "获取 Git 远程地址失败!请检查 remote 配置"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# 初始化认证 URL(默认使用 origin 远程)
|
|
|
|
|
+CLEAN_URL=${ORIGIN_URL#*://}
|
|
|
|
|
+AUTH_URL="http://${GIT_USER}:${GIT_PASS}@${CLEAN_URL}"
|
|
|
|
|
+# 定义备用远程(upstream)及认证 URL
|
|
|
|
|
+UPSTREAM_URL=$(git remote get-url upstream 2>/dev/null)
|
|
|
|
|
+if [ $? -ne 0 ]; then
|
|
|
|
|
+ log_warn "未配置 upstream 远程,503 时无法切换备用源"
|
|
|
|
|
+ UPSTREAM_AVAILABLE=0
|
|
|
|
|
+else
|
|
|
|
|
+ UPSTREAM_CLEAN_URL=${UPSTREAM_URL#*://}
|
|
|
|
|
+ UPSTREAM_AUTH_URL="http://${GIT_USER}:${GIT_PASS}@${UPSTREAM_CLEAN_URL}"
|
|
|
|
|
+ UPSTREAM_AVAILABLE=1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+MAX_RETRIES=3
|
|
|
|
|
+COUNT=0
|
|
|
|
|
+GIT_SUCCESS=0
|
|
|
|
|
+CURRENT_AUTH_URL="$AUTH_URL" # 当前使用的认证 URL
|
|
|
|
|
+
|
|
|
|
|
+while [ $COUNT -lt $MAX_RETRIES ]; do
|
|
|
|
|
+ log_info "正在执行 Git Pull (第 $((COUNT+1)) 次尝试) - 强制拉取 dev 分支最新代码..."
|
|
|
|
|
+ log_info "当前使用远程地址:${CURRENT_AUTH_URL}"
|
|
|
|
|
+
|
|
|
|
|
+ # 执行 git pull 并捕获错误输出
|
|
|
|
|
+ PULL_OUTPUT=$(git pull "$CURRENT_AUTH_URL" dev --force --allow-unrelated-histories 2>&1)
|
|
|
|
|
+ PULL_EXIT_CODE=$?
|
|
|
|
|
+
|
|
|
|
|
+ if [ $PULL_EXIT_CODE -eq 0 ]; then
|
|
|
|
|
+ # 拉取成功:输出结果并退出循环
|
|
|
|
|
+ GIT_SUCCESS=1
|
|
|
|
|
+ LATEST_COMMIT=$(git log -1 --format="%h - %s ")
|
|
|
|
|
+ log_info "Git Pull 成功!当前部署提交版本:$LATEST_COMMIT"
|
|
|
|
|
+ break
|
|
|
|
|
+ else
|
|
|
|
|
+ # 拉取失败:判断错误类型(新增 returned error: 503 匹配规则)
|
|
|
|
|
+ if echo "$PULL_OUTPUT" | grep -qiE "503 Service Unavailable|503 Unavailable|returned error: 503" && [ $UPSTREAM_AVAILABLE -eq 1 ]; then
|
|
|
|
|
+ # 错误类型:503 服务不可用 + 有备用 upstream 远程
|
|
|
|
|
+ log_error "Git Pull 失败:当前远程(origin)返回 503 不可达,切换到备用远程(upstream)重试..."
|
|
|
|
|
+ log_error "错误详情:$PULL_OUTPUT"
|
|
|
|
|
+ CURRENT_AUTH_URL="$UPSTREAM_AUTH_URL" # 切换为 upstream 认证 URL
|
|
|
|
|
+ COUNT=$((COUNT+1))
|
|
|
|
|
+ sleep 3
|
|
|
|
|
+ elif echo "$PULL_OUTPUT" | grep -qiE "503 Service Unavailable|503 Unavailable|returned error: 503" && [ $UPSTREAM_AVAILABLE -eq 0 ]; then
|
|
|
|
|
+ # 错误类型:503 但无备用源
|
|
|
|
|
+ log_error "Git Pull 失败:远程返回 503 不可达,但未配置 upstream 备用源,无法切换..."
|
|
|
|
|
+ log_error "错误详情:$PULL_OUTPUT"
|
|
|
|
|
+ COUNT=$((COUNT+1))
|
|
|
|
|
+ sleep 3
|
|
|
|
|
+ else
|
|
|
|
|
+ # 其他错误(如认证失败、网络不通、分支不存在等):按原逻辑重试
|
|
|
|
|
+ log_error "Git Pull 失败(非 503 错误),准备重试..."
|
|
|
|
|
+ log_error "错误详情:$PULL_OUTPUT"
|
|
|
|
|
+ COUNT=$((COUNT+1))
|
|
|
|
|
+ sleep 3
|
|
|
|
|
+ fi
|
|
|
|
|
+ fi
|
|
|
|
|
+done
|
|
|
|
|
+
|
|
|
|
|
+# 所有重试失败后的处理
|
|
|
|
|
+if [ $GIT_SUCCESS -eq 0 ]; then
|
|
|
|
|
+ log_error "Git Pull 已重试 $MAX_RETRIES 次,全部失败!"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 2: 关闭当前容器 =================
|
|
|
|
|
+log_info "步骤 2: 关闭正在运行的容器..."
|
|
|
|
|
+
|
|
|
|
|
+if [ ! -d "$DOCKER_APP_DIR" ]; then
|
|
|
|
|
+ log_error "Docker 运行目录不存在: $DOCKER_APP_DIR"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+cd "$DOCKER_APP_DIR"
|
|
|
|
|
+check_status "进入 Docker 运行目录"
|
|
|
|
|
+
|
|
|
|
|
+docker compose down
|
|
|
|
|
+# 即使 down 失败(例如没启动),也继续执行,只记录错误
|
|
|
|
|
+if [ $? -ne 0 ]; then
|
|
|
|
|
+ log_error "警告: Docker Compose Down 返回非零状态,尝试继续..."
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 3: 提取版本号并删除旧镜像 =================
|
|
|
|
|
+log_info "步骤 3: 查找旧镜像并计算新版本号..."
|
|
|
|
|
+
|
|
|
|
|
+# 获取镜像信息,例如: lq_agent_platform_server v0.13 76d87fcfb5e5
|
|
|
|
|
+IMAGE_INFO=$(docker images | grep "^${IMAGE_NAME} " | awk '{print $2, $3}' | head -n 1)
|
|
|
|
|
+
|
|
|
|
|
+NEW_TAG="v0.01" # 默认初始版本
|
|
|
|
|
+
|
|
|
|
|
+if [ -n "$IMAGE_INFO" ]; then
|
|
|
|
|
+ OLD_TAG=$(echo "$IMAGE_INFO" | awk '{print $1}')
|
|
|
|
|
+ IMAGE_ID=$(echo "$IMAGE_INFO" | awk '{print $2}')
|
|
|
|
|
+
|
|
|
|
|
+ log_info "找到旧镜像: Tag=$OLD_TAG, ID=$IMAGE_ID"
|
|
|
|
|
+
|
|
|
|
|
+ # 提取版本号数字 (去掉 'v'),例如 v0.13 -> 0.13
|
|
|
|
|
+ VERSION_NUM=$(echo "$OLD_TAG" | sed 's/v//')
|
|
|
|
|
+
|
|
|
|
|
+ # 计算新版本号 (这里设置为 +0.01,即 0.13 -> 0.14)
|
|
|
|
|
+ NEW_VERSION_NUM=$(echo "$VERSION_NUM" | awk '{printf "%.2f", $1 + 0.01}')
|
|
|
|
|
+ NEW_TAG="v$NEW_VERSION_NUM"
|
|
|
|
|
+
|
|
|
|
|
+ log_info "计算出的新版本号为: $NEW_TAG"
|
|
|
|
|
+
|
|
|
|
|
+ log_info "正在删除旧镜像 ID: $IMAGE_ID ..."
|
|
|
|
|
+ docker rmi -f "$IMAGE_ID"
|
|
|
|
|
+ if [ $? -ne 0 ]; then
|
|
|
|
|
+ log_error "警告: 删除旧镜像失败,可能被占用,将继续构建。"
|
|
|
|
|
+ else
|
|
|
|
|
+ log_info "旧镜像删除成功。"
|
|
|
|
|
+ fi
|
|
|
|
|
+else
|
|
|
|
|
+ log_info "未找到旧镜像,将使用默认版本 $NEW_TAG 构建。"
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 4: 构建新镜像 =================
|
|
|
|
|
+log_info "步骤 4: 构建新镜像 $IMAGE_NAME:$NEW_TAG ..."
|
|
|
|
|
+
|
|
|
|
|
+cd "$SOURCE_DIR"
|
|
|
|
|
+check_status "返回源码目录"
|
|
|
|
|
+
|
|
|
|
|
+docker build -t "${IMAGE_NAME}:${NEW_TAG}" .
|
|
|
|
|
+check_status "镜像构建"
|
|
|
|
|
+log_info "镜像构建成功: ${IMAGE_NAME}:${NEW_TAG}"
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 5: 修改 docker-compose.yml 版本号 =================
|
|
|
|
|
+log_info "步骤 5: 更新 docker-compose.yml 中的版本号..."
|
|
|
|
|
+
|
|
|
|
|
+cd "$DOCKER_APP_DIR"
|
|
|
|
|
+check_status "进入 Docker 运行目录"
|
|
|
|
|
+
|
|
|
|
|
+if [ ! -f "$COMPOSE_FILE" ]; then
|
|
|
|
|
+ log_error "找不到配置文件: $COMPOSE_FILE"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+fi
|
|
|
|
|
+
|
|
|
|
|
+# 使用 sed 正则替换
|
|
|
|
|
+# 匹配: image: lq_agent_platform_server:任意字符
|
|
|
|
|
+# 替换为: image: lq_agent_platform_server:新版本号
|
|
|
|
|
+sed -i "s|image: ${IMAGE_NAME}:.*|image: ${IMAGE_NAME}:${NEW_TAG}|" "$COMPOSE_FILE"
|
|
|
|
|
+check_status "修改 docker-compose.yml"
|
|
|
|
|
+
|
|
|
|
|
+# 验证修改结果
|
|
|
|
|
+MATCH_LINE=$(grep "image: ${IMAGE_NAME}:" "$COMPOSE_FILE")
|
|
|
|
|
+log_info "配置文件已更新: $MATCH_LINE"
|
|
|
|
|
+
|
|
|
|
|
+# ================= 步骤 6: 启动容器 =================
|
|
|
|
|
+log_info "步骤 6: 启动 Docker Compose..."
|
|
|
|
|
+
|
|
|
|
|
+docker compose up -d
|
|
|
|
|
+check_status "启动容器"
|
|
|
|
|
+
|
|
|
|
|
+log_info "===================================================="
|
|
|
|
|
+log_info " 开发版部署成功!当前运行端口8002,部署版本: $NEW_TAG"
|
|
|
|
|
+log_info "===================================================="
|