From cb54c53937c0bf8d029b5c5966a6d89b284b1197 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 15 Sep 2026 13:41:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=A8=E7=AB=AF:=20=E8=A1=A5=E4=B8=8A=20--ch?= =?UTF-8?q?eck=20=E7=9A=84=E5=8F=8D=E5=90=91=E6=B4=9E=EF=BC=9ACHECK=5FGATE?= =?UTF-8?q?=5FRC=20=E7=9C=9F=E6=AD=A3=20exit=20=E5=87=BA=E5=8E=BB=EF=BC=88?= =?UTF-8?q?=E7=BA=A2=E9=97=A8=E7=A6=81=E4=B8=8D=E5=86=8D=E9=80=80=200?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E5=B9=B2=E8=B7=91=E4=B8=8D=E5=86=8D=E9=87=8D?= =?UTF-8?q?=E5=86=99=20dist=EF=BC=88BUILD=5FINFO=5FOUT=20=E6=94=B9?= =?UTF-8?q?=E9=81=93=E4=B8=B4=E6=97=B6=E7=9B=AE=E5=BD=95=EF=BC=8Cbuild-sta?= =?UTF-8?q?mp=20=E7=9A=84=E7=BA=A2=E4=B8=8D=E4=BC=9A=E8=A2=AB=E5=B9=B2?= =?UTF-8?q?=E8=B7=91=E5=88=B7=E6=8E=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi 2026-09-15 两条都成立,而且第一条是我**自己上一个 commit 的注释在骗人**。 ## 一★★ `CHECK_GATE_RC` 只被打印、**从来没被 `exit` 用** —— 我把它从末尾挪走时丢了退出 ``` $ grep -n "exit.*CHECK_GATE_RC" deploy/install.sh (无) $ 干跑分支终点: exit 0 ← 无条件 0 ``` 而我上一版注释写的是"**最后用记下的码退出(见文件末尾的 `CHECK_GATE_RC`)**" —— **文件末尾根本没有那一行**(那里是正式安装路径的 systemctl/curl,干跑走不到)。 我上一轮把那段从"文件末尾"挪到 `go vet` 之前时,**挪了打印、丢了 `exit`**。 ⇒ 后果是我上一个 commit **把病修反了**: | | 第一道红的行为 | 命令退出码 | |---|---|---| | 修之前 | 中止一切,诊断全丢 | **失败**(正确的那半) | | 我那一版 | 诊断全跑完 | **0**(错的那半) | 而丢掉的这一半**正好是会被引用的那一半** —— 这条链上引用过 `install.sh --check` 的 `exit 0` 当绿读数。**"注释说的事和代码做的事不一致"这是我第三次犯**(前两次:`unreportedFiles` 说修了没修、 `ok 5` 报文认证了没人判的界)。 **修法**:干跑分支终点 `exit "${CHECK_GATE_RC:-0}"`。 ## 二★★ "干跑"并不只读:它重写 `client/electron/dist`,能**静默刷掉 `build-stamp` 的红** 同一个脚本在别处很小心地不写 `node_modules`("干跑不写依赖"),却**照跑 `npm run build`**。 两个后果(你指出的): 1. **干跑改共享状态** —— 而它自己那句"干跑没有执行"的清单只列了**工作区外**的路径,`dist` 悄悄在射程内; 2. **它能把 `build-stamp` 从红刷成绿** —— 那条比的是 `dist/BUILD_INFO.json` 的 `gitRev`/`srcHash` 与当前源码,而它现在的红**恰恰就是"产物是在旧提交上构建的"**(实测 `gitRev=7b16fec` vs `HEAD=406bb34`)。 跑一次干跑 ⇒ 重构建 ⇒ 不匹配消失 ⇒ **一条红没了,而没有任何人决定过"重构建"**。 **修法**:干跑改成 `gen:bg` + **`vite build --outDir <临时目录>`** + `build-info.mjs`(新增 `BUILD_INFO_OUT`)。 **真装的 `npm run build` 一字不改**(仍写真正的 `dist`)。 **实测**(`dist` 内容指纹 sha256): ``` 跑之前 ff97258fbcc1de49… 跑之后 ff97258fbcc1de49… ✓ 未动 BUILD_INFO.json: gitRev=7b16fec vs HEAD=406bb34 ⇒ **红还在**(没被干跑刷掉) 退出码=1(红门禁 ⇒ 非 0) ``` **退出码两侧都验了**(不是只看"非 0"就收): - 真跑一次全脚本 ⇒ `bash -x` 追到 `+ exit 1`(**真的走到了我那一行**); - 抽出那一行(逐字节)配三种前置状态 ⇒ `CHECK_GATE_RC=1` → 1;空 → 0;未设 → 0; - 构造全绿情形(中和两处真门禁)⇒ **退出码 0**,`FAIL` 行数 0。 ★ 顺带一条**我自己踩到的坑**:第一次构造"全绿"副本时我把脚本拷到 `/tmp` 跑, 而 `REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"` ⇒ `REPO=/tmp` ⇒ `env-defaults.sh` 找不到 ⇒ 退出 1,**看着像"绿路径也退 1"**。放到仓内(临时文件、跑完即删)才是对的。 **又一次"我的验证环境和生产不同"** —— 这次差异是脚本位置。 ## 三、你指出的"同一现象两种政策":加了互相指向,并写明**为什么不能统一** - `install.sh` 的 origin WARN 旁边写明:AGC 闹钟对同一现象判**红**,**有意如此**; - `criteria-hygiene` 第 6 条的报错里加上镜像的一句。 理由(写进两处):那边问"**本机配置能不能用**" ⇒ 离线不是配置错 ⇒ WARN; 本条问"**凭证有没有进过远端历史**" ⇒ **查不了就答不出** ⇒ 红。 并且写明统一到任一边的代价:统一到 WARN ⇒ **泄漏告警在离线时静默变绿**; 统一到红 ⇒ 离线机器上一次干跑就报假红。 ## 四、残余(照实写) - `gen:bg` 会**原地重写** `src/background-takeover.generated.css`(已跟踪)。实测内容幂等 (`git status` 干净),所以**不改内容**;但"写文件这个动作"仍在 ⇒ "干跑只读"的准确说法是"**不改任何被跟踪文件的内容、不碰 dist**",不是"一个字节都不写"。 我把这句写进了注释 —— **没验到的部分不写成保证**。 - `go vet` 那条红是 `/root/.cache/go-build` **权限**问题(环境),不是代码;维持既有记录。 - 你上封提的"记录投影"已在 `406bb34` 做完;与本文无关。 - 红线仍 4 条(`narrow-layout`/`nav-merge`/`harmony-presets`/`build-stamp`),**都不是我的**。 --- client/electron/scripts/build-info.mjs | 13 ++++- .../electron/test/criteria-hygiene.test.mjs | 5 ++ deploy/install.sh | 56 +++++++++++++++++-- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/client/electron/scripts/build-info.mjs b/client/electron/scripts/build-info.mjs index d73ff3f..46dc626 100644 --- a/client/electron/scripts/build-info.mjs +++ b/client/electron/scripts/build-info.mjs @@ -109,7 +109,18 @@ export function writeBuildInfo(root = ELECTRON_DIR, buildCmd = process.env.npm_l releaseCandidate: !st.gitDirty, note: '构建自证:test/build-stamp.test.mjs 用 srcHash/gitRev 精确比对,不是比时间戳' }; - const out = join(root, 'dist', 'BUILD_INFO.json'); + /* + * ★ 输出路径可覆盖(`BUILD_INFO_OUT`),**为了让"干跑"真的只读**。 + * + * 原来这里硬编码 `dist/`,于是 `install.sh --check`(自称干跑)跑 `npm run build` + * **会重写共享工作树的 `dist/BUILD_INFO.json`** —— 而 `build-stamp` 那条判据 + * 正是拿它跟当前源码比 ⇒ **一次"干跑"能把一条红静默刷成绿,而没有人决定过"重构建"**。 + * (pi 2026-09-15 抓到这一点;他**刻意没跑 `--check` 去坐实**,理由是共享树里跑它 + * 等于在别人脚下换东西 —— 这个判断我认,所以本轮我在 `/tmp` 的隔离副本里验,没在共享树上跑。) + */ + const out = process.env.BUILD_INFO_OUT + ? process.env.BUILD_INFO_OUT + : join(root, 'dist', 'BUILD_INFO.json'); mkdirSync(dirname(out), { recursive: true }); writeFileSync(out, `${JSON.stringify(info, null, 2)}\n`); return info; diff --git a/client/electron/test/criteria-hygiene.test.mjs b/client/electron/test/criteria-hygiene.test.mjs index 33fdc8b..03fd1ef 100644 --- a/client/electron/test/criteria-hygiene.test.mjs +++ b/client/electron/test/criteria-hygiene.test.mjs @@ -542,6 +542,11 @@ test('★ AGC 真身从未进过远端历史(泄露之后响的闹钟,不是 assert.deepEqual(unresolved, [], '这几条远端 ref 的 tip **拿不到、也抓不回来** ——\n' + ' ★ 按"不知道"处理,**不当绿**:**查不了 ≠ 干净**。\n' + + ' ★★ 这里对"远端不可达"判**红**,而 `deploy/install.sh --check` 的 origin 可达性检查\n' + + ' 对同一现象判 **WARN** —— **两处政策相反是有意的**,别以"一致性"为名统一掉:\n' + + ' · 那边问"本机配置能不能用" ⇒ 离线不是配置错 ⇒ WARN;\n' + + ' · 本条问"凭证有没有进过远端历史" ⇒ **查不了就答不出** ⇒ 红。\n' + + ' (统一到 WARN ⇒ 泄漏告警在离线时静默变绿;统一到红 ⇒ 离线机器上一次干跑就报假红。)\n' + ' ★ 真因不是"远端不可达"(sha 是从 `ls-remote` 拿的,**远端当然可达它**),\n' + ' 而是"**本地没有这个对象**",且 `git log ` 必须先有对象。\n' + ' ★ 修法(**只 `git fetch` 不够** —— 它默认只抓 `refs/heads/*`,\n' diff --git a/deploy/install.sh b/deploy/install.sh index f3fe7cc..e833e87 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -141,15 +141,54 @@ fi # 但**这个机制比它看起来脆**:它依赖"门禁恰好全过"。) # # 修法:`--check` 下把这一门的失败**记下来**、继续跑完所有诊断, -# 最后用记下的码退出(见文件末尾的 `CHECK_GATE_RC`)。 +# 最后用记下的码 `exit` 出去(**不是在文件末尾 —— 见下面"两个洞"**)。 # **真装路径行为完全不变**(仍 `set -e` 立刻中止 —— 装机器时不该带着红往下走)。 +# +# ★★ 这一版修的是 pi 2026-09-15 抓到的**两个反向的洞**(我上一版自己造的): +# +# ① **`CHECK_GATE_RC` 只被打印、从来没被 `exit` 用**。 +# 我上一版的注释写着"见文件末尾的 `CHECK_GATE_RC`",而**文件末尾根本没有那一行** +# (文件末尾是正式安装路径的 systemctl/curl,干跑走不到)—— 我把它从末尾挪走时, +# **挪了打印、丢了退出**。后果是**我上一个 commit 修的病反过来**: +# 修之前:第一道红中止一切,但命令**失败**; +# 修之后:诊断全跑完,命令**通过**(干跑分支无条件 `exit 0`)。 +# 而丢掉的这一半**正好是会被引用的那一半** —— 这条链上引用过 `install.sh --check` 的 +# `exit 0` 当绿读数。**"说法(注释)与实现不一致"第三次出现在我手上。** +# ⇒ 现在干跑分支的终点是 `exit "${CHECK_GATE_RC:-0}"`。 +# +# ② **"干跑"并不只读:它会重写 `client/electron/dist`。** +# 同一个脚本在别处很小心地不写 `node_modules`("干跑不写依赖"),却**照跑 `npm run build`**。 +# 两个后果: +# · **"干跑"改共享状态**(而它自己那句"干跑没有执行"的单子只列了**工作区外**的路径, +# `dist` 悄悄在射程内); +# · **它能把 `build-stamp` 从红刷成绿** —— 那条比的是 `dist/BUILD_INFO.json` 的 +# `gitRev`/`srcHash` 与当前源码,而它的红**恰恰就是"产物是在旧提交上构建的"**。 +# 跑一次干跑 ⇒ 重构建 ⇒ 不匹配消失 ⇒ **一条红没了,而没有人决定过"重构建"**。 +# ⇒ 干跑改成:`gen:bg`(幂等生成,本来就在 build 里)+ **`vite build --outDir <临时目录>`** +# + `build-info.mjs`(`BUILD_INFO_OUT` 指到那个临时目录)。 +# **真装的 build 一字不改**(仍然写真正的 `dist`)。 +# ⚠️ 残余:`gen:bg` 会**原地重写** `src/background-takeover.generated.css`(已跟踪)。 +# 实测它内容幂等(`git status` 干净),所以**不改内容**;但"写文件这个动作"仍在, +# 所以"干跑只读"这句话**准确的说法是"不改任何被跟踪文件的内容、不碰 dist"**, +# 不是"一个字节都不写"。我看不出的部分不写死。 if [[ $CHECK_ONLY -eq 1 ]]; then npm_rc=0 - ( cd "$REPO/client/electron" && npm run typecheck && AGENTMAIL_CRITERIA_PHASE=install npm test && npm run build ) \ - || npm_rc=$? + # 产物写到临时目录:干跑不该动 dist(否则会静默刷掉 build-stamp 的红) + CHECK_DIST="$(mktemp -d -t agentmail-check-dist-XXXXXX)" + ( + cd "$REPO/client/electron" \ + && npm run typecheck \ + && AGENTMAIL_CRITERIA_PHASE=install npm test \ + && npm run gen:bg \ + && npx vite build --outDir "$CHECK_DIST" --emptyOutDir \ + && BUILD_INFO_OUT="$CHECK_DIST/BUILD_INFO.json" node scripts/build-info.mjs + ) || npm_rc=$? + rm -rf "$CHECK_DIST" if [[ $npm_rc -ne 0 ]]; then echo " [FAIL] 前端门禁(typecheck / 判据 / build)退出码 ${npm_rc} —— 详见上面的红" >&2 CHECK_GATE_RC="$npm_rc" + else + echo " [ OK ] 前端门禁通过(构建产物落在临时目录,**没有动 dist**)" fi else ( cd "$REPO/client/electron" && npm run typecheck && AGENTMAIL_CRITERIA_PHASE=install npm test && npm run build ) @@ -339,6 +378,12 @@ if [[ $CHECK_ONLY -eq 1 ]]; then else echo " [WARN] origin **不可达**(或 15s 内没应答)—— 本机的推送/取远端会失败" echo " 注意:这条只说明'到不了',不说明'地址写错了';两者要分开看。" + echo " ★ 这里判 WARN、而 AGC 闹钟(criteria-hygiene 第 6 条)对'远端不可达'判**红** ——" + echo " 两处政策相反是**有意的**,别以'一致性'为名统一掉(统一到哪边都会弄坏一半):" + echo " · 这里问的是'本机配置能不能用' ⇒ 离线不是配置错 ⇒ **WARN**;" + echo " · 那边问的是'凭证有没有进过远端历史' ⇒ 查不了就**答不出** ⇒ **红**" + echo " ('看不到'不等于'干净')。" + echo " 理由原文见 client/electron/test/criteria-hygiene.test.mjs 该条注释。" fi fi @@ -382,7 +427,10 @@ if [[ $CHECK_ONLY -eq 1 ]]; then 拿不准就先跑 ./deploy/install.sh --check;正式安装用 sudo ./deploy/install.sh。 EOF - exit 0 + # ★ **无条件 `exit 0` 是上一个版本的洞**(pi 2026-09-15):干跑在门禁红时也报成功, + # 而这条链上引用过 `--check` 的 `exit 0` 当绿读数 ⇒ 那个读数不可信了。 + # 现在只要前面记下了非零码就把那个码传出去。**"诊断跑完了" ≠ "门禁都过了"。** + exit "${CHECK_GATE_RC:-0}" fi echo "==> 前端产物嵌入 Gateway"