Files
dpb/deploy.sh
T
34047007@qq.com 86126ea16f checkpoint: 架构治理四件套——统一 async / 测试真 PG / Redis 降级可选 / DPB 扶正
- A1 业务层统一 async:gencode 表内省改 run_sync 异步(不再阻塞事件循环)、
  check_db 改 async_engine、dict_util 启动预热异步载入;psycopg 降为
  APScheduler SQLAlchemyJobStore 专用同步孤岛(注释标注)
- A2 测试真 PG 化:conftest 弃 SQLite + mock Redis,改连真实 PG16(dpb_test)+
  Redis(number_gen advisory lock 路径真被测);新增 docker/test-compose.yaml
  测试依赖栈(postgres:16:5433 + redis:7:6380);run_ci 前置探测 + 容器兜底,
  pytest -m pg 10 套正式 tc 全过(真实 PG16 + Redis)
- B1 Redis 启动不强依赖:redis_connect 失败降级启动,缓存类回源 DB、存储类
  (会话/AI 配置/调度)经 require_redis 守卫返回 503;真机 3 阶段降级测试
  PASS=11 FAIL=0(独立 Redis 6390 + 后端 8091,不动共享 dev 服务)
- C 扶正 DPB:后端横幅/日志(dpb.log)/README/pyproject/ai_factory agent 名、
  前端 package.json/署名注释/链接文案/deploy.sh/docker 注释全部去 fastapiadmin;
  grep backend/app + frontend/web/src 零残留
- 验证:全量回归 pass=72 fail=0
2026-08-07 09:06:19 +08:00

187 lines
6.1 KiB
Bash

#!/bin/bash
set -euo pipefail
# ==================== 配置 ====================
PROJECT_NAME="dpb"
WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
DOCKER_DIR="${WORK_DIR}/docker"
ENV_FILE="${DOCKER_DIR}/.env"
GIT_REPO="http://123.207.9.209:3000/gonsun/dpb.git"
COLOR_GREEN='\033[0;32m'; COLOR_BLUE='\033[0;34m'; COLOR_YELLOW='\033[0;33m'; COLOR_RED='\033[0;31m'; COLOR_RESET='\033[0m'
log() {
local color; local level=${2:-INFO}
case $level in
INFO) color="${COLOR_BLUE}" ;; WARN) color="${COLOR_YELLOW}" ;;
ERROR) color="${COLOR_RED}" ;; SUCCESS) color="${COLOR_GREEN}" ;;
*) color="${COLOR_RESET}" ;;
esac
echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] ${1}${COLOR_RESET}"
}
# ==================== 核心函数 ====================
load_env() {
if [ -f "${ENV_FILE}" ]; then
set -a; source "${ENV_FILE}"; set +a
elif [ -f "${DOCKER_DIR}/.env.example" ]; then
cp "${DOCKER_DIR}/.env.example" "${ENV_FILE}"
set -a; source "${ENV_FILE}"; set +a
else
log ".env 文件不存在" "ERROR"; exit 1
fi
log "✅ 环境变量已加载" "SUCCESS"
}
check_deps() {
for dir in "${DOCKER_DIR}/postgres/data" "${DOCKER_DIR}/redis/data"; do
[ -d "$dir" ] || mkdir -p "$dir"
done
local missing=()
for cmd in git docker; do
command -v $cmd &>/dev/null || missing+=($cmd)
done
if ! docker compose version &>/dev/null && ! docker-compose --version &>/dev/null; then
missing+=("docker compose")
fi
if [ ${#missing[@]} -gt 0 ]; then
log "缺少依赖: ${missing[*]}" "ERROR"; exit 1
fi
log "✅ 依赖检查通过" "SUCCESS"
}
pull_code() {
cd "${WORK_DIR}"
local script_md5=""
if command -v md5sum &>/dev/null; then
script_md5=$(md5sum "$0" | awk '{print $1}')
elif command -v md5 &>/dev/null; then
script_md5=$(md5 -q "$0")
fi
if [ -d ".git" ]; then
local branch
branch=$(git rev-parse --abbrev-ref HEAD)
log "分支: ${branch}"
local old_head
old_head=$(git rev-parse HEAD 2>/dev/null || echo "")
git fetch origin || true
git pull || { log "git pull 失败" "ERROR"; exit 1; }
local new_head
new_head=$(git rev-parse HEAD 2>/dev/null || echo "")
if [ -n "$old_head" ] && [ "$old_head" != "$new_head" ]; then
log "📋 本次拉取提交:"
git log --oneline --no-decorate "${old_head}..${new_head}" 2>/dev/null | sed 's/^/ /' || true
fi
# deploy.sh 自身有更新则重载
if [ -n "$script_md5" ]; then
local new_md5=""
command -v md5sum &>/dev/null && new_md5=$(md5sum "$0" | awk '{print $1}') || new_md5=$(md5 -q "$0")
if [ "$script_md5" != "$new_md5" ]; then
log "🔄 deploy.sh 已更新,重新执行..." "WARN"
exec "$0" "$@"
fi
fi
else
git init && git remote add origin "${GIT_REPO}"
git pull origin master || git pull origin main || { log "拉取代码失败" "ERROR"; exit 1; }
fi
log "✅ 代码已更新至 $(git log -1 --oneline)" "SUCCESS"
}
build_image() {
cd "${DOCKER_DIR}"
export DOCKER_BUILDKIT=1
docker compose build || { log "镜像构建失败" "ERROR"; exit 1; }
log "✅ 镜像构建完成" "SUCCESS"
}
start_service() {
cd "${DOCKER_DIR}"
docker compose up -d --force-recreate || { log "容器启动失败" "ERROR"; exit 1; }
log "⏳ 等待服务就绪..."
for i in $(seq 1 30); do
if docker compose ps postgres --format '{{.Status}}' 2>/dev/null | grep -q "healthy"; then
log "✅ PostgreSQL 已就绪" "SUCCESS"; break
fi
sleep 2
done
docker compose ps
log "✅ 服务启动完成" "SUCCESS"
}
stop_service() {
cd "${DOCKER_DIR}" 2>/dev/null || true
docker compose down 2>/dev/null || true
log "✅ 服务已停止" "SUCCESS"
}
show_logs() {
cd "${DOCKER_DIR}"
docker compose ps --format "table {{.Service}}\t{{.Name}}\t{{.Status}}\t{{.Ports}}"
echo "--- 最近 50 行日志 ---"
docker compose logs --tail=50 2>/dev/null
}
verify() {
local ok=true
for svc in postgres redis backend nginx; do
local st=$(docker compose ps "$svc" --format '{{.Status}}' 2>/dev/null || echo "not found")
if echo "$st" | grep -qE "Up|healthy"; then
log "✅ $svc: $st" "SUCCESS"
else
log "❌ $svc: $st" "ERROR"; ok=false
fi
done
$ok && log "✅ 所有服务正常" "SUCCESS" || log "⚠️ 部分服务异常" "WARN"
}
cleanup() {
docker system prune -a -f >/dev/null 2>&1 || true
log "✅ 所有资源已清理" "SUCCESS"
}
# ==================== 主流程 ====================
full_deploy() {
log "========== 🚀 开始完整部署 ==========" "INFO"
log "时间: $(date '+%Y-%m-%d %H:%M:%S')"
load_env
check_deps
stop_service
pull_code "$@"
build_image
start_service
verify
show_logs
cleanup
log "========== 🎉 部署完成 ==========" "SUCCESS"
}
# ==================== 入口 ====================
trap 'stop_service; exit 130' INT TERM
case ${1:-} in
start) load_env; start_service ;;
stop) load_env; stop_service ;;
restart) load_env; cd "${DOCKER_DIR}"; docker compose restart; docker compose ps ;;
logs) load_env; show_logs ;;
verify) load_env; verify ;;
migrate) load_env; cd "${DOCKER_DIR}"; docker compose exec -T backend python /home/scripts/run_migrations.py || { log "breeding 迁移失败" "ERROR"; exit 1; } ;;
clean) cleanup ;;
help|-h|--help)
echo "用法: $0 [命令]"
echo " 无参数 完整部署(拉代码→构建→启动→清理)"
echo " start 启动服务"
echo " stop 停止服务"
echo " restart 重启服务"
echo " logs 查看日志"
echo " verify 验证服务"
echo " migrate 执行全部 breeding 迁移(幂等:列补齐/审计列/唯一约束/字典英码)"
echo " clean 清理构建缓存"
;;
*) full_deploy "$@" ;;
esac