fix: expand playground diagnostics and compose fallback

This commit is contained in:
linkong
2026-04-09 16:38:55 +08:00
parent 39f90bd575
commit 306ba7f850
9 changed files with 394 additions and 26 deletions

174
planet.sh
View File

@@ -59,6 +59,10 @@ DEFAULT_AI_PROVIDER_PORT="${DEFAULT_AI_PROVIDER_PORT:-8010}"
FRONTEND_RUNTIME_BIN="${FRONTEND_RUNTIME_BIN:-}"
FRONTEND_RUNTIME_SOURCE="${FRONTEND_RUNTIME_SOURCE:-}"
FRONTEND_PID_FILE="/tmp/planet_frontend.pid"
AI_PROVIDER_BUILD_STAMP_FILE="/tmp/planet_aiprovider_build.sha256"
AI_PROVIDER_IMAGE_NAME="${AI_PROVIDER_IMAGE_NAME:-planet_aiprovider:latest}"
AI_PROVIDER_CONTAINER_NAME="${AI_PROVIDER_CONTAINER_NAME:-planet_aiprovider}"
AI_PROVIDER_RECREATE_REQUIRED=0
prepend_path_once() {
local path_entry="$1"
@@ -79,12 +83,55 @@ ensure_local_runtime_bins_on_path() {
ensure_local_runtime_bins_on_path
# Shell / Docker helpers
compose_v2_available() {
docker compose version >/dev/null 2>&1
}
compose_v1_available() {
command -v docker-compose >/dev/null 2>&1
}
report_missing_compose() {
clear_wait_spinner
log_error "未检测到可用的 Docker Compose"
log_note "优先需要 Docker Compose v2 (`docker compose`)"
log_note "如果当前机器仍只装有旧版 `docker-compose`,也请确认该命令可执行"
log_note "推荐安装 Docker Compose v2 插件:"
log_note ' mkdir -p ~/.docker/cli-plugins'
log_note ' curl -SL https://github.com/docker/compose/releases/download/v2.40.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose'
log_note ' chmod +x ~/.docker/cli-plugins/docker-compose'
log_note ' docker compose version'
return 1
}
compose_up() {
if docker compose version >/dev/null 2>&1; then
docker compose "$@"
else
docker-compose "$@"
local args=("$@")
local arg_text="$*"
if compose_v2_available; then
set_wait_detail "正在用 docker compose v2 执行: ${arg_text}"
if docker compose "${args[@]}"; then
return 0
fi
log_warn "docker compose v2 执行失败,回退到 docker-compose v1"
fi
if compose_v1_available; then
set_wait_detail "正在用 docker-compose v1 执行: ${arg_text}"
if docker-compose "${args[@]}"; then
return 0
fi
clear_wait_spinner
log_error "docker compose v2 与 docker-compose v1 均执行失败"
log_note "最后尝试的命令: docker-compose ${arg_text}"
return 1
fi
report_missing_compose
}
compose_supports_build() {
compose_up build --help >/dev/null 2>&1
}
# Terminal rendering helpers
@@ -338,6 +385,102 @@ run_with_retry() {
return 1
}
compute_ai_provider_build_fingerprint() {
(
cd "$SCRIPT_DIR" || exit 1
tar -cf - \
aiprovider \
pyproject.toml \
uv.lock \
docker-compose.yml \
docker-compose.simple.yml 2>/dev/null
) | sha256sum | awk '{print $1}'
}
read_ai_provider_build_stamp() {
[ -f "$AI_PROVIDER_BUILD_STAMP_FILE" ] || return 1
tr -d '[:space:]' < "$AI_PROVIDER_BUILD_STAMP_FILE"
}
write_ai_provider_build_stamp() {
local fingerprint="$1"
printf "%s\n" "$fingerprint" > "$AI_PROVIDER_BUILD_STAMP_FILE"
}
ai_provider_image_exists() {
docker image inspect "$AI_PROVIDER_IMAGE_NAME" >/dev/null 2>&1
}
list_ai_provider_containers() {
docker ps -aq --filter "name=planet_aiprovider" 2>/dev/null || true
}
remove_ai_provider_containers() {
local container_ids
container_ids="$(list_ai_provider_containers)"
[ -n "$container_ids" ] || return 0
echo "$container_ids" | xargs -r docker rm -f >/dev/null 2>&1 || true
}
run_ai_provider_container_manually() {
local ai_provider_port="${1:-$DEFAULT_AI_PROVIDER_PORT}"
local env_file_args=()
if [ -f "$SCRIPT_DIR/aiprovider/.env" ]; then
env_file_args=(--env-file "$SCRIPT_DIR/aiprovider/.env")
fi
docker run -d \
--name "$AI_PROVIDER_CONTAINER_NAME" \
"${env_file_args[@]}" \
-p "${ai_provider_port}:8010" \
"$AI_PROVIDER_IMAGE_NAME" >/dev/null
}
recreate_ai_provider_container() {
local ai_provider_port="${1:-$DEFAULT_AI_PROVIDER_PORT}"
remove_ai_provider_containers
if compose_up up -d aiprovider >/dev/null 2>&1; then
return 0
fi
run_ai_provider_container_manually "$ai_provider_port"
}
ensure_ai_provider_image_current() {
local current_fingerprint=""
local previous_fingerprint=""
current_fingerprint="$(compute_ai_provider_build_fingerprint)"
previous_fingerprint="$(read_ai_provider_build_stamp || true)"
if ai_provider_image_exists && [ -n "$previous_fingerprint" ] && [ "$current_fingerprint" = "$previous_fingerprint" ]; then
AI_PROVIDER_RECREATE_REQUIRED=0
return 0
fi
set_wait_detail "检测到 AI Provider 代码或配置变化,重建镜像"
if ! compose_supports_build; then
log_error "当前 docker compose 不支持 build请检查 Docker / Compose 环境"
exit 1
fi
if ! run_with_retry \
"$AI_PROVIDER_START_MAX_RETRIES" \
"$AI_PROVIDER_RETRY_INTERVAL" \
"AI Provider 镜像构建失败,已重试 ${AI_PROVIDER_START_MAX_RETRIES}" \
"构建 AI Provider 镜像" \
compose_up build aiprovider; then
exit 1
fi
write_ai_provider_build_stamp "$current_fingerprint"
AI_PROVIDER_RECREATE_REQUIRED=1
}
install_uv_if_needed() {
if command -v uv >/dev/null 2>&1; then
return 0
@@ -753,9 +896,19 @@ start_ai_provider_service() {
local retry=1
set_wait_detail "启动 AI Provider"
ensure_ai_provider_image_current
while [ "$retry" -le "$AI_PROVIDER_START_MAX_RETRIES" ]; do
if docker start planet_aiprovider >/dev/null 2>&1 || compose_up up -d aiprovider >/dev/null 2>&1; then
if wait_for_container_health "planet_aiprovider" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider" &&
if [ "$AI_PROVIDER_RECREATE_REQUIRED" -eq 1 ]; then
if recreate_ai_provider_container "$ai_provider_port"; then
AI_PROVIDER_RECREATE_REQUIRED=0
if wait_for_container_health "$AI_PROVIDER_CONTAINER_NAME" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider" &&
wait_for_http "http://localhost:${ai_provider_port}/health" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider"; then
return 0
fi
fi
elif docker start "$AI_PROVIDER_CONTAINER_NAME" >/dev/null 2>&1 || compose_up up -d aiprovider >/dev/null 2>&1; then
if wait_for_container_health "$AI_PROVIDER_CONTAINER_NAME" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider" &&
wait_for_http "http://localhost:${ai_provider_port}/health" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider"; then
return 0
fi
@@ -763,17 +916,18 @@ start_ai_provider_service() {
if [ "$retry" -eq "$AI_PROVIDER_START_MAX_RETRIES" ]; then
log_error "AI Provider 启动失败,已重试 ${AI_PROVIDER_START_MAX_RETRIES}"
docker logs --tail 20 planet_aiprovider 2>/dev/null || true
docker logs --tail 20 "$AI_PROVIDER_CONTAINER_NAME" 2>/dev/null || true
exit 1
fi
docker restart planet_aiprovider >/dev/null 2>&1 || true
remove_ai_provider_containers
AI_PROVIDER_RECREATE_REQUIRED=1
animate_wait_spinner "AI Provider 第 ${retry}/${AI_PROVIDER_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器" "$AI_PROVIDER_RETRY_INTERVAL"
retry=$((retry + 1))
done
log_error "AI Provider 启动失败"
docker logs --tail 20 planet_aiprovider 2>/dev/null || true
docker logs --tail 20 "$AI_PROVIDER_CONTAINER_NAME" 2>/dev/null || true
exit 1
}
@@ -1179,7 +1333,7 @@ stop_backend_service() {
}
stop_ai_provider_service() {
stop_container_if_running "planet_aiprovider" "AI Provider"
stop_container_if_running "$AI_PROVIDER_CONTAINER_NAME" "AI Provider"
}
stop_frontend_service() {