diff --git a/.gitignore b/.gitignore index 6e5a2cbc..6634b43c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ temp*build*/ /public/generated-animations /public/generated-character-drafts /public/generated-characters +/target/ diff --git a/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md b/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md index 30d307d1..d9c7fc4a 100644 --- a/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md +++ b/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md @@ -33,13 +33,15 @@ npm run dev:rust 1. 检查 `cargo`、`node` 与 `spacetime` CLI。 2. Windows Git Bash 下如 `server-rs/.spacetimedb/local/bin/current/spacetimedb-cli.exe` 不存在,先把本机 `spacetime` 所在安装目录的 `bin/` 与 `spacetime.exe` 同步到 `server-rs/.spacetimedb/local/`。 -3. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。 -4. 等待 `spacetime --root-dir=server-rs/.spacetimedb/local server ping http://127.0.0.1:3101` 可用。 -5. 执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`,确保 publish 的签名身份与 standalone 的本地控制库一致,并在当前开发阶段允许新版模块表结构变化且发生 schema 冲突时清除旧模块数据。 -6. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。 -7. 等待 `http://127.0.0.1:/healthz` 返回 HTTP 响应后再启动 Vite,避免前端初始化请求早于 Rust `api-server` 监听完成并在终端刷出 `ECONNREFUSED 127.0.0.1:`。 -8. 注入 `GENARRATIVE_BACKEND_STACK=rust`、`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` 后启动 Vite。 -9. 任一子进程退出时,脚本回收其余子进程。 +3. 启动前先用同一个 `--root-dir` ping `http://127.0.0.1:3101`;如果目标已就绪则直接复用,避免重复启动同一份 `server-rs/.spacetimedb/local`。 +4. 如果同一个 `root-dir` 已被其他 SpacetimeDB 进程占用但目标端口未就绪,脚本会打印占用进程与命令行并退出,避免继续撞上 `spacetime.pid` 的 Windows 文件锁。 +5. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。 +6. 等待 `spacetime --root-dir=server-rs/.spacetimedb/local server ping http://127.0.0.1:3101` 可用。 +7. 执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`,确保 publish 的签名身份与 standalone 的本地控制库一致,并在当前开发阶段允许新版模块表结构变化且发生 schema 冲突时清除旧模块数据。 +8. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。 +9. 等待 `http://127.0.0.1:/healthz` 返回 HTTP 响应后再启动 Vite,避免前端初始化请求早于 Rust `api-server` 监听完成并在终端刷出 `ECONNREFUSED 127.0.0.1:`。 +10. 注入 `GENARRATIVE_BACKEND_STACK=rust`、`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` 后启动 Vite。 +11. 任一子进程退出时,脚本回收其余子进程。 Vite 代理覆盖范围: @@ -86,6 +88,7 @@ npm run dev:rust:logs -- --follow 2. `spacetime --root-dir=server-rs/.spacetimedb/local list --server http://127.0.0.1:3101` 应能看到 `spacetime.local.json` 中的库名;若没有,执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`。 3. 发布库名与 `GENARRATIVE_SPACETIME_DATABASE` 不一致时,`/api/runtime/custom-world-gallery` 会从 Rust `api-server` 返回 `502`,前端首页只能展示空态或错误提示,无法自行修复。 4. 如果 Vite 输出 `/api/auth/refresh`、`/api/auth/login-options` 或 `/api/runtime/custom-world-gallery` 的 `ECONNREFUSED`,先确认当前脚本是否已经打印 `等待 api-server 就绪` 并通过;正常情况下 Vite 只会在 `/healthz` 可访问后启动,不应再因为 Rust 监听未完成而代理失败。 +5. 如果启动阶段提示 `当前 root-dir 已被其他 SpacetimeDB 实例占用`,先看脚本打印的进程命令行:端口就是要复用的实例时,重新执行 `npm run dev:rust -- --spacetime-port <实际端口>`;不是要复用的实例时,先停止该 SpacetimeDB 进程再启动,避免 `spacetime.pid` 文件锁报 `os error 33`。 编译警告治理: diff --git a/scripts/dev-rust-stack.sh b/scripts/dev-rust-stack.sh index e01b93bd..535a5de3 100644 --- a/scripts/dev-rust-stack.sh +++ b/scripts/dev-rust-stack.sh @@ -88,6 +88,39 @@ wait_for_spacetime() { exit 1 } +is_spacetime_ready() { + local server="$1" + local root_dir="$2" + + spacetime --root-dir="${root_dir}" server ping "${server}" >/dev/null 2>&1 +} + +describe_spacetime_root_owner() { + local root_dir="$1" + local windows_root_dir="${root_dir}" + + if [[ "${windows_root_dir}" =~ ^/([a-zA-Z])/(.*)$ ]]; then + windows_root_dir="${BASH_REMATCH[1]}:/${BASH_REMATCH[2]}" + fi + + # Windows 本地开发最常见的失败是同一个 root-dir 下已有 standalone 持有 spacetime.pid; + # 启动前先打印占用进程,避免用户只看到底层 os error 33 而不知道该停哪个实例。 + if command -v powershell.exe >/dev/null 2>&1; then + ROOT_DIR_FOR_POWERSHELL="${windows_root_dir}" powershell.exe -NoProfile -Command ' +$rootDir = $env:ROOT_DIR_FOR_POWERSHELL +$normalized = $rootDir.Replace("/", "\") +Get-CimInstance Win32_Process | + Where-Object { $_.Name -match "spacetime" -and $_.CommandLine -and $_.CommandLine.Replace("/", "\") -like "*$normalized*" } | + ForEach-Object { "pid=$($_.ProcessId) name=$($_.Name) command=$($_.CommandLine)" } +' 2>/dev/null || true + return + fi + + if command -v ps >/dev/null 2>&1; then + ps -ef 2>/dev/null | grep '[s]pacetime' | grep -F "${root_dir}" || true + fi +} + wait_for_api_server() { local health_url="$1" local timeout_seconds="$2" @@ -326,17 +359,29 @@ echo "[dev:rust] spacetime root: ${SPACETIME_ROOT_DIR}" if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then mkdir -p "${SPACETIME_ROOT_DIR}" sync_local_spacetime_install "${SPACETIME_ROOT_DIR}" - echo "[dev:rust] 启动 spacetimedb" - ( - cd "${SERVER_RS_DIR}" - exec spacetime \ - --root-dir="${SPACETIME_ROOT_DIR}" \ - start \ - --edition standalone \ - --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}" - ) & - PIDS+=("$!") - NAMES+=("spacetimedb") + if is_spacetime_ready "${SPACETIME_SERVER}" "${SPACETIME_ROOT_DIR}"; then + echo "[dev:rust] 复用已运行的 SpacetimeDB: ${SPACETIME_SERVER}" + else + SPACETIME_ROOT_OWNER="$(describe_spacetime_root_owner "${SPACETIME_ROOT_DIR}")" + if [[ -n "${SPACETIME_ROOT_OWNER}" ]]; then + echo "[dev:rust] 当前 root-dir 已被其他 SpacetimeDB 实例占用,无法再次启动。" >&2 + echo "[dev:rust] 目标地址未就绪: ${SPACETIME_SERVER}" >&2 + echo "[dev:rust] 如需复用,请传入占用实例实际端口,例如 --spacetime-port 3199;如需重启,请先停止下列进程。" >&2 + echo "${SPACETIME_ROOT_OWNER}" >&2 + exit 1 + fi + echo "[dev:rust] 启动 spacetimedb" + ( + cd "${SERVER_RS_DIR}" + exec spacetime \ + --root-dir="${SPACETIME_ROOT_DIR}" \ + start \ + --edition standalone \ + --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}" + ) & + PIDS+=("$!") + NAMES+=("spacetimedb") + fi fi if [[ "${SKIP_PUBLISH}" -ne 1 ]]; then