| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/bin/bash
- #
- # math_cms 统一部署脚本
- #
- # 用法:
- # ./deploy.sh # 部署第一台服务器(主站)
- # ./deploy.sh api # 部署第二台服务器(API)
- # ./deploy.sh pdf # 部署第三/四台服务器(PDF Worker)
- # ./deploy.sh all # 部署当前服务器所有服务
- #
- # 流程:git pull → 前端构建 → 清 Laravel 缓存 → 重建并重启容器
- #
- set -e
- # ============ 配置 ============
- COMPOSE_FILES_MAIN="-f docker-compose.yml -f docker-compose.mount.yml"
- COMPOSE_FILES_API="-f docker-compose.api.yml -f docker-compose.api.mount.yml"
- COMPOSE_FILES_PDF="-f docker-compose.pdf.yml -f docker-compose.pdf.mount.yml"
- # ============ 颜色 ============
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- NC='\033[0m'
- info() { echo -e "${BLUE}[INFO]${NC} $1"; }
- ok() { echo -e "${GREEN}[OK]${NC} $1"; }
- warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
- error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
- # ============ 步骤 1:拉取代码 ============
- git_pull() {
- info "拉取最新代码..."
- git fetch origin main
- LOCAL=$(git rev-parse HEAD)
- REMOTE=$(git rev-parse origin/main)
- if [ "$LOCAL" = "$REMOTE" ]; then
- ok "代码已是最新 ($LOCAL)"
- else
- git pull origin main
- ok "代码已更新"
- fi
- }
- # ============ 步骤 2:前端构建 ============
- build_frontend() {
- info "构建前端资源..."
- # 检测宿主机是否有 bun/npm
- if command -v bun &>/dev/null; then
- info "使用 bun 构建..."
- bun install
- bun run build
- elif command -v npm &>/dev/null; then
- info "使用 npm 构建..."
- npm install --prefer-offline
- npm run build
- else
- # 宿主机没有 Node.js,用 Docker 容器构建
- info "宿主机无 Node.js,使用 Docker 容器构建..."
- docker run --rm \
- -v "$(pwd):/app" \
- -w /app \
- node:20-alpine \
- sh -c "npm config set registry https://registry.npmmirror.com && npm install && npm run build"
- fi
- # 验证构建产物
- if [ ! -f "public/build/manifest.json" ]; then
- error "前端构建失败:public/build/manifest.json 不存在"
- fi
- ok "前端构建完成"
- info "构建产物:"
- ls -la public/build/assets/
- }
- # ============ 步骤 3:清 Laravel 缓存 ==========
- clear_cache() {
- info "清除 Laravel 缓存..."
- # 如果容器正在运行,通过容器执行
- if docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'math_cms_app'; then
- local container=$(docker ps --format '{{.Names}}' | grep 'math_cms_app' | head -1)
- docker exec "$container" php artisan view:clear 2>/dev/null || true
- docker exec "$container" php artisan cache:clear 2>/dev/null || true
- ok "通过容器清除缓存"
- else
- # 容器未运行时本地执行(需要 PHP)
- php artisan view:clear 2>/dev/null || true
- php artisan cache:clear 2>/dev/null || true
- ok "本地清除缓存"
- fi
- }
- # ============ 步骤 4:构建镜像并重启 ============
- deploy_services() {
- local compose_files="$1"
- shift
- local services="$@"
- info "构建 Docker 镜像..."
- docker compose $compose_files build --no-cache app
- if [ -z "$services" ]; then
- services="app"
- fi
- info "重启服务: $services"
- docker compose $compose_files up -d --no-deps --force-recreate $services
- ok "服务已重启: $services"
- }
- # ============ 步骤 5:验证 ============
- verify() {
- local compose_files="$1"
- info "等待服务启动..."
- sleep 5
- # 检查容器状态
- local status=$(docker compose $compose_files ps --format '{{.Status}}' 2>/dev/null | head -1)
- if echo "$status" | grep -q 'Up'; then
- ok "容器运行正常 ($status)"
- else
- warn "容器状态: $status"
- warn "请检查日志: docker compose $compose_files logs --tail=50"
- fi
- # 检查前端资源是否可访问
- local port=$(docker compose $compose_files port app 8000 2>/dev/null | cut -d: -f2)
- if [ -n "$port" ]; then
- local manifest_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port/build/manifest.json" 2>/dev/null || echo "000")
- if [ "$manifest_status" = "200" ]; then
- ok "前端资源验证通过 (manifest.json HTTP $manifest_status)"
- else
- warn "前端资源验证失败 (manifest.json HTTP $manifest_status)"
- fi
- fi
- }
- # ============ 主流程 ============
- main() {
- local mode="${1:-main}"
- echo ""
- echo -e "${BLUE}========================================${NC}"
- echo -e "${BLUE} math_cms 部署脚本${NC}"
- echo -e "${BLUE}========================================${NC}"
- echo ""
- git_pull
- build_frontend
- clear_cache
- case "$mode" in
- main|1)
- info "部署模式:主站(第一台服务器)"
- deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
- verify "$COMPOSE_FILES_MAIN"
- ;;
- api|2)
- info "部署模式:API(第二台服务器)"
- deploy_services "$COMPOSE_FILES_API" app
- verify "$COMPOSE_FILES_API"
- ;;
- pdf|3)
- info "部署模式:PDF Worker(第三/四台服务器)"
- deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
- verify "$COMPOSE_FILES_PDF"
- ;;
- all)
- info "部署模式:所有服务"
- # 自动检测当前服务器使用的 compose 文件
- if [ -f "docker-compose.pdf.yml" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'pdf_worker'; then
- deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
- verify "$COMPOSE_FILES_PDF"
- elif docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'math_cms_queue'; then
- deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
- verify "$COMPOSE_FILES_MAIN"
- else
- deploy_services "$COMPOSE_FILES_API" app
- verify "$COMPOSE_FILES_API"
- fi
- ;;
- *)
- echo "用法: $0 [main|api|pdf|all]"
- echo " main - 第一台服务器(主站,默认)"
- echo " api - 第二台服务器(API)"
- echo " pdf - 第三/四台服务器(PDF Worker)"
- echo " all - 自动检测并部署当前服务器所有服务"
- exit 1
- ;;
- esac
- echo ""
- ok "部署完成!"
- echo ""
- }
- main "$@"
|