feat(install): 完善安装脚本,多distro可用

This commit is contained in:
ClovertaTheTrilobita 2025-10-16 17:31:03 +00:00
parent 49358cd1c1
commit ace50650f6
5 changed files with 216 additions and 117 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ __pycache__
run run
models models
backend/train/ backend/train/
.bak

View file

@ -1,5 +1,5 @@
backend: backend:
ip: 192.168.193.141 ip: 10.10.25.240
port: 5000 port: 5000
model: model:

View file

@ -14,7 +14,7 @@ from flask import Flask, send_from_directory, request, jsonify
from flask_cors import CORS from flask_cors import CORS
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from backend.cp_train import Cptrain from cp_train import Cptrain
from cp_run import Cprun from cp_run import Cprun
app = Flask(__name__) app = Flask(__name__)

View file

@ -1,7 +1,7 @@
const config = { const config = {
server: { server: {
protocol: 'http', protocol: 'http',
host: '192.168.193.141', host: '10.10.25.240',
port: 5000 port: 5000
} }
}; };

View file

@ -1,54 +1,57 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
GREEN='\033[32m'; RESET='\033[0m'; YELLOW='\033[33m'; RED='\033[41m' GREEN='\033[32m'
RESET='\033[0m'
YELLOW='\033[33m'
RED='\033[31m'
ask_yn() { ask_yn() {
# $1=提示语 $2=默认值 y|n # $1=提示语 $2=默认值 y|n
local prompt="$1" local prompt="$1"
local def="${2:-n}" local def="${2:-n}"
local show="[y/N]"; [ "$def" = "y" ] && show="[Y/n]" local show="[y/N]"
[ "$def" = "y" ] && show="[Y/n]"
while :; do while :; do
printf "%s %s " "$prompt" "$show" > /dev/tty printf "%s %s " "$prompt" "$show" >/dev/tty
IFS= read -r ans < /dev/tty || return 1 IFS= read -r ans </dev/tty || return 1
[ -z "$ans" ] && ans="$def" [ -z "$ans" ] && ans="$def"
case "$ans" in case "$ans" in
[Yy]|[Yy][Ee][Ss]) return 0 ;; [Yy] | [Yy][Ee][Ss]) return 0 ;;
[Nn]|[Nn][Oo]) return 1 ;; [Nn] | [Nn][Oo]) return 1 ;;
*) echo "Please answer y or n." > /dev/tty ;; *) echo "Please answer y or n." >/dev/tty ;;
esac esac
done done
} }
#--- 小工具:取当前 backend.ip --- #--- 小工具:取当前 backend.ip ---
get_current_ip() { get_current_ip() {
if command -v ip >/dev/null 2>&1; then if command -v ip >/dev/null 2>&1; then
ip -4 route get 1.1.1.1 2>/dev/null \ ip -4 route get 1.1.1.1 2>/dev/null |
| awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}' \ awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}' |
| grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0 grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
# 次选hostname -I # 次选hostname -I
hostname -I 2>/dev/null \ hostname -I 2>/dev/null |
| awk '{print $1}' \ awk '{print $1}' |
| grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0 grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
fi fi
# 2) macOS: ipconfig # 2) macOS: ipconfig
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
for i in en0 en1 en2; do for i in en0 en1 en2; do
ipconfig getifaddr "$i" 2>/dev/null \ ipconfig getifaddr "$i" 2>/dev/null |
| grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0 grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
done done
fi fi
# 3) 通用回退ifconfig 解析 # 3) 通用回退ifconfig 解析
if command -v ifconfig >/dev/null 2>&1; then if command -v ifconfig >/dev/null 2>&1; then
ifconfig 2>/dev/null \ ifconfig 2>/dev/null |
| awk '/inet (addr:)?([0-9]+\.){3}[0-9]+/ { awk '/inet (addr:)?([0-9]+\.){3}[0-9]+/ {
ip=$2; sub("addr:","",ip); ip=$2; sub("addr:","",ip);
if (ip!="127.0.0.1" && ip !~ /^127\./) {print ip; exit} if (ip!="127.0.0.1" && ip !~ /^127\./) {print ip; exit}
}' \ }' |
| grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0 grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
fi fi
# 4) 实在拿不到 # 4) 实在拿不到
@ -57,20 +60,20 @@ get_current_ip() {
#--- 简单 IPv4 校验(也可接受主机名;如只要 IPv4保留第一个分支即可 --- #--- 简单 IPv4 校验(也可接受主机名;如只要 IPv4保留第一个分支即可 ---
is_valid_host() { is_valid_host() {
case "$1" in local s=${1:-}
# IPv4 快速匹配 + 数值检查 0255
([0-9]*.[0-9]*.[0-9]*.[0-9]*) # 先判 IPv4 形状
IFS=. read -r a b c d <<<"$1" || return 1 if [[ $s =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
for o in "$a" "$b" "$c" "$d"; do IFS=. read -r a b c d <<<"$s" || return 1
[[ "$o" =~ ^[0-9]{1,3}$ ]] || return 1 for o in "$a" "$b" "$c" "$d"; do
(( o >= 0 && o <= 255 )) || return 1 [[ $o =~ ^[0-9]{1,3}$ ]] || return 1
done ((o >= 0 && o <= 255)) || return 1
return 0 done
;; return 0
# 主机名(允许 localhost、域名等 fi
([A-Za-z0-9-]*([.][A-Za-z0-9-]+)*) return 0 ;;
(*) return 1 ;; # 再判主机名RFC1123 宽松版:标签 1-63允许中划线但不能以 - 开头/结尾)
esac [[ $s =~ ^([A-Za-z0-9]([-A-Za-z0-9]{0,61}[A-Za-z0-9])?\.)*([A-Za-z0-9]([-A-Za-z0-9]{0,61}[A-Za-z0-9])?)$ ]]
} }
update_frontend_ip() { update_frontend_ip() {
@ -85,7 +88,7 @@ update_frontend_ip() {
} }
need_sudo() { need_sudo() {
if [ "$EUID" -ne 0 ]; then echo "sudo" ; else echo "" ; fi if [ "$EUID" -ne 0 ]; then echo "sudo"; else echo ""; fi
} }
detect_distro() { detect_distro() {
@ -98,63 +101,90 @@ detect_distro() {
} }
install_debian() { install_debian() {
local package=$1
local SUDO=$(need_sudo) local SUDO=$(need_sudo)
$SUDO apt-get update -y if [ "$package" = "python" ]; then
# python3-venv 是 Debian/Ubuntu 系 venv 必需包 $SUDO apt-get update -y
$SUDO apt-get install -y python3 python3-venv python3-pip build-essential # python3-venv 是 Debian/Ubuntu 系 venv 必需包
$SUDO apt-get install -y python3 python3-venv python3-pip build-essential
elif [[ "$package" = "redis" ]]; then
$SUDO apt-get install -y redis-server redis-cli
$SUDO systemctl enable --now redis-server
fi
} }
install_arch() { install_arch() {
local package=$1
local SUDO=$(need_sudo) local SUDO=$(need_sudo)
# Arch 的 python 包自带 venv 模块 # Arch 的 python 包自带 venv 模块
$SUDO pacman -Sy --needed --noconfirm python python-pip base-devel if [[ "$package" = "python" ]]; then
$SUDO pacman -Sy --needed --noconfirm python python-pip base-devel
elif [[ "$package" = "redis" ]]; then
$SUDO pacman -Sy --needed --noconfirm valkey
$SUDO systemctl enable --now valkey
fi
} }
install_rhel() { install_rhel() {
local package=$1
local SUDO=$(need_sudo) local SUDO=$(need_sudo)
# 优先 dnf旧版用 yum。RHEL/CentOS/Fedora 的 python3 自带 venv # 优先 dnf旧版用 yum。RHEL/CentOS/Fedora 的 python3 自带 venv
if command -v dnf >/dev/null 2>&1; then if command -v dnf >/dev/null 2>&1; then
$SUDO dnf -y install python3 python3-pip if [[ "$package" = "python" ]]; then
# 开发工具组(可选) $SUDO dnf -y install python3 python3-pip
# 开发工具组(可选)
elif [[ "$package" = "redis" ]]; then
$SUDO dnf -y install redis
fi
$SUDO dnf -y groupinstall "Development Tools" || true $SUDO dnf -y groupinstall "Development Tools" || true
else else
$SUDO yum -y install python3 python3-pip || true if [[ "$package" = "python" ]]; then
$SUDO yum -y install python3 python3-pip || true
elif [[ "$package" = "redis" ]]; then
$SUDO yum install -y redis
fi
$SUDO yum -y groupinstall "Development Tools" || true $SUDO yum -y groupinstall "Development Tools" || true
fi fi
} }
install_python_venv() { install_python_venv() {
local info; info=$(detect_distro) local info
local id="${info%%|*}"; local like="${info#*|}" info=$(detect_distro)
local id="${info%%|*}"
local like="${info#*|}"
echo "Detected: id=$id, like=$like" echo "Detected: id=$id, like=$like"
case "$id" in case "$id" in
arch|manjaro) arch | manjaro)
install_arch ;; install_arch
ubuntu|debian|linuxmint|pop|elementary|neon|kali|raspbian) ;;
install_debian ;; ubuntu | debian | linuxmint | pop | elementary | neon | kali | raspbian)
fedora|centos|rhel|rocky|almalinux|ol) install_debian
install_rhel ;; ;;
*) fedora | centos | rhel | rocky | almalinux | ol)
# 用 ID_LIKE 兜底 install_rhel
if echo "$like" | grep -qi "debian"; then ;;
install_debian *)
elif echo "$like" | grep -Eiq "rhel|fedora|centos"; then # 用 ID_LIKE 兜底
install_rhel if echo "$like" | grep -qi "debian"; then
elif echo "$like" | grep -qi "arch"; then install_debian "python"
install_arch elif echo "$like" | grep -Eiq "rhel|fedora|centos"; then
else install_rhel "python"
echo "Unknown Linux distro: $id. Please install it manualy" elif echo "$like" | grep -qi "arch"; then
echo " Debian/Ubuntu: sudo apt-get install -y python3 python3-venv python3-pip" install_arch "python"
echo " Arch/Manjaro: sudo pacman -S python python-pip" else
echo " RHEL/CentOS: sudo dnf/yum install -y python3 python3-pip" echo "Unknown Linux distro: $id. Please install it manualy"
exit 1 echo " Debian/Ubuntu: sudo apt-get install -y python3 python3-venv python3-pip"
fi echo " Arch/Manjaro: sudo pacman -S python python-pip"
;; echo " RHEL/CentOS: sudo dnf/yum install -y python3 python3-pip"
esase exit 1
fi
;;
esac
# 验证 venv 可用 # 验证 venv 可用
if python3 - <<'PY' if python3 - <<'PY'; then
import sys, subprocess, tempfile, os import sys, subprocess, tempfile, os
try: try:
d = tempfile.mkdtemp(prefix="venv_test_") d = tempfile.mkdtemp(prefix="venv_test_")
@ -164,7 +194,6 @@ except Exception as e:
print("ERR", e) print("ERR", e)
raise raise
PY PY
then
echo "✅ python3 venv usable" echo "✅ python3 venv usable"
else else
echo "❌ venv not usable Please check whether it's installed (Debian/Ubuntu: python3-venv)" >&2 echo "❌ venv not usable Please check whether it's installed (Debian/Ubuntu: python3-venv)" >&2
@ -172,8 +201,47 @@ PY
fi fi
} }
install_redis() {
local info
info=$(detect_distro)
local id="${info%%|*}"
local like="${info#*|}"
echo "Detected: id=$id, like=$like"
if [ -n "$NO_COLOR" ]; then GREEN=''; RESET=''; fi case "$id" in
arch | manjaro)
install_arch
;;
ubuntu | debian | linuxmint | pop | elementary | neon | kali | raspbian)
install_debian
;;
fedora | centos | rhel | rocky | almalinux | ol)
install_rhel
;;
*)
# 用 ID_LIKE 兜底
if echo "$like" | grep -qi "debian"; then
install_debian "redis"
elif echo "$like" | grep -Eiq "rhel|fedora|centos"; then
install_rhel "redis"
elif echo "$like" | grep -qi "arch"; then
install_arch "redis"
else
echo "Unknown Linux distro: $id. Please install it manualy"
echo " Debian/Ubuntu: sudo apt-get install -y redis-server redis-cli"
echo " Arch/Manjaro: sudo pacman -S valkey"
echo " RHEL/CentOS: sudo dnf/yum install -y redis"
exit 1
fi
;;
esac
}
#if [ -n "$NO_COLOR" ]; then
# GREEN=''
# RESET=''
#fi
root=$(git rev-parse --show-toplevel 2>/dev/null) || root="" root=$(git rev-parse --show-toplevel 2>/dev/null) || root=""
if [ -n "$root" ]; then if [ -n "$root" ]; then
@ -185,23 +253,36 @@ else
if ask_yn "Do you wish to clone it from github?" y; then if ask_yn "Do you wish to clone it from github?" y; then
echo "${GREEN}==>${RESET} Cloning from remote..." echo "${GREEN}==>${RESET} Cloning from remote..."
git clone https://github.com/ClovertaTheTrilobita/cellpose-web.git git clone https://github.com/ClovertaTheTrilobita/cellpose-web.git
fi
fi
if ! cd cellpose-web; then if ! cd cellpose-web; then
echo "${RED}==>${RESET} directory 'cellpose-web' not found or inaccessible, did git clone run successfully?" >&2 echo -e "${RED}==>${RESET} directory 'cellpose-web' not found or inaccessible, did git clone run successfully?" >&2
exit 1 exit 1
fi
fi
fi fi
root=$(pwd) root=$(pwd)
echo -e "${GREEN}==>${RESET} STARTING DEPLOY..." echo -e "${GREEN}==>${RESET} STARTING DEPLOY..."
if command -v valkey-server >/dev/null 2>&1 || command -v redis-cli >/dev/null 2>&1 || command -v redis-server >/dev/null 2>&1; then
# 打印版本(有哪个用哪个)
ver="$(redis-cli --version 2>/dev/null || redis-server --version 2>/dev/null)"
echo -e "${GREEN}==>${RESET} Redis installed: $ver"
else
echo -e "${YELLOW}==>${RESET} Redis not detected."
if ask_yn "Do you like to install redis on your machine?" y; then
install_redis
fi
fi
FILE="${root}/backend/config.yaml" FILE="${root}/backend/config.yaml"
DEFAULT_IP="$(get_current_ip || true)" DEFAULT_IP="$(get_current_ip || true)"
echo -e "${GREEN}==>${RESET} Enter IP address of your machine (empty: default ${DEFAULT_IP}):" echo -e "${GREEN}==>${RESET} Enter IP address of your machine (empty: default ${DEFAULT_IP}):"
prompt=${prompt:-"Enter server IP/host"} # 先初始化 prompt
curr_ip=${curr_ip:-}
[ -n "${curr_ip:-}" ] && prompt="$prompt (current: $curr_ip)" [ -n "${curr_ip:-}" ] && prompt="$prompt (current: $curr_ip)"
printf "%s: " "$prompt" > /dev/tty printf "%s: " "$prompt" >/dev/tty
IFS= read -r NEW_IP < /dev/tty IFS= read -r NEW_IP </dev/tty
# 默认回车使用探测到的 IP # 默认回车使用探测到的 IP
if [ -z "${NEW_IP}" ]; then if [ -z "${NEW_IP}" ]; then
@ -223,36 +304,47 @@ if ! is_valid_host "$NEW_IP"; then
fi fi
fi fi
cp -a -- "$FILE" "$FILE.bak.$(date +%Y%m%d%H%M%S)" #cp -a -- "$FILE" "$FILE.bak.$(date +%Y%m%d%H%M%S)"
if command -v yq >/dev/null 2>&1; then if command -v yq >/dev/null 2>&1; then
NEW_IP_ENV="$NEW_IP" yq -i '.backend.ip = strenv(NEW_IP_ENV)' "$FILE" NEW_IP_ENV="$NEW_IP" yq -i '.backend.ip = strenv(NEW_IP_ENV)' "$FILE"
else else
awk -v newip="$NEW_IP" ' awk -v newip="$NEW_IP" '
BEGIN{in=0; base=-1; done=0} BEGIN { inblk=0; base=-1; done=0 }
/^[[:space:]]*backend:[[:space:]]*$/ {in=1; base=match($0,/[^ ]/)-1; print; next} /^[[:space:]]*backend:[[:space:]]*$/ { inblk=1; base=indent($0); print; next }
{ {
if(in && !done){ if (inblk && !done) {
ind=match($0,/[^ ]/)-1 ind = indent($0)
if(ind<=base){in=0} if (ind <= base) { inblk=0 }
else if($0 ~ /^[[:space:]]*ip:[[:space:]]*/){ else if ($0 ~ /^[[:space:]]*ip:[[:space:]]*/) {
match($0,/^[[:space:]]*/); sp=substr($0,1,RLENGTH) match($0, /^[[:space:]]*/); sp = substr($0, 1, RLENGTH)
print sp "ip: " newip print sp "ip: " newip
done=1; next done=1; next
} }
} }
print print
} }
' "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" function indent(s, n) {
match(s, /^[[:space:]]*/)
return RLENGTH
}
' "$FILE" >"$FILE.tmp" && mv "$FILE.tmp" "$FILE"
fi fi
echo -e "${GREEN}==>${RESET} Updated backend.ip -> $NEW_IP in $FILE" echo -e "${GREEN}==>${RESET} Updated backend.ip -> $NEW_IP in $FILE"
update_frontend_ip $NEW_IP update_frontend_ip $NEW_IP $root
echo -e "${GREEN}==>${RESET} Update host -> $NEW_IP in frontend/api.js" echo -e "${GREEN}==>${RESET} Update host -> $NEW_IP in frontend/api.js"
if ! conda; then if command -v conda >/dev/null 2>&1; then
echo "Conda already installed: $(conda --version)"
CONDA_EXE="${CONDA_EXE:-$(command -v conda)}"
CONDA_BASE="$("$CONDA_EXE" info --base)"
eval "$("$CONDA_EXE" shell.bash hook)"
conda create -n cpweb python=3.12
conda activate cpweb
else
echo -e "${YELLOW}==>${RESET} Conda enviroment NOT FOUND" echo -e "${YELLOW}==>${RESET} Conda enviroment NOT FOUND"
echo "We highly recommend you to use Conda to manage python enviroments" echo "We highly recommend you to use Conda to manage python enviroments"
if ask_yn "Do you wish to install miniconda?" y; then if ask_yn "Do you wish to install miniconda?" y; then
@ -261,24 +353,27 @@ if ! conda; then
rm miniconda.sh rm miniconda.sh
eval "$($HOME/miniconda3/bin/conda shell.bash hook)" eval "$($HOME/miniconda3/bin/conda shell.bash hook)"
conda init conda init
conda create -n cpweb python=3.12 || true
conda activate cpweb
else else
echo -e "${GREEN}==>${RESET} Creating Python venv..." echo -e "${GREEN}==>${RESET} Creating Python venv..."
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
if ! brew install python; them if ! command -v brew >/dev/null 2>&1; then
echo -e"${RED}==>${RESET} You are using macOS/BSD with no homebrew, we don't recommend you to use system python on macOS, aborting..." echo -e "${RED}==>${RESET} You are using macOS/BSD with no homebrew, we don't recommend you to use system python on macOS, aborting..."
exit 1 exit 1
fi fi
else if ! brew install python >/dev/null 2>&1; then
install_python_venv "$@" echo -e "${RED}==>${RESET} Failed to install Python via Homebrew.\n"
python3 -m venv .venv exit 1
. .venv/bin/activate fi
python -V else
echo -e "${GREEN}==>${RESET} Successfully created venv" install_python_venv "$@"
fi python3 -m venv .venv
. .venv/bin/activate
python -V
echo -e "${GREEN}==>${RESET} Successfully created venv"
fi
fi fi
else
conda create -n cpweb python=3.12 || true
conda activate cpweb
fi fi
if ! cd "${root}/backend"; then if ! cd "${root}/backend"; then
@ -288,7 +383,12 @@ fi
echo -e "${GREEN}==>${RESET} Preparing to install dependencies..." echo -e "${GREEN}==>${RESET} Preparing to install dependencies..."
if ask_yn "Do you want to install it now?" y; then if ask_yn "Do you want to install it now?" y; then
pip install -r requirements.txt if ! cd "${root}"; then
echo -e "${RED}==>${RESET} unable to access ${root}, did you edit project structre?"
exit 1
fi
python -m pip install -r requirements.txt
else else
echo -e "${YELLOW}==>${RESET} Operation canceled, exiting..." echo -e "${YELLOW}==>${RESET} Operation canceled, exiting..."
exit 1 exit 1
@ -296,7 +396,5 @@ fi
echo -e "${GREEN}==>${RESET} Deployment successfull" echo -e "${GREEN}==>${RESET} Deployment successfull"
if ask_yn "Do you wish to start cellpose server now?" y; then if ask_yn "Do you wish to start cellpose server now?" y; then
python main.py python ${root}/backend/main.py
fi fi