Files
crypto_monitor/deploy/lib/common.sh
T
dekun 337a6bc54f Show pip progress bars during deploy instead of silent installs.
Replace -q with --progress-bar so dependency installation visibly continues in setup_env and update flows.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 12:42:31 +08:00

365 lines
9.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# deploy/lib/common.sh — 部署管理器公共函数
set -e
set -u
if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail
fi
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="${CM_TZ:-Asia/Shanghai}"
NODE_MAJOR="${NODE_MAJOR:-20}"
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
PM2_APPS=(
crypto_binance
crypto_gate
crypto_okx
manual-trading-hub
manual-agent-binance
manual-agent-okx
manual-agent-gate
)
CONFIG_PATHS=(
crypto_monitor_binance/.env
crypto_monitor_okx/.env
crypto_monitor_gate/.env
manual_trading_hub/.env
manual_trading_hub/hub_settings.json
)
INSTANCE_DIRS=(
crypto_monitor_binance
crypto_monitor_gate
crypto_monitor_okx
manual_trading_hub
)
log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
step() { echo ""; log "==> $*"; }
die() {
echo "错误: $*" >&2
exit 1
}
require_root() {
if [[ "$(id -u)" -ne 0 ]]; then
die "请使用 root 执行(推荐: sudo -i 后运行)"
fi
}
require_ubuntu() {
if [[ ! -f /etc/os-release ]]; then
log "警告: 未检测到 /etc/os-release,跳过 Ubuntu 版本检查"
return 0
fi
# shellcheck source=/dev/null
source /etc/os-release
if [[ "${ID:-}" != "ubuntu" ]]; then
log "警告: 当前系统为 ${ID:-unknown},官方仅测试 Ubuntu 22.04/24.04"
return 0
fi
local ver="${VERSION_ID:-}"
if [[ "${ver}" != "22.04" && "${ver}" != "24.04" ]]; then
log "警告: Ubuntu ${ver} 未在文档中明确测试,继续执行"
fi
}
detect_server_ip() {
local ip=""
if command -v hostname >/dev/null 2>&1; then
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
if [[ -z "${ip}" ]]; then
ip="127.0.0.1"
fi
echo "${ip}"
}
confirm_yes() {
local msg="$1"
local ans=""
cm_read ans "${msg} [y/N] "
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
}
confirm_uninstall() {
local ans=""
echo "此操作将停止全部 PM2 进程并移走安装目录."
cm_read ans "输入 UNINSTALL 确认卸载: "
[[ "${ans}" == "UNINSTALL" ]]
}
# curl | bash 时 stdin 是管道,须从 /dev/tty 读取用户输入
cm_read() {
local __var="$1"
local __prompt="$2"
local __val=""
if [[ -r /dev/tty ]]; then
IFS= read -r -p "${__prompt}" __val </dev/tty || true
else
IFS= read -r -p "${__prompt}" __val || true
fi
printf -v "${__var}" '%s' "${__val}"
}
repo_ready() {
[[ -f "${1}/deploy/setup_env.sh" && -f "${1}/deploy/manage.sh" ]]
}
resolve_repo_root() {
if repo_ready "${INSTALL_ROOT}"; then
echo "${INSTALL_ROOT}"
return 0
fi
if repo_ready "${REPO_ROOT}"; then
echo "${REPO_ROOT}"
return 0
fi
return 1
}
install_log_file() {
local stamp
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
mkdir -p "${BACKUP_ROOT}/deploy-logs"
echo "${BACKUP_ROOT}/deploy-logs/manage-${stamp}.log"
}
backup_configs_to() {
local src_root="$1"
local dest="$2"
mkdir -p "${dest}"
local rel copied=0
for rel in "${CONFIG_PATHS[@]}"; do
local src="${src_root}/${rel}"
if [[ -f "${src}" ]]; then
mkdir -p "${dest}/$(dirname "${rel}")"
cp -a "${src}" "${dest}/${rel}"
log "backup ${rel}"
copied=$((copied + 1))
fi
done
if [[ "${copied}" -eq 0 ]]; then
log "提示: 未找到可备份的配置文件"
fi
}
install_system_packages() {
step "安装系统依赖"
if ! command -v apt-get >/dev/null 2>&1; then
die "未检测到 apt-get,请手动安装 python3-venv git curl"
fi
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y python3 python3-pip python3-venv curl git ca-certificates
local pyver=""
if command -v python3 >/dev/null 2>&1; then
pyver="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
apt-get install -y "python${pyver}-venv" 2>/dev/null || apt-get install -y python3-venv
fi
}
install_node_pm2() {
step "检查 Node.js 与 PM2"
if command -v pm2 >/dev/null 2>&1; then
log "PM2 已安装: $(pm2 -v)"
return 0
fi
if ! command -v node >/dev/null 2>&1; then
log "安装 Node.js ${NODE_MAJOR}.x ..."
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
apt-get install -y nodejs
fi
log "安装 PM2 ..."
npm install -g pm2
log "PM2: $(pm2 -v)"
}
# pip 安装时显示进度条(避免 -q 静默导致用户以为卡死)
pip_progress_bar_arg() {
if [[ -t 1 ]] || [[ -t 2 ]]; then
echo "on"
else
echo "ascii"
fi
}
pip_upgrade_tools() {
local pip_bin="$1"
local bar
bar="$(pip_progress_bar_arg)"
echo " 升级 pip ..."
"${pip_bin}" install -U pip setuptools wheel \
--disable-pip-version-check \
--progress-bar "${bar}"
}
pip_install_requirements() {
local pip_bin="$1"
local req_file="$2"
local label="${3:-依赖}"
local bar
bar="$(pip_progress_bar_arg)"
echo " 安装${label} (下方为 pip 进度) ..."
[[ -f "${req_file}" ]] || die "缺少依赖文件: ${req_file}"
"${pip_bin}" install -r "${req_file}" \
--disable-pip-version-check \
--progress-bar "${bar}"
echo " ${label}安装完成"
}
install_backup_cron_all() {
step "安装三所每日备份 cron"
local dir
for dir in crypto_monitor_binance crypto_monitor_gate crypto_monitor_okx; do
local inst="${REPO_ROOT}/${dir}/scripts/install_backup_cron.sh"
if [[ -f "${inst}" ]]; then
chmod +x "${inst}"
bash "${inst}" || log "警告: ${dir} cron 安装失败"
fi
done
}
remove_backup_cron_all() {
step "移除三所备份 cron"
local tmp removed=0
tmp="$(mktemp)"
if ! crontab -l 2>/dev/null >"${tmp}"; then
rm -f "${tmp}"
return 0
fi
local filtered
filtered="$(grep -vF "backup_data.sh" "${tmp}" || true)"
if [[ "${filtered}" != "$(cat "${tmp}")" ]]; then
printf '%s\n' "${filtered}" | awk '
BEGIN { tz = 0 }
/^CRON_TZ=Asia\/Shanghai$/ {
if (tz++) next
}
{ print }
' | crontab -
removed=1
fi
rm -f "${tmp}"
if [[ "${removed}" -eq 1 ]]; then
log "已移除 backup_data.sh 相关 cron"
fi
}
pm2_save_startup() {
step "PM2 save & startup"
pm2 save 2>/dev/null || true
if pm2 startup systemd -u root --hp /root 2>/dev/null | grep -q "sudo"; then
pm2 startup systemd -u root --hp /root 2>/dev/null | grep "^sudo" | bash || true
else
pm2 startup 2>/dev/null || true
fi
}
pm2_count_online() {
local name online=0
if ! command -v pm2 >/dev/null 2>&1; then
echo "0"
return 0
fi
for name in "${PM2_APPS[@]}"; do
if pm2 pid "${name}" >/dev/null 2>&1; then
online=$((online + 1))
fi
done
echo "${online}"
}
is_deployed() {
local root="$1"
[[ -x "${root}/crypto_monitor_binance/.venv/bin/python" ]] \
|| [[ -x "${root}/manual_trading_hub/.venv/bin/python" ]]
}
check_http() {
local url="$1"
local code
code="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 5 "${url}" 2>/dev/null || echo "000")"
[[ "${code}" == "200" || "${code}" == "302" || "${code}" == "301" ]]
}
verify_deployment() {
local ip="${1:-$(detect_server_ip)}"
local ok=1
local online total
step "部署验收"
total="${#PM2_APPS[@]}"
online="$(pm2_count_online)"
if [[ "${online}" -eq "${total}" ]]; then
echo " [✓] PM2 进程 ${online}/${total} online"
else
echo " [✗] PM2 进程 ${online}/${total} online"
ok=0
pm2 list 2>/dev/null || true
fi
local checks=(
"中控:http://127.0.0.1:5100/"
"Binance:http://127.0.0.1:5001/"
"Gate:http://127.0.0.1:5000/"
"OKX:http://127.0.0.1:5004/"
)
local item label url
for item in "${checks[@]}"; do
label="${item%%:*}"
url="${item#*:}"
if check_http "${url}"; then
echo " [✓] ${label} 可访问"
else
echo " [✗] ${label} 不可访问 (${url})"
ok=0
fi
done
if [[ "${ok}" -eq 1 ]]; then
echo ""
log "验收通过: 进程都在 + 页面可打开"
return 0
fi
echo ""
log "验收未完全通过,可执行 pm2 logs <进程名> --lines 30 排查"
return 1
}
print_post_install_guide() {
local ip="${1:-$(detect_server_ip)}"
cat <<EOF
═══════════════════════════════════════════
✅ 部署完成
中控: http://${ip}:5100
Binance: http://${ip}:5001
Gate: http://${ip}:5000
OKX: http://${ip}:5004
登录账号: admin
登录密码: admin123
(请尽快在「系统设置」中修改)
下一步(浏览器):
1. 中控 → 系统设置 → 确认账户 URL(有域名时改 flask_url
2. 各所 → env 配置 → 填 API、风控、企业微信
3. 中控 → AI 配置(如需复盘)
4. 各所 env 配置 → 开启实盘下单 → 保存并重启
agent_url 默认本机 127.0.0.1:15200/15201/15202,一般无需修改
═══════════════════════════════════════════
EOF
}