#!/usr/bin/env bash # gold_monitor 部署管理器 — 一键部署 / 卸载 / 更新 # # 新服务器(免克隆): # curl -fsSL https://git.bz121.com/dekun/gold_monitor/raw/branch/main/deploy/manage.sh | bash # # 已安装(推荐,可交互): # bash /opt/gold_monitor/deploy/manage.sh # # 非交互快速更新: # bash /opt/gold_monitor/deploy/manage.sh update # curl -fsSL .../deploy/manage.sh | bash -s -- update # set -e if [ -n "${BASH_VERSION:-}" ]; then set -o pipefail fi INSTALL_ROOT="${INSTALL_ROOT:-/opt/gold_monitor}" GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/gold_monitor.git}" GIT_BRANCH="${GIT_BRANCH:-main}" _script_src="${BASH_SOURCE[0]:-}" if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)" REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" LIB_DIR="${DEPLOY_DIR}/lib" else # curl | bash 时脚本从 stdin 执行,无本地路径 DEPLOY_DIR="" REPO_ROOT="" LIB_DIR="" fi unset _script_src set -u repo_ready() { [[ -f "${1}/deploy/setup_env.sh" && -f "${1}/deploy/manage.sh" ]] } sync_repo_if_present() { local root="$1" if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then git -C "${root}" pull -q --ff-only 2>/dev/null || true fi } bootstrap_repo() { if repo_ready "${INSTALL_ROOT}"; then REPO_ROOT="${INSTALL_ROOT}" DEPLOY_DIR="${REPO_ROOT}/deploy" LIB_DIR="${DEPLOY_DIR}/lib" sync_repo_if_present "${REPO_ROOT}" return 0 fi if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then DEPLOY_DIR="${REPO_ROOT}/deploy" LIB_DIR="${DEPLOY_DIR}/lib" return 0 fi echo "gold_monitor 部署管理器 — 首次自举" echo "将克隆到: ${INSTALL_ROOT}" if [[ "$(id -u)" -ne 0 ]]; then echo "错误: 请使用 root 执行" >&2 exit 1 fi if ! command -v git >/dev/null 2>&1; then if command -v apt-get >/dev/null 2>&1; then export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y git ca-certificates curl else echo "错误: 未找到 git" >&2 exit 1 fi fi if [[ -d "${INSTALL_ROOT}" ]]; then echo "错误: ${INSTALL_ROOT} 已存在但不是有效仓库" >&2 exit 1 fi mkdir -p "$(dirname "${INSTALL_ROOT}")" git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}" exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" /dev/tty 2>/dev/tty } # curl|bash 时 stdin 是脚本管道,菜单 read 会立刻 EOF;挂到真实终端 attach_tty() { # 把 0/1/2 都接到控制终端,避免只重定向 stdin 后 read 仍异常 if [[ -e /dev/tty ]]; then exec /dev/tty 2>/dev/tty fi } ensure_interactive_tty() { if [[ -t 0 ]]; then return 0 fi local local_mgr="${INSTALL_ROOT}/deploy/manage.sh" if [[ -n "${REPO_ROOT}" && -f "${REPO_ROOT}/deploy/manage.sh" ]]; then local_mgr="${REPO_ROOT}/deploy/manage.sh" fi # 1) 同进程直接挂接 /dev/tty(不二次启动,少一层「像故障」的提示) if [[ -e /dev/tty ]] && attach_tty 2>/dev/null && [[ -t 0 ]]; then echo "已挂接本机终端,请继续选择。" return 0 fi # 2) 回退:用本机已安装脚本重新 exec(stdin/stdout/stderr 全挂 tty) if [[ -e /dev/tty && -f "${local_mgr}" ]]; then echo "改用本机管理器启动: ${local_mgr}" exec bash "${local_mgr}" "$@" /dev/tty 2>/dev/tty fi echo "错误: 当前没有可用交互终端(curl|bash 的管道不能读菜单)。" >&2 echo "请改用:" >&2 echo " bash ${INSTALL_ROOT}/deploy/manage.sh" >&2 echo " bash ${INSTALL_ROOT}/deploy/manage.sh update" >&2 echo " curl -fsSL https://git.bz121.com/dekun/gold_monitor/raw/branch/main/deploy/manage.sh | bash -s -- update" >&2 exit 1 } read_menu_choice() { # set -e 下 read 遇 EOF 会直接退出,这里显式处理 local choice="" if ! IFS= read -r -p "请选择: " choice &2 echo "错误: 读不到键盘输入(终端未挂接成功)。" >&2 echo "请直接执行: bash ${INSTALL_ROOT}/deploy/manage.sh" >&2 exit 1 fi # 去掉 Windows 粘贴带来的 \\r choice="${choice//$'\r'/}" printf '%s' "${choice}" } run_update_quick() { export REPO_ROOT DEPLOY_DIR LIB_DIR INSTALL_ROOT GIT_URL GIT_BRANCH # shellcheck source=/dev/null source "${LIB_DIR}/common.sh" need_root echo "快速更新: git pull + pm2 restart" git -C "${REPO_ROOT}" pull --ff-only pm2 restart gold_monitor echo "更新完成" } show_banner() { local ip path path="${INSTALL_ROOT}" if repo_ready "${REPO_ROOT}"; then path="${REPO_ROOT}" fi if [[ -f "${LIB_DIR}/common.sh" ]]; then # shellcheck source=/dev/null source "${LIB_DIR}/common.sh" ip="$(detect_server_ip)" else ip="?" fi echo "" echo "╔══════════════════════════════════════╗" echo "║ gold_monitor 部署管理器 ║" echo "║ 路径: ${path}" echo "║ 端口: 5800 IP: ${ip}" echo "╚══════════════════════════════════════╝" echo "" } show_menu() { echo " 1) 一键部署(Web + API + PM2)" echo " 2) 一键卸载" echo " 3) 更新" echo " 0) 退出" echo "" } main_menu() { bootstrap_repo "$@" export REPO_ROOT DEPLOY_DIR LIB_DIR INSTALL_ROOT GIT_URL GIT_BRANCH local cmd="${1:-}" case "${cmd}" in update|3) # shellcheck source=/dev/null source "${LIB_DIR}/common.sh" run_update_quick exit 0 ;; update-deps) # shellcheck source=/dev/null source "${LIB_DIR}/common.sh" need_root git -C "${REPO_ROOT}" pull --ff-only bash "${REPO_ROOT}/deploy/setup_env.sh" pm2 restart gold_monitor echo "依赖更新完成" exit 0 ;; esac ensure_interactive_tty "$@" # shellcheck source=/dev/null source "${LIB_DIR}/common.sh" show_banner while true; do show_menu choice="$(read_menu_choice)" case "${choice}" in 1) bash "${LIB_DIR}/install.sh" ;; 2) bash "${LIB_DIR}/uninstall.sh" ;; 3) bash "${LIB_DIR}/update.sh" ;; 0) exit 0 ;; *) echo "无效选项: ${choice:-"(空)"}" ;; esac echo "" done } main_menu "$@"