Document the one-command startup contract

Constraint: macOS double-click launch must keep one Terminal window alive
Rejected: LaunchAgent | hides process ownership and complicates safe cleanup
Confidence: high
Scope-risk: narrow
Directive: Keep cleanup scoped to processes started by run.command
Tested: Spec self-review for placeholders, contradictions, ambiguity, and scope
Not-tested: Launcher implementation pending written-spec review
This commit is contained in:
Qiufeng
2026-07-15 15:26:42 +08:00
parent 65e64cc717
commit 672569f199
@@ -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 回归测试全部通过。