Files
zkxw/backend/start-test.sh
T
2026-06-17 10:00:54 +08:00

356 lines
8.5 KiB
Bash
Executable File
Raw 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
# ============================================
# 陇耘鲜达 Backend 测试环境启动脚本
# 适配: python3.9+(推荐 venv;显式不使用 python2)
#
# 测试服务器目录示例: /root/zkxw-backend/backend
# 默认监听: 0.0.0.0:8112(生产为 8111,同机部署勿冲突)
#
# 首次部署:
# cd /root/zkxw-backend/backend
# cp .env.test.example .env.test # 或从 .env.example 复制后按测试库填写
# python3.9 -m venv .venv && ./.venv/bin/pip install -r requirements.txt
# ./start-test.sh activate # source .venv/bin/activate
# ./start-test.sh start
# ============================================
set -euo pipefail
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$BASE_DIR"
APP_MODULE="${APP_MODULE:-app.main:app}"
HOST="${HOST:-0.0.0.0}"
PORT="${PORT:-8112}"
WORKERS="${WORKERS:-1}"
LOG_LEVEL="${LOG_LEVEL:-debug}"
ENV_FILE="${ENV_FILE:-$BASE_DIR/.env.test}"
VENV_DIR="${VENV_DIR:-$BASE_DIR/.venv}"
ACTIVATE_SCRIPT="$VENV_DIR/bin/activate"
PYTHON_BIN="${PYTHON_BIN:-$VENV_DIR/bin/python}"
MIN_PYTHON_MAJOR=3
MIN_PYTHON_MINOR=9
SYSTEM_PYTHON=""
LOG_DIR="$BASE_DIR/logs"
LOG_FILE="$LOG_DIR/backend-test.log"
PID_FILE="$BASE_DIR/backend-test.pid"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
show_help() {
cat << 'EOF'
用法:
./start-test.sh [start|stop|restart|status|logs|activate]
命令:
start 启动服务(默认)
stop 停止服务
restart 重启服务
status 查看状态
logs 查看日志(最近100行)
activate 激活 .venv 并进入交互 shell(等价 source .venv/bin/activate)
可选环境变量:
HOST=0.0.0.0
PORT=8112
WORKERS=1
LOG_LEVEL=debug
APP_MODULE=app.main:app
VENV_DIR=./.venv
PYTHON_FOR_VENV=python3.9
ENV_FILE=./.env.test
测试部署目录(示例):
/root/zkxw-backend/backend
cd /root/zkxw-backend/backend && ./start-test.sh start
说明:
- 读取 .env.test(非 .env.production)
- 默认端口 8112;生产 start-prod.sh 使用 8111
- 可与生产共用 venv,但 PID/日志文件独立,勿混用 start/stop 命令
EOF
}
ensure_dirs() {
mkdir -p "$LOG_DIR"
}
python_version_ge() {
local ver="$1"
local major="${ver%%.*}"
local minor="${ver#*.}"
if [ "$major" -gt "$MIN_PYTHON_MAJOR" ]; then
return 0
fi
if [ "$major" -eq "$MIN_PYTHON_MAJOR" ] && [ "$minor" -ge "$MIN_PYTHON_MINOR" ]; then
return 0
fi
return 1
}
resolve_system_python() {
local candidate ver
if [ -n "${PYTHON_FOR_VENV:-}" ] && command -v "$PYTHON_FOR_VENV" >/dev/null 2>&1; then
ver="$("$PYTHON_FOR_VENV" -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
if python_version_ge "$ver"; then
echo "$PYTHON_FOR_VENV"
return 0
fi
log_error "PYTHON_FOR_VENV=$PYTHON_FOR_VENV 版本为 $ver,需要 >= ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}"
exit 1
fi
for candidate in python3.11 python3.10 python3.9 python3; do
if command -v "$candidate" >/dev/null 2>&1; then
ver="$("$candidate" -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
if python_version_ge "$ver"; then
echo "$candidate"
return 0
fi
fi
done
return 1
}
check_python() {
if ! SYSTEM_PYTHON="$(resolve_system_python)"; then
log_error "未找到 python${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+,请安装 python39 或设置 PYTHON_FOR_VENV=python3.9"
exit 1
fi
log_info "系统 Python: $SYSTEM_PYTHON ($("$SYSTEM_PYTHON" --version 2>&1))"
}
ensure_venv_python_version() {
local ver
ver="$("$PYTHON_BIN" -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
if python_version_ge "$ver"; then
log_info "虚拟环境 Python: $ver ($VENV_DIR)"
return 0
fi
log_error "虚拟环境 Python 版本过低: $ver(需要 >= ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR})"
log_error "当前虚拟环境目录: $VENV_DIR"
log_warn "请执行: rm -rf \"$VENV_DIR\" && $SYSTEM_PYTHON -m venv \"$VENV_DIR\" && \"$VENV_DIR/bin/pip\" install -r requirements.txt"
exit 1
}
has_uvicorn() {
"$PYTHON_BIN" -c "import uvicorn" >/dev/null 2>&1
}
ensure_venv_and_runtime() {
log_info "虚拟环境目录: $VENV_DIR"
if [ ! -d "$VENV_DIR" ]; then
log_warn "未找到虚拟环境,开始自动创建: $VENV_DIR"
if ! "$SYSTEM_PYTHON" -m venv "$VENV_DIR" >/dev/null 2>&1; then
log_error "自动创建 venv 失败,请先安装 python3-venv 或手动创建虚拟环境"
log_warn "可尝试: $SYSTEM_PYTHON -m venv \"$VENV_DIR\""
exit 1
fi
fi
if [ ! -x "$PYTHON_BIN" ]; then
log_error "虚拟环境 python 不存在: $PYTHON_BIN"
exit 1
fi
ensure_venv_python_version
if [ ! -x "$VENV_DIR/bin/pip" ]; then
log_warn "虚拟环境 pip 不存在,尝试引导安装"
"$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 || true
fi
if ! has_uvicorn; then
log_warn "未检测到 uvicorn,开始安装依赖 requirements.txt"
if [ ! -x "$VENV_DIR/bin/pip" ]; then
log_error "pip 仍不可用,请手动执行依赖初始化"
exit 1
fi
"$VENV_DIR/bin/pip" install --upgrade pip
"$VENV_DIR/bin/pip" install -r "$BASE_DIR/requirements.txt"
if ! has_uvicorn; then
log_error "依赖安装后仍无法 import uvicorn,请检查: $PYTHON_BIN -m pip install -r requirements.txt"
exit 1
fi
fi
}
is_running() {
if [ ! -f "$PID_FILE" ]; then
return 1
fi
local pid
pid="$(cat "$PID_FILE")"
if [ -z "$pid" ]; then
return 1
fi
if ps -p "$pid" >/dev/null 2>&1; then
return 0
fi
return 1
}
start_server() {
check_python
ensure_venv_and_runtime
ensure_dirs
if is_running; then
local running_pid
running_pid="$(cat "$PID_FILE")"
log_warn "测试服务已在运行 (PID: $running_pid)"
exit 0
fi
if [ -f "$PID_FILE" ]; then
rm -f "$PID_FILE"
fi
log_info "启动测试环境服务..."
log_info "监听地址: $HOST:$PORT"
log_info "工作进程: $WORKERS"
log_info "日志文件: $LOG_FILE"
log_info "环境文件: $ENV_FILE"
if [ ! -f "$ENV_FILE" ]; then
log_error "环境文件不存在: $ENV_FILE"
log_warn "可先执行: cp .env.test.example .env.test 并按测试库修改"
exit 1
fi
export ENV_FILE
nohup "$PYTHON_BIN" -m uvicorn "$APP_MODULE" \
--host "$HOST" \
--port "$PORT" \
--workers "$WORKERS" \
--log-level "$LOG_LEVEL" \
>> "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
sleep 2
if is_running; then
log_info "测试服务启动成功 (PID: $(cat "$PID_FILE"))"
log_info "健康检查: http://127.0.0.1:$PORT/health"
log_info "API 文档: http://127.0.0.1:$PORT/docs"
else
log_error "测试服务启动失败,请检查日志: $LOG_FILE"
rm -f "$PID_FILE"
exit 1
fi
}
stop_server() {
if ! is_running; then
log_warn "测试服务未运行"
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"
exit 0
fi
local pid
pid="$(cat "$PID_FILE")"
log_info "正在停止测试服务 (PID: $pid)"
kill -TERM "$pid" >/dev/null 2>&1 || true
for _ in 1 2 3 4 5 6 7 8 9 10; do
if ! ps -p "$pid" >/dev/null 2>&1; then
break
fi
sleep 1
done
if ps -p "$pid" >/dev/null 2>&1; then
log_warn "优雅停止超时,执行强制停止"
kill -9 "$pid" >/dev/null 2>&1 || true
fi
rm -f "$PID_FILE"
log_info "测试服务已停止"
}
status_server() {
if is_running; then
echo -e "${GREEN}RUNNING (test)${NC} PID=$(cat "$PID_FILE") HOST=$HOST PORT=$PORT"
else
echo -e "${RED}STOPPED (test)${NC}"
fi
}
show_logs() {
ensure_dirs
if [ ! -f "$LOG_FILE" ]; then
log_warn "暂无日志文件: $LOG_FILE"
exit 0
fi
tail -n 100 "$LOG_FILE"
}
activate_venv() {
if [ ! -f "$ACTIVATE_SCRIPT" ]; then
log_error "虚拟环境激活脚本不存在: $ACTIVATE_SCRIPT"
log_warn "可先执行: python3.9 -m venv .venv && ./.venv/bin/pip install -r requirements.txt"
exit 1
fi
log_info "激活虚拟环境: source $ACTIVATE_SCRIPT"
exec "$SHELL" -i -c "cd '$BASE_DIR' && source '$ACTIVATE_SCRIPT' && exec -i '$SHELL'"
}
ACTION="${1:-start}"
case "$ACTION" in
start)
start_server
;;
stop)
stop_server
;;
restart)
stop_server || true
start_server
;;
status)
status_server
;;
logs)
show_logs
;;
activate)
activate_venv
;;
-h|--help|help)
show_help
;;
*)
log_error "未知命令: $ACTION"
show_help
exit 1
;;
esac