229805a070
Add WeChat service account routing with sync acks, connectivity tests, and context isolation; document deploy runbooks; and bundle related MindSpace, voice, Plaza, and server gateway changes for production rollout. Co-authored-by: Cursor <cursoragent@cursor.com>
497 lines
22 KiB
Bash
Executable File
497 lines
22 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 一键部署:本地 dev → Studio(100) → 105
|
||
#
|
||
# 用法:
|
||
# ./rsync_to_server.sh # 全量部署(100 先,105 后)
|
||
# ./rsync_to_server.sh --only-100 # 只更新 Studio
|
||
# ./rsync_to_server.sh --only-105 # 只触发 105 同步(Studio 代码已是最新)
|
||
# ./rsync_to_server.sh --skip-build # 跳过 vite build(纯后端改动时加速)
|
||
# ./rsync_to_server.sh --no-restart # 只同步代码,不重启服务
|
||
# ./rsync_to_server.sh --dry-run # 预览将同步哪些文件,不做任何修改
|
||
|
||
set -euo pipefail
|
||
|
||
# ── 配置 ──────────────────────────────────────────────────────────────────────
|
||
STUDIO_HOST="${STUDIO_HOST:-100.99.38.66}"
|
||
STUDIO_REMOTE="${STUDIO_REMOTE:-/Users/john/Project/Memind}"
|
||
STUDIO_NODE_BIN="${STUDIO_NODE_BIN:-/opt/homebrew/opt/node@24/bin}"
|
||
STUDIO_LAUNCHAGENT_PORTAL="cn.tkmind.memind-portal"
|
||
STUDIO_LAUNCHAGENT_PLAZA="cn.tkmind.plaza"
|
||
STUDIO_HEALTH="http://127.0.0.1:8081/api/status"
|
||
STUDIO_PLAZA_HEALTH="http://127.0.0.1:3001/plaza"
|
||
STUDIO_LOG_PORTAL="$HOME/Library/Logs/memind-portal.log"
|
||
STUDIO_LOG_PLAZA="$HOME/Library/Logs/memind-plaza.log"
|
||
|
||
LOCAL_ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||
EXCLUDE_STUDIO="${LOCAL_ROOT}/scripts/.rsync-exclude-studio"
|
||
|
||
# ── 参数解析 ──────────────────────────────────────────────────────────────────
|
||
ONLY_100=0
|
||
ONLY_105=0
|
||
SKIP_BUILD=0
|
||
NO_RESTART=0
|
||
DRY_RUN=0
|
||
AUTO_YES=0
|
||
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--only-100) ONLY_100=1 ;;
|
||
--only-105) ONLY_105=1 ;;
|
||
--skip-build) SKIP_BUILD=1 ;;
|
||
--no-restart) NO_RESTART=1 ;;
|
||
--dry-run) DRY_RUN=1 ;;
|
||
--yes|-y) AUTO_YES=1 ;;
|
||
-h|--help)
|
||
sed -n '2,9p' "$0"
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo "未知参数: $arg" >&2
|
||
echo "用法: $0 [--only-100] [--only-105] [--skip-build] [--no-restart] [--dry-run]" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
if [[ "${ONLY_100}" -eq 1 && "${ONLY_105}" -eq 1 ]]; then
|
||
echo "错误: --only-100 与 --only-105 不能同时使用" >&2; exit 1
|
||
fi
|
||
|
||
# ── 工具函数 ──────────────────────────────────────────────────────────────────
|
||
RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; BOLD='\033[1m'; NC='\033[0m'
|
||
|
||
hr() { echo "──────────────────────────────────────────────────────"; }
|
||
ok() { echo -e " ${GREEN}✓${NC} $*"; }
|
||
warn() { echo -e " ${YELLOW}⚠${NC} $*"; }
|
||
fail() { echo -e " ${RED}✗${NC} $*" >&2; }
|
||
die() { fail "$*"; hr; exit 1; }
|
||
|
||
ssh_studio() { ssh -o BatchMode=yes -o ConnectTimeout=15 "${STUDIO_HOST}" "$@"; }
|
||
ssh_105() { ssh_studio "ssh -o BatchMode=yes -o ConnectTimeout=15 ssh105 '$*'" 2>/dev/null; }
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ① 本地 Pre-flight
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
preflight_local() {
|
||
echo ""
|
||
echo -e "${BOLD}[Pre-flight] 本地检查${NC}"
|
||
|
||
# exclude 文件
|
||
[[ -f "${EXCLUDE_STUDIO}" ]] \
|
||
&& ok "exclude 文件存在: scripts/.rsync-exclude-studio" \
|
||
|| die "缺少 ${EXCLUDE_STUDIO},无法继续"
|
||
|
||
# 关键源码文件
|
||
local missing=()
|
||
for f in server.mjs user-auth.mjs user-publish.mjs user-space.mjs package.json; do
|
||
[[ -f "${LOCAL_ROOT}/${f}" ]] || missing+=("${f}")
|
||
done
|
||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||
die "本地缺少关键文件: ${missing[*]}"
|
||
fi
|
||
ok "关键源码文件存在"
|
||
|
||
# WORKSPACE_MAINTENANCE patch —— 必须存在,否则 105 下次重启会卡死
|
||
if ! grep -q 'WORKSPACE_MAINTENANCE_ENABLED' "${LOCAL_ROOT}/server.mjs"; then
|
||
die "server.mjs 缺少 WORKSPACE_MAINTENANCE_ENABLED patch!\n 此 patch 防止 105 在 rclone 挂载上启动时卡死。\n 请合并 Studio 生产版的 server.mjs patch 后再部署。"
|
||
fi
|
||
ok "server.mjs 含 WORKSPACE_MAINTENANCE patch"
|
||
|
||
# 本地 .env 不能被意外同步(确认在 exclude 里)
|
||
if grep -q '^\.env$' "${EXCLUDE_STUDIO}"; then
|
||
ok ".env 已在 exclude 列表,不会被同步到 Studio"
|
||
else
|
||
die "exclude 文件未排除 .env!请检查 scripts/.rsync-exclude-studio"
|
||
fi
|
||
|
||
# MindSpace 已被排除
|
||
if grep -q '^MindSpace/' "${EXCLUDE_STUDIO}"; then
|
||
ok "MindSpace/ 已在 exclude 列表"
|
||
else
|
||
die "exclude 文件未排除 MindSpace/!"
|
||
fi
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ② 远端 Pre-flight(Studio)
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
preflight_studio() {
|
||
echo ""
|
||
echo -e "${BOLD}[Pre-flight] Studio(${STUDIO_HOST}) 检查${NC}"
|
||
|
||
# SSH 连通性
|
||
if ! ssh_studio "echo ok" &>/dev/null; then
|
||
die "无法 SSH 到 Studio (${STUDIO_HOST})"
|
||
fi
|
||
ok "SSH 连通"
|
||
|
||
# .env 存在且非空
|
||
local env_size
|
||
env_size="$(ssh_studio "stat -f%z '${STUDIO_REMOTE}/.env' 2>/dev/null || echo 0")"
|
||
if [[ "${env_size}" -lt 10 ]]; then
|
||
die "Studio .env 不存在或为空!(size=${env_size})"
|
||
fi
|
||
ok ".env 存在(${env_size} bytes)"
|
||
|
||
# MindSpace 有内容
|
||
local ms_count
|
||
ms_count="$(ssh_studio "ls '${STUDIO_REMOTE}/MindSpace' | wc -l | tr -d ' '")"
|
||
if [[ "${ms_count}" -lt 1 ]]; then
|
||
die "Studio MindSpace/ 为空!部署前请确认生产数据正常。"
|
||
fi
|
||
ok "MindSpace/ 有 ${ms_count} 个条目"
|
||
|
||
# data/ 目录存在
|
||
if ! ssh_studio "test -d '${STUDIO_REMOTE}/data'"; then
|
||
die "Studio data/ 目录不存在!"
|
||
fi
|
||
ok "data/ 目录存在"
|
||
|
||
# 记录 .env 快照(大小 + 修改时间),用于 post-rsync 验证
|
||
STUDIO_ENV_STAT="$(ssh_studio "stat -f'%z %m' '${STUDIO_REMOTE}/.env'")"
|
||
STUDIO_MS_COUNT="${ms_count}"
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ③ Dry-run 预览
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
show_dry_run() {
|
||
echo ""
|
||
echo -e "${BOLD}[预览] 将同步以下文件变更到 Studio${NC}"
|
||
rsync -az --delete --dry-run --itemize-changes \
|
||
--exclude-from="${EXCLUDE_STUDIO}" \
|
||
"${LOCAL_ROOT}/" \
|
||
"${STUDIO_HOST}:${STUDIO_REMOTE}/" \
|
||
2>/dev/null \
|
||
| grep -v '^\.' \
|
||
| head -80 \
|
||
|| true
|
||
echo ""
|
||
|
||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||
echo "(--dry-run 模式,未做任何实际修改)"
|
||
return 0
|
||
fi
|
||
|
||
if [[ "${AUTO_YES}" -eq 1 ]]; then
|
||
ok "已自动确认(--yes)"
|
||
return 0
|
||
fi
|
||
|
||
# 交互确认(直接读 /dev/tty,不受 stdin pipe 影响)
|
||
local confirm
|
||
read -r -p " 确认同步?[y/N] " confirm </dev/tty
|
||
if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then
|
||
echo "已取消。"
|
||
exit 0
|
||
fi
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ④ Post-rsync 安全校验
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
verify_studio_after_rsync() {
|
||
echo ""
|
||
echo -e "${BOLD}[校验] rsync 后 Studio 生产数据完整性${NC}"
|
||
|
||
# .env 未被覆盖(大小+修改时间应与 rsync 前完全一致)
|
||
local env_stat_now
|
||
env_stat_now="$(ssh_studio "stat -f'%z %m' '${STUDIO_REMOTE}/.env'")"
|
||
if [[ "${env_stat_now}" != "${STUDIO_ENV_STAT}" ]]; then
|
||
die ".env 在 rsync 后发生变化!(before: ${STUDIO_ENV_STAT} after: ${env_stat_now})\n 立即检查 Studio .env 是否被覆盖!"
|
||
fi
|
||
ok ".env 未变动(${env_stat_now})"
|
||
|
||
# MindSpace 条目数未减少
|
||
local ms_now
|
||
ms_now="$(ssh_studio "ls '${STUDIO_REMOTE}/MindSpace' | wc -l | tr -d ' '")"
|
||
if [[ "${ms_now}" -lt "${STUDIO_MS_COUNT}" ]]; then
|
||
die "MindSpace/ 条目从 ${STUDIO_MS_COUNT} 减少到 ${ms_now}!rsync 可能误删了用户数据!"
|
||
fi
|
||
ok "MindSpace/ 条目数正常(${ms_now})"
|
||
|
||
# data/ 仍存在
|
||
if ! ssh_studio "test -d '${STUDIO_REMOTE}/data'"; then
|
||
die "data/ 目录在 rsync 后消失!"
|
||
fi
|
||
ok "data/ 目录存在"
|
||
|
||
# server.mjs patch 仍在(rsync 不能丢失 patch)
|
||
local patch_count
|
||
patch_count="$(ssh_studio "grep -c 'WORKSPACE_MAINTENANCE_ENABLED' '${STUDIO_REMOTE}/server.mjs' || echo 0")"
|
||
if [[ "${patch_count}" -lt 1 ]]; then
|
||
die "Studio server.mjs 的 WORKSPACE_MAINTENANCE patch 丢失!\n 这会导致 105 下次重启时卡死,请立即还原!"
|
||
fi
|
||
ok "server.mjs WORKSPACE_MAINTENANCE patch 存在(${patch_count} 处)"
|
||
|
||
# 关键文件 md5 一致
|
||
echo ""
|
||
echo -e "${BOLD}[校验] 关键文件 md5${NC}"
|
||
local all_ok=1
|
||
for f in server.mjs user-publish.mjs user-space.mjs user-auth.mjs mindspace-sandbox-mcp.mjs capabilities.mjs package.json; do
|
||
[[ ! -f "${LOCAL_ROOT}/${f}" ]] && continue
|
||
local local_sum remote_sum
|
||
local_sum="$(md5 -q "${LOCAL_ROOT}/${f}" 2>/dev/null || md5sum "${LOCAL_ROOT}/${f}" | awk '{print $1}')"
|
||
remote_sum="$(ssh_studio "openssl md5 '${STUDIO_REMOTE}/${f}' 2>/dev/null | awk '{print \$2}'")"
|
||
if [[ "${local_sum}" == "${remote_sum}" ]]; then
|
||
ok "${f}"
|
||
else
|
||
fail "${f} md5 不一致(local=${local_sum} remote=${remote_sum})"
|
||
all_ok=0
|
||
fi
|
||
done
|
||
[[ "${all_ok}" -eq 1 ]] || die "md5 校验失败,请检查文件同步是否正常"
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ⑤ Studio 重启并健康检查(Portal → Plaza 顺序)
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
wait_studio_healthy() {
|
||
local STUDIO_UID
|
||
STUDIO_UID="$(ssh_studio "id -u")"
|
||
|
||
# ── Portal ──────────────────────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${BOLD}[重启] Studio portal (cn.tkmind.memind-portal)${NC}"
|
||
ssh_studio "launchctl kickstart -k gui/${STUDIO_UID}/${STUDIO_LAUNCHAGENT_PORTAL}" >/dev/null
|
||
|
||
echo -n " 等待 portal :8081..."
|
||
local ok_flag=0
|
||
for i in $(seq 1 20); do
|
||
sleep 2
|
||
if ssh_studio "curl -sf '${STUDIO_HEALTH}'" &>/dev/null; then
|
||
echo " ✓ (${i}×2s = $((i*2))s)"
|
||
ok_flag=1
|
||
break
|
||
fi
|
||
printf "."
|
||
done
|
||
if [[ "${ok_flag}" -eq 0 ]]; then
|
||
echo ""
|
||
fail "portal 40s 内未就绪"
|
||
echo " 最近日志:"
|
||
ssh_studio "tail -30 '${STUDIO_LOG_PORTAL}' 2>/dev/null" | sed 's/^/ /'
|
||
die "Studio portal 启动失败,已中止,不会继续重启 Plaza 或同步 105"
|
||
fi
|
||
|
||
# 日志扫描
|
||
local portal_errors
|
||
portal_errors="$(ssh_studio "tail -60 '${STUDIO_LOG_PORTAL}' 2>/dev/null \
|
||
| grep -iE 'error|exception|uncaught|fatal' || true")"
|
||
if [[ -n "${portal_errors}" ]]; then
|
||
warn "portal 启动日志含警告/错误(请人工确认):"
|
||
echo "${portal_errors}" | head -8 | sed 's/^/ /'
|
||
else
|
||
ok "portal 启动日志无 ERROR"
|
||
fi
|
||
|
||
# ── Plaza ───────────────────────────────────────────────────────────────────
|
||
# Plaza 依赖 portal 先就绪(run-plaza-prod.sh 内有等待逻辑),所以 portal 健康后才重启
|
||
echo ""
|
||
echo -e "${BOLD}[重启] Studio plaza (cn.tkmind.plaza)${NC}"
|
||
ssh_studio "launchctl kickstart -k gui/${STUDIO_UID}/${STUDIO_LAUNCHAGENT_PLAZA}" >/dev/null
|
||
|
||
echo -n " 等待 plaza :3001..."
|
||
ok_flag=0
|
||
for i in $(seq 1 30); do
|
||
sleep 3
|
||
if ssh_studio "curl -sf -o /dev/null '${STUDIO_PLAZA_HEALTH}'" &>/dev/null; then
|
||
echo " ✓ (${i}×3s = $((i*3))s)"
|
||
ok_flag=1
|
||
break
|
||
fi
|
||
printf "."
|
||
done
|
||
if [[ "${ok_flag}" -eq 0 ]]; then
|
||
echo ""
|
||
warn "plaza 90s 内未就绪(非致命,portal 和 105 仍可用)"
|
||
echo " 最近日志:"
|
||
ssh_studio "tail -20 '${STUDIO_LOG_PLAZA}' 2>/dev/null" | sed 's/^/ /'
|
||
else
|
||
local plaza_errors
|
||
plaza_errors="$(ssh_studio "tail -40 '${STUDIO_LOG_PLAZA}' 2>/dev/null \
|
||
| grep -iE 'error|exception|uncaught|fatal' || true")"
|
||
if [[ -n "${plaza_errors}" ]]; then
|
||
warn "plaza 启动日志含警告/错误:"
|
||
echo "${plaza_errors}" | head -8 | sed 's/^/ /'
|
||
else
|
||
ok "plaza 启动日志无 ERROR"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# ⑥ 105 Post-sync 额外校验
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
verify_105_after_sync() {
|
||
echo ""
|
||
echo -e "${BOLD}[校验] 105 部署后检查${NC}"
|
||
|
||
# goose-h5 正在运行
|
||
local svc_status
|
||
svc_status="$(ssh_studio "ssh ssh105 'systemctl is-active goose-h5 2>/dev/null'" 2>/dev/null || echo "unknown")"
|
||
if [[ "${svc_status}" == "active" ]]; then
|
||
ok "105 goose-h5 状态: active"
|
||
else
|
||
die "105 goose-h5 不是 active 状态(${svc_status})"
|
||
fi
|
||
|
||
# 105 .env 关键开关仍然正确
|
||
local maintenance_val
|
||
maintenance_val="$(ssh_studio "ssh ssh105 'grep MEMIND_WORKSPACE_MAINTENANCE /root/tkmind_go/ui/h5/.env 2>/dev/null'" 2>/dev/null || echo "")"
|
||
if echo "${maintenance_val}" | grep -q 'MEMIND_WORKSPACE_MAINTENANCE=0'; then
|
||
ok "105 .env: MEMIND_WORKSPACE_MAINTENANCE=0 ✓"
|
||
else
|
||
die "105 .env 缺少或错误设置 MEMIND_WORKSPACE_MAINTENANCE!\n 当前值: ${maintenance_val}\n 必须为 0,否则 105 重启会卡死!"
|
||
fi
|
||
|
||
# 105 server.mjs patch 存在
|
||
local patch_count_105
|
||
patch_count_105="$(ssh_studio "ssh ssh105 'grep -c WORKSPACE_MAINTENANCE_ENABLED /root/tkmind_go/ui/h5/server.mjs 2>/dev/null || echo 0'" 2>/dev/null || echo "0")"
|
||
if [[ "${patch_count_105}" -ge 1 ]]; then
|
||
ok "105 server.mjs WORKSPACE_MAINTENANCE patch 存在"
|
||
else
|
||
die "105 server.mjs 缺少 WORKSPACE_MAINTENANCE patch!\n 105 重启时将对 rclone 挂载做 fs.watch,导致卡死!"
|
||
fi
|
||
|
||
# 105 MindSpace 挂载有内容
|
||
local ms_105
|
||
ms_105="$(ssh_studio "ssh ssh105 'ls /mnt/memind-shared/MindSpace 2>/dev/null | wc -l | tr -d \" \"'" 2>/dev/null || echo "0")"
|
||
if [[ "${ms_105}" -gt 0 ]]; then
|
||
ok "105 MindSpace 挂载正常(${ms_105} 个条目)"
|
||
else
|
||
warn "105 /mnt/memind-shared/MindSpace 为空,请确认 rclone 挂载是否正常"
|
||
fi
|
||
|
||
# 105 goose-h5 健康检查
|
||
if ssh_studio "ssh ssh105 'curl -sf http://127.0.0.1:8080/api/status'" &>/dev/null; then
|
||
ok "105 goose-h5 :8080/api/status 健康"
|
||
else
|
||
die "105 goose-h5 健康检查失败"
|
||
fi
|
||
|
||
# 105 goose-plaza-web(可选服务,不在 sync-to-105.sh 里重启,检查状态即可)
|
||
local plaza_105_status
|
||
plaza_105_status="$(ssh_studio "ssh ssh105 'systemctl is-active goose-plaza-web 2>/dev/null || echo inactive'" 2>/dev/null || echo "unknown")"
|
||
if [[ "${plaza_105_status}" == "active" ]]; then
|
||
ok "105 goose-plaza-web 状态: active"
|
||
else
|
||
warn "105 goose-plaza-web 不是 active(${plaza_105_status})"
|
||
warn "如有 plaza 代码变更,需手动重启: ssh ssh105 systemctl restart goose-plaza-web"
|
||
fi
|
||
}
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# Banner
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
hr
|
||
echo -e " ${BOLD}Memind 一键部署${NC} $(date '+%Y-%m-%d %H:%M:%S')"
|
||
[[ "${ONLY_105}" -eq 0 ]] && echo " 本地 → Studio: ${STUDIO_HOST}:${STUDIO_REMOTE}"
|
||
[[ "${ONLY_100}" -eq 0 ]] && echo " Studio → 105: 经 sync-to-105.sh 触发"
|
||
[[ "${SKIP_BUILD}" -eq 1 ]] && echo " 模式: --skip-build(跳过 vite build)"
|
||
[[ "${NO_RESTART}" -eq 1 ]] && echo " 模式: --no-restart(只同步代码)"
|
||
[[ "${DRY_RUN}" -eq 1 ]] && echo " 模式: --dry-run(预览,不做修改)"
|
||
hr
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# 主流程
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
if [[ "${ONLY_105}" -eq 0 ]]; then
|
||
preflight_local
|
||
preflight_studio
|
||
show_dry_run
|
||
|
||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||
echo ""
|
||
echo "(--dry-run 完成:以上仅为预览,未对 Studio/105 做任何修改)"
|
||
exit 0
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 1/4] rsync 代码...${NC}"
|
||
rsync -az --delete \
|
||
--exclude-from="${EXCLUDE_STUDIO}" \
|
||
"${LOCAL_ROOT}/" \
|
||
"${STUDIO_HOST}:${STUDIO_REMOTE}/"
|
||
ok "代码同步完成"
|
||
|
||
verify_studio_after_rsync
|
||
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 2/4] npm install...${NC}"
|
||
ssh_studio "cd '${STUDIO_REMOTE}' && PATH='${STUDIO_NODE_BIN}':'\$PATH' npm install --prefer-offline 2>&1 | tail -5"
|
||
|
||
if [[ "${SKIP_BUILD}" -eq 0 ]]; then
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 3/4] npm run build...${NC}"
|
||
if ! ssh_studio "cd '${STUDIO_REMOTE}' && PATH='${STUDIO_NODE_BIN}':'\$PATH' npm run build"; then
|
||
warn "vite build 失败,服务仍在运行旧产物"
|
||
if [[ "${AUTO_YES}" -eq 0 ]]; then
|
||
local confirm
|
||
read -r -p " 继续重启服务?[y/N] " confirm </dev/tty
|
||
[[ "${confirm}" =~ ^[Yy]$ ]] || exit 1
|
||
else
|
||
warn "已自动继续重启(--yes)"
|
||
fi
|
||
else
|
||
ok "build 完成"
|
||
fi
|
||
else
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 3/4] 跳过 build(--skip-build)${NC}"
|
||
fi
|
||
|
||
if [[ "${NO_RESTART}" -eq 0 ]]; then
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 4/4] 重启服务(portal → plaza)${NC}"
|
||
wait_studio_healthy
|
||
else
|
||
echo ""
|
||
echo -e "${BOLD}▶ [Studio 4/4] 跳过重启(--no-restart)${NC}"
|
||
fi
|
||
fi
|
||
|
||
if [[ "${ONLY_100}" -eq 0 ]]; then
|
||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||
echo ""
|
||
echo "(--dry-run:跳过 105 同步,未做任何修改)"
|
||
exit 0
|
||
fi
|
||
|
||
# 确保 Studio 健康再触发 105
|
||
if [[ "${NO_RESTART}" -eq 0 && "${ONLY_105}" -eq 0 ]]; then
|
||
echo ""
|
||
echo -e "${BOLD}[安全] 再次确认 Studio 健康后再推 105...${NC}"
|
||
if ! ssh_studio "curl -sf '${STUDIO_HEALTH}'" &>/dev/null; then
|
||
die "Studio portal 此刻不健康,已暂停 105 同步,请先排查 Studio 问题"
|
||
fi
|
||
ok "Studio portal 确认健康"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${BOLD}▶ [105] 触发 Studio → 105 同步...${NC}"
|
||
SYNC_ENV=""
|
||
[[ "${SKIP_BUILD}" -eq 1 ]] && SYNC_ENV="${SYNC_ENV} SKIP_BUILD=1"
|
||
[[ "${NO_RESTART}" -eq 1 ]] && SYNC_ENV="${SYNC_ENV} NO_RESTART=1"
|
||
ssh_studio "cd '${STUDIO_REMOTE}' && env ${SYNC_ENV} bash scripts/sync-to-105.sh"
|
||
|
||
if [[ "${NO_RESTART}" -eq 0 ]]; then
|
||
verify_105_after_sync
|
||
fi
|
||
fi
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# 完成
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
echo ""
|
||
hr
|
||
if [[ "${ONLY_105}" -eq 1 ]]; then
|
||
echo -e " ${GREEN}${BOLD}✅ 105 已更新${NC}"
|
||
elif [[ "${ONLY_100}" -eq 1 ]]; then
|
||
echo -e " ${GREEN}${BOLD}✅ Studio(100) 已更新${NC}"
|
||
else
|
||
echo -e " ${GREEN}${BOLD}✅ Studio(100) + 105 均已更新${NC}"
|
||
fi
|
||
echo " $(date '+%Y-%m-%d %H:%M:%S')"
|
||
hr
|