cellpose-web/install.sh
2025-10-17 14:00:16 +03:00

439 lines
13 KiB
Bash
Executable file
Raw Permalink 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
set -euo pipefail
GREEN='\033[32m'
RESET='\033[0m'
YELLOW='\033[33m'
RED='\033[31m'
ask_yn() {
# $1=提示语 $2=默认值 y|n
local prompt="$1"
local def="${2:-n}"
local show="[y/N]"
[ "$def" = "y" ] && show="[Y/n]"
while :; do
printf "%s %s " "$prompt" "$show" >/dev/tty
IFS= read -r ans </dev/tty || return 1
[ -z "$ans" ] && ans="$def"
case "$ans" in
[Yy] | [Yy][Ee][Ss]) return 0 ;;
[Nn] | [Nn][Oo]) return 1 ;;
*) echo "Please answer y or n." >/dev/tty ;;
esac
done
}
#--- 小工具:取当前 backend.ip ---
get_current_ip() {
if command -v ip >/dev/null 2>&1; then
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}}' |
grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
# 次选hostname -I
hostname -I 2>/dev/null |
awk '{print $1}' |
grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
fi
# 2) macOS: ipconfig
if [ "$(uname -s)" = "Darwin" ]; then
for i in en0 en1 en2; do
ipconfig getifaddr "$i" 2>/dev/null |
grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
done
fi
# 3) 通用回退ifconfig 解析
if command -v ifconfig >/dev/null 2>&1; then
ifconfig 2>/dev/null |
awk '/inet (addr:)?([0-9]+\.){3}[0-9]+/ {
ip=$2; sub("addr:","",ip);
if (ip!="127.0.0.1" && ip !~ /^127\./) {print ip; exit}
}' |
grep -E '^[0-9]+(\.[0-9]+){3}$' && return 0
fi
# 4) 实在拿不到
return 1
}
#--- 简单 IPv4 校验(也可接受主机名;如只要 IPv4保留第一个分支即可 ---
is_valid_host() {
local s=${1:-}
# 先判 IPv4 形状
if [[ $s =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
IFS=. read -r a b c d <<<"$s" || return 1
for o in "$a" "$b" "$c" "$d"; do
[[ $o =~ ^[0-9]{1,3}$ ]] || return 1
((o >= 0 && o <= 255)) || return 1
done
return 0
fi
# 再判主机名RFC1123 宽松版:标签 1-63允许中划线但不能以 - 开头/结尾)
[[ $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() {
local IP=$1
local ROOT_DIR=$2
if [ "$(uname -s)" = "Darwin" ]; then
sed -E -i '' "s/(host[[:space:]]*:[[:space:]]*['\"])([^'\"]*)(['\"])/\1$NEW_IP\3/" frontend/api.js
else
sed -E -i.bak "s/(host[[:space:]]*:[[:space:]]*['\"])([^'\"]*)(['\"])/\1$NEW_IP\3/" frontend/api.js
fi
}
need_sudo() {
if [ "$EUID" -ne 0 ]; then echo "sudo"; else echo ""; fi
}
detect_distro() {
if [ -r /etc/os-release ]; then
. /etc/os-release
echo "${ID:-unknown}|${ID_LIKE:-}"
else
echo "unknown|"
fi
}
install_debian() {
local package=$1
local SUDO=$(need_sudo)
if [ "$package" = "python" ]; then
$SUDO apt-get update -y
# 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() {
local package=$1
local SUDO=$(need_sudo)
# Arch 的 python 包自带 venv 模块
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() {
local package=$1
local SUDO=$(need_sudo)
set -e
local PM
if command -v dnf >/dev/null 2>&1; then PM=dnf; else PM=yum; fi
# 识别 RHEL/CentOS 主版本号7/8/9
local ELVER; ELVER="$(rpm -E %rhel 2>/dev/null || echo 0)"
# 优先 dnf旧版用 yum。RHEL/CentOS/Fedora 的 python3 自带 venv
if [[ "$package" = "python" ]]; then
if command -v dnf >/dev/null 2>&1; then
$SUDO dnf -y install python3 python3-pip
$SUDO dnf -y groupinstall "Development Tools" || true
else
$SUDO yum -y install python3 python3-pip || true
$SUDO yum -y groupinstall "Development Tools" || true
fi
elif [[ "$package" = "redis" ]]; then
if [ "$ELVER" -eq 7 ]; then
sudo $PM install -y epel-release
sudo $PM install -y redis
elif [ "$ELVER" -eq 8 ]; then
sudo $PM install -y epel-release
# 启用 PowerToolsEL8
if command -v dnf >/dev/null 2>&1; then
sudo dnf config-manager --set-enabled powertools || true
else
sudo yum install -y dnf-plugins-core || true
sudo yum config-manager --set-enabled powertools || true
fi
# 启用 AppStream 模块里的 redis常见是 6
sudo dnf -y module reset redis 2>/dev/null || true
sudo dnf -y module enable redis:6 2>/dev/null || true
sudo $PM install -y redis
else # EL9 及以上CentOS Stream 9 / RHEL 9
sudo $PM install -y epel-release
# 启用 CRBEL9 的 PowerTools
if command -v dnf >/dev/null 2>&1; then
sudo dnf config-manager --set-enabled crb || true
fi
# 先尝试 EPEL 提供的 redis有的镜像没有
if ! sudo $PM install -y redis; then
# 回退到 Remi 仓库(提供 redis:7
sudo $PM install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf -y module reset redis || true
sudo dnf -y module enable redis:7 || true
sudo $PM install -y redis
fi
fi
# 安装后启动服务(不同发行服务名不同)
if command -v systemctl >/dev/null 2>&1; then
sudo systemctl enable --now redis 2>/dev/null || \
sudo systemctl enable --now redis-server 2>/dev/null || true
fi
fi
}
install_python_venv() {
local info
info=$(detect_distro)
local id="${info%%|*}"
local like="${info#*|}"
echo "Detected: id=$id, like=$like"
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 "python"
elif echo "$like" | grep -Eiq "rhel|fedora|centos"; then
install_rhel "python"
elif echo "$like" | grep -qi "arch"; then
install_arch "python"
else
echo "Unknown Linux distro: $id. Please install it manualy"
echo " Debian/Ubuntu: sudo apt-get install -y python3 python3-venv python3-pip"
echo " Arch/Manjaro: sudo pacman -S python python-pip"
echo " RHEL/CentOS: sudo dnf/yum install -y python3 python3-pip"
exit 1
fi
;;
esac
# 验证 venv 可用
if python3 - <<'PY'; then
import sys, subprocess, tempfile, os
try:
d = tempfile.mkdtemp(prefix="venv_test_")
subprocess.check_call([sys.executable, "-m", "venv", os.path.join(d, "v")], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("OK")
except Exception as e:
print("ERR", e)
raise
PY
echo "✅ python3 venv usable"
else
echo "❌ venv not usable Please check whether it's installed (Debian/Ubuntu: python3-venv)" >&2
exit 1
fi
}
install_redis() {
local info
info=$(detect_distro)
local id="${info%%|*}"
local like="${info#*|}"
echo "Detected: id=$id, like=$like"
case "$id" in
arch | manjaro)
install_arch "redis"
;;
ubuntu | debian | linuxmint | pop | elementary | neon | kali | raspbian)
install_debian "redis"
;;
fedora | centos | rhel | rocky | almalinux | ol)
install_rhel "redis"
;;
*)
# 用 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=""
if [ -n "$root" ]; then
echo -e "${GREEN}==>${RESET} ✅Inside a git repo, root: ${root}"
echo -e "${GREEN}==>${RESET} Checking for update..."
git pull origin master
else
echo -e "${YELLOW}==>${RESET} Repo enviroment not found"
if ask_yn "Do you wish to clone it from github?" y; then
echo -e "${GREEN}==>${RESET} Cloning from remote..."
git clone https://github.com/ClovertaTheTrilobita/cellpose-web.git
if ! cd cellpose-web; then
echo -e "${RED}==>${RESET} directory 'cellpose-web' not found or inaccessible, did git clone run successfully?" >&2
exit 1
fi
fi
fi
root=$(pwd)
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"
DEFAULT_IP="$(get_current_ip || true)"
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)"
printf "%s: " "$prompt" >/dev/tty
IFS= read -r NEW_IP </dev/tty
# 默认回车使用探测到的 IP
if [ -z "${NEW_IP}" ]; then
if [ -n "${DEFAULT_IP:-}" ]; then
NEW_IP="$DEFAULT_IP"
echo -e "${GREEN}==>${RESET} Using detected IP: $NEW_IP"
else
echo -e "${RED}==>${RESET} No input and no IP detected. Aborted." >&2
exit 1
fi
fi
if ! is_valid_host "$NEW_IP"; then
echo -e "${RED}==>${RESET} Invalid IP/hostname: $NEW_IP" >&2
if ask_yn "Do you wish to use default ip (${DEFAULT_IP})?" y; then
echo -e "${GREEN}==>${RESET} Using detected IP: $NEW_IP"
else
exit 1
fi
fi
#cp -a -- "$FILE" "$FILE.bak.$(date +%Y%m%d%H%M%S)"
if command -v yq >/dev/null 2>&1; then
NEW_IP_ENV="$NEW_IP" yq -i '.backend.ip = strenv(NEW_IP_ENV)' "$FILE"
else
awk -v newip="$NEW_IP" '
BEGIN { inblk=0; base=-1; done=0 }
/^[[:space:]]*backend:[[:space:]]*$/ { inblk=1; base=indent($0); print; next }
{
if (inblk && !done) {
ind = indent($0)
if (ind <= base) { inblk=0 }
else if ($0 ~ /^[[:space:]]*ip:[[:space:]]*/) {
match($0, /^[[:space:]]*/); sp = substr($0, 1, RLENGTH)
print sp "ip: " newip
done=1; next
}
}
print
}
function indent(s, n) {
match(s, /^[[:space:]]*/)
return RLENGTH
}
' "$FILE" >"$FILE.tmp" && mv "$FILE.tmp" "$FILE"
fi
echo -e "${GREEN}==>${RESET} Updated backend.ip -> $NEW_IP in $FILE"
update_frontend_ip $NEW_IP $root
echo -e "${GREEN}==>${RESET} Update host -> $NEW_IP in frontend/api.js"
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 "We highly recommend you to use Conda to manage python enviroments"
if ask_yn "Do you wish to install miniconda?" y; then
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
bash miniconda.sh -b -p $HOME/miniconda3
rm miniconda.sh
eval "$($HOME/miniconda3/bin/conda shell.bash hook)"
conda init
conda create -n cpweb python=3.12 || true
conda activate cpweb
else
echo -e "${GREEN}==>${RESET} Creating Python venv..."
if [ "$(uname -s)" = "Darwin" ]; then
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..."
exit 1
fi
if ! brew install python >/dev/null 2>&1; then
echo -e "${RED}==>${RESET} Failed to install Python via Homebrew.\n"
exit 1
fi
else
install_python_venv "$@"
python3 -m venv .venv
. .venv/bin/activate
python -V
echo -e "${GREEN}==>${RESET} Successfully created venv"
fi
fi
fi
if ! cd "${root}/backend"; then
echo -e "${RED}==>${RESET} dir backend/ missing, did you edit project structre?"
exit 1
fi
echo -e "${GREEN}==>${RESET} Preparing to install dependencies..."
if ask_yn "Do you want to install it now?" y; then
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
echo -e "${YELLOW}==>${RESET} Operation canceled, exiting..."
exit 1
fi
echo -e "${GREEN}==>${RESET} Deployment successfull"
if ask_yn "Do you wish to start cellpose developmental server now?" y; then
python ${root}/backend/main.py
fi