feat: 新增 Plan B 整目录重装脚本,不影响 setup_env 一键安装
添加 deploy/reinstall.sh 备份 env、克隆、调 setup_env、恢复配置并 PM2 启动; 附带 pm2_start_all.sh 与 hub_settings 清理工具。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/env bash
|
||||
# Plan B:整目录重装 /opt/crypto_monitor(备份 .env → 移走旧目录 → git clone → setup_env → 恢复配置 → PM2)
|
||||
#
|
||||
# 与 deploy/setup_env.sh 分工:
|
||||
# setup_env.sh — 首次 / 日常:建 venv、装依赖、复制 .env.example(一键安装,不变)
|
||||
# reinstall.sh — 生产清库重装:保留密钥与 hub 配置,丢弃旧代码/旧库/脏 PM2
|
||||
#
|
||||
# 用法(在现有安装目录以 root 执行):
|
||||
# cd /opt/crypto_monitor
|
||||
# bash deploy/reinstall.sh # 交互确认
|
||||
# bash deploy/reinstall.sh --yes # 跳过确认
|
||||
# bash deploy/reinstall.sh --dry-run # 仅打印步骤
|
||||
#
|
||||
# 可选环境变量:
|
||||
# INSTALL_ROOT=/opt/crypto_monitor
|
||||
# GIT_URL=https://git.bz121.com/dekun/crypto_monitor.git
|
||||
# GIT_BRANCH=main
|
||||
# BACKUP_ROOT=/root/backups
|
||||
#
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_SOURCE="${DEPLOY_DIR}/reinstall.sh"
|
||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||
|
||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_monitor}"
|
||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_monitor.git}"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}"
|
||||
TZ_NAME="${REINSTALL_TZ:-Asia/Shanghai}"
|
||||
|
||||
ASSUME_YES=0
|
||||
DRY_RUN=0
|
||||
INSTALL_BACKUP_CRON=1
|
||||
|
||||
CONFIG_PATHS=(
|
||||
"crypto_monitor_binance/.env"
|
||||
"crypto_monitor_okx/.env"
|
||||
"crypto_monitor_gate/.env"
|
||||
"manual_trading_hub/.env"
|
||||
"manual_trading_hub/hub_settings.json"
|
||||
)
|
||||
|
||||
usage() {
|
||||
sed -n '2,18p' "$0" | sed 's/^# \?//'
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--yes|-y) ASSUME_YES=1; shift ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
--no-backup-cron) INSTALL_BACKUP_CRON=0; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) echo "未知参数: $1" >&2; usage 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||
step() { echo ""; log "==> $*"; }
|
||||
|
||||
run() {
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] $*"
|
||||
return 0
|
||||
fi
|
||||
log "+ $*"
|
||||
"$@"
|
||||
}
|
||||
|
||||
confirm() {
|
||||
if [[ "${ASSUME_YES}" -eq 1 || "${DRY_RUN}" -eq 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
local msg="$1"
|
||||
read -r -p "${msg} [y/N] " ans
|
||||
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
|
||||
}
|
||||
|
||||
resolve_path() {
|
||||
local base="$1"
|
||||
local rel="$2"
|
||||
printf '%s/%s' "${base}" "${rel}"
|
||||
}
|
||||
|
||||
backup_configs() {
|
||||
local src_root="$1"
|
||||
local dest="$2"
|
||||
mkdir -p "${dest}"
|
||||
local rel copied=0
|
||||
for rel in "${CONFIG_PATHS[@]}"; do
|
||||
local src
|
||||
src="$(resolve_path "${src_root}" "${rel}")"
|
||||
if [[ -f "${src}" ]]; then
|
||||
mkdir -p "${dest}/$(dirname "${rel}")"
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] backup ${src} -> ${dest}/${rel}"
|
||||
else
|
||||
cp -a "${src}" "${dest}/${rel}"
|
||||
log "backup ${rel}"
|
||||
fi
|
||||
copied=$((copied + 1))
|
||||
else
|
||||
log "skip (missing): ${rel}"
|
||||
fi
|
||||
done
|
||||
if [[ "${copied}" -eq 0 ]]; then
|
||||
echo "错误: 未备份到任何配置文件,请检查 ${src_root}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -f "${src_root}/scripts/one_shot_backup_config_before_cleanup.py" ]]; then
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] python3 scripts/one_shot_backup_config_before_cleanup.py (in ${src_root})"
|
||||
else
|
||||
(cd "${src_root}" && python3 scripts/one_shot_backup_config_before_cleanup.py) || true
|
||||
if compgen -G "${src_root}/backups/one-shot-*" >/dev/null; then
|
||||
cp -a "${src_root}"/backups/one-shot-* "${dest}/" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ "${DRY_RUN}" -eq 0 ]]; then
|
||||
{
|
||||
echo "created_at=${STAMP}"
|
||||
echo "install_root=${INSTALL_ROOT}"
|
||||
echo "old_dir=${OLD_DIR}"
|
||||
echo "git_url=${GIT_URL}"
|
||||
echo "git_branch=${GIT_BRANCH}"
|
||||
echo "script=${SCRIPT_SOURCE}"
|
||||
} >"${dest}/reinstall.manifest"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_configs() {
|
||||
local backup_dir="$1"
|
||||
local dest_root="$2"
|
||||
local rel
|
||||
for rel in "${CONFIG_PATHS[@]}"; do
|
||||
local src dest
|
||||
src="${backup_dir}/${rel}"
|
||||
dest="$(resolve_path "${dest_root}" "${rel}")"
|
||||
if [[ -f "${src}" ]]; then
|
||||
mkdir -p "$(dirname "${dest}")"
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] restore ${src} -> ${dest}"
|
||||
else
|
||||
cp -a "${src}" "${dest}"
|
||||
log "restore ${rel}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
local hub_settings
|
||||
hub_settings="$(resolve_path "${dest_root}" "manual_trading_hub/hub_settings.json")"
|
||||
if [[ -f "${hub_settings}" && "${DRY_RUN}" -eq 0 ]]; then
|
||||
python3 "${dest_root}/deploy/sanitize_hub_settings.py" "${hub_settings}" || true
|
||||
fi
|
||||
}
|
||||
|
||||
install_instance_backup_cron() {
|
||||
local dest_root="$1"
|
||||
local dir
|
||||
for dir in crypto_monitor_binance crypto_monitor_gate crypto_monitor_okx; do
|
||||
local proj="${dest_root}/${dir}"
|
||||
local inst="${proj}/scripts/install_backup_cron.sh"
|
||||
local data="${proj}/scripts/backup_data.sh"
|
||||
if [[ -f "${inst}" && -f "${data}" ]]; then
|
||||
chmod +x "${inst}" "${data}"
|
||||
run bash "${inst}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
verify_pm2() {
|
||||
log "预期 PM2 进程(7 个): crypto_binance crypto_gate crypto_okx manual-trading-hub manual-agent-*"
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
pm2 list || true
|
||||
if pm2 list 2>/dev/null | grep -qiE 'gate_bot|15203'; then
|
||||
log "警告: PM2 列表仍含 gate_bot 相关进程,请 pm2 delete 后 pm2 save"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 前置检查 ---
|
||||
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "请使用 root 执行(推荐路径 ${INSTALL_ROOT})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${REPO_ROOT}/deploy/setup_env.sh" ]]; then
|
||||
echo "当前脚本不在有效仓库内: ${REPO_ROOT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${REPO_ROOT}" != "${INSTALL_ROOT}" ]]; then
|
||||
log "提示: 当前仓库 ${REPO_ROOT} 与 INSTALL_ROOT=${INSTALL_ROOT} 不一致;将备份当前仓库并克隆到 INSTALL_ROOT"
|
||||
fi
|
||||
|
||||
STAMP="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
|
||||
BACKUP_DIR="${BACKUP_ROOT}/pre-reinstall-${STAMP}"
|
||||
OLD_DIR="${INSTALL_ROOT}.old.${STAMP}"
|
||||
SRC_ROOT="${REPO_ROOT}"
|
||||
|
||||
if [[ -d "${INSTALL_ROOT}" && "${REPO_ROOT}" != "${INSTALL_ROOT}" ]]; then
|
||||
SRC_ROOT="${INSTALL_ROOT}"
|
||||
fi
|
||||
|
||||
step "计划"
|
||||
echo " 备份目录: ${BACKUP_DIR}"
|
||||
echo " 配置来源: ${SRC_ROOT}"
|
||||
echo " 旧目录移走: ${OLD_DIR}"
|
||||
echo " 新克隆: ${GIT_URL} (${GIT_BRANCH}) -> ${INSTALL_ROOT}"
|
||||
echo " 环境: deploy/setup_env.sh --skip-env-copy --recreate-venv --skip-pm2"
|
||||
echo ""
|
||||
echo " 将停止并 delete 全部 PM2 进程;不备份 crypto.db / hub data / 图片。"
|
||||
|
||||
if ! confirm "确认执行 Plan B 整目录重装?"; then
|
||||
log "已取消"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- 1. 备份 ---
|
||||
|
||||
step "备份配置到 ${BACKUP_DIR}"
|
||||
backup_configs "${SRC_ROOT}" "${BACKUP_DIR}"
|
||||
|
||||
# --- 2. 停 PM2 ---
|
||||
|
||||
step "停止并清空 PM2"
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
run pm2 stop all || true
|
||||
run pm2 delete all || true
|
||||
else
|
||||
log "未安装 pm2,跳过"
|
||||
fi
|
||||
|
||||
# --- 3. 移走旧目录 ---
|
||||
|
||||
step "移走旧安装 ${INSTALL_ROOT} -> ${OLD_DIR}"
|
||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] mv ${INSTALL_ROOT} ${OLD_DIR}"
|
||||
else
|
||||
mv "${INSTALL_ROOT}" "${OLD_DIR}"
|
||||
fi
|
||||
else
|
||||
log "目标目录不存在,跳过 mv"
|
||||
fi
|
||||
|
||||
# --- 4. 克隆 ---
|
||||
|
||||
step "git clone"
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] git clone -b ${GIT_BRANCH} ${GIT_URL} ${INSTALL_ROOT}"
|
||||
else
|
||||
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||
fi
|
||||
|
||||
# --- 5. setup_env(一键安装逻辑,不复制 .env)---
|
||||
|
||||
step "重建 Python 虚拟环境 (setup_env.sh)"
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
log "[dry-run] bash ${INSTALL_ROOT}/deploy/setup_env.sh --skip-env-copy --recreate-venv --skip-pm2"
|
||||
else
|
||||
bash "${INSTALL_ROOT}/deploy/setup_env.sh" --skip-env-copy --recreate-venv --skip-pm2
|
||||
fi
|
||||
|
||||
# --- 6. 恢复配置 ---
|
||||
|
||||
step "恢复 .env 与 hub_settings.json"
|
||||
restore_configs "${BACKUP_DIR}" "${INSTALL_ROOT}"
|
||||
|
||||
# --- 7. PM2 启动 ---
|
||||
|
||||
step "PM2 启动全部进程"
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
run bash "${INSTALL_ROOT}/deploy/pm2_start_all.sh"
|
||||
run pm2 save
|
||||
else
|
||||
log "未安装 pm2;请手动: bash ${INSTALL_ROOT}/deploy/pm2_start_all.sh"
|
||||
fi
|
||||
|
||||
# --- 8. 定时备份 cron(可选)---
|
||||
|
||||
if [[ "${INSTALL_BACKUP_CRON}" -eq 1 ]]; then
|
||||
step "安装三所每日备份 cron"
|
||||
install_instance_backup_cron "${INSTALL_ROOT}"
|
||||
fi
|
||||
|
||||
# --- 完成 ---
|
||||
|
||||
step "完成"
|
||||
verify_pm2
|
||||
echo ""
|
||||
echo "备份: ${BACKUP_DIR}"
|
||||
echo "旧目录(确认无误后可删): ${OLD_DIR}"
|
||||
echo ""
|
||||
echo "验收建议:"
|
||||
echo " pm2 list"
|
||||
echo " curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5100/"
|
||||
echo " 浏览器打开中控 /monitor,确认三所 LINK 正常"
|
||||
echo ""
|
||||
echo "回滚(未删旧目录时):"
|
||||
echo " pm2 delete all"
|
||||
echo " rm -rf ${INSTALL_ROOT}"
|
||||
echo " mv ${OLD_DIR} ${INSTALL_ROOT}"
|
||||
echo " cp -a ${BACKUP_DIR}/*/ ${INSTALL_ROOT}/ # 若需恢复配置"
|
||||
echo " bash ${INSTALL_ROOT}/deploy/pm2_start_all.sh"
|
||||
Reference in New Issue
Block a user