release: bump version to 0.41.2
This commit is contained in:
186
planet.sh
186
planet.sh
@@ -35,6 +35,10 @@ WAIT_SPINNER_TICKS_PER_SECOND=8
|
||||
WAIT_SPINNER_DETAIL=""
|
||||
WAIT_SPINNER_MESSAGE=""
|
||||
WAIT_SESSION_ACTIVE=0
|
||||
VERBOSE=0
|
||||
WAIT_VERBOSE_LOG_FILE=""
|
||||
WAIT_VERBOSE_LINE_COUNT="${WAIT_VERBOSE_LINE_COUNT:-5}"
|
||||
WAIT_VERBOSE_TAIL_LINE_COUNT="${WAIT_VERBOSE_TAIL_LINE_COUNT:-40}"
|
||||
HTTP_CHECK_MAX_TIME="${HTTP_CHECK_MAX_TIME:-0.2}"
|
||||
|
||||
BACKEND_MAX_RETRIES="${BACKEND_MAX_RETRIES:-3}"
|
||||
@@ -186,7 +190,36 @@ render_wait_spinner() {
|
||||
local frame_index=$(( (step - 1) % ${#WAIT_SPINNER_FRAMES[@]} ))
|
||||
local frame="${WAIT_SPINNER_FRAMES[$frame_index]}"
|
||||
local detail="${WAIT_SPINNER_DETAIL:- }"
|
||||
printf "\r${DIM}·${NC} ${CYAN}%-2s${NC} ${WHITE}%s${NC}\033[K\n${DIM} %s${NC}\033[K\033[1A\r" "$frame" "$message" "$detail" >&2
|
||||
local verbose_line=""
|
||||
local verbose_lines=()
|
||||
local line_count=0
|
||||
local cursor_up=1
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ] && [ -n "$WAIT_VERBOSE_LOG_FILE" ] && [ -f "$WAIT_VERBOSE_LOG_FILE" ]; then
|
||||
while IFS= read -r verbose_line; do
|
||||
verbose_line="$(sanitize_wait_detail "$verbose_line")"
|
||||
[ -n "$verbose_line" ] || continue
|
||||
verbose_lines+=("$verbose_line")
|
||||
done < <(tail -n "$WAIT_VERBOSE_TAIL_LINE_COUNT" "$WAIT_VERBOSE_LOG_FILE" 2>/dev/null)
|
||||
|
||||
while [ "${#verbose_lines[@]}" -gt "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
verbose_lines=("${verbose_lines[@]:1}")
|
||||
done
|
||||
fi
|
||||
|
||||
printf "\r${DIM}·${NC} ${CYAN}%-2s${NC} ${WHITE}%s${NC}\033[K\n${DIM} %s${NC}\033[K" "$frame" "$message" "$detail" >&2
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
line_count=1
|
||||
while [ "$line_count" -le "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
verbose_line="${verbose_lines[$line_count]:- }"
|
||||
printf "\n${DIM} %s${NC}\033[K" "$verbose_line" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
cursor_up=$((WAIT_VERBOSE_LINE_COUNT + 1))
|
||||
fi
|
||||
|
||||
printf "\033[%sA\r" "$cursor_up" >&2
|
||||
}
|
||||
|
||||
animate_wait_spinner() {
|
||||
@@ -208,7 +241,29 @@ animate_wait_spinner() {
|
||||
}
|
||||
|
||||
clear_wait_spinner() {
|
||||
printf "\r\033[K\n\033[K\033[1A\r" >&2
|
||||
local line_count=0
|
||||
local clear_verbose_lines=0
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ] && { [ "$WAIT_SESSION_ACTIVE" -eq 1 ] || [ -n "$WAIT_VERBOSE_LOG_FILE" ]; }; then
|
||||
clear_verbose_lines=1
|
||||
fi
|
||||
|
||||
printf "\r\033[K" >&2
|
||||
line_count=0
|
||||
while [ "$line_count" -lt 1 ]; do
|
||||
printf "\n\033[K" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
|
||||
if [ "$clear_verbose_lines" -eq 1 ]; then
|
||||
line_count=0
|
||||
while [ "$line_count" -lt "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
printf "\n\033[K" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
fi
|
||||
|
||||
printf "\033[%sA\r" "$((clear_verbose_lines == 1 ? WAIT_VERBOSE_LINE_COUNT + 1 : 1))" >&2
|
||||
}
|
||||
|
||||
start_wait_session() {
|
||||
@@ -247,6 +302,7 @@ close_wait_session_context() {
|
||||
reset_wait_spinner_state() {
|
||||
WAIT_SPINNER_DETAIL=""
|
||||
WAIT_SPINNER_MESSAGE=""
|
||||
WAIT_VERBOSE_LOG_FILE=""
|
||||
}
|
||||
|
||||
sanitize_wait_detail() {
|
||||
@@ -274,12 +330,19 @@ set_wait_detail() {
|
||||
run_command_with_spinner() {
|
||||
local message="$1"
|
||||
shift
|
||||
local verbose_log_file=""
|
||||
|
||||
if [ "$WAIT_SESSION_ACTIVE" -eq 1 ]; then
|
||||
set_wait_detail "$message"
|
||||
fi
|
||||
|
||||
"$@" &
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
verbose_log_file="$(mktemp /tmp/planet_verbose.XXXXXX.log)"
|
||||
WAIT_VERBOSE_LOG_FILE="$verbose_log_file"
|
||||
"$@" > "$verbose_log_file" 2>&1 &
|
||||
else
|
||||
"$@" &
|
||||
fi
|
||||
local command_pid=$!
|
||||
local exit_code=0
|
||||
|
||||
@@ -294,6 +357,9 @@ run_command_with_spinner() {
|
||||
done
|
||||
|
||||
wait "$command_pid" || exit_code=$?
|
||||
if [ "$VERBOSE" -eq 1 ] && [ -n "$verbose_log_file" ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="$verbose_log_file"
|
||||
fi
|
||||
return "$exit_code"
|
||||
}
|
||||
|
||||
@@ -619,7 +685,7 @@ ensure_ai_provider_image_current() {
|
||||
build_ai_provider_image_with_fallback() {
|
||||
if compose_available; then
|
||||
set_wait_detail "使用 docker compose 构建 AI Provider 镜像"
|
||||
if run_command_with_spinner "构建 AI Provider 镜像" sh -c "docker compose build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"; then
|
||||
if run_ai_provider_build_command "docker compose"; then
|
||||
return 0
|
||||
fi
|
||||
if compose_v1_available; then
|
||||
@@ -632,7 +698,7 @@ build_ai_provider_image_with_fallback() {
|
||||
|
||||
if compose_v1_available; then
|
||||
set_wait_detail "使用 docker-compose v1 构建 AI Provider 镜像"
|
||||
if run_command_with_spinner "构建 AI Provider 镜像" sh -c "docker-compose build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"; then
|
||||
if run_ai_provider_build_command "docker-compose"; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
@@ -641,6 +707,17 @@ build_ai_provider_image_with_fallback() {
|
||||
report_missing_compose
|
||||
}
|
||||
|
||||
run_ai_provider_build_command() {
|
||||
local compose_command="$1"
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
run_command_with_spinner "构建 AI Provider 镜像" sh -c "${compose_command} build aiprovider 2>&1 | tee \"$AI_PROVIDER_BUILD_LOG_FILE\""
|
||||
return $?
|
||||
fi
|
||||
|
||||
run_command_with_spinner "构建 AI Provider 镜像" sh -c "${compose_command} build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"
|
||||
}
|
||||
|
||||
install_uv_if_needed() {
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
return 0
|
||||
@@ -909,6 +986,9 @@ wait_for_frontend_ready() {
|
||||
else
|
||||
set_wait_detail "启动 Vite 开发服务器"
|
||||
fi
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="$log_file"
|
||||
fi
|
||||
|
||||
while [ "$tick" -lt "$max_ticks" ]; do
|
||||
WAIT_SPINNER_STEP=$((WAIT_SPINNER_STEP + 1))
|
||||
@@ -1045,17 +1125,25 @@ start_backend_with_retry() {
|
||||
: > /tmp/planet_backend.log
|
||||
PYTHONPATH="$SCRIPT_DIR/backend" nohup uv run --project "$SCRIPT_DIR" python -m uvicorn app.main:app --host 0.0.0.0 --port "$backend_port" --reload > /tmp/planet_backend.log 2>&1 &
|
||||
BACKEND_PID=$!
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="/tmp/planet_backend.log"
|
||||
fi
|
||||
|
||||
if wait_for_http "http://localhost:${backend_port}/health" "$BACKEND_HEALTH_CHECK_ATTEMPTS" "$BACKEND_HEALTH_CHECK_INTERVAL" "后端"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
kill "$BACKEND_PID" 2>/dev/null || true
|
||||
if backend_log_indicates_port_conflict "/tmp/planet_backend.log"; then
|
||||
report_backend_port_conflict_if_needed "$backend_port" "/tmp/planet_backend.log"
|
||||
return 1
|
||||
fi
|
||||
animate_wait_spinner "后端第 ${retry}/${BACKEND_MAX_RETRIES} 次启动未就绪,准备重试" "$BACKEND_HEALTH_CHECK_INTERVAL"
|
||||
retry=$((retry + 1))
|
||||
done
|
||||
|
||||
clear_wait_spinner
|
||||
report_backend_port_conflict_if_needed "$backend_port" "/tmp/planet_backend.log" || true
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -1387,6 +1475,66 @@ frontend_log_indicates_port_conflict() {
|
||||
grep -q "Port .* is already in use" "$log_file" 2>/dev/null
|
||||
}
|
||||
|
||||
backend_log_indicates_port_conflict() {
|
||||
local log_file="$1"
|
||||
|
||||
[ -f "$log_file" ] || return 1
|
||||
grep -Eiq "Address already in use|Errno 98" "$log_file" 2>/dev/null
|
||||
}
|
||||
|
||||
print_port_listener_details() {
|
||||
local port="$1"
|
||||
local found=0
|
||||
local pid=""
|
||||
local command_line=""
|
||||
local lsof_output=""
|
||||
local ss_output=""
|
||||
local line=""
|
||||
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
lsof_output="$(lsof -nP -iTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true)"
|
||||
if [ -n "$lsof_output" ]; then
|
||||
printf "%s\n" "$lsof_output" | awk '{print " " $0}'
|
||||
found=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss_output="$(ss -ltnpH "( sport = :${port} )" 2>/dev/null || true)"
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] || continue
|
||||
printf "${DIM} ss: %s${NC}\n" "$line"
|
||||
found=1
|
||||
done <<EOF
|
||||
$ss_output
|
||||
EOF
|
||||
fi
|
||||
|
||||
for pid in $(collect_port_pids "$port" || true); do
|
||||
command_line="$(ps -p "$pid" -o args= 2>/dev/null | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')"
|
||||
[ -n "$command_line" ] || command_line="未知命令"
|
||||
printf "${DIM} pid %s: %s${NC}\n" "$pid" "$command_line"
|
||||
found=1
|
||||
done
|
||||
|
||||
if [ "$found" -eq 0 ]; then
|
||||
log_note "未能在当前环境内定位端口 ${port} 的监听进程,可能被宿主机或外部网络命名空间占用。"
|
||||
fi
|
||||
}
|
||||
|
||||
report_backend_port_conflict_if_needed() {
|
||||
local backend_port="$1"
|
||||
local log_file="$2"
|
||||
|
||||
if backend_log_indicates_port_conflict "$log_file" || ! can_bind_port "$backend_port"; then
|
||||
log_error "后端地址已被占用: 0.0.0.0:${backend_port} / 127.0.0.1:${backend_port} / [::1]:${backend_port}"
|
||||
print_port_listener_details "$backend_port"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Frontend lifecycle helpers
|
||||
cleanup_frontend_processes() {
|
||||
local frontend_port="${1:-$DEFAULT_FRONTEND_PORT}"
|
||||
@@ -1527,6 +1675,10 @@ parse_service_args() {
|
||||
FRONTEND_LAN_ENABLED=1
|
||||
shift 1
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
log_error "未知参数: $1"
|
||||
exit 1
|
||||
@@ -1831,6 +1983,25 @@ log() {
|
||||
esac
|
||||
}
|
||||
|
||||
parse_global_args() {
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
GLOBAL_ARG_REMAINDER=("$@")
|
||||
}
|
||||
|
||||
parse_global_args "$@"
|
||||
set -- "${GLOBAL_ARG_REMAINDER[@]}"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
shift
|
||||
@@ -1854,9 +2025,10 @@ case "$1" in
|
||||
;;
|
||||
*)
|
||||
log_error "用法: ./planet.sh {start|stop|restart|createuser|health|log}"
|
||||
log_note "start 启动服务,可选: -b <后端端口> -f <前端端口> -a <AI Provider 端口> --allow-lan"
|
||||
log_note "全局参数: -v, --verbose 在当前执行行下方滚动显示最多 5 行命令输出"
|
||||
log_note "start 启动服务,可选: -b <后端端口> -f <前端端口> -a <AI Provider 端口> --allow-lan --verbose"
|
||||
log_note "stop 停止服务"
|
||||
log_note "restart 重启服务,可选: -b [后端端口] -f [前端端口] -a [AI Provider 端口] -d --allow-lan"
|
||||
log_note "restart 重启服务,可选: -b [后端端口] -f [前端端口] -a [AI Provider 端口] -d --allow-lan --verbose"
|
||||
log_note "createuser 交互创建用户"
|
||||
log_note "health 检查健康状态"
|
||||
log_note "log 查看日志"
|
||||
|
||||
Reference in New Issue
Block a user