install.sh 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #!/bin/sh
  2. # 观澜·如东样板 v2 — Linux/macOS 离线安装 (支持 银河麒麟 V10 / 统信 UOS 20 / Ubuntu / CentOS / macOS)
  3. # 用法: sh install.sh (自动选 Python; 无合适系统 Python 时用包内便携运行时)
  4. # GUANLAN_PY=/path/python3 sh install.sh (指定 Python, 多版本机器用)
  5. set -e
  6. # 同理: 全局 PYTHONPATH 会让 pip 误判依赖已满足而不装进 .venv (见 install.ps1 注释)
  7. unset PYTHONPATH
  8. cd "$(dirname "$0")"; ROOT=$(pwd); export PYTHONUTF8=1 LC_ALL=${LC_ALL:-C.UTF-8}
  9. say(){ printf '%s\n' "$*"; }
  10. ARCH=$(uname -m); case "$ARCH" in x86_64|amd64) A=x86_64;; aarch64|arm64) A=aarch64;; *) A=$ARCH;; esac
  11. OS=$(uname -s)
  12. DISTRO=$( [ -f /etc/os-release ] && . /etc/os-release && echo "${PRETTY_NAME:-$NAME}" || echo "$OS" )
  13. say "== 观澜 v2 安装 · $DISTRO · $ARCH"
  14. # 1) 选 Python: 显式指定 > 系统 3.11/3.12 > 包内便携运行时 (麒麟/统信自带多为 3.7/3.9, 跑不了本项目依赖)
  15. PY=""
  16. if [ -n "$GUANLAN_PY" ]; then PY="$GUANLAN_PY"
  17. else
  18. for c in python3.12 python3.13 python3.11 python3; do
  19. p=$(command -v $c 2>/dev/null) || continue
  20. "$p" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3,11) else 1)' 2>/dev/null && { PY="$p"; break; }
  21. done
  22. fi
  23. if [ -z "$PY" ]; then
  24. RT=$(ls vendor/python/cpython-3.12*-$A-*linux-gnu-install_only*.tar.gz 2>/dev/null | head -1)
  25. [ "$OS" = Darwin ] && RT=""
  26. if [ -n "$RT" ]; then
  27. say " 系统无 Python 3.11+, 用包内便携运行时: $(basename "$RT")"
  28. rm -rf runtime && mkdir -p runtime && tar -xzf "$RT" -C runtime
  29. PY="$ROOT/runtime/python/bin/python3"
  30. else
  31. say " ✗ 未找到 Python 3.11+ , 包内也没有本架构 ($A) 的便携运行时。"
  32. say " 办法一: 装系统 Python 3.11+ ; 办法二: GUANLAN_PY=/路径/python3 sh install.sh"
  33. exit 2
  34. fi
  35. fi
  36. say " Python: $PY ($("$PY" -V 2>&1))"
  37. # 2) 虚拟环境 + 离线依赖 (轮子在 wheels/linux_<arch> 或 wheels/macos_arm64)
  38. "$PY" -m venv .venv 2>/dev/null || "$PY" -m venv --without-pip .venv
  39. VPY=$ROOT/.venv/bin/python
  40. "$VPY" -m ensurepip --upgrade >/dev/null 2>&1 || true
  41. "$VPY" -m pip install --upgrade pip --quiet 2>/dev/null || true
  42. case "$OS" in Darwin) W=wheels/macos_arm64;; *) W=wheels/linux_$A;; esac
  43. if [ -d "$W" ] && [ "$(ls -A "$W" 2>/dev/null | wc -l)" -gt 0 ]; then
  44. say "== 离线安装依赖 ($W)"; "$VPY" -m pip install --no-index --find-links "$W" -r requirements.txt --quiet
  45. else
  46. say "== 包内无本平台轮子 ($W), 改为联网安装"; "$VPY" -m pip install -r requirements.txt --quiet
  47. fi
  48. # 3) 写配置 (python 指向本 venv)
  49. "$VPY" - <<'PYEOF'
  50. import json, pathlib, sys
  51. p = pathlib.Path('configs/serve.json') # portability-allow: 安装脚本已 cd 到安装根
  52. c = json.loads(p.read_text(encoding='utf-8'))
  53. # 写**相对**路径 (相对安装根): 整个目录拷到别的电脑/别的盘后仍然有效 (绝对路径会在换机后失效)
  54. exe = pathlib.Path(sys.executable).resolve(); root = pathlib.Path('.').resolve()
  55. try:
  56. c['python'] = exe.relative_to(root).as_posix()
  57. except ValueError:
  58. c['python'] = exe.as_posix()
  59. p.write_text(json.dumps(c, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
  60. print(' configs/serve.json -> ' + c['python'] + ' (相对安装根)')
  61. PYEOF
  62. # 4) Ollama (可选; 只影响问答与本地审核)
  63. if command -v ollama >/dev/null 2>&1; then say "== Ollama 已安装: $(ollama -v 2>&1 | head -1)"
  64. elif [ -f vendor/ollama/ollama-linux-$( [ "$A" = x86_64 ] && echo amd64 || echo arm64 ).tar.zst ]; then
  65. say "== 检测到包内 Ollama 离线包; 安装需 root:"
  66. say " sudo tar -I zstd -C /usr -xf vendor/ollama/ollama-linux-*.tar.zst && sudo useradd -r -s /bin/false -m -d /usr/share/ollama ollama 2>/dev/null; ollama serve &"
  67. say " (麒麟/统信若无 zstd: sudo yum install zstd 或 sudo apt install zstd)"
  68. else say "== 未装 Ollama: 问答与本地审核不可用, 其余页面正常 (装法见 说明书 §5)"; fi
  69. say "== 自检"; "$VPY" guanlan.py check || true
  70. say ""
  71. say "安装完成. 启动: $VPY guanlan.py serve 停止: $VPY guanlan.py stop 入口: http://127.0.0.1:28084/"