diff --git a/deploy/lib/common.sh b/deploy/lib/common.sh index 0a87cd5..b8b94aa 100755 --- a/deploy/lib/common.sh +++ b/deploy/lib/common.sh @@ -84,22 +84,128 @@ resolve_repo_root() { return 1 } -ensure_system_deps() { - step "检查系统依赖" +# ---------- 运行时检测(有则跳过,无则安装) ---------- + +python_runtime_ok() { + command -v python3 >/dev/null 2>&1 || return 1 + # 能创建 venv 即可;pip 在项目 .venv 内安装 + python3 -c "import venv" >/dev/null 2>&1 || return 1 + return 0 +} + +node_runtime_ok() { + command -v node >/dev/null 2>&1 || return 1 + command -v npm >/dev/null 2>&1 || return 1 + return 0 +} + +pm2_runtime_ok() { + command -v pm2 >/dev/null 2>&1 +} + +print_runtime_probe() { + echo "环境检测:" + if python_runtime_ok; then + echo " [OK] Python3 + venv → $(python3 --version 2>&1)" + else + echo " [缺] Python3 / venv → 将自动安装" + fi + if node_runtime_ok; then + echo " [OK] Node.js + npm → $(node --version 2>&1) / npm $(npm --version 2>&1)" + else + echo " [缺] Node.js / npm → 将自动安装 Node ${NODE_MAJOR}" + fi + if pm2_runtime_ok; then + echo " [OK] PM2 → $(pm2 -v 2>&1)" + else + echo " [缺] PM2 → 将自动安装" + fi +} + +install_python_runtime() { + step "安装 Python3 环境 (python3 / venv / pip)" export DEBIAN_FRONTEND=noninteractive - if command -v apt-get >/dev/null 2>&1; then - apt-get update -qq - apt-get install -y git ca-certificates curl python3 python3-venv python3-pip + if ! command -v apt-get >/dev/null 2>&1; then + die "非 apt 系统,请手动安装 python3 python3-venv python3-pip" fi - if ! command -v node >/dev/null 2>&1; then - step "安装 Node.js ${NODE_MAJOR}" - curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - - apt-get install -y nodejs + apt-get update -qq + apt-get install -y python3 python3-venv python3-pip + if ! python_runtime_ok; then + die "Python 环境安装后仍不可用,请检查 apt 源" fi - if ! command -v pm2 >/dev/null 2>&1; then - step "安装 PM2" - npm install -g pm2 + log "Python 已就绪: $(python3 --version 2>&1)" +} + +install_node_runtime() { + step "安装 Node.js ${NODE_MAJOR}" + export DEBIAN_FRONTEND=noninteractive + if ! command -v apt-get >/dev/null 2>&1; then + die "非 apt 系统,请手动安装 Node.js ${NODE_MAJOR}+" fi + apt-get update -qq + apt-get install -y ca-certificates curl + curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - + apt-get install -y nodejs + if ! node_runtime_ok; then + die "Node.js 安装后仍不可用" + fi + log "Node 已就绪: $(node --version 2>&1)" +} + +install_pm2_runtime() { + step "安装 PM2" + if ! command -v npm >/dev/null 2>&1; then + die "安装 PM2 需要先有 npm" + fi + npm install -g pm2 + if ! pm2_runtime_ok; then + die "PM2 安装后仍不可用" + fi + log "PM2 已就绪: $(pm2 -v 2>&1)" +} + +ensure_base_packages() { + local need=() + command -v git >/dev/null 2>&1 || need+=(git) + command -v curl >/dev/null 2>&1 || need+=(curl) + if [[ ${#need[@]} -eq 0 ]]; then + log "基础包 git/curl 已存在,跳过" + return 0 + fi + if ! command -v apt-get >/dev/null 2>&1; then + die "缺少依赖: ${need[*]},且无 apt-get" + fi + step "安装基础包: ${need[*]}" + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -y ca-certificates "${need[@]}" +} + +ensure_system_deps() { + step "环境检测 (Python / Node / PM2)" + print_runtime_probe + ensure_base_packages + + if python_runtime_ok; then + log "Python 环境已存在,跳过安装" + else + install_python_runtime + fi + + if node_runtime_ok; then + log "Node.js 环境已存在,跳过安装" + else + install_node_runtime + fi + + if pm2_runtime_ok; then + log "PM2 已存在,跳过安装" + else + install_pm2_runtime + fi + + step "环境确认" + print_runtime_probe } pm2_stop_project() { diff --git a/deploy/lib/install.sh b/deploy/lib/install.sh index 437fc6b..c420a18 100755 --- a/deploy/lib/install.sh +++ b/deploy/lib/install.sh @@ -22,6 +22,7 @@ run_pipeline() { install_fresh() { require_root + step "一键部署 — 环境检测与依赖" ensure_system_deps step "克隆仓库 → ${INSTALL_ROOT}" if [[ -d "${INSTALL_ROOT}" ]]; then @@ -34,6 +35,7 @@ install_fresh() { install_repair() { require_root + step "修复部署 — 环境检测与依赖" ensure_system_deps if ! REPO_ROOT="$(resolve_repo_root)"; then die "未找到安装目录 ${INSTALL_ROOT}" diff --git a/deploy/lib/uninstall.sh b/deploy/lib/uninstall.sh index 0790a19..fd67007 100755 --- a/deploy/lib/uninstall.sh +++ b/deploy/lib/uninstall.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# deploy/lib/uninstall.sh — 一键卸载(仅本项目 PM2,不动其它进程) +# deploy/lib/uninstall.sh — 一键卸载(仅本项目 PM2,删除 /opt 安装目录) set -e set -u if [ -n "${BASH_VERSION:-}" ]; then @@ -26,22 +26,21 @@ main_uninstall() { echo "" echo "将卸载 比特骆驼自动化对冲系统 (eth_hedge_sim):" echo " - 停止并删除 PM2 进程: ${PM2_APP_NAME}" - echo " - 移走目录: ${INSTALL_ROOT}" - echo " - 备份 .env 到 ${BACKUP_ROOT}" - echo " - 不影响其它 PM2 应用" + echo " - 删除安装目录: ${INSTALL_ROOT}" + echo " - 备份 .env / 数据库到 ${BACKUP_ROOT}" + echo " - 不影响其它 PM2 应用 / 不卸载系统 Python、Node、PM2" echo "" - if ! confirm_yes "确认卸载?"; then + if ! confirm_yes "确认卸载并删除 ${INSTALL_ROOT}?"; then log "已取消卸载" return 0 fi - local stamp backup_dir removed_dir + local stamp backup_dir stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)" backup_dir="${BACKUP_ROOT}/pre-uninstall-${stamp}" - removed_dir="${INSTALL_ROOT}.removed.${stamp}" mkdir -p "${backup_dir}" - step "备份配置" + step "备份配置与数据" if [[ -f "${REPO_ROOT}/.env" ]]; then cp -a "${REPO_ROOT}/.env" "${backup_dir}/.env" fi @@ -51,30 +50,51 @@ main_uninstall() { { echo "created_at=${stamp}" echo "install_root=${INSTALL_ROOT}" - echo "removed_dir=${removed_dir}" echo "pm2_app=${PM2_APP_NAME}" + echo "action=rm_rf_install_root" } >"${backup_dir}/uninstall.manifest" step "停止并移除 PM2 进程 ${PM2_APP_NAME}" pm2_stop_project pm2_delete_project - step "移走安装目录" + step "删除安装目录 ${INSTALL_ROOT}" if [[ -d "${INSTALL_ROOT}" ]]; then - mv "${INSTALL_ROOT}" "${removed_dir}" - log "已移动: ${INSTALL_ROOT} -> ${removed_dir}" + # 安全:只允许删除约定安装路径(默认 /opt/eth_hedge_sim) + case "${INSTALL_ROOT}" in + /opt/eth_hedge_sim|/opt/eth_hedge_sim/) + rm -rf "${INSTALL_ROOT}" + log "已删除: ${INSTALL_ROOT}" + ;; + *) + if [[ "${ALLOW_UNSAFE_UNINSTALL:-}" == "1" ]]; then + rm -rf "${INSTALL_ROOT}" + log "已删除(ALLOW_UNSAFE_UNINSTALL=1): ${INSTALL_ROOT}" + else + die "拒绝删除非默认路径 ${INSTALL_ROOT};若确认,设置 ALLOW_UNSAFE_UNINSTALL=1" + fi + ;; + esac else - log "安装目录不存在,跳过" + log "安装目录不存在,跳过删除" fi + # 清理历史「移走未删」残留 + local leftover + for leftover in /opt/eth_hedge_sim.removed.* /opt/eth_hedge_sim.old.*; do + if [[ -e "${leftover}" ]]; then + rm -rf "${leftover}" + log "已清理残留: ${leftover}" + fi + done + echo "" echo "卸载完成." - echo " 配置备份: ${backup_dir}" - echo " 旧目录: ${removed_dir} (确认无误后可手动 rm -rf)" + echo " 配置/数据备份: ${backup_dir}" + echo " 安装目录已删除: ${INSTALL_ROOT}" echo "" - echo "回滚示例:" - echo " mv ${removed_dir} ${INSTALL_ROOT}" - echo " bash ${INSTALL_ROOT}/deploy/manage.sh # 选 1 修复环境" + echo "重新部署:" + echo " curl -fsSL https://git.bz121.com/dekun/eth_hedge_sim/raw/branch/main/deploy/manage.sh | bash" } main_uninstall "$@" diff --git a/deploy/lib/update.sh b/deploy/lib/update.sh index 1c43aa3..76b0dc8 100755 --- a/deploy/lib/update.sh +++ b/deploy/lib/update.sh @@ -19,6 +19,8 @@ main_update() { die "安装不完整,请先执行「1) 一键部署」" fi + step "更新 — 环境检测 (缺则装,有则跳过)" + ensure_system_deps step "更新 比特骆驼自动化对冲系统 (git pull + 构建 + pm2 reload)" bash "${REPO_ROOT}/deploy/pull_and_restart.sh" verify_health || true diff --git a/deploy/pull_and_restart.sh b/deploy/pull_and_restart.sh index 66fd5f2..31f4565 100755 --- a/deploy/pull_and_restart.sh +++ b/deploy/pull_and_restart.sh @@ -8,6 +8,13 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" +# 有 root 时:检测 Python / Node / PM2,缺则装、有则跳过 +if [[ -f "$ROOT/deploy/lib/common.sh" ]] && [[ "$(id -u)" -eq 0 ]]; then + # shellcheck source=lib/common.sh + source "$ROOT/deploy/lib/common.sh" + ensure_system_deps +fi + echo "[比特骆驼] pull @ $ROOT" if [[ -d "$ROOT/.git" ]]; then git fetch --all --prune @@ -25,7 +32,13 @@ if [[ ! -f "$ROOT/.env" ]]; then echo "[比特骆驼] wrote .env — please review AUTH_* secrets" fi +if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 not found. 请用 manage.sh 一键部署自动安装。" + exit 1 +fi + if [[ ! -d "$ROOT/.venv" ]]; then + echo "[比特骆驼] create venv" python3 -m venv "$ROOT/.venv" fi # shellcheck disable=SC1091 @@ -45,7 +58,10 @@ fi ) if ! command -v pm2 >/dev/null 2>&1; then + echo "[比特骆驼] install pm2" npm install -g pm2 +else + echo "[比特骆驼] pm2 already present, skip install" fi # 仅 reload 本项目进程,禁止 pm2 restart all diff --git a/docs/更新说明.md b/docs/更新说明.md index 6fa70f0..5a12f00 100644 --- a/docs/更新说明.md +++ b/docs/更新说明.md @@ -5,6 +5,15 @@ --- +## 2026-07-26 — 一键部署环境检测 / 卸载删目录 + +### 变更 + +1. **一键部署 / 更新**:检测 Python3+venv、Node/npm、PM2;**已有则跳过**,缺失才 apt/npm 安装。 +2. **一键卸载**:备份 `.env` 与数据库后,**直接删除** `/opt/eth_hedge_sim`(并清理历史 `.removed` / `.old` 残留);不卸载系统级 Python/Node/PM2。 + +--- + ## 2026-07-26 — 自动备份 / 下载 / 上传恢复 ### 变更