Files
cfdaily cd2523057d
Deploy / ci (push) Waiting to run
Deploy / deploy (push) Blocked by required conditions
Deploy / notify-deploy-failure (push) Blocked by required conditions
auto-sync: 2026-06-08 20:23:20 (catch-all)
2026-06-08 20:23:20 +08:00

253 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# deploy.sh — 部署/升级 moziplus v2
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# 默认值
SOURCE_DIR="$PROJECT_DIR"
TARGET_DIR="${MOZIPLUS_V2_DIR:-$HOME/.sanguo_projects/sanguo_moziplus_v2}"
PM2_NAME="sanguo-moziplus-v2"
SKIP_BUILD=false
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --source=DIR 源码目录 (default: 项目开发目录)"
echo " --target=DIR 安装目标目录 (default: ~/.sanguo_projects/sanguo_moziplus_v2)"
echo " --skip-build 跳过前端构建"
echo " --version 显示当前部署版本"
echo " --rollback 回滚到上一个部署版本"
echo " -h, --help 显示帮助"
exit 0
}
for arg in "$@"; do
case "$arg" in
--source=*) SOURCE_DIR="${arg#*=}" ;;
--target=*) TARGET_DIR="${arg#*=}" ;;
--skip-build) SKIP_BUILD=true ;;
--version) ACTION=version ;;
--rollback) ACTION=rollback ;;
--health-check) ;; # 保留兼容,无额外操作
-h|--help) usage ;;
esac
done
ACTION="${ACTION:-deploy}"
# ── 部署历史文件 ──
HISTORY_FILE="$TARGET_DIR/data/deploy-history.jsonl"
# ── version 分支 ──
if [ "$ACTION" = "version" ]; then
if [ -f "$HISTORY_FILE" ]; then
LAST_COMMIT=$(tail -1 "$HISTORY_FILE" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("commit","unknown"))' 2>/dev/null || echo "unknown")
echo "$LAST_COMMIT"
else
echo "No deployment history found."
fi
exit 0
fi
# ── rollback 分支 ──
if [ "$ACTION" = "rollback" ]; then
if [ ! -f "$HISTORY_FILE" ]; then
echo "❌ No deployment history, cannot rollback."
exit 1
fi
LINE_COUNT=$(wc -l < "$HISTORY_FILE")
if [ "$LINE_COUNT" -lt 2 ]; then
echo "❌ Not enough history for rollback (need at least 2 entries)."
exit 1
fi
ROLLBACK_COMMIT=$(tail -2 "$HISTORY_FILE" | head -1 | python3 -c 'import sys,json; print(json.load(sys.stdin).get("commit",""))' 2>/dev/null || echo "")
if [ -z "$ROLLBACK_COMMIT" ]; then
echo "❌ Could not parse previous commit from history."
exit 1
fi
echo "🔄 Rolling back to commit: $ROLLBACK_COMMIT"
# 保存当前分支/commit 以便恢复
CURRENT_REF=$(git -C "$SOURCE_DIR" rev-parse HEAD 2>/dev/null || echo "")
# checkout 到目标 commit
git -C "$SOURCE_DIR" checkout "$ROLLBACK_COMMIT" 2>/dev/null
# 使用 DEPLOY_OVERRIDE_COMMIT 显式传 commit hash
DEPLOY_OVERRIDE_COMMIT="$ROLLBACK_COMMIT" bash "$0" --source="$SOURCE_DIR" --target="$TARGET_DIR" --skip-build
# 恢复到原来的 commit
if [ -n "$CURRENT_REF" ]; then
git -C "$SOURCE_DIR" checkout "$CURRENT_REF" 2>/dev/null || true
fi
exit 0
fi
echo "🚀 Deploying moziplus v2"
echo " Source: $SOURCE_DIR"
echo " Target: $TARGET_DIR"
# ── 前置检查 ──
echo ""
echo "📋 Pre-flight checks..."
if ! command -v python3 &>/dev/null; then
echo "❌ python3 not found" && exit 1
fi
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
echo " Python: $PYTHON_VERSION"
if ! command -v pm2 &>/dev/null; then
echo "❌ pm2 not found" && exit 1
fi
echo " PM2: $(pm2 --version)"
if [ ! -d "$SOURCE_DIR/src" ]; then
echo "❌ Source directory invalid: $SOURCE_DIR/src not found" && exit 1
fi
# ── 前端构建 ──
if [ "$SKIP_BUILD" = false ]; then
echo ""
echo "🔨 Building frontend..."
bash "$SCRIPT_DIR/build-frontend.sh"
else
echo ""
echo "⏭️ Skipping frontend build (--skip-build)"
# 确保前端 dist 存在
if [ ! -d "$SOURCE_DIR/src/frontend/dist" ]; then
echo "❌ Frontend dist not found. Run without --skip-build first." && exit 1
fi
fi
# ── 创建目标目录 ──
FIRST_INSTALL=false
if [ ! -d "$TARGET_DIR" ]; then
FIRST_INSTALL=true
echo ""
echo "📁 Creating target directory: $TARGET_DIR"
mkdir -p "$TARGET_DIR"
fi
# ── 同步代码 ──
echo ""
echo "🔄 Syncing code..."
rsync -a --delete \
--exclude='.git/' \
--exclude='docs/' \
--exclude='tests/' \
--exclude='scripts/' \
--exclude='data/' \
--exclude='logs/' \
--exclude='inbox/' \
--exclude='node_modules/' \
--exclude='tsconfig.tsbuildinfo' \
--exclude='__pycache__/' \
--exclude='.venv/' \
--exclude='.DS_Store' \
--exclude='config/' \
"$SOURCE_DIR/" "$TARGET_DIR/"
# 但不覆盖运行时配置和数据
# rsync --delete 不影响 exclude 的目录
echo " Code synced ✅"
# ── 确保目录结构 ──
mkdir -p "$TARGET_DIR/data"
mkdir -p "$TARGET_DIR/logs"
mkdir -p "$TARGET_DIR/inbox"
# 确保默认配置存在(不覆盖已有配置)
if [ ! -f "$TARGET_DIR/config/default.yaml" ]; then
if [ -f "$SOURCE_DIR/config/default.yaml" ]; then
cp "$SOURCE_DIR/config/default.yaml" "$TARGET_DIR/config/default.yaml"
echo " Created default config ✅"
fi
fi
if [ ! -f "$TARGET_DIR/config/guardrails.yaml" ]; then
if [ -f "$SOURCE_DIR/config/guardrails.yaml" ]; then
cp "$SOURCE_DIR/config/guardrails.yaml" "$TARGET_DIR/config/guardrails.yaml"
echo " Created guardrails config ✅"
fi
fi
# ── PM2 管理 ──
echo ""
if [ "$FIRST_INSTALL" = true ]; then
echo "🆕 First install — starting with PM2..."
cd "$TARGET_DIR"
pm2 start ecosystem.config.cjs
else
echo "♻️ Upgrading — restarting PM2..."
pm2 restart "$PM2_NAME" --update-env
fi
pm2 save
# ── 前端健康检查 ──
echo " Checking frontend..."
FRONTEND_OK=false
for i in $(seq 1 5); do
if curl -sf http://localhost:8083/ >/dev/null 2>&1; then
FRONTEND_OK=true
break
fi
sleep 1
done
if [ "$FRONTEND_OK" = true ]; then
echo " Frontend check passed ✅"
else
echo " ⚠️ Frontend check timeout"
fi
# ── 后端健康检查 ──
echo " Checking backend..."
HEALTH_URL="http://localhost:8083/api/daemon/status"
OK=false
for i in $(seq 1 10); do
if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then
OK=true
break
fi
sleep 1
done
if [ "$OK" = true ]; then
echo " Health check passed ✅"
else
echo " ⚠️ Health check timeout (service may still be starting)"
echo " Check: pm2 logs $PM2_NAME"
fi
# ── 记录部署历史 ──
mkdir -p "$(dirname "$HISTORY_FILE")"
if [ -n "${DEPLOY_OVERRIDE_COMMIT:-}" ]; then
DEPLOYED_COMMIT="$DEPLOY_OVERRIDE_COMMIT"
else
DEPLOYED_COMMIT=$(git -C "$SOURCE_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")
fi
DEPLOY_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
DEPLOY_SOURCE="${SOURCE_DIR}"
HISTORY_ENTRY=$(printf '%s' '{"timestamp":"'$DEPLOY_TIMESTAMP'","commit":"'$DEPLOYED_COMMIT'","source":"'$DEPLOY_SOURCE'"}')
echo "$HISTORY_ENTRY" >> "$HISTORY_FILE"
# 保留最近 10 条
if [ -f "$HISTORY_FILE" ]; then
TMPFILE=$(mktemp)
tail -10 "$HISTORY_FILE" > "$TMPFILE"
mv "$TMPFILE" "$HISTORY_FILE"
fi
echo " Deploy history recorded ($DEPLOYED_COMMIT) ✅"
# ── 完成 ──
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "$FIRST_INSTALL" = true ]; then
echo "✅ moziplus v2 installed successfully!"
else
echo "✅ moziplus v2 upgraded successfully!"
fi
echo " URL: http://localhost:8083/"
echo " PM2: pm2 status $PM2_NAME"
echo " Logs: pm2 logs $PM2_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"