diff --git a/docs/superpowers/specs/2026-07-15-run-command-design.md b/docs/superpowers/specs/2026-07-15-run-command-design.md new file mode 100644 index 0000000..cd60e53 --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-run-command-design.md @@ -0,0 +1,84 @@ +# `run.command` 一键启动器设计 + +日期:2026-07-15 +状态:用户已确认设计方向,等待书面规格复核 + +## 目标 + +在 macOS Finder 中双击项目根目录的 `run.command`,用一个持续打开的终端窗口启动并监督当前 ERP 主链路: + +- Spring Boot 单体应用监听 `8091`,同时提供 Vue 前端和 `/api/oa/*` 后端。 +- ngrok 固定域名 `https://resonant-elated-launder.ngrok-free.dev` 转发到 `8091`。 +- 两项服务就绪后自动打开公网地址。 +- 用户按 `Ctrl+C` 或关闭终端时,只停止本次脚本启动的进程。 + +不启动已弃用的 OFBiz、独立 Vite 开发服务器、PostgreSQL 或 nginx;当前可交付预览已包含在 Spring Boot JAR 中。 + +## 启动体验 + +1. 脚本以自身所在目录作为项目根目录,不依赖 Finder/Terminal 的当前工作目录。 +2. 终端逐步显示“环境检查、后端启动、ngrok 启动、公网验证、运行中”。 +3. 后端通常约 15 秒就绪;脚本按真实 HTTP 状态等待,不使用固定时长假定成功。 +4. 成功后显示本地地址、公网地址、日志路径和停止方法,并调用 macOS `open` 打开公网地址。 +5. 终端保持运行并监控服务;任一由脚本启动的子进程意外退出时,脚本报告错误并输出对应日志末尾。 + +## 组件与流程 + +### 1. 环境与路径 + +- 项目根目录:由 `run.command` 的绝对路径推导。 +- 后端工作目录:固定为 `<项目>/oa-backend`,确保相对数据源 `./data/oa.db` 始终指向 `oa-backend/data/oa.db`。 +- Java:优先使用项目内 `.jdks/jdk-17.0.19+10/Contents/Home/bin/java`。 +- JAR:`oa-backend/build/libs/oa-backend-0.1.0.jar`。 +- ngrok:优先使用 `$HOME/bin/ngrok`,否则回退到 `PATH` 中的 `ngrok`。 +- 日志:写入 `${TMPDIR:-/tmp}/kaidi-erp-run/`,不污染 Git 工作区。 + +缺少 Java、JAR、ngrok、`curl`、`lsof` 或 `python3` 时立即给出可操作错误并退出。 + +### 2. 后端复用与启动 + +- 若 `8091` 无监听进程,脚本从 `oa-backend` 目录启动 Java,并记录为“本脚本拥有”。 +- 若 `8091` 已监听,脚本请求根路径并核对页面标题“凯迪协同办公平台”:匹配则复用;不匹配则报端口冲突,不杀进程。 +- 启动后最多等待 60 秒,要求首页 HTTP 200 且登录接口能返回标准 JSON;超时或进程提前退出时显示后端日志末尾。 + +### 3. ngrok 复用与启动 + +- 查询本地 ngrok API `127.0.0.1:4040/api/tunnels`。 +- 若已有固定公网域名且目标为 `http://localhost:8091`,直接复用,不再启动第二个 ngrok。 +- 若 `4040` 被占用但不存在正确隧道,安全报错,不覆盖现有隧道。 +- 否则启动 `ngrok http --url=resonant-elated-launder.ngrok-free.dev 8091`,记录为“本脚本拥有”,最多等待 30 秒确认隧道登记成功。 + +### 4. 公网验证与浏览器 + +- 使用 `ngrok-skip-browser-warning: true` 请求公网首页,必须返回 HTTP 200 且标题正确。 +- 验证成功后自动打开公网地址。 +- 环境变量 `ERP_RUN_NO_OPEN=1` 可禁止自动打开,供测试或无界面环境使用。 + +### 5. 生命周期与清理 + +- `INT`、`TERM`、`EXIT` 使用同一清理函数。 +- 只向脚本保存的 Java/ngrok PID 发送 `TERM`,等待短时间后才对仍未退出的自有 PID 使用 `KILL`。 +- 复用的既有服务 PID 不写入“自有 PID”,因此 `Ctrl+C` 不会误杀它们。 +- 运行阶段周期性检查本地首页和公网隧道;异常时提示并保留日志证据。 + +## 可测试性 + +脚本使用 Bash 函数组织,并仅在直接执行时进入 `main`;测试可 `source run.command` 后单独验证函数。 + +新增 `tests/run-command.test.sh`,覆盖: + +1. `bash -n run.command` 语法检查。 +2. 脚本从任意当前目录都能解析正确项目根目录。 +3. 已存在且健康的 `8091` 服务会被复用。 +4. 非本项目进程占用 `8091` 时返回错误且不会发送终止信号。 +5. 清理函数只停止记录为本脚本启动的 PID,不停止外部 PID。 +6. `ERP_RUN_NO_OPEN=1` 时不调用浏览器。 +7. 实机冒烟:运行启动器,确认本地首页、公网首页、登录和受保护业务接口均成功;随后 `Ctrl+C` 验证自有进程退出。 + +## 验收标准 + +- Finder 双击一次即可完成后端、ngrok、公网验证和浏览器打开。 +- 重复双击不会产生第二个 Java/ngrok,也不会杀死已运行实例。 +- 成功路径明确显示两个地址和 `Ctrl+C` 停止说明。 +- 失败路径在 60 秒内结束等待,说明失败阶段并展示相关日志。 +- `run.command` 与测试脚本具有可执行权限,Shell 回归测试全部通过。