fix: normalize deploy shell scripts to LF for Linux curl|bash

CRLF line endings broke manage.sh on Ubuntu (set -e and function syntax errors).
Add .gitattributes to keep *.sh on LF in git.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-02 08:41:15 +08:00
parent d9a34d4f20
commit 4743f3efdd
7 changed files with 825 additions and 816 deletions
+9
View File
@@ -0,0 +1,9 @@
# Shell scripts must use LF (Linux deploy via curl | bash)
*.sh text eol=lf
# Docker / compose
Dockerfile text eol=lf
docker-compose.yml text eol=lf
# Default for text
* text=auto
+5 -5
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# 兼容入口:转发到 manage.sh # 兼容入口:转发到 manage.sh
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)" ROOT="$(cd "$(dirname "$0")/.." && pwd)"
exec bash "${ROOT}/deploy/manage.sh" "$@" exec bash "${ROOT}/deploy/manage.sh" "$@"
+358 -358
View File
@@ -1,358 +1,358 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# deploy/lib/common.sh — market_intel 部署公共函数(Docker Compose # deploy/lib/common.sh — market_intel 部署公共函数(Docker Compose
# 目标系统: Ubuntu 22.04 LTS # 目标系统: Ubuntu 22.04 LTS
set -e set -e
set -u set -u
if [ -n "${BASH_VERSION:-}" ]; then if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail set -o pipefail
fi fi
INSTALL_ROOT="${INSTALL_ROOT:-/opt/market_intel}" INSTALL_ROOT="${INSTALL_ROOT:-/opt/market_intel}"
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/market_intel.git}" GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/market_intel.git}"
GIT_BRANCH="${GIT_BRANCH:-main}" GIT_BRANCH="${GIT_BRANCH:-main}"
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups/market_intel}" BACKUP_ROOT="${BACKUP_ROOT:-/root/backups/market_intel}"
TZ_NAME="${MI_TZ:-Asia/Shanghai}" TZ_NAME="${MI_TZ:-Asia/Shanghai}"
MI_PORT_DEFAULT="${MI_PORT_DEFAULT:-5170}" MI_PORT_DEFAULT="${MI_PORT_DEFAULT:-5170}"
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)" DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; } log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
step() { echo ""; log "==> $*"; } step() { echo ""; log "==> $*"; }
die() { die() {
echo "错误: $*" >&2 echo "错误: $*" >&2
exit 1 exit 1
} }
require_root() { require_root() {
if [[ "$(id -u)" -ne 0 ]]; then if [[ "$(id -u)" -ne 0 ]]; then
die "请使用 root 执行(推荐: sudo -i 后运行)" die "请使用 root 执行(推荐: sudo -i 后运行)"
fi fi
} }
_apt_lock_holders() { _apt_lock_holders() {
ps -eo pid,cmd 2>/dev/null | grep -E '[u]nattended-upgr|[a]pt-get|[a]pt |[d]pkg ' | head -n 8 || true ps -eo pid,cmd 2>/dev/null | grep -E '[u]nattended-upgr|[a]pt-get|[a]pt |[d]pkg ' | head -n 8 || true
if command -v fuser >/dev/null 2>&1; then if command -v fuser >/dev/null 2>&1; then
fuser -v /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/lib/apt/lists/lock 2>&1 | head -n 12 || true fuser -v /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/lib/apt/lists/lock 2>&1 | head -n 12 || true
fi fi
} }
_stop_auto_apt_for_deploy() { _stop_auto_apt_for_deploy() {
if [[ "${APT_STOP_AUTO:-1}" != "1" ]]; then if [[ "${APT_STOP_AUTO:-1}" != "1" ]]; then
return 0 return 0
fi fi
if command -v systemctl >/dev/null 2>&1; then if command -v systemctl >/dev/null 2>&1; then
log "临时停止 unattended-upgrades / apt-daily,避免占锁…" log "临时停止 unattended-upgrades / apt-daily,避免占锁…"
systemctl stop unattended-upgrades.service 2>/dev/null || true systemctl stop unattended-upgrades.service 2>/dev/null || true
systemctl stop apt-daily.service apt-daily-upgrade.service 2>/dev/null || true systemctl stop apt-daily.service apt-daily-upgrade.service 2>/dev/null || true
systemctl kill --kill-who=all unattended-upgrades.service 2>/dev/null || true systemctl kill --kill-who=all unattended-upgrades.service 2>/dev/null || true
fi fi
if [[ "${APT_FORCE_UNLOCK:-0}" == "1" ]]; then if [[ "${APT_FORCE_UNLOCK:-0}" == "1" ]]; then
log "APT_FORCE_UNLOCK=1:结束残留 apt/dpkg 进程…" log "APT_FORCE_UNLOCK=1:结束残留 apt/dpkg 进程…"
pkill -9 -x unattended-upgr 2>/dev/null || true pkill -9 -x unattended-upgr 2>/dev/null || true
pkill -9 -x apt-get 2>/dev/null || true pkill -9 -x apt-get 2>/dev/null || true
pkill -9 -x apt 2>/dev/null || true pkill -9 -x apt 2>/dev/null || true
pkill -9 -x dpkg 2>/dev/null || true pkill -9 -x dpkg 2>/dev/null || true
sleep 2 sleep 2
dpkg --configure -a 2>/dev/null || true dpkg --configure -a 2>/dev/null || true
fi fi
} }
wait_for_apt_lock() { wait_for_apt_lock() {
local max_wait="${1:-600}" local max_wait="${1:-600}"
local waited=0 local waited=0
local tried_stop=0 local tried_stop=0
if ! command -v apt-get >/dev/null 2>&1; then if ! command -v apt-get >/dev/null 2>&1; then
return 0 return 0
fi fi
while true; do while true; do
local busy=0 local busy=0
if pgrep -x unattended-upgr >/dev/null 2>&1 \ if pgrep -x unattended-upgr >/dev/null 2>&1 \
|| pgrep -x apt-get >/dev/null 2>&1 \ || pgrep -x apt-get >/dev/null 2>&1 \
|| pgrep -x apt >/dev/null 2>&1 \ || pgrep -x apt >/dev/null 2>&1 \
|| pgrep -x dpkg >/dev/null 2>&1; then || pgrep -x dpkg >/dev/null 2>&1; then
busy=1 busy=1
fi fi
if command -v fuser >/dev/null 2>&1; then if command -v fuser >/dev/null 2>&1; then
if fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 \ if fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 \
|| fuser /var/lib/dpkg/lock >/dev/null 2>&1 \ || fuser /var/lib/dpkg/lock >/dev/null 2>&1 \
|| fuser /var/lib/apt/lists/lock >/dev/null 2>&1; then || fuser /var/lib/apt/lists/lock >/dev/null 2>&1; then
busy=1 busy=1
fi fi
fi fi
if [[ "${busy}" -eq 0 ]]; then if [[ "${busy}" -eq 0 ]]; then
[[ "${waited}" -gt 0 ]] && log "apt 锁已释放,继续安装" [[ "${waited}" -gt 0 ]] && log "apt 锁已释放,继续安装"
return 0 return 0
fi fi
if [[ "${waited}" -eq 0 ]]; then if [[ "${waited}" -eq 0 ]]; then
log "检测到 apt/dpkg 正被占用,等待释放…" log "检测到 apt/dpkg 正被占用,等待释放…"
_apt_lock_holders | while IFS= read -r line; do log " ${line}"; done _apt_lock_holders | while IFS= read -r line; do log " ${line}"; done
elif [[ "${tried_stop}" -eq 0 && "${waited}" -ge 15 ]]; then elif [[ "${tried_stop}" -eq 0 && "${waited}" -ge 15 ]]; then
tried_stop=1 tried_stop=1
_stop_auto_apt_for_deploy _stop_auto_apt_for_deploy
elif [[ $((waited % 60)) -eq 0 ]]; then elif [[ $((waited % 60)) -eq 0 ]]; then
log "仍在等待 apt 锁…已等 ${waited}s / ${max_wait}s" log "仍在等待 apt 锁…已等 ${waited}s / ${max_wait}s"
fi fi
if [[ "${waited}" -ge "${max_wait}" ]]; then if [[ "${waited}" -ge "${max_wait}" ]]; then
die "等待 apt 锁超时(${max_wait}s)" die "等待 apt 锁超时(${max_wait}s)"
fi fi
sleep 5 sleep 5
waited=$((waited + 5)) waited=$((waited + 5))
done done
} }
apt_update() { apt_update() {
wait_for_apt_lock wait_for_apt_lock
apt-get update -qq apt-get update -qq
} }
apt_install() { apt_install() {
wait_for_apt_lock wait_for_apt_lock
apt-get install -y "$@" apt-get install -y "$@"
} }
detect_server_ip() { detect_server_ip() {
local ip="" local ip=""
if command -v hostname >/dev/null 2>&1; then if command -v hostname >/dev/null 2>&1; then
ip="$(hostname -I 2>/dev/null | awk '{print $1}')" ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi fi
[[ -z "${ip}" ]] && ip="127.0.0.1" [[ -z "${ip}" ]] && ip="127.0.0.1"
echo "${ip}" echo "${ip}"
} }
cm_read() { cm_read() {
local __var="$1" local __var="$1"
local __prompt="${2:-}" local __prompt="${2:-}"
local __line="" local __line=""
if [[ -n "${__prompt}" ]]; then if [[ -n "${__prompt}" ]]; then
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}" printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
fi fi
if [[ -r /dev/tty ]]; then if [[ -r /dev/tty ]]; then
IFS= read -r __line </dev/tty || true IFS= read -r __line </dev/tty || true
else else
IFS= read -r __line || true IFS= read -r __line || true
fi fi
printf -v "${__var}" '%s' "${__line}" printf -v "${__var}" '%s' "${__line}"
} }
confirm_yes() { confirm_yes() {
local msg="$1" local msg="$1"
local ans="" local ans=""
cm_read ans "${msg} [y/N] " cm_read ans "${msg} [y/N] "
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]] [[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
} }
repo_ready() { repo_ready() {
[[ -f "${1}/deploy/manage.sh" && -f "${1}/docker-compose.yml" && -d "${1}/deploy/lib" ]] [[ -f "${1}/deploy/manage.sh" && -f "${1}/docker-compose.yml" && -d "${1}/deploy/lib" ]]
} }
resolve_repo_root() { resolve_repo_root() {
if repo_ready "${INSTALL_ROOT}"; then if repo_ready "${INSTALL_ROOT}"; then
echo "${INSTALL_ROOT}" echo "${INSTALL_ROOT}"
return 0 return 0
fi fi
if [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then if [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then
echo "${REPO_ROOT}" echo "${REPO_ROOT}"
return 0 return 0
fi fi
return 1 return 1
} }
read_env_value() { read_env_value() {
local file="$1" local file="$1"
local key="$2" local key="$2"
local val="" local val=""
if [[ -f "${file}" ]]; then if [[ -f "${file}" ]]; then
val="$(grep -E "^${key}=" "${file}" 2>/dev/null | tail -n1 | cut -d= -f2- || true)" val="$(grep -E "^${key}=" "${file}" 2>/dev/null | tail -n1 | cut -d= -f2- || true)"
fi fi
printf '%s' "${val}" printf '%s' "${val}"
} }
# 已有非空不覆盖;空或缺省则写入/询问 # 已有非空不覆盖;空或缺省则写入/询问
ensure_env_key() { ensure_env_key() {
local file="$1" local file="$1"
local key="$2" local key="$2"
local prompt="$3" local prompt="$3"
local default="$4" local default="$4"
local current local current
current="$(read_env_value "${file}" "${key}")" current="$(read_env_value "${file}" "${key}")"
if [[ -n "${current}" ]]; then if [[ -n "${current}" ]]; then
log "保留 ${key}=***(已有非空值)" log "保留 ${key}=***(已有非空值)"
return 0 return 0
fi fi
local input="" local input=""
if [[ -n "${prompt}" ]]; then if [[ -n "${prompt}" ]]; then
cm_read input "${prompt} [${default}]: " cm_read input "${prompt} [${default}]: "
fi fi
if [[ -z "${input}" ]]; then if [[ -z "${input}" ]]; then
input="${default}" input="${default}"
fi fi
if grep -qE "^${key}=" "${file}" 2>/dev/null; then if grep -qE "^${key}=" "${file}" 2>/dev/null; then
# 替换空值行 # 替换空值行
local tmp local tmp
tmp="$(mktemp)" tmp="$(mktemp)"
awk -v k="${key}" -v v="${input}" ' awk -v k="${key}" -v v="${input}" '
BEGIN{FS=OFS="="} BEGIN{FS=OFS="="}
$1==k {$0=k"="v} $1==k {$0=k"="v}
{print} {print}
' "${file}" >"${tmp}" && mv "${tmp}" "${file}" ' "${file}" >"${tmp}" && mv "${tmp}" "${file}"
else else
printf '%s=%s\n' "${key}" "${input}" >>"${file}" printf '%s=%s\n' "${key}" "${input}" >>"${file}"
fi fi
log "已设置 ${key}" log "已设置 ${key}"
} }
ensure_dotenv() { ensure_dotenv() {
local root="$1" local root="$1"
local envf="${root}/.env" local envf="${root}/.env"
if [[ ! -f "${envf}" ]]; then if [[ ! -f "${envf}" ]]; then
if [[ -f "${root}/.env.example" ]]; then if [[ -f "${root}/.env.example" ]]; then
cp -a "${root}/.env.example" "${envf}" cp -a "${root}/.env.example" "${envf}"
log "已从 .env.example 生成 .env" log "已从 .env.example 生成 .env"
else else
touch "${envf}" touch "${envf}"
fi fi
fi fi
echo "" echo ""
echo "配置 .env(回车采用默认;已有非空值不覆盖)" echo "配置 .env(回车采用默认;已有非空值不覆盖)"
ensure_env_key "${envf}" "MI_PORT" "HTTP 端口" "${MI_PORT_DEFAULT}" ensure_env_key "${envf}" "MI_PORT" "HTTP 端口" "${MI_PORT_DEFAULT}"
ensure_env_key "${envf}" "SAMPLE_INTERVAL_SEC" "采样间隔秒" "30" ensure_env_key "${envf}" "SAMPLE_INTERVAL_SEC" "采样间隔秒" "30"
ensure_env_key "${envf}" "MIN_OPTION_LEVERAGE" "杠杆达标线" "100" ensure_env_key "${envf}" "MIN_OPTION_LEVERAGE" "杠杆达标线" "100"
ensure_env_key "${envf}" "AUTH_SECRET" "鉴权密钥(disabled 关闭)" "change-me" ensure_env_key "${envf}" "AUTH_SECRET" "鉴权密钥(disabled 关闭)" "change-me"
ensure_env_key "${envf}" "ADMIN_PASSWORD" "管理员密码" "admin123" ensure_env_key "${envf}" "ADMIN_PASSWORD" "管理员密码" "admin123"
ensure_env_key "${envf}" "OKX_API_KEY" "OKX API Key(可空)" "" ensure_env_key "${envf}" "OKX_API_KEY" "OKX API Key(可空)" ""
ensure_env_key "${envf}" "OKX_API_SECRET" "OKX API Secret(可空)" "" ensure_env_key "${envf}" "OKX_API_SECRET" "OKX API Secret(可空)" ""
ensure_env_key "${envf}" "OKX_API_PASSPHRASE" "OKX Passphrase(可空)" "" ensure_env_key "${envf}" "OKX_API_PASSPHRASE" "OKX Passphrase(可空)" ""
ensure_env_key "${envf}" "WECOM_ENABLED" "企微告警 1/0" "0" ensure_env_key "${envf}" "WECOM_ENABLED" "企微告警 1/0" "0"
ensure_env_key "${envf}" "WECOM_WEBHOOK_URL" "企微 Webhook(可空)" "" ensure_env_key "${envf}" "WECOM_WEBHOOK_URL" "企微 Webhook(可空)" ""
ensure_env_key "${envf}" "WECOM_MACHINE_NAME" "机器名(可空)" "" ensure_env_key "${envf}" "WECOM_MACHINE_NAME" "机器名(可空)" ""
ensure_env_key "${envf}" "ALERT_FAIL_THRESHOLD" "连续失败告警阈值" "5" ensure_env_key "${envf}" "ALERT_FAIL_THRESHOLD" "连续失败告警阈值" "5"
# 固定库路径(容器内) # 固定库路径(容器内)
if [[ -z "$(read_env_value "${envf}" "MI_DB_PATH")" ]]; then if [[ -z "$(read_env_value "${envf}" "MI_DB_PATH")" ]]; then
ensure_env_key "${envf}" "MI_DB_PATH" "" "/app/data/market_intel.db" ensure_env_key "${envf}" "MI_DB_PATH" "" "/app/data/market_intel.db"
fi fi
} }
docker_ok() { docker_ok() {
command -v docker >/dev/null 2>&1 || return 1 command -v docker >/dev/null 2>&1 || return 1
docker compose version >/dev/null 2>&1 || return 1 docker compose version >/dev/null 2>&1 || return 1
return 0 return 0
} }
ensure_docker() { ensure_docker() {
step "环境检测 (Docker / Compose)" step "环境检测 (Docker / Compose)"
if docker_ok; then if docker_ok; then
log "Docker 已就绪: $(docker --version 2>&1)" log "Docker 已就绪: $(docker --version 2>&1)"
log "Compose: $(docker compose version 2>&1)" log "Compose: $(docker compose version 2>&1)"
return 0 return 0
fi fi
if ! command -v apt-get >/dev/null 2>&1; then if ! command -v apt-get >/dev/null 2>&1; then
die "未找到 Docker,且无 apt-get,请手动安装 Docker + Compose 插件" die "未找到 Docker,且无 apt-get,请手动安装 Docker + Compose 插件"
fi fi
step "安装 Docker Engine + Compose 插件" step "安装 Docker Engine + Compose 插件"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt_update apt_update
apt_install ca-certificates curl gnupg apt_install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg
fi fi
local codename local codename
codename="$(. /etc/os-release && echo "${VERSION_CODENAME}")" codename="$(. /etc/os-release && echo "${VERSION_CODENAME}")"
echo \ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${codename} stable" \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${codename} stable" \
>/etc/apt/sources.list.d/docker.list >/etc/apt/sources.list.d/docker.list
apt_update apt_update
apt_install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin apt_install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker 2>/dev/null || true systemctl enable --now docker 2>/dev/null || true
if ! docker_ok; then if ! docker_ok; then
die "Docker 安装后仍不可用" die "Docker 安装后仍不可用"
fi fi
log "Docker 安装完成" log "Docker 安装完成"
} }
compose() { compose() {
local root="${REPO_ROOT:-${INSTALL_ROOT}}" local root="${REPO_ROOT:-${INSTALL_ROOT}}"
(cd "${root}" && docker compose "$@") (cd "${root}" && docker compose "$@")
} }
compose_up_build() { compose_up_build() {
step "docker compose up -d --build" step "docker compose up -d --build"
compose up -d --build compose up -d --build
} }
compose_stop() { compose_stop() {
require_root require_root
if ! REPO_ROOT="$(resolve_repo_root)"; then if ! REPO_ROOT="$(resolve_repo_root)"; then
die "未找到安装目录 ${INSTALL_ROOT}" die "未找到安装目录 ${INSTALL_ROOT}"
fi fi
step "停止服务" step "停止服务"
compose stop compose stop
log "已停止" log "已停止"
} }
compose_start() { compose_start() {
require_root require_root
if ! REPO_ROOT="$(resolve_repo_root)"; then if ! REPO_ROOT="$(resolve_repo_root)"; then
die "未找到安装目录 ${INSTALL_ROOT}" die "未找到安装目录 ${INSTALL_ROOT}"
fi fi
step "启动服务" step "启动服务"
compose up -d compose up -d
verify_health || true verify_health || true
} }
read_mi_port() { read_mi_port() {
local root="${1:-${REPO_ROOT:-${INSTALL_ROOT}}}" local root="${1:-${REPO_ROOT:-${INSTALL_ROOT}}}"
local p local p
p="$(read_env_value "${root}/.env" "MI_PORT")" p="$(read_env_value "${root}/.env" "MI_PORT")"
if [[ -z "${p}" ]]; then if [[ -z "${p}" ]]; then
p="${MI_PORT_DEFAULT}" p="${MI_PORT_DEFAULT}"
fi fi
echo "${p}" echo "${p}"
} }
verify_health() { verify_health() {
local port local port
port="$(read_mi_port)" port="$(read_mi_port)"
local url="http://127.0.0.1:${port}/health" local url="http://127.0.0.1:${port}/health"
step "健康检查 ${url}" step "健康检查 ${url}"
local i local i
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
if curl -fsS "${url}" >/dev/null 2>&1; then if curl -fsS "${url}" >/dev/null 2>&1; then
log "health OK" log "health OK"
curl -fsS "${url}" || true curl -fsS "${url}" || true
echo "" echo ""
return 0 return 0
fi fi
sleep 2 sleep 2
done done
log "警告: health 暂未就绪,请检查: docker compose -f ${REPO_ROOT}/docker-compose.yml logs" log "警告: health 暂未就绪,请检查: docker compose -f ${REPO_ROOT}/docker-compose.yml logs"
return 1 return 1
} }
show_status() { show_status() {
if ! REPO_ROOT="$(resolve_repo_root)"; then if ! REPO_ROOT="$(resolve_repo_root)"; then
die "未找到安装目录 ${INSTALL_ROOT}" die "未找到安装目录 ${INSTALL_ROOT}"
fi fi
step "容器状态" step "容器状态"
compose ps || true compose ps || true
verify_health || true verify_health || true
} }
print_post_install_guide() { print_post_install_guide() {
local ip port local ip port
ip="$(detect_server_ip)" ip="$(detect_server_ip)"
port="$(read_mi_port)" port="$(read_mi_port)"
echo "" echo ""
echo "══════════════════════════════════════" echo "══════════════════════════════════════"
echo " 比特骆驼行情采集分析 部署完成" echo " 比特骆驼行情采集分析 部署完成"
echo " 目录: ${INSTALL_ROOT}" echo " 目录: ${INSTALL_ROOT}"
echo " 本机: http://${ip}:${port}/health" echo " 本机: http://${ip}:${port}/health"
echo " 看板: http://${ip}:${port}/" echo " 看板: http://${ip}:${port}/"
echo " 管理: bash ${INSTALL_ROOT}/deploy/manage.sh" echo " 管理: bash ${INSTALL_ROOT}/deploy/manage.sh"
echo " 配置: ${INSTALL_ROOT}/.env" echo " 配置: ${INSTALL_ROOT}/.env"
echo "══════════════════════════════════════" echo "══════════════════════════════════════"
echo "" echo ""
} }
+79 -79
View File
@@ -1,79 +1,79 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# deploy/lib/install.sh — 一键部署 market_intelDocker Compose # deploy/lib/install.sh — 一键部署 market_intelDocker Compose
set -e set -e
set -u set -u
if [ -n "${BASH_VERSION:-}" ]; then if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail set -o pipefail
fi fi
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh # shellcheck source=common.sh
source "${LIB_DIR}/common.sh" source "${LIB_DIR}/common.sh"
run_pipeline() { run_pipeline() {
local root="$1" local root="$1"
REPO_ROOT="${root}" REPO_ROOT="${root}"
ensure_dotenv "${root}" ensure_dotenv "${root}"
compose_up_build compose_up_build
verify_health || true verify_health || true
print_post_install_guide print_post_install_guide
} }
install_fresh() { install_fresh() {
require_root require_root
step "一键部署 — Docker 环境检测" step "一键部署 — Docker 环境检测"
ensure_docker ensure_docker
# 基础包 # 基础包
if ! command -v git >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then if ! command -v git >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt_update apt_update
apt_install ca-certificates git curl apt_install ca-certificates git curl
fi fi
step "克隆仓库 → ${INSTALL_ROOT}" step "克隆仓库 → ${INSTALL_ROOT}"
if [[ -d "${INSTALL_ROOT}" ]]; then if [[ -d "${INSTALL_ROOT}" ]]; then
die "目录已存在: ${INSTALL_ROOT},请先卸载或选修复" die "目录已存在: ${INSTALL_ROOT},请先卸载或选修复"
fi fi
mkdir -p "$(dirname "${INSTALL_ROOT}")" mkdir -p "$(dirname "${INSTALL_ROOT}")"
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}" git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
run_pipeline "${INSTALL_ROOT}" run_pipeline "${INSTALL_ROOT}"
} }
install_repair() { install_repair() {
require_root require_root
step "修复部署 — Docker 环境检测" step "修复部署 — Docker 环境检测"
ensure_docker ensure_docker
if ! REPO_ROOT="$(resolve_repo_root)"; then if ! REPO_ROOT="$(resolve_repo_root)"; then
die "未找到安装目录 ${INSTALL_ROOT}" die "未找到安装目录 ${INSTALL_ROOT}"
fi fi
step "修复环境(保留 .env 与 data volume)" step "修复环境(保留 .env 与 data volume)"
if [[ -d "${REPO_ROOT}/.git" ]]; then if [[ -d "${REPO_ROOT}/.git" ]]; then
git -C "${REPO_ROOT}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \ git -C "${REPO_ROOT}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \
|| git -C "${REPO_ROOT}" pull --ff-only 2>/dev/null \ || git -C "${REPO_ROOT}" pull --ff-only 2>/dev/null \
|| true || true
fi fi
run_pipeline "${REPO_ROOT}" run_pipeline "${REPO_ROOT}"
} }
handle_existing() { handle_existing() {
echo "" echo ""
echo "检测到已部署: ${INSTALL_ROOT}" echo "检测到已部署: ${INSTALL_ROOT}"
echo " a) 取消" echo " a) 取消"
echo " b) 修复/重装环境(保留 .env 与数据 volume)" echo " b) 修复/重装环境(保留 .env 与数据 volume)"
local choice="" local choice=""
cm_read choice "请选择 [a/b]: " cm_read choice "请选择 [a/b]: "
case "${choice}" in case "${choice}" in
b|B) install_repair ;; b|B) install_repair ;;
*) log "已取消" ;; *) log "已取消" ;;
esac esac
} }
main_install() { main_install() {
require_root require_root
if repo_ready "${INSTALL_ROOT}"; then if repo_ready "${INSTALL_ROOT}"; then
handle_existing handle_existing
else else
install_fresh install_fresh
fi fi
} }
main_install "$@" main_install "$@"
+105 -105
View File
@@ -1,105 +1,105 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# deploy/lib/uninstall.sh — 停容器;可选删 volume;备份 .env;删除安装目录 # deploy/lib/uninstall.sh — 停容器;可选删 volume;备份 .env;删除安装目录
set -e set -e
set -u set -u
if [ -n "${BASH_VERSION:-}" ]; then if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail set -o pipefail
fi fi
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh # shellcheck source=common.sh
source "${LIB_DIR}/common.sh" source "${LIB_DIR}/common.sh"
main_uninstall() { main_uninstall() {
require_root require_root
local root="" local root=""
if ! root="$(resolve_repo_root)"; then if ! root="$(resolve_repo_root)"; then
if [[ -d "${INSTALL_ROOT}" ]]; then if [[ -d "${INSTALL_ROOT}" ]]; then
root="${INSTALL_ROOT}" root="${INSTALL_ROOT}"
else else
die "未找到安装目录 ${INSTALL_ROOT}" die "未找到安装目录 ${INSTALL_ROOT}"
fi fi
fi fi
REPO_ROOT="${root}" REPO_ROOT="${root}"
echo "" echo ""
echo "将卸载 比特骆驼行情采集分析 (market_intel):" echo "将卸载 比特骆驼行情采集分析 (market_intel):"
echo " - 停止并移除 Compose 容器" echo " - 停止并移除 Compose 容器"
echo " - 备份 .env 到 ${BACKUP_ROOT}" echo " - 备份 .env 到 ${BACKUP_ROOT}"
echo " - 删除安装目录: ${INSTALL_ROOT}" echo " - 删除安装目录: ${INSTALL_ROOT}"
echo " - 可选:删除 data volume" echo " - 可选:删除 data volume"
echo "" echo ""
if ! confirm_yes "确认卸载并删除 ${INSTALL_ROOT}?"; then if ! confirm_yes "确认卸载并删除 ${INSTALL_ROOT}?"; then
log "已取消卸载" log "已取消卸载"
return 0 return 0
fi fi
local remove_volume=0 local remove_volume=0
if confirm_yes "是否同时删除 Docker data volume (mi_data)?"; then if confirm_yes "是否同时删除 Docker data volume (mi_data)?"; then
remove_volume=1 remove_volume=1
fi fi
local stamp backup_dir local stamp backup_dir
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)" stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
backup_dir="${BACKUP_ROOT}/pre-uninstall-${stamp}" backup_dir="${BACKUP_ROOT}/pre-uninstall-${stamp}"
mkdir -p "${backup_dir}" mkdir -p "${backup_dir}"
step "备份 .env" step "备份 .env"
if [[ -f "${REPO_ROOT}/.env" ]]; then if [[ -f "${REPO_ROOT}/.env" ]]; then
cp -a "${REPO_ROOT}/.env" "${backup_dir}/.env" cp -a "${REPO_ROOT}/.env" "${backup_dir}/.env"
fi fi
{ {
echo "created_at=${stamp}" echo "created_at=${stamp}"
echo "install_root=${INSTALL_ROOT}" echo "install_root=${INSTALL_ROOT}"
echo "remove_volume=${remove_volume}" echo "remove_volume=${remove_volume}"
echo "action=rm_rf_install_root" echo "action=rm_rf_install_root"
} >"${backup_dir}/uninstall.manifest" } >"${backup_dir}/uninstall.manifest"
step "停止并移除容器" step "停止并移除容器"
if command -v docker >/dev/null 2>&1 && [[ -f "${REPO_ROOT}/docker-compose.yml" ]]; then if command -v docker >/dev/null 2>&1 && [[ -f "${REPO_ROOT}/docker-compose.yml" ]]; then
if [[ "${remove_volume}" -eq 1 ]]; then if [[ "${remove_volume}" -eq 1 ]]; then
(cd "${REPO_ROOT}" && docker compose down -v) || true (cd "${REPO_ROOT}" && docker compose down -v) || true
else else
(cd "${REPO_ROOT}" && docker compose down) || true (cd "${REPO_ROOT}" && docker compose down) || true
fi fi
fi fi
step "删除安装目录 ${INSTALL_ROOT}" step "删除安装目录 ${INSTALL_ROOT}"
if [[ -d "${INSTALL_ROOT}" ]]; then if [[ -d "${INSTALL_ROOT}" ]]; then
case "${INSTALL_ROOT}" in case "${INSTALL_ROOT}" in
/opt/market_intel|/opt/market_intel/) /opt/market_intel|/opt/market_intel/)
rm -rf "${INSTALL_ROOT}" rm -rf "${INSTALL_ROOT}"
log "已删除: ${INSTALL_ROOT}" log "已删除: ${INSTALL_ROOT}"
;; ;;
*) *)
if [[ "${ALLOW_UNSAFE_UNINSTALL:-}" == "1" ]]; then if [[ "${ALLOW_UNSAFE_UNINSTALL:-}" == "1" ]]; then
rm -rf "${INSTALL_ROOT}" rm -rf "${INSTALL_ROOT}"
log "已删除(ALLOW_UNSAFE_UNINSTALL=1): ${INSTALL_ROOT}" log "已删除(ALLOW_UNSAFE_UNINSTALL=1): ${INSTALL_ROOT}"
else else
die "拒绝删除非默认路径 ${INSTALL_ROOT};若确认,设置 ALLOW_UNSAFE_UNINSTALL=1" die "拒绝删除非默认路径 ${INSTALL_ROOT};若确认,设置 ALLOW_UNSAFE_UNINSTALL=1"
fi fi
;; ;;
esac esac
else else
log "安装目录不存在,跳过删除" log "安装目录不存在,跳过删除"
fi fi
local leftover local leftover
for leftover in /opt/market_intel.removed.* /opt/market_intel.old.*; do for leftover in /opt/market_intel.removed.* /opt/market_intel.old.*; do
if [[ -e "${leftover}" ]]; then if [[ -e "${leftover}" ]]; then
rm -rf "${leftover}" rm -rf "${leftover}"
log "已清理残留: ${leftover}" log "已清理残留: ${leftover}"
fi fi
done done
echo "" echo ""
echo "卸载完成." echo "卸载完成."
echo " 配置备份: ${backup_dir}" echo " 配置备份: ${backup_dir}"
echo " volume 已删除: $([[ ${remove_volume} -eq 1 ]] && echo yes || echo no)" echo " volume 已删除: $([[ ${remove_volume} -eq 1 ]] && echo yes || echo no)"
echo "" echo ""
echo "重新部署:" echo "重新部署:"
echo " curl -fsSL https://git.bz121.com/dekun/market_intel/raw/branch/main/deploy/manage.sh | bash" echo " curl -fsSL https://git.bz121.com/dekun/market_intel/raw/branch/main/deploy/manage.sh | bash"
} }
main_uninstall "$@" main_uninstall "$@"
+41 -41
View File
@@ -1,41 +1,41 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# deploy/lib/update.sh — git pull + compose build/up;保留 .env 与 volume # deploy/lib/update.sh — git pull + compose build/up;保留 .env 与 volume
set -e set -e
set -u set -u
if [ -n "${BASH_VERSION:-}" ]; then if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail set -o pipefail
fi fi
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh # shellcheck source=common.sh
source "${LIB_DIR}/common.sh" source "${LIB_DIR}/common.sh"
main_update() { main_update() {
require_root require_root
if ! REPO_ROOT="$(resolve_repo_root)"; then if ! REPO_ROOT="$(resolve_repo_root)"; then
die "未找到安装目录 ${INSTALL_ROOT},请先执行「1) 一键部署」" die "未找到安装目录 ${INSTALL_ROOT},请先执行「1) 一键部署」"
fi fi
if ! repo_ready "${REPO_ROOT}"; then if ! repo_ready "${REPO_ROOT}"; then
die "安装不完整,请先执行「1) 一键部署」" die "安装不完整,请先执行「1) 一键部署」"
fi fi
step "更新 — Docker 环境检测" step "更新 — Docker 环境检测"
ensure_docker ensure_docker
step "git pull" step "git pull"
if [[ -d "${REPO_ROOT}/.git" ]]; then if [[ -d "${REPO_ROOT}/.git" ]]; then
git -C "${REPO_ROOT}" fetch --all --prune git -C "${REPO_ROOT}" fetch --all --prune
git -C "${REPO_ROOT}" checkout "${GIT_BRANCH}" 2>/dev/null || true git -C "${REPO_ROOT}" checkout "${GIT_BRANCH}" 2>/dev/null || true
git -C "${REPO_ROOT}" pull --ff-only origin "${GIT_BRANCH}" \ git -C "${REPO_ROOT}" pull --ff-only origin "${GIT_BRANCH}" \
|| git -C "${REPO_ROOT}" pull --ff-only || git -C "${REPO_ROOT}" pull --ff-only
else else
log "警告: 非 git 目录,跳过 pull" log "警告: 非 git 目录,跳过 pull"
fi fi
# 补全缺失 env key,不覆盖已有 # 补全缺失 env key,不覆盖已有
ensure_dotenv "${REPO_ROOT}" ensure_dotenv "${REPO_ROOT}"
compose_up_build compose_up_build
verify_health || true verify_health || true
echo "" echo ""
log "更新完成(.env 与 data volume 已保留)" log "更新完成(.env 与 data volume 已保留)"
} }
main_update "$@" main_update "$@"
+228 -228
View File
@@ -1,228 +1,228 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# 比特骆驼行情采集分析 部署管理器(工程 market_intel # 比特骆驼行情采集分析 部署管理器(工程 market_intel
# 部署环境: Ubuntu 22.04 LTS · Docker Compose # 部署环境: Ubuntu 22.04 LTS · Docker Compose
# #
# 新服务器(免克隆): # 新服务器(免克隆):
# curl -fsSL https://git.bz121.com/dekun/market_intel/raw/branch/main/deploy/manage.sh | bash # curl -fsSL https://git.bz121.com/dekun/market_intel/raw/branch/main/deploy/manage.sh | bash
# #
# 已安装: # 已安装:
# bash /opt/market_intel/deploy/manage.sh # bash /opt/market_intel/deploy/manage.sh
# #
set -e set -e
if [ -n "${BASH_VERSION:-}" ]; then if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail set -o pipefail
fi fi
INSTALL_ROOT="${INSTALL_ROOT:-/opt/market_intel}" INSTALL_ROOT="${INSTALL_ROOT:-/opt/market_intel}"
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/market_intel.git}" GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/market_intel.git}"
GIT_BRANCH="${GIT_BRANCH:-main}" GIT_BRANCH="${GIT_BRANCH:-main}"
_script_src="${BASH_SOURCE[0]:-}" _script_src="${BASH_SOURCE[0]:-}"
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)" DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)"
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
LIB_DIR="${DEPLOY_DIR}/lib" LIB_DIR="${DEPLOY_DIR}/lib"
else else
DEPLOY_DIR="" DEPLOY_DIR=""
REPO_ROOT="" REPO_ROOT=""
LIB_DIR="" LIB_DIR=""
fi fi
unset _script_src unset _script_src
set -u set -u
repo_ready() { repo_ready() {
[[ -f "${1}/deploy/manage.sh" && -f "${1}/docker-compose.yml" && -d "${1}/deploy/lib" ]] [[ -f "${1}/deploy/manage.sh" && -f "${1}/docker-compose.yml" && -d "${1}/deploy/lib" ]]
} }
ensure_git_cli() { ensure_git_cli() {
if command -v git >/dev/null 2>&1; then if command -v git >/dev/null 2>&1; then
return 0 return 0
fi fi
if command -v apt-get >/dev/null 2>&1; then if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
local waited=0 local waited=0
while pgrep -x unattended-upgr >/dev/null 2>&1 \ while pgrep -x unattended-upgr >/dev/null 2>&1 \
|| pgrep -x apt-get >/dev/null 2>&1 \ || pgrep -x apt-get >/dev/null 2>&1 \
|| pgrep -x apt >/dev/null 2>&1 \ || pgrep -x apt >/dev/null 2>&1 \
|| pgrep -x dpkg >/dev/null 2>&1 \ || pgrep -x dpkg >/dev/null 2>&1 \
|| { command -v fuser >/dev/null 2>&1 && fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; }; do || { command -v fuser >/dev/null 2>&1 && fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; }; do
if [[ "${waited}" -eq 0 ]]; then if [[ "${waited}" -eq 0 ]]; then
echo "等待 apt 锁释放(unattended-upgrades)…" echo "等待 apt 锁释放(unattended-upgrades)…"
fi fi
if [[ "${waited}" -ge 600 ]]; then if [[ "${waited}" -ge 600 ]]; then
echo "错误: 等待 apt 锁超时,请稍后再试" >&2 echo "错误: 等待 apt 锁超时,请稍后再试" >&2
exit 1 exit 1
fi fi
sleep 5 sleep 5
waited=$((waited + 5)) waited=$((waited + 5))
done done
apt-get update -qq apt-get update -qq
apt-get install -y git ca-certificates curl apt-get install -y git ca-certificates curl
else else
echo "错误: 未找到 git" >&2 echo "错误: 未找到 git" >&2
exit 1 exit 1
fi fi
} }
sync_repo_if_present() { sync_repo_if_present() {
local root="$1" local root="$1"
if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then
git -C "${root}" fetch --all --prune 2>/dev/null || true git -C "${root}" fetch --all --prune 2>/dev/null || true
git -C "${root}" checkout "${GIT_BRANCH}" 2>/dev/null || true git -C "${root}" checkout "${GIT_BRANCH}" 2>/dev/null || true
git -C "${root}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \ git -C "${root}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \
|| git -C "${root}" pull --ff-only 2>/dev/null \ || git -C "${root}" pull --ff-only 2>/dev/null \
|| true || true
fi fi
} }
heal_existing_install() { heal_existing_install() {
local root="$1" local root="$1"
echo "检测到已有目录但缺少管理脚本: ${root}" echo "检测到已有目录但缺少管理脚本: ${root}"
echo "尝试同步最新代码…" echo "尝试同步最新代码…"
ensure_git_cli ensure_git_cli
if [[ -d "${root}/.git" ]]; then if [[ -d "${root}/.git" ]]; then
sync_repo_if_present "${root}" sync_repo_if_present "${root}"
if repo_ready "${root}"; then if repo_ready "${root}"; then
echo "同步成功,切换到仓库内 manage.sh" echo "同步成功,切换到仓库内 manage.sh"
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
fi fi
echo "git pull 后仍不完整,尝试强制对齐 origin/${GIT_BRANCH}" echo "git pull 后仍不完整,尝试强制对齐 origin/${GIT_BRANCH}"
git -C "${root}" fetch origin "${GIT_BRANCH}" || true git -C "${root}" fetch origin "${GIT_BRANCH}" || true
git -C "${root}" reset --hard "origin/${GIT_BRANCH}" || true git -C "${root}" reset --hard "origin/${GIT_BRANCH}" || true
if repo_ready "${root}"; then if repo_ready "${root}"; then
echo "对齐成功,切换到仓库内 manage.sh" echo "对齐成功,切换到仓库内 manage.sh"
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
fi fi
fi fi
echo "将备份配置并重新克隆到 ${root}" echo "将备份配置并重新克隆到 ${root}"
local stamp backup_dir local stamp backup_dir
stamp="$(date +%Y%m%d-%H%M%S)" stamp="$(date +%Y%m%d-%H%M%S)"
backup_dir="/root/backups/market_intel/heal-${stamp}" backup_dir="/root/backups/market_intel/heal-${stamp}"
mkdir -p "${backup_dir}" mkdir -p "${backup_dir}"
[[ -f "${root}/.env" ]] && cp -a "${root}/.env" "${backup_dir}/.env" [[ -f "${root}/.env" ]] && cp -a "${root}/.env" "${backup_dir}/.env"
echo "备份目录: ${backup_dir}" echo "备份目录: ${backup_dir}"
local removed="${root}.old.${stamp}" local removed="${root}.old.${stamp}"
mv "${root}" "${removed}" mv "${root}" "${removed}"
echo "旧目录已移至: ${removed}" echo "旧目录已移至: ${removed}"
mkdir -p "$(dirname "${root}")" mkdir -p "$(dirname "${root}")"
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${root}" git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${root}"
if [[ -f "${backup_dir}/.env" ]]; then if [[ -f "${backup_dir}/.env" ]]; then
cp -a "${backup_dir}/.env" "${root}/.env" cp -a "${backup_dir}/.env" "${root}/.env"
echo "已恢复 .env" echo "已恢复 .env"
fi fi
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
} }
bootstrap_repo() { bootstrap_repo() {
if repo_ready "${INSTALL_ROOT}"; then if repo_ready "${INSTALL_ROOT}"; then
REPO_ROOT="${INSTALL_ROOT}" REPO_ROOT="${INSTALL_ROOT}"
DEPLOY_DIR="${REPO_ROOT}/deploy" DEPLOY_DIR="${REPO_ROOT}/deploy"
LIB_DIR="${DEPLOY_DIR}/lib" LIB_DIR="${DEPLOY_DIR}/lib"
sync_repo_if_present "${REPO_ROOT}" sync_repo_if_present "${REPO_ROOT}"
if ! repo_ready "${REPO_ROOT}"; then if ! repo_ready "${REPO_ROOT}"; then
heal_existing_install "${INSTALL_ROOT}" "$@" heal_existing_install "${INSTALL_ROOT}" "$@"
fi fi
return 0 return 0
fi fi
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
DEPLOY_DIR="${REPO_ROOT}/deploy" DEPLOY_DIR="${REPO_ROOT}/deploy"
LIB_DIR="${DEPLOY_DIR}/lib" LIB_DIR="${DEPLOY_DIR}/lib"
return 0 return 0
fi fi
if [[ -d "${INSTALL_ROOT}" ]]; then if [[ -d "${INSTALL_ROOT}" ]]; then
if [[ "$(id -u)" -ne 0 ]]; then if [[ "$(id -u)" -ne 0 ]]; then
echo "错误: 请使用 root 执行" >&2 echo "错误: 请使用 root 执行" >&2
exit 1 exit 1
fi fi
heal_existing_install "${INSTALL_ROOT}" "$@" heal_existing_install "${INSTALL_ROOT}" "$@"
fi fi
echo "比特骆驼行情采集分析 部署管理器 — 首次自举" echo "比特骆驼行情采集分析 部署管理器 — 首次自举"
echo "将克隆到: ${INSTALL_ROOT}" echo "将克隆到: ${INSTALL_ROOT}"
if [[ "$(id -u)" -ne 0 ]]; then if [[ "$(id -u)" -ne 0 ]]; then
echo "错误: 请使用 root 执行" >&2 echo "错误: 请使用 root 执行" >&2
exit 1 exit 1
fi fi
ensure_git_cli ensure_git_cli
mkdir -p "$(dirname "${INSTALL_ROOT}")" mkdir -p "$(dirname "${INSTALL_ROOT}")"
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}" git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
} }
show_banner() { show_banner() {
local path local path
path="${INSTALL_ROOT}" path="${INSTALL_ROOT}"
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
path="${REPO_ROOT}" path="${REPO_ROOT}"
fi fi
echo "" echo ""
echo "╔══════════════════════════════════════╗" echo "╔══════════════════════════════════════╗"
echo "║ 比特骆驼行情采集分析 部署管理器 ║" echo "║ 比特骆驼行情采集分析 部署管理器 ║"
echo "║ 路径: ${path}" echo "║ 路径: ${path}"
echo "╚══════════════════════════════════════╝" echo "╚══════════════════════════════════════╝"
echo "" echo ""
} }
show_menu() { show_menu() {
echo " 1) 一键部署" echo " 1) 一键部署"
echo " 2) 更新" echo " 2) 更新"
echo " 3) 停止" echo " 3) 停止"
echo " 4) 启动" echo " 4) 启动"
echo " 5) 查看状态" echo " 5) 查看状态"
echo " 6) 一键卸载" echo " 6) 一键卸载"
echo " 0) 退出" echo " 0) 退出"
echo "" echo ""
} }
cm_read() { cm_read() {
local __var="$1" local __var="$1"
local __prompt="${2:-}" local __prompt="${2:-}"
local __line="" local __line=""
if [[ -n "${__prompt}" ]]; then if [[ -n "${__prompt}" ]]; then
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}" printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
fi fi
if [[ -r /dev/tty ]]; then if [[ -r /dev/tty ]]; then
IFS= read -r __line </dev/tty || true IFS= read -r __line </dev/tty || true
else else
IFS= read -r __line || true IFS= read -r __line || true
fi fi
printf -v "${__var}" '%s' "${__line}" printf -v "${__var}" '%s' "${__line}"
} }
main_menu() { main_menu() {
bootstrap_repo bootstrap_repo
# shellcheck source=lib/common.sh # shellcheck source=lib/common.sh
source "${LIB_DIR}/common.sh" source "${LIB_DIR}/common.sh"
REPO_ROOT="$(resolve_repo_root || echo "${INSTALL_ROOT}")" REPO_ROOT="$(resolve_repo_root || echo "${INSTALL_ROOT}")"
while true; do while true; do
show_banner show_banner
show_menu show_menu
local choice="" local choice=""
cm_read choice "请选择 [0-6]: " cm_read choice "请选择 [0-6]: "
case "${choice}" in case "${choice}" in
1) bash "${LIB_DIR}/install.sh" ;; 1) bash "${LIB_DIR}/install.sh" ;;
2) bash "${LIB_DIR}/update.sh" ;; 2) bash "${LIB_DIR}/update.sh" ;;
3) compose_stop ;; 3) compose_stop ;;
4) compose_start ;; 4) compose_start ;;
5) show_status ;; 5) show_status ;;
6) bash "${LIB_DIR}/uninstall.sh" ;; 6) bash "${LIB_DIR}/uninstall.sh" ;;
0) 0)
echo "再见." echo "再见."
exit 0 exit 0
;; ;;
*) *)
echo "无效选项,请输入 0-6" echo "无效选项,请输入 0-6"
;; ;;
esac esac
echo "" echo ""
cm_read _ "按 Enter 返回菜单..." cm_read _ "按 Enter 返回菜单..."
done done
} }
main_menu "$@" main_menu "$@"