release: bump version to 0.41.2

This commit is contained in:
rayd1o
2026-04-27 23:22:04 +08:00
parent 655e2a7d2d
commit eeee788530
14 changed files with 265 additions and 25 deletions

186
planet.sh
View File

@@ -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 查看日志"