Skip to content

Commit 235b94a

Browse files
baozhoutaoclaude
andauthored
fix(cli): os doctor loads objectstack.config.ts under serve's .env* overlay (#5397) (#5402)
#5387 taught doctor to READ serve's `.env*` cascade but applied the overlay to one reader only — the env-derived checks. `loadConfig()` stayed outside it, so both commands still bundled the user's config under two different environments: a config reading `process.env` at top level saw different values, and a config that throws on a missing value landed in the wide config-analysis `catch` and printed "Could not load config for analysis" — #5382's misattribution surviving in the one path #5387 deliberately left alone — while `os serve` booted the same directory. `loadConfig()` now runs under the same `dotenvReading` resolved once at the top of `run()`. A new `withDotenvOverlayAsync` is required rather than cosmetic: a config's top level runs inside `bundleRequire`'s dynamic `import()`, so the synchronous wrapper's `finally` fires while the promise is still pending — overlay applied, then removed before anything reads it. Both wrappers share one apply/revert (dotenv-flow's own `unload()` test), so the policy is not restated. Config-check verdicts that change are the fix, not a side effect: a config that previously failed to load now loads and its checks RUN, so warnings never printed before can appear. A genuinely broken config still warns — the fix must not be a silencer. `environmentSourcesCheck` remains the single place reporting the cascade and now states that these files also reach the config load; sources only, never values, which matters more now that the overlay carries whatever keys the config chose rather than a declared subset. Claude-Session: https://claude.ai/code/session_01VkPSGsX9o17MsGv3Lbxu2w Co-authored-by: Claude <noreply@anthropic.com>
1 parent 98369a8 commit 235b94a

3 files changed

Lines changed: 544 additions & 12 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
fix(cli): `os doctor``os serve` 的环境载入 `objectstack.config.ts`(#5397)
6+
7+
#5387 让 doctor ****到了 `os serve` 的那份 `.env*` cascade,但 overlay 只套在一处读取上:
8+
env 派生检查(posture,以及以它为闸门的 ADR-0120 D5e 建议)。`loadConfig()` 留在了外面。
9+
于是两条命令仍然在**两份不同的环境**下打包同一个配置文件:
10+
11+
```
12+
# .env
13+
OS_DATABASE_URL=postgres://…
14+
15+
# objectstack.config.ts —— 顶层读环境变量是常见写法,不是刁钻写法
16+
const url = process.env.OS_DATABASE_URL;
17+
if (!url) throw new Error('OS_DATABASE_URL is required');
18+
19+
$ os serve # dotenvFlow.config()(serve.ts:520)→ 之后才 bundleRequire → 正常启动
20+
$ os doctor # 无 overlay 直接 bundleRequire → 抛错 → 被 config 分析那个很宽的 try 吞掉
21+
# → 「⚠ Could not load config for analysis (config checks skipped)」,warning,exit 0
22+
```
23+
24+
两种危害,安静的那种更糟:
25+
26+
1. **响的** —— 上面那句话把责任推给配置文件,而配置文件没问题,同一个目录 `os serve`
27+
正常启动。它正是 #5382 判定为「归因错误」的那句,残存在 #5387 刻意没动的这条路径上。
28+
2. **哑的** —— 配置文件只要**按环境值分支**(条件声明的 object / datasource),它对 doctor
29+
和对服务器就声明了不同的形状,于是下面每一项检查(循环依赖、未引用对象、孤儿视图、
30+
仪表盘完整性、spec 版本)判定的都是一份**服务器不会运行**的配置。全程不打印任何东西。
31+
这一半没有任何 warning 会浮现出来。
32+
33+
**现在的行为。** `loadConfig()` 套上 `run()` 顶部已经解析好的**同一份** `dotenvReading`
34+
(不是第二次 `readDotenvFiles()` —— 一轮 doctor 只解析一次 cascade,否则 `Environment files`
35+
那一行就未必是 config 载入真正看到的那份)。
36+
37+
`withDotenvOverlayAsync` 而不是既有的同步版:配置文件的顶层跑在 `bundleRequire` 的动态
38+
`import()` 里面,同步版的 `finally` 会在 `loadConfig()` 交回 pending promise 的那一刻就把
39+
overlay 摘掉 —— 套上了,又在被读之前摘掉,等于没套。两者共用同一套 apply/revert
40+
(dotenv-flow 自己 `unload()` 的判定:只删掉仍然等于写入值的那些),不是抄一份。
41+
42+
**判定面的变化是本单的修复内容,不是副作用**(与 #5398 对 D5e 建议的处理同一姿态)。
43+
可观察的差异有三类,都如实呈现:
44+
45+
- 此前因缺值抛错而被跳过的配置,现在**载入成功**,那句归因错误的 warning 不再出现,而
46+
下面那一整组 config 检查**开始运行** —— 因此可能新增此前从未打印过的 warning
47+
(真机复现:`⚠ Object "account" is defined but not referenced by any view, flow, app, or lookup field`,
48+
在修复前整块被跳过,一条都看不到);
49+
- 按环境值分支的配置,doctor 判定的对象/视图集合改为与 `os serve` 一致;
50+
- 配置**确实**坏掉时,`Could not load config for analysis (config checks skipped)`
51+
**照旧触发**。套上 cascade 之后仍然载入不了的配置,`os serve` 同样载入不了 —— 这句话
52+
从此归因正确,而不是被消音。把它一并静默,等于用「没有 warning」换掉「归因错误的
53+
warning」。
54+
55+
**来源口径不变:只报来源,从不报值。** 这一点在本次改动后更吃重:overlay 现在携带的是
56+
配置文件想读的**任意**变量,而不再是 `DOCTOR_ENV_INPUTS` 这个声明过的子集,而 `.env` 正是
57+
密钥的常见住处。变量名无法预先枚举,提供它们的**文件**可以 —— `Environment files` 仍是
58+
报告 cascade 的唯一一处,并在其中说明这些文件同样施加于配置载入:
59+
60+
```
61+
These files are also applied while objectstack.config.ts is loaded, so a config
62+
that reads process.env at top level sees the values `os serve` gives it.
63+
```
64+
65+
overlay 的边界也照旧:它在配置文件**载入**期间有效,而不是常驻整轮运行 —— doctor 分析的
66+
一切都是模块求值时读出的普通值,载入结束即摘除(回调抛出时同样摘除,有测试钉住)。

0 commit comments

Comments
 (0)