Files
hoywu 8457a65b14 docs(setup): document Linux support and ship one-click-start.sh in release zips
The Linux one-click-start.sh and setup_backend_linux.sh scripts landed in
the previous setup commits but were not surfaced in user-facing docs or
in the release zip layout, so Linux users had no on-ramp. This commit
adds the missing user-facing documentation and ships the Linux entry
point in both release zip builders.

- README.md: extend 快速开始 / 启动后端 to cover Linux alongside Windows
  and macOS, update the zip table to mark online-installer as the
  macOS / Linux recommendation, add Linux quick-start snippet, and
  refresh 常用文件 / 常用启动方式 to list one-click-start.sh and the
  new docs/linux-setup.md. Wording tightened so Windows users are not
  told to follow Linux commands and vice versa.
- docs/linux-setup.md: new Chinese-language install guide covering scope,
  prerequisites (Python 3.12 / uv, NVIDIA GPU optional), one-click and
  manual setup paths, virtualenv layout, known limitations, port
  troubleshooting, post-install verification, and a Windows/macOS/Linux
  comparison table.
- one-click-start.sh: refresh the top-of-file comment so it describes
  the actual entry point (start_backend.py --headless ->
  captcha_server_headless) instead of the old "pipeline backend" copy
  from when the Linux path was a stub.
- scripts/release/build_portable.ps1: include one-click-start.sh in the
  portable zip and add a Linux section to the embedded portable README
  pointing users at online-installer for Linux with a fallback chmod +
  run snippet.
- scripts/release/build_release_zips.ps1: include one-click-start.sh in
  the common zip items and rewrite ONLINE_INSTALLER_README.txt to give
  per-platform launch instructions, document auto PyPI mirror detection,
  the .venv_paddle / .venv_paddle_gpu layout, and link to
  docs/linux-setup.md.

No code logic change; only docs + packaging so Linux is a first-class
release target alongside Windows and macOS.
2026-06-24 20:53:24 +08:00

302 lines
9.8 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.
#!/bin/bash
# GLM Coding Helper 一键启动(Linux)
# 用途:首次安装 CPU/GPU 后端环境并启动 headless captcha_server 后端(start_backend.py --headless)。
#
# 命令行运行:./one-click-start.sh
#
# Linux 说明:默认 auto 模式会优先尝试 NVIDIA GPU;GPU 环境不可用时回退 CPU。
# 如果系统中存在 uv,会优先用 uv 管理 Python 3.12、venv 和依赖安装。
# 未传 --pip-arg 时,会自动探测可用 PyPI 镜像(国内优先)。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
export LC_ALL="${LC_ALL:-C.UTF-8}"
export LANG="${LANG:-C.UTF-8}"
export PYTHONUTF8=1
export PYTHONIOENCODING=utf-8
# ── 解析参数 ───────────────────────────────────────────────────
TARGET="auto"
PORT="${CNCAPTCHA_PORT:-8888}"
PIP_ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--target)
shift
if [ $# -eq 0 ]; then
echo "[错误] --target 需要跟一个参数:auto/cpu/gpu" >&2
exit 1
fi
TARGET="$1"
shift
;;
--port)
shift
if [ $# -eq 0 ]; then
echo "[错误] --port 需要跟一个端口号" >&2
exit 1
fi
PORT="$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,8p' "$0"
exit 0
;;
*)
echo "[错误] 未知参数:$1" >&2
exit 1
;;
esac
done
case "$TARGET" in
auto|cpu|gpu) ;;
*)
echo "[错误] --target 仅支持 auto/cpu/gpu,当前为:$TARGET" >&2
exit 1
;;
esac
cat <<'EOF'
------------------------------------------------------------
GLM Coding Helper 一键启动(Linux)
------------------------------------------------------------
首次运行会自动创建 .venv_paddle / .venv_paddle_gpu 并安装依赖。
安装包较大(paddle / ultralytics),请保持网络畅通。
如果系统中存在 uv,将优先使用 uv 管理 Python 3.12、venv 和依赖。
------------------------------------------------------------
EOF
# ── 1. 检查系统和 Release 文件 ────────────────────────────────
if [ "$(uname -s)" != "Linux" ]; then
echo "[错误] 此脚本仅支持 Linux。" >&2
pause_if_tty "按回车键退出..."
exit 1
fi
# ── 辅助函数 ───────────────────────────────────────────────────
pause_if_tty() {
local prompt="${1:-按回车键退出...}"
if [ -t 0 ]; then
read -r -p "$prompt"
fi
}
has_nvidia_gpu() {
command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1
}
test_python_imports() {
local py="$1"
local code="$2"
[ -x "$py" ] || return 1
"$py" -c "$code" >/dev/null 2>&1
}
test_foreign_venv() {
local venv_dir="$1"
local cfg="$venv_dir/pyvenv.cfg"
[ -d "$venv_dir" ] || return 1
[ -f "$cfg" ] || return 0
local executable
executable="$(sed -n 's/^executable[[:space:]]*=[[:space:]]*//p' "$cfg" | head -n1 || true)"
if [ -n "$executable" ] && [ ! -x "$executable" ]; then
return 0
fi
local created_at
created_at="$(sed -n 's/^command[[:space:]]*=.* -m venv //p' "$cfg" | head -n1 | sed 's/^"//; s/"$//' || true)"
if [ -n "$created_at" ]; then
local expected actual
expected="$(cd "$venv_dir" && pwd -P)"
if [ -d "$created_at" ]; then
actual="$(cd "$created_at" && pwd -P)"
[ "$actual" = "$expected" ] || return 0
fi
fi
return 1
}
# shellcheck source=scripts/pypi_mirror.sh
source "$SCRIPT_DIR/scripts/pypi_mirror.sh"
assert_required_files() {
local missing=()
local required=(
"scripts/setup_backend_linux.sh"
"scripts/tools/start_backend.py"
"requirements-backend-cpu.txt"
"requirements-backend-gpu.txt"
)
for rel in "${required[@]}"; do
if [ ! -f "$SCRIPT_DIR/$rel" ]; then
missing+=("$rel")
fi
done
if [ "${#missing[@]}" -gt 0 ]; then
echo "[FAIL] Release package is incomplete. Missing files:" >&2
for item in "${missing[@]}"; do
echo " - $item" >&2
done
echo "Please re-extract the full latest release zip and retry." >&2
pause_if_tty "Press Enter to exit"
exit 1
fi
}
invoke_setup() {
local setup_target="$1"
local venv_py="$2"
local force_recreate="${3:-0}"
local setup_script="$SCRIPT_DIR/scripts/setup_backend_linux.sh"
local -a setup_args=("--target" "$setup_target")
# 与 Windows Invoke-Bootstrap 一致:外来/损坏 venv,或目标 python 已存在时先删再建。
if [ "$force_recreate" -eq 1 ] || { [ -n "$venv_py" ] && [ -e "$venv_py" ]; }; then
echo "Existing backend environment failed portability/import checks. Recreating it..."
setup_args+=("--recreate")
fi
for arg in "${PIP_ARGS[@]}"; do
setup_args+=("--pip-arg" "$arg")
done
mkdir -p "$SCRIPT_DIR/logs"
echo "详细安装日志: $SCRIPT_DIR/logs/backend-install.log"
bash "$setup_script" "${setup_args[@]}" 2>&1 | tee "$SCRIPT_DIR/logs/backend-install.log"
}
assert_required_files
# ── 2. 选择安装目标和虚拟环境 ──────────────────────────────────
INSTALL_TARGET="$TARGET"
if [ "$INSTALL_TARGET" = "auto" ]; then
if has_nvidia_gpu; then
INSTALL_TARGET="gpu"
else
INSTALL_TARGET="cpu"
fi
fi
CPU_VENV="$SCRIPT_DIR/.venv_paddle"
GPU_VENV="$SCRIPT_DIR/.venv_paddle_gpu"
CPU_PY="$CPU_VENV/bin/python"
GPU_PY="$GPU_VENV/bin/python"
IMPORT_CODE="import fastapi, uvicorn, psutil, ultralytics, paddleocr, paddlex, paddle, cv2, PIL, numpy"
if [ "$INSTALL_TARGET" = "gpu" ]; then
SELECTED_VENV="$GPU_VENV"
SELECTED_PY="$GPU_PY"
else
SELECTED_VENV="$CPU_VENV"
SELECTED_PY="$CPU_PY"
fi
# ── 3. 安装或修复虚拟环境 ──────────────────────────────────────
NEEDS_RECREATE=0
if test_foreign_venv "$SELECTED_VENV"; then
NEEDS_RECREATE=1
echo "[WARN] Existing backend environment was created on another machine or in another folder. It will be rebuilt locally." >&2
fi
READY=0
if [ "$NEEDS_RECREATE" -eq 0 ] && test_python_imports "$SELECTED_PY" "$IMPORT_CODE"; then
READY=1
fi
if [ "$READY" -eq 0 ]; then
echo "Backend environment is missing or incomplete. Installing $INSTALL_TARGET environment..."
ensure_pypi_mirror_pip_args
if ! invoke_setup "$INSTALL_TARGET" "$SELECTED_PY" "$NEEDS_RECREATE"; then
SETUP_EXIT=1
else
SETUP_EXIT=0
fi
READY=0
if test_python_imports "$SELECTED_PY" "$IMPORT_CODE"; then
READY=1
fi
if [ "$SETUP_EXIT" -ne 0 ] || [ "$READY" -eq 0 ]; then
if [ "$TARGET" = "auto" ] && [ "$INSTALL_TARGET" = "gpu" ]; then
echo "[WARN] GPU bootstrap failed or remained incomplete. Falling back to CPU environment..." >&2
INSTALL_TARGET="cpu"
SELECTED_VENV="$CPU_VENV"
SELECTED_PY="$CPU_PY"
if ! invoke_setup "cpu" "$CPU_PY" 0; then
SETUP_EXIT=1
else
SETUP_EXIT=0
fi
READY=0
if test_python_imports "$SELECTED_PY" "$IMPORT_CODE"; then
READY=1
fi
fi
fi
if [ "$READY" -eq 0 ]; then
echo "[FAIL] Backend environment repair failed. Required deps still missing." >&2
if [ "$TARGET" = "auto" ]; then
echo " Auto mode already attempted GPU/CPU fallback." >&2
fi
echo " 完整安装日志已保存到 logs/backend-install.log,排查请提供此文件。" >&2
pause_if_tty "按回车键退出..."
exit 1
fi
fi
# ── 4. 安装 CPU fallback 环境 ──────────────────────────────────
if [ "$TARGET" = "auto" ] && [ "$INSTALL_TARGET" = "gpu" ] && ! test_python_imports "$CPU_PY" "$IMPORT_CODE"; then
echo "CPU fallback environment is missing. Installing CPU environment for auto fallback..."
if ! invoke_setup "cpu" "$CPU_PY" 0; then
echo "[WARN] CPU fallback environment installation failed. Auto mode will still try GPU first." >&2
fi
fi
# ── 5. 检查 YOLO 权重 ──────────────────────────────────────────
WEIGHT="$SCRIPT_DIR/models/weights/yolo-captcha-detector.pt"
if [ ! -f "$WEIGHT" ]; then
echo "[错误] 缺少检测权重:$WEIGHT" >&2
echo " 请从 Release 包补齐 models/weights/yolo-captcha-detector.pt 后再启动。" >&2
pause_if_tty "按回车键退出..."
exit 1
fi
export CNCAPTCHA_PORT="$PORT"
export CNCAPTCHA_CPU_OCR_PYTHON="$CPU_PY"
export CNCAPTCHA_GPU_OCR_PYTHON="$GPU_PY"
# ── 6. 启动后端(headless)────────────────────────────────────
START_MODE="$INSTALL_TARGET"
if [ "$TARGET" = "auto" ]; then
START_MODE="auto"
fi
echo ""
echo "[INFO] 使用 Python:$SELECTED_PY"
echo "[INFO] 启动后端:http://127.0.0.1:$PORT (Ctrl+C 停止)"
echo ""
exec "$SELECTED_PY" "$SCRIPT_DIR/scripts/tools/start_backend.py" --headless --mode "$START_MODE" --port "$PORT"