- Require non-default WEBHOOK_SECRET\n- Strict main/master ref matching\n- Constant-time HMAC signature check\n- Limit request body and add server timeouts\n- Single-flight deploy lock; pass ref/commit to deploy.sh\n- deploy.sh deploys correct branch (main/master)
147 lines
3.5 KiB
Bash
147 lines
3.5 KiB
Bash
#!/bin/bash
|
||
|
||
# BillAI 自动部署脚本
|
||
# 此脚本由 Gitea webhook 触发,自动执行 git pull 并重新构建部署
|
||
|
||
set -e
|
||
|
||
REPO_ROOT="/app"
|
||
LOG_FILE="/tmp/billai_deploy.log"
|
||
|
||
# 可由 webhook 传入:GIT_REF=refs/heads/main 或 refs/heads/master
|
||
GIT_REF="${GIT_REF:-}"
|
||
GIT_COMMIT="${GIT_COMMIT:-}"
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
log() {
|
||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
error() {
|
||
echo -e "${RED}[错误]${NC} $1" | tee -a "$LOG_FILE"
|
||
exit 1
|
||
}
|
||
|
||
success() {
|
||
echo -e "${GREEN}[成功]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log "=========================================="
|
||
log "🚀 BillAI 自动部署开始"
|
||
log "=========================================="
|
||
|
||
# 进入仓库目录
|
||
cd "$REPO_ROOT" || error "无法进入仓库目录: $REPO_ROOT"
|
||
log "📁 工作目录: $(pwd)"
|
||
|
||
if [ -n "$GIT_REF" ]; then
|
||
log "🌿 触发分支: $GIT_REF"
|
||
fi
|
||
if [ -n "$GIT_COMMIT" ]; then
|
||
log "🧾 触发提交: ${GIT_COMMIT:0:7}"
|
||
fi
|
||
|
||
# 拉取最新代码
|
||
log "📥 正在拉取最新代码..."
|
||
if ! git fetch origin; then
|
||
error "git fetch 失败"
|
||
fi
|
||
|
||
# 选择部署分支(优先使用 webhook 传入的 ref)
|
||
DEPLOY_BRANCH=""
|
||
if [ "$GIT_REF" = "refs/heads/main" ]; then
|
||
DEPLOY_BRANCH="main"
|
||
elif [ "$GIT_REF" = "refs/heads/master" ]; then
|
||
DEPLOY_BRANCH="master"
|
||
fi
|
||
|
||
# 兜底:按远端分支存在性选择(兼容仓库从 master 切到 main)
|
||
if [ -z "$DEPLOY_BRANCH" ]; then
|
||
if git show-ref --verify --quiet refs/remotes/origin/main; then
|
||
DEPLOY_BRANCH="main"
|
||
else
|
||
DEPLOY_BRANCH="master"
|
||
fi
|
||
fi
|
||
|
||
log "🌿 部署分支: $DEPLOY_BRANCH"
|
||
|
||
if ! git reset --hard "origin/$DEPLOY_BRANCH"; then
|
||
error "git reset 失败"
|
||
fi
|
||
|
||
success "代码已更新"
|
||
|
||
# 显示当前提交信息
|
||
log "📝 当前提交:"
|
||
git log --oneline -1
|
||
|
||
# 检查 docker-compose 是否存在
|
||
if [ ! -f "docker-compose.yaml" ]; then
|
||
error "docker-compose.yaml 不存在"
|
||
fi
|
||
|
||
log "🐳 开始热更新部署(不停机)..."
|
||
|
||
# 定义需要重新部署的服务(排除 webhook 自身,否则会自杀)
|
||
SERVICES="web server analyzer"
|
||
|
||
# 拉取基础镜像更新
|
||
log "📦 检查基础镜像更新..."
|
||
docker-compose pull mongodb mongo-express || true
|
||
|
||
# 热更新:重新构建并替换容器(不停止旧服务,直接替换)
|
||
log "🔨 热更新服务(构建 + 替换)..."
|
||
if ! docker-compose up -d --build --force-recreate --no-deps $SERVICES; then
|
||
error "Docker 热更新失败"
|
||
fi
|
||
|
||
# 清理旧的未使用镜像
|
||
log "🧹 清理旧镜像..."
|
||
docker image prune -f || true
|
||
|
||
# 等待服务启动
|
||
log "⏳ 等待服务启动..."
|
||
sleep 5
|
||
|
||
# 检查服务健康状态
|
||
log "🏥 检查服务健康状态..."
|
||
for i in {1..30}; do
|
||
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
|
||
success "后端服务已启动"
|
||
break
|
||
fi
|
||
if [ $i -eq 30 ]; then
|
||
error "后端服务启动超时"
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
for i in {1..30}; do
|
||
if curl -f http://localhost:3000 > /dev/null 2>&1; then
|
||
success "前端服务已启动"
|
||
break
|
||
fi
|
||
if [ $i -eq 30 ]; then
|
||
error "前端服务启动超时"
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
log "=========================================="
|
||
success "🎉 部署完成"
|
||
log "=========================================="
|
||
log "⏰ 完成时间: $(date +'%Y-%m-%d %H:%M:%S')"
|
||
log "✅ 所有服务已启动并正常运行"
|
||
log ""
|
||
log "📍 访问地址:"
|
||
log " 前端: http://localhost:3000"
|
||
log " 后端: http://localhost:8080"
|
||
log ""
|