fix(dev): resolve local stack ports before startup
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-05-09 21:49:25 +08:00
committed by kdletters
parent 46a254f142
commit f74717c415
8 changed files with 428 additions and 37 deletions

View File

@@ -111,8 +111,8 @@
- 现象:本地 `npm run dev``3101` 已占用、重复发布 SpacetimeDB wasm 编译太慢,或只想检查 `spacetime-module` 语法而被完整联调链路拖慢。
- 原因:`npm run dev` 默认同时启动 SpacetimeDB standalone、发布 `server-rs/crates/spacetime-module`、启动 Rust `api-server`、主站 Vite 与后台 Vite并非每个阶段都需要完整重启和重新发布。
- 处理:`npm run dev` 启动后会把实际 SpacetimeDB URL 记录到 `server-rs/.spacetimedb/local/data/dev-rust-spacetime-url`。下次启动即使没有传 `--skip-spacetime`,脚本也会先检查 `spacetime.pid` 对应进程和该 URL 是否在线;在线则直接复用现有宿主。确认需要新启动 SpacetimeDB 时,脚本先检测 `3101`,被占用则输出占用进程并选择最近可用端口,保证 publish 与 `api-server` 都连接同一个实际 SpacetimeDB URL。`api-server` 启动前也会检测 `8082` 并选择最近可用端口。Windows / Git Bash 下不要用 `tr/head/xargs` 管道读取 `spacetime.pid` 或 URL 记录,脚本应使用 Node 读取并短重试,避免 `tr: read error: Device or resource busy`;未修改 `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``3101``8082` 被其他进程占用时,脚本输出占用进程并使用最近可用端口;`--skip-publish` 后不再进入 publish 阶段;`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 能完成 Rust 语法和类型检查。
- 处理:`npm run dev` 启动后会把实际 SpacetimeDB URL 记录到 `server-rs/.spacetimedb/local/data/dev-rust-spacetime-url`。下次启动即使没有传 `--skip-spacetime`,脚本也会先检查 `spacetime.pid` 对应进程和该 URL 是否在线;在线则直接复用现有宿主。确认需要新启动 SpacetimeDB 时,脚本先检测 `3101`,被占用则输出占用进程并选择最近可用端口,保证 publish 与 `api-server` 都连接同一个实际 SpacetimeDB URL。`api-server` 启动前也会检测 `8082` 并选择最近可用端口。Windows / Git Bash 下不要用 `tr/head/xargs` 管道读取 `spacetime.pid` 或 URL 记录,脚本应使用 Node 读取并短重试,避免 `tr: read error: Device or resource busy`;未修改 `spacetime-module` 时使用 `npm run dev -- --skip-publish`;只查模块语法时执行 `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml``npm run dev` 会在启动前检查 SpacetimeDB、api-server、主站 Vite、后台 Vite 端口,不可用时自动寻找后续可用端口,并把实际端口传给 publish、后端环境变量和前端代理目标。
- 验证:`--skip-spacetime` 后脚本复用现有 `http://127.0.0.1:3101``3101``8082` 被其他进程占用时,脚本输出占用进程并使用最近可用端口;`--skip-publish` 后不再进入 publish 阶段;`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 能完成 Rust 语法和类型检查。端口漂移时控制台会打印 `[dev:ports] ... 不可用,改用 ...`,后续 `[dev:rust] web/admin web/rust api/spacetime` 地址应与实际端口一致。
- 关联:`docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md``scripts/dev-rust-stack.sh`
## 本地 SpacetimeDB publish 401 可清本地库重发

View File

@@ -0,0 +1,127 @@
---
name: genarrative-dev-stack-port-routing
short_description: 修改 Genarrative 本地 dev 启动端口、代理目标、端口冲突处理时使用。
description: 在 Genarrative 中修改 npm run dev / npm run dev:rust / npm run dev:web 的本地启动端口、端口可用性探测、端口漂移、SpacetimeDB publish server、api-server 环境变量、Vite 代理目标和后台 admin-web 启动串联时使用。
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
hermes:
tags: [Genarrative, dev-stack, 端口探测, Vite, api-server, SpacetimeDB, npm-run-dev]
related_skills: [genarrative-admin-backoffice]
---
# Genarrative 本地 dev 启动端口与代理目标串联流程
用于维护 Genarrative 本地开发栈启动脚本,重点覆盖 `npm run dev` / `npm run dev:rust` / `npm run dev:web` 的端口检查、端口漂移和后续流程目标传递。
## 适用场景
- 修改 `scripts/dev-rust-stack.sh``scripts/dev-web-rust.mjs``scripts/dev-stack-port-utils.mjs`
- 处理 `3000``3101``3102``8082` 等端口被占用导致本地开发栈启动失败。
- 排查 Vite 代理仍指向旧 api-server 端口、前端打开了旧 dev server、后台代理错配。
- 调整 SpacetimeDB standalone、publish、Rust `api-server`、主站 Vite、后台 Vite 的启动顺序。
- 修改本地联调文档或 `.hermes/shared-memory/pitfalls.md` 中的 dev 启动口径。
## 当前端口职责
默认优先端口:
1. 主站 Vite`3000`,对浏览器通常展示为 `http://127.0.0.1:<web-port>/`
2. Rust `api-server``8082`,健康检查为 `http://127.0.0.1:<api-port>/healthz`
3. SpacetimeDB standalone`3101`,健康检查为 `http://127.0.0.1:<spacetime-port>/v1/ping`
4. 后台 Vite`3102`,后台地址为 `http://127.0.0.1:<admin-web-port>/admin/`
端口不可用时,脚本会从优先端口开始向后寻找可用端口。后续流程必须以解析后的实际端口为准,不能继续使用默认端口。
## 实现入口
- `package.json`
- `dev``dev:rust`:执行 `node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh`
- `dev:web`:执行 `node scripts/dev-web-rust.mjs`
- `scripts/dev-stack-port-utils.mjs`
- `isPortAvailable(...)`:探测端口是否可监听。
- `findAvailablePort(...)`:从优先端口向后寻找可用端口,`0` 表示申请临时端口。
- `resolveDevStackPorts(...)`:一次性解析 SpacetimeDB、api-server、主站 Vite、后台 Vite 端口,并避免本次解析结果互相冲突。
- CLI 模式:`node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:3101 api:127.0.0.1:8082 web:0.0.0.0:3000 adminWeb:127.0.0.1:3102`
- `scripts/dev-rust-stack.sh`
- 解析 CLI 参数后,先计算 `API_TARGET_HOST``ADMIN_WEB_TARGET_HOST`
- 调用 `resolve_dev_stack_ports` 覆盖 `SPACETIME_PORT``API_PORT``WEB_PORT``ADMIN_WEB_PORT`
- 再构造 `SPACETIME_SERVER``RUST_SERVER_TARGET`
- `scripts/dev-web-rust.mjs`
- 单独启动主站前端时,也先用 `findAvailablePort` 检查 `WEB_PORT` / 默认 `3000`
## 必须保持的传递链路
`npm run dev` / `npm run dev:rust` 中端口解析后,必须同步到以下位置:
1. SpacetimeDB 启动:`spacetime start --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"`
2. SpacetimeDB 发布:`spacetime publish ... --server "${SPACETIME_SERVER}"`
3. Rust api-server`GENARRATIVE_API_HOST``GENARRATIVE_API_PORT``GENARRATIVE_SPACETIME_SERVER_URL``GENARRATIVE_SPACETIME_DATABASE`
4. api-server 健康检查:`wait_for_api_server "${RUST_SERVER_TARGET}/healthz" ...`
5. 主站 Vite`RUST_SERVER_TARGET``GENARRATIVE_RUNTIME_SERVER_TARGET``ADMIN_WEB_TARGET``ADMIN_WEB_PORT``--port=${WEB_PORT}``--host=${WEB_HOST}`
6. 后台 Vite`ADMIN_API_TARGET``GENARRATIVE_API_TARGET``GENARRATIVE_API_PORT``--port=${ADMIN_WEB_PORT}`
7. 控制台日志:`[dev:ports]``[dev:rust] web/admin web/rust api/spacetime` 必须显示最终实际地址。
如果只改了其中一段,通常会出现:浏览器打开的前端可用,但 `/api/*` 代理到旧端口;后台页面可用但后台 API 失败SpacetimeDB 启动在新端口但 publish 仍发往旧端口。
## 修改流程
1. 先读当前脚本和文档:
- `scripts/dev-stack-port-utils.mjs`
- `scripts/dev-rust-stack.sh`
- `scripts/dev-web-rust.mjs`
- `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`
- `.hermes/shared-memory/pitfalls.md`
2. 优先改公共端口工具,不要把端口探测逻辑复制到多个脚本。
3. 对 Bash 脚本只做局部补丁,避免整文件重写导致中文注释或换行大面积变化。
4. 修改 `dev-rust-stack.sh` 时确认变量顺序:
- 先有 `REPO_ROOT`
- 再计算 `API_TARGET_HOST` / `ADMIN_WEB_TARGET_HOST`
- 再调用端口解析工具。
- 再构造 `SPACETIME_SERVER` / `RUST_SERVER_TARGET`
5. 修改 `dev:web` 时不要自动改后端目标策略;`dev:web` 只负责主站 Vite 端口可用性与已有后端目标选择。
6. 同步更新技术文档和团队共享记忆。
## 测试与验证
最小验证:
```bash
bash -n scripts/dev-rust-stack.sh
npm run test -- scripts/dev-stack-port-utils.test.ts
npm run check:encoding
node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 api:127.0.0.1:0 web:0.0.0.0:0 adminWeb:127.0.0.1:0
```
端口冲突回归测试建议:
1. 用测试或临时 Node server 占用某个优先端口。
2. 调用 `findAvailablePort`,断言结果大于被占用端口。
3. 调用 `resolveDevStackPorts`,断言四个结果互不相同。
4. 如果实际启动完整栈,观察控制台:
- `[dev:ports] ... 不可用,改用 ...`
- `[dev:rust] rust api: http://...:<actual-api-port>`
- `[dev:rust] spacetime: http://...:<actual-spacetime-port>`
- 主站和后台 Vite 启动端口与日志一致。
完整启动属于长驻进程。需要 smoke 时用 background 方式启动,并另开命令检查 `/healthz``/v1/ping` 和页面端口;不要等待 `npm run dev` 自然退出。
## 常见坑
1. **只让 Vite 自己漂移端口。** 这样终端可能出现可访问前端,但脚本和文档仍认为是 `3000`,后台目标或日志会错。
2. **只改 SpacetimeDB start不改 publish。** standalone 可能监听新端口,但 publish 仍连旧 `3101`
3. **只改 `GENARRATIVE_API_PORT`,不改 `RUST_SERVER_TARGET`。** api-server 已在新端口监听,但 Vite 代理仍打旧端口。
4. **使用 `0.0.0.0` 作为浏览器访问地址。** 监听可以是 `0.0.0.0`,展示给用户和健康检查通常用 `127.0.0.1`
5. **端口探测和实际启动之间存在竞态。** 已经探测可用的端口仍可能被外部进程抢占SpacetimeDB 启动后仍要解析实际监听地址api-server 和 Vite 失败时要打印清晰日志。
6. **运行全仓库 lint 误判。** 当前仓库可能有既有 lint 问题。验证本功能时优先运行定向测试、Bash 语法检查、编码检查,并在最终说明中区分既有 lint 失败与本次改动。
## 验收清单
- [ ] 端口工具有测试覆盖端口被占用和多端口互斥解析。
- [ ] `dev-rust-stack.sh` 通过 `bash -n`
- [ ] `npm run dev` / `npm run dev:rust` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
- [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。
- [ ] 文档同步更新 `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`
- [ ] 长期踩坑同步更新 `.hermes/shared-memory/pitfalls.md`
- [ ] 修改中文文件后运行 `npm run check:encoding`