Document local SpacetimeDB dev skip and clear workflows
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -83,6 +83,22 @@
|
||||
- 验证:`spacetime --root-dir server-rs/.spacetimedb/local list --server http://127.0.0.1:3101` 能看到目标库;重新发布不再使用无权限的全局 identity。
|
||||
- 关联:`scripts/dev-rust-stack.sh`、`docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md`。
|
||||
|
||||
## 本地 SpacetimeDB 联调可按阶段跳过宿主或发布
|
||||
|
||||
- 现象:本地 `npm run dev` 因 `3101` 已占用、重复发布 SpacetimeDB wasm 编译太慢,或只想检查 `spacetime-module` 语法而被完整联调链路拖慢。
|
||||
- 原因:`npm run dev` 默认同时启动 SpacetimeDB standalone、发布 `server-rs/crates/spacetime-module`、启动 Rust `api-server`、主站 Vite 与后台 Vite;并非每个阶段都需要完整重启和重新发布。
|
||||
- 处理:`3101` 已被可复用 standalone 占用时使用 `npm run dev -- --skip-spacetime`;未修改 `spacetime-module` 时使用 `npm run dev -- --skip-publish`;只查模块语法时执行 `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`。
|
||||
- 验证:`--skip-spacetime` 后脚本复用现有 `http://127.0.0.1:3101`;`--skip-publish` 后不再进入 publish 阶段;`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 能完成 Rust 语法和类型检查。
|
||||
- 关联:`docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`、`scripts/dev-rust-stack.sh`。
|
||||
|
||||
## 本地 SpacetimeDB publish 401 可清本地库重发
|
||||
|
||||
- 现象:本地 `spacetime publish` 显示 `401` 无权限,或重新发布仍像是在更新旧库。
|
||||
- 原因:本地开发 root-dir 中保留的数据库、控制库身份或发布身份与当前目标不一致。
|
||||
- 处理:确认本地开发数据可以丢弃后,执行 `spacetime --root-dir=server-rs/.spacetimedb/local server clear`,再重新运行 `npm run dev` 或本地 publish。
|
||||
- 验证:重新发布日志应显示创建新的数据库,而不是更新旧数据库;若仍显示更新或继续 `401`,继续检查 root-dir、库名和 CLI 身份。
|
||||
- 关联:`docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`、`docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md`。
|
||||
|
||||
## Vite SPA fallback 吞掉 API 请求
|
||||
|
||||
- 现象:本地请求 `/api/profile/*` 等接口时返回 HTML,被前端当 JSON 解析报错。
|
||||
|
||||
@@ -58,14 +58,23 @@ Vite 代理覆盖范围:
|
||||
4. 如只想启动进程不发布模块,可传 `--skip-publish`。
|
||||
5. 后续进入正式版本前,涉及表结构变化时必须在开发阶段补齐迁移表与迁移函数,不能依赖清库发布作为正式升级策略。
|
||||
|
||||
本地联调跳过策略:
|
||||
|
||||
1. 如果 `3101` 已被当前可复用的 SpacetimeDB standalone 占用,可使用 `npm run dev -- --skip-spacetime` 跳过 SpacetimeDB 宿主启动,只复用现有监听实例并继续后续发布、`api-server` 与前端启动。若占用方不是本仓库本地 SpacetimeDB,先停止占用进程或改用 `--spacetime-port`。
|
||||
2. 如果当前没有修改 `server-rs/crates/spacetime-module`,可使用 `npm run dev -- --skip-publish` 跳过数据库发布,降低本地启动时的 SpacetimeDB wasm 编译耗时。
|
||||
3. 如果当前阶段只需要检查 `spacetime-module` 语法,不需要重新发布本地数据库,可执行 `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`。该命令只做 Rust 编译检查,不生成新数据库,也不刷新 bindings。
|
||||
|
||||
常用示例:
|
||||
|
||||
```bash
|
||||
npm run dev:rust
|
||||
npm run dev -- --skip-spacetime
|
||||
npm run dev -- --skip-publish
|
||||
./scripts/dev-rust-stack.sh
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110 --database genarrative-dev
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --preserve-database
|
||||
cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml
|
||||
```
|
||||
|
||||
bindings 生成:
|
||||
@@ -107,6 +116,7 @@ npm run dev:rust:logs -- --follow
|
||||
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. 如果 `spacetime server ping` 打印 `Server could not be reached (502 Bad Gateway)`,即使命令退出码为 `0` 也不能直接视为已就绪;本地脚本会继续探测 `/v1/ping`。若 `/v1/ping` 返回 `200`,说明 standalone 已经可用,可以继续发布模块;若 `/v1/ping` 也失败,脚本会继续等待新启动实例,或在 root-dir 已被其他实例占用时输出占用进程。
|
||||
6. 如果本地 `spacetime publish` 显示 `401` 无权限,且确认本地开发数据可以丢弃,可执行 `spacetime --root-dir=server-rs/.spacetimedb/local server clear` 清除本地 SpacetimeDB 数据库后重新发布。重新发布时日志应表现为创建新的数据库,而不是更新旧数据库;如果仍显示更新旧库或继续无权限,说明 root-dir、库名或 CLI 身份仍未对齐。
|
||||
|
||||
编译警告治理:
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ is_spacetime_ready() {
|
||||
local server="$1"
|
||||
local output
|
||||
|
||||
if output="$(spacetime --root-dir="${SPACETIME_ROOT_DIR}" server ping "${server}" 2>&1)" &&
|
||||
if output="$(spacetime server ping "${server}" 2>&1)" &&
|
||||
[[ "${output}" == *"Server is online:"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
@@ -345,48 +345,6 @@ prepare_migration_bootstrap_secret() {
|
||||
echo "[dev:rust] 迁移引导密钥: ${MIGRATION_BOOTSTRAP_SECRET}"
|
||||
}
|
||||
|
||||
is_sccache_wrapper_failure_log() {
|
||||
local log_file="$1"
|
||||
|
||||
grep -Eiq \
|
||||
'sccache: error|could not execute process.*sccache|Failed to send data to or receive data from server|Mismatch of client/server versions|Failed to read response header|failed to fill whole buffer' \
|
||||
"${log_file}"
|
||||
}
|
||||
|
||||
run_spacetime_publish_with_sccache_fallback() {
|
||||
local root_dir="$1"
|
||||
shift
|
||||
|
||||
local publish_log
|
||||
publish_log="$(mktemp -t genarrative-spacetime-publish.XXXXXX.log)"
|
||||
|
||||
set +e
|
||||
spacetime --root-dir="${root_dir}" "$@" 2> >(tee "${publish_log}" >&2)
|
||||
local publish_status="$?"
|
||||
set -e
|
||||
|
||||
if [[ "${publish_status}" -eq 0 ]]; then
|
||||
rm -f "${publish_log}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! is_sccache_wrapper_failure_log "${publish_log}"; then
|
||||
rm -f "${publish_log}"
|
||||
return "${publish_status}"
|
||||
fi
|
||||
|
||||
echo "[dev:rust] 检测到 sccache wrapper 通信异常,改用 rustc 直连重试 SpacetimeDB 发布。"
|
||||
echo "[dev:rust] 这只影响本次 publish;项目级 sccache 配置不会被修改。"
|
||||
|
||||
set +e
|
||||
RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= spacetime --root-dir="${root_dir}" "$@"
|
||||
publish_status="$?"
|
||||
set -e
|
||||
|
||||
rm -f "${publish_log}"
|
||||
return "${publish_status}"
|
||||
}
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||
SERVER_RS_DIR="${REPO_ROOT}/server-rs"
|
||||
@@ -606,7 +564,6 @@ if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then
|
||||
# 当目标端口被占用时,SpacetimeDB 会询问是否使用最近的可用端口;
|
||||
# 这里直接发送回车接受默认建议,再从启动日志解析实际监听端口。
|
||||
printf '\n' | spacetime \
|
||||
--root-dir="${SPACETIME_ROOT_DIR}" \
|
||||
start \
|
||||
--data-dir "${SPACETIME_DATA_DIR}" \
|
||||
--listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}" \
|
||||
@@ -644,9 +601,7 @@ if [[ "${SKIP_PUBLISH}" -ne 1 ]]; then
|
||||
cd "${SERVER_RS_DIR}"
|
||||
# spacetime publish 会在内部调用 Cargo;从 server-rs 目录执行,确保读取
|
||||
# server-rs/.cargo/config.toml 中的 sccache/linker 配置,并复用同一套 target 缓存。
|
||||
# Windows 本地 sccache server 偶发通信异常时,保留 root-dir/target 缓存路径,
|
||||
# 仅对重试子进程清空 Cargo wrapper,避免可选缓存工具阻断真实构建。
|
||||
run_spacetime_publish_with_sccache_fallback "${SPACETIME_ROOT_DIR}" "${PUBLISH_ARGS[@]}"
|
||||
spacetime "${PUBLISH_ARGS[@]}"
|
||||
)
|
||||
fi
|
||||
|
||||
|
||||
@@ -37,30 +37,14 @@ loadEnvFile(resolve(repoRoot, '.env'), fileEnv);
|
||||
loadEnvFile(resolve(repoRoot, '.env.local'), fileEnv);
|
||||
loadEnvFile(resolve(repoRoot, '.env.secrets.local'), fileEnv);
|
||||
|
||||
function resolveConfiguredTarget() {
|
||||
if (fileEnv.GENARRATIVE_RUNTIME_SERVER_TARGET) {
|
||||
return fileEnv.GENARRATIVE_RUNTIME_SERVER_TARGET;
|
||||
}
|
||||
|
||||
if (fileEnv.RUST_SERVER_TARGET) {
|
||||
return fileEnv.RUST_SERVER_TARGET;
|
||||
}
|
||||
|
||||
if (fileEnv.GENARRATIVE_API_TARGET) {
|
||||
return fileEnv.GENARRATIVE_API_TARGET;
|
||||
}
|
||||
|
||||
if (fileEnv.GENARRATIVE_API_PORT) {
|
||||
return `http://127.0.0.1:${fileEnv.GENARRATIVE_API_PORT}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function buildFallbackCandidates() {
|
||||
function buildTargetCandidates() {
|
||||
const candidates = [
|
||||
'http://127.0.0.1:3100',
|
||||
fileEnv.GENARRATIVE_RUNTIME_SERVER_TARGET,
|
||||
fileEnv.RUST_SERVER_TARGET,
|
||||
fileEnv.GENARRATIVE_API_TARGET,
|
||||
`http://127.0.0.1:${fileEnv.GENARRATIVE_API_PORT || '3100'}`,
|
||||
'http://127.0.0.1:8082',
|
||||
'http://127.0.0.1:3100',
|
||||
].filter(Boolean);
|
||||
|
||||
return Array.from(new Set(candidates));
|
||||
@@ -86,30 +70,39 @@ async function isTargetReachable(target) {
|
||||
}
|
||||
|
||||
async function resolveRuntimeTarget() {
|
||||
const configuredTarget = resolveConfiguredTarget();
|
||||
const candidates = buildTargetCandidates();
|
||||
const reachableTargets = [];
|
||||
|
||||
if (configuredTarget) {
|
||||
return {
|
||||
target: configuredTarget,
|
||||
fallbackUsed: false,
|
||||
targetUnavailable: !(await isTargetReachable(configuredTarget)),
|
||||
};
|
||||
}
|
||||
|
||||
for (const target of buildFallbackCandidates()) {
|
||||
for (const target of candidates) {
|
||||
if (await isTargetReachable(target)) {
|
||||
return {
|
||||
target,
|
||||
fallbackUsed: true,
|
||||
targetUnavailable: false,
|
||||
};
|
||||
reachableTargets.push(target);
|
||||
if (
|
||||
target === fileEnv.GENARRATIVE_RUNTIME_SERVER_TARGET ||
|
||||
target === fileEnv.RUST_SERVER_TARGET ||
|
||||
target === fileEnv.GENARRATIVE_API_TARGET
|
||||
) {
|
||||
return {
|
||||
target,
|
||||
fallbackUsed: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reachableTargets.length > 0) {
|
||||
return {
|
||||
target: reachableTargets[0],
|
||||
fallbackUsed: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
target: 'http://127.0.0.1:3100',
|
||||
target:
|
||||
fileEnv.GENARRATIVE_RUNTIME_SERVER_TARGET ||
|
||||
fileEnv.RUST_SERVER_TARGET ||
|
||||
fileEnv.GENARRATIVE_API_TARGET ||
|
||||
`http://127.0.0.1:${fileEnv.GENARRATIVE_API_PORT || '3100'}`,
|
||||
fallbackUsed: false,
|
||||
targetUnavailable: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -120,12 +113,6 @@ if (runtimeTarget.fallbackUsed) {
|
||||
);
|
||||
}
|
||||
|
||||
if (runtimeTarget.targetUnavailable) {
|
||||
console.warn(
|
||||
`[dev:web] Rust target 当前不可用: ${runtimeTarget.target},请先启动 api-server。`,
|
||||
);
|
||||
}
|
||||
|
||||
const mergedEnv = {
|
||||
...fileEnv,
|
||||
RUST_SERVER_TARGET: runtimeTarget.target,
|
||||
|
||||
Reference in New Issue
Block a user