mirror of
https://github.com/OLmatter/glm-coding-helper.git
synced 2026-10-07 16:18:10 +08:00
The Linux one-click-start and setup_backend_linux scripts were both assuming interactive stdin: - one-click-start.sh called `read -r -p ...` on every fatal exit path (non-Linux host, missing release files, install failure, missing weight). When the script is launched from a desktop entry, systemd user unit, CI, or any other context where stdin is not a TTY, `read` would block waiting for input that will never come, hanging the process instead of exiting promptly. Replace each site with a small `pause_if_tty` helper that only prompts when `[ -t 0 ]`, so the script still pauses for a user at a real terminal but returns immediately when run unattended. - scripts/setup_backend_linux.sh printed a single, hard-coded launch block that only mentioned `.venv_paddle` (CPU). After the previous --target auto/cpu/gpu/both refactor, the user could have built any combination of CPU and GPU venvs but the hints always pointed at the CPU one. Wrap the post-install message in `print_completion_hints`, iterate over the actually-selected modes to print the correct `<venv>/bin/python` for each (CPU -> .venv_paddle, GPU -> .venv_paddle_gpu), and add an auto-mode command line when both venvs exist or a hint to install the CPU fallback when only GPU was built. No behaviour change for the install flow itself; only the prompt handling and the post-install guidance.
315 lines
9.8 KiB
Bash
Executable File
315 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
||
# GLM Coding Helper 后端环境搭建(Linux)
|
||
#
|
||
# 等价于 Windows 的 setup_backend.ps1 / setup_backend.py:
|
||
# 1. 检测 Linux、NVIDIA GPU 和 Python 3.12
|
||
# 2. 优先使用 uv 创建 .venv_paddle / .venv_paddle_gpu
|
||
# 3. pip install -r requirements-backend-*.txt
|
||
# 4. smoke test 核心依赖
|
||
# 5. 检查 YOLO 权重
|
||
#
|
||
# 用法:
|
||
# ./scripts/setup_backend_linux.sh # 自动选择 GPU/CPU 环境
|
||
# ./scripts/setup_backend_linux.sh --target cpu # 安装 CPU 环境
|
||
# ./scripts/setup_backend_linux.sh --target gpu # 安装 GPU 环境
|
||
# ./scripts/setup_backend_linux.sh --target both # 同时安装 CPU/GPU 环境
|
||
# ./scripts/setup_backend_linux.sh --recreate # 删除并重建选中环境
|
||
# ./scripts/setup_backend_linux.sh --skip-install # 只创建 venv,不安装依赖
|
||
# ./scripts/setup_backend_linux.sh --no-smoke-test # 跳过导入冒烟测试
|
||
# ./scripts/setup_backend_linux.sh --pip-arg -i --pip-arg https://pypi.tuna.tsinghua.edu.cn/simple
|
||
#
|
||
# 未传 --pip-arg 时,会自动探测可用 PyPI 镜像(国内优先,与 Windows one-click 一致)。
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
cd "$ROOT"
|
||
|
||
export LC_ALL="${LC_ALL:-C.UTF-8}"
|
||
export LANG="${LANG:-C.UTF-8}"
|
||
export PYTHONUTF8=1
|
||
export PYTHONIOENCODING=utf-8
|
||
|
||
# ── 解析参数 ───────────────────────────────────────────────────
|
||
TARGET="auto"
|
||
RECREATE=0
|
||
SKIP_INSTALL=0
|
||
NO_SMOKE_TEST=0
|
||
PIP_ARGS=()
|
||
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--target)
|
||
shift
|
||
if [ $# -eq 0 ]; then
|
||
echo "[错误] --target 需要跟一个参数:auto/cpu/gpu/both" >&2
|
||
exit 1
|
||
fi
|
||
TARGET="$1"
|
||
shift
|
||
;;
|
||
--recreate)
|
||
RECREATE=1
|
||
shift
|
||
;;
|
||
--skip-install)
|
||
SKIP_INSTALL=1
|
||
shift
|
||
;;
|
||
--no-smoke-test)
|
||
NO_SMOKE_TEST=1
|
||
shift
|
||
;;
|
||
--pip-arg)
|
||
shift
|
||
if [ $# -eq 0 ]; then
|
||
echo "[错误] --pip-arg 需要跟一个参数" >&2
|
||
exit 1
|
||
fi
|
||
PIP_ARGS+=("$1")
|
||
shift
|
||
;;
|
||
--help|-h)
|
||
sed -n '2,20p' "$0"
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo "[错误] 未知参数:$1" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
case "$TARGET" in
|
||
auto|cpu|gpu|both) ;;
|
||
*)
|
||
echo "[错误] --target 仅支持 auto/cpu/gpu/both,当前为:$TARGET" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
echo "GLM Coding Helper 后端环境搭建(Linux)"
|
||
echo "仓库根目录:$ROOT"
|
||
echo ""
|
||
|
||
# ── 1. 检查系统并选择 Python 3.12 ─────────────────────────────
|
||
if [ "$(uname -s)" != "Linux" ]; then
|
||
echo "[错误] 此脚本仅支持 Linux。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# shellcheck source=scripts/pypi_mirror.sh
|
||
source "$SCRIPT_DIR/pypi_mirror.sh"
|
||
|
||
has_uv() {
|
||
command -v uv >/dev/null 2>&1
|
||
}
|
||
|
||
has_nvidia_gpu() {
|
||
command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1
|
||
}
|
||
|
||
resolve_python_312() {
|
||
if [ -n "${CNCAPTCHA_PYTHON:-}" ]; then
|
||
if [ ! -x "$CNCAPTCHA_PYTHON" ] && ! command -v "$CNCAPTCHA_PYTHON" >/dev/null 2>&1; then
|
||
echo "[错误] CNCAPTCHA_PYTHON 指定的解释器不可用:$CNCAPTCHA_PYTHON" >&2
|
||
exit 1
|
||
fi
|
||
echo "$CNCAPTCHA_PYTHON"
|
||
return
|
||
fi
|
||
|
||
if has_uv; then
|
||
if ! uv python find 3.12 >/dev/null 2>&1; then
|
||
echo "[INFO] uv 可用,但未找到 Python 3.12;开始执行 uv python install 3.12" >&2
|
||
uv python install 3.12 >&2
|
||
fi
|
||
uv python find 3.12
|
||
return
|
||
fi
|
||
|
||
if command -v python3.12 >/dev/null 2>&1; then
|
||
command -v python3.12
|
||
return
|
||
fi
|
||
|
||
echo "[错误] 没有找到 Python 3.12。请先安装 Python 3.12,或安装 uv 后重试:" >&2
|
||
echo " curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
|
||
echo " uv python install 3.12" >&2
|
||
exit 1
|
||
}
|
||
|
||
PY="$(resolve_python_312)"
|
||
PY_VERSION="$("$PY" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || true)"
|
||
if [ "$PY_VERSION" != "3.12" ]; then
|
||
echo "[错误] 需要 Python 3.12,当前解释器版本为 ${PY_VERSION:-未知}:$PY" >&2
|
||
echo " 可设置 CNCAPTCHA_PYTHON=/path/to/python3.12 后重试。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if has_uv; then
|
||
echo "[INFO] 使用 uv 管理虚拟环境和依赖"
|
||
fi
|
||
echo "[INFO] 使用 Python:$PY ($PY_VERSION)"
|
||
|
||
# ── 2. 选择安装目标 ─────────────────────────────────────────────
|
||
if [ "$TARGET" = "auto" ]; then
|
||
if has_nvidia_gpu; then
|
||
TARGET="gpu"
|
||
else
|
||
TARGET="cpu"
|
||
fi
|
||
echo "[INFO] 自动选择安装目标:$TARGET"
|
||
fi
|
||
|
||
if [ "$TARGET" = "both" ]; then
|
||
SELECTED=("cpu" "gpu")
|
||
else
|
||
SELECTED=("$TARGET")
|
||
fi
|
||
|
||
venv_python() {
|
||
echo "$1/bin/python"
|
||
}
|
||
|
||
# ── 3. 创建 / 重建 venv ────────────────────────────────────────
|
||
create_venv() {
|
||
local venv_dir="$1"
|
||
local venv_py
|
||
venv_py="$(venv_python "$venv_dir")"
|
||
|
||
if [ "$RECREATE" -eq 1 ] && [ -d "$venv_dir" ]; then
|
||
echo "[INFO] 删除已有环境:$venv_dir"
|
||
rm -rf "$venv_dir"
|
||
fi
|
||
|
||
if [ ! -x "$venv_py" ]; then
|
||
echo "[INFO] 创建虚拟环境:$venv_dir"
|
||
if has_uv; then
|
||
uv venv --python "$PY" "$venv_dir"
|
||
else
|
||
"$PY" -m venv "$venv_dir"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# ── 4. 安装依赖 ────────────────────────────────────────────────
|
||
install_with_pip() {
|
||
local venv_py="$1"
|
||
local req="$2"
|
||
|
||
if [ ! -f "$req" ]; then
|
||
echo "[错误] 缺少 $req,请确认是完整的 Release 包。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "[INFO] 升级 pip / setuptools / wheel"
|
||
if has_uv; then
|
||
uv pip install --python "$venv_py" --upgrade pip setuptools wheel "${PIP_ARGS[@]}"
|
||
else
|
||
"$venv_py" -m pip install --upgrade pip setuptools wheel "${PIP_ARGS[@]}"
|
||
fi
|
||
|
||
echo "[INFO] 安装依赖:$req(可能需要几分钟)..."
|
||
if has_uv; then
|
||
uv pip install --python "$venv_py" -r "$req" "${PIP_ARGS[@]}"
|
||
else
|
||
"$venv_py" -m pip install -r "$req" "${PIP_ARGS[@]}"
|
||
fi
|
||
}
|
||
|
||
# ── 5. smoke test ─────────────────────────────────────────────
|
||
smoke_test() {
|
||
local venv_py="$1"
|
||
local mode="$2"
|
||
|
||
echo ""
|
||
echo "[INFO] 运行 ${mode^^} 导入冒烟测试..."
|
||
"$venv_py" -c "import PIL, cv2, numpy, ultralytics; from paddleocr import TextRecognition; print('core imports ok')"
|
||
"$venv_py" -c "import fastapi, uvicorn, psutil; print('backend deps ok')"
|
||
|
||
if [ "$mode" = "gpu" ]; then
|
||
"$venv_py" -c "import paddle; print('cuda_compiled=', paddle.is_compiled_with_cuda()); print('cuda_count=', paddle.device.cuda.device_count() if paddle.is_compiled_with_cuda() else 0)"
|
||
fi
|
||
}
|
||
|
||
if [ "$SKIP_INSTALL" -eq 0 ]; then
|
||
ensure_pypi_mirror_pip_args
|
||
fi
|
||
|
||
for mode in "${SELECTED[@]}"; do
|
||
if [ "$mode" = "gpu" ]; then
|
||
VENV_DIR="$ROOT/.venv_paddle_gpu"
|
||
REQ="$ROOT/requirements-backend-gpu.txt"
|
||
else
|
||
VENV_DIR="$ROOT/.venv_paddle"
|
||
REQ="$ROOT/requirements-backend-cpu.txt"
|
||
fi
|
||
VENV_PY="$(venv_python "$VENV_DIR")"
|
||
|
||
echo ""
|
||
echo "=== Setting up ${mode^^} backend environment ==="
|
||
create_venv "$VENV_DIR"
|
||
|
||
if [ "$SKIP_INSTALL" -eq 0 ]; then
|
||
install_with_pip "$VENV_PY" "$REQ"
|
||
fi
|
||
|
||
if [ "$NO_SMOKE_TEST" -eq 0 ]; then
|
||
smoke_test "$VENV_PY" "$mode"
|
||
fi
|
||
done
|
||
|
||
# ── 6. 检查 YOLO 权重 ──────────────────────────────────────────
|
||
WEIGHT="$ROOT/models/weights/yolo-captcha-detector.pt"
|
||
echo ""
|
||
if [ -f "$WEIGHT" ]; then
|
||
echo "[OK] 检测权重就绪:$WEIGHT"
|
||
else
|
||
echo "[WARN] 缺少检测权重:$WEIGHT" >&2
|
||
echo " 请从 Release 包补齐该文件后再启动后端。" >&2
|
||
fi
|
||
|
||
# ── 7. 完成 ────────────────────────────────────────────────────
|
||
print_completion_hints() {
|
||
local mode py has_cpu=0 has_gpu=0
|
||
|
||
for mode in "${SELECTED[@]}"; do
|
||
case "$mode" in
|
||
cpu) has_cpu=1 ;;
|
||
gpu) has_gpu=1 ;;
|
||
esac
|
||
done
|
||
|
||
echo ""
|
||
echo "完成。启动后端:"
|
||
echo ""
|
||
|
||
for mode in "${SELECTED[@]}"; do
|
||
if [ "$mode" = "gpu" ]; then
|
||
py="$ROOT/.venv_paddle_gpu/bin/python"
|
||
else
|
||
py="$ROOT/.venv_paddle/bin/python"
|
||
fi
|
||
echo " ${mode^^} 环境:"
|
||
echo " GUI: $py $ROOT/scripts/tools/start_backend.py --mode $mode"
|
||
echo " headless: $py $ROOT/scripts/tools/start_backend.py --headless --mode $mode"
|
||
echo ""
|
||
done
|
||
|
||
if [ "$has_gpu" -eq 1 ] && [ "$has_cpu" -eq 1 ]; then
|
||
echo " auto 模式(GPU 优先,失败回退 CPU):"
|
||
echo " $ROOT/.venv_paddle_gpu/bin/python $ROOT/scripts/tools/start_backend.py --headless --mode auto"
|
||
echo ""
|
||
elif [ "$has_gpu" -eq 1 ]; then
|
||
echo " 提示:使用 auto 模式前,建议再安装 CPU 回退环境:"
|
||
echo " ./scripts/setup_backend_linux.sh --target cpu"
|
||
echo ""
|
||
fi
|
||
|
||
echo " Linux 支持 CPU;如 NVIDIA/CUDA/PaddlePaddle 环境可用,也支持 GPU/auto 模式。"
|
||
}
|
||
|
||
print_completion_hints
|