From 55158e166653258b7d8efb9f445ff017327228ac Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Fri, 25 Sep 2026 06:31:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20=E8=A1=A5=20=E2=91=A4b=20?= =?UTF-8?q?=E7=9A=84**=E5=81=87=E7=BB=BF=E6=B4=9E**=20`revision=3D=3DHEAD`?= =?UTF-8?q?=20=E2=89=A0=20"=E5=B0=B1=E6=98=AF=E9=82=A3=E4=BB=BD=E6=8F=90?= =?UTF-8?q?=E4=BA=A4"=EF=BC=88pi=20=E6=8C=87=E5=87=BA=EF=BC=8C=E6=88=91?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B=E5=A4=8D=E7=8E=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 洞 树脏时 Go **照样**写 `vcs.revision=`,只在 `vcs.modified` 上留痕 ⇒ 只比 revision 会把"**未提交的代码**"当成"就是那份提交"。 实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build): revision=2931050a == HEAD ⇒ 我 ⑤b 判**绿** 但二进制里是未提交的那行代码 ⇒ **假绿** ✓ pi 对 线上那份正是 `vcs.modified=true` ⇒ 它**不对应任何已提交版本**。 ## 修法:说清楚,但**不判红** `revision == HEAD` 与"不含未提交代码"是**两个量**,不能拿前者冒充后者 —— 但脏树在本仓是**多 agent 并发编辑的常态**,判红就是造一条总在亮的判据。 ★ 同一个事实在本仓**已有既定严重度**:`check-deploy-drift` 判据⑥ // ⑥ 工作区干净度 —— **WARN,不参与退出码**。 ⇒ 两处都改成 **WARN/note 披露,不改 ok / 不加 CHECK_FAIL**, 与判据⑥ 同严重度 —— 否则同仓同一个量会有两套红绿。 redeploy-gateway.sh §7: modified=true ⇒ warn(不动 CHECK_FAIL) check-deploy-drift ⑤b: revision 相同但 modified=true ⇒ 绿 + note 点明"含未提交代码" ## 判据自检 新增一格:`revision 相同但 modified=true ⇒ 绿,且 note 点明"含未提交代码"`。 自检 **61 → 62 项**,`判据自检失败` 不出现。 ★ 这条是本判据**唯一**的假绿形状,所以它必须有一格专门钉住—— 否则下一个人把 note 删了、或把 ok 改成 false,都没有判据会响。 --- deploy/check-deploy-drift.mjs | 28 ++++++++++++++++++++++++---- deploy/redeploy-gateway.sh | 19 ++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/deploy/check-deploy-drift.mjs b/deploy/check-deploy-drift.mjs index 6f48dc2..7443dc3 100644 --- a/deploy/check-deploy-drift.mjs +++ b/deploy/check-deploy-drift.mjs @@ -1216,7 +1216,17 @@ export function checkLayout(inject = {}) { : !got && head ? `${BIN} 里没有 vcs.revision(可能是 -buildvcs=false 或老构建)⇒ 不判` : `两侧都拿不到 ⇒ 不判`; } else if (got === head) { - binRevNote = `与仓库 HEAD 一致(${got.slice(0, 8)})`; + // ★ `revision == HEAD` **不等于**"二进制就是那份提交的代码"(pi 2026-09-25 指出): + // 树脏时 Go 照样写 `vcs.revision=`,只在 `vcs.modified` 上留痕。 + // 实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build): + // revision=2931050a == HEAD ⇒ 本判据绿,而二进制里是未提交的代码 —— 假绿。 + // ⇒ 把 modified 也读出来说清楚;**不改 ok**(脏树在本仓是常态, + // 判红会造出总在亮的判据 —— 与本文件 ⑥ 的既定严重度一致:WARN,不参与退出码)。 + const mod = (text.match(/vcs\.modified=(\w+)/) || [])[1] ?? ''; + binRevNote = `与仓库 HEAD 一致(${got.slice(0, 8)})` + + (mod === 'true' + ? ',但 vcs.modified=true ⇒ **含未提交代码**,不对应任何已提交版本' + : mod === 'false' ? '' : '(未记录 vcs.modified ⇒ 是否含未提交代码未知)'); } else { binRevOk = false; binRevNote = `${BIN} 是 ${got.slice(0, 8)},仓库 HEAD 是 ${head.slice(0, 8)} —— ` @@ -1293,13 +1303,14 @@ export function layoutSelfCheck() { // ⑤b 版本判据的样本构造器。`rev` 是喂给二进制的 `vcs.revision=`, // `head` 是 `git rev-parse HEAD` 的回包 —— 两侧**分别**可喂, // 否则测不出"拿不到其中一侧 ⇒ 不判"那个分支。 - const binRevOf = ({ rev }, head) => checkLayout({ + const binRevOf = ({ rev, modified }, head) => checkLayout({ ...fake({ '/etc/systemd/system': [], '/repo/systemd': [], '/opt/agentmail/bin/service-failure-notify.mjs': 'x', - '/opt/agentmail/agentmail-gateway': - rev ? `ELF…vcs.revision=${rev}…github.com/agentmail/gateway…` : 'ELF…-buildmode=exe…github.com/agentmail/gateway…' + '/opt/agentmail/agentmail-gateway': rev + ? `ELF…vcs.revision=${rev}…${modified ? `vcs.modified=${modified}…` : ''}github.com/agentmail/gateway…` + : 'ELF…-buildmode=exe…github.com/agentmail/gateway…' }), git: args => (args[0] === 'rev-parse' ? head : '') }); @@ -1570,6 +1581,15 @@ export function layoutSelfCheck() { name: '★网关二进制:revision 与 HEAD 相同 ⇒ 必须绿', ok: seventh(binRevOf({ rev: 'c'.repeat(40) }, 'c'.repeat(40)))?.ok === true }, + { + // ★ revision 相同 **但** modified=true ⇒ 仍是绿,**但 note 必须点明**"含未提交代码"。 + // 这是本判据唯一的**假绿**形状(pi 2026-09-25 指出):树脏时 Go 照样写 + // `vcs.revision=`,只有 modified 留痕 ⇒ 只比 revision 会把"未提交的代码" + // 当成"就是那份提交"。不改 ok(脏树在本仓是常态,判红=总在亮),但**不许不说**。 + name: '★网关二进制:revision 相同但 modified=true ⇒ 绿,且 note 点明"含未提交代码"', + ok: seventh(binRevOf({ rev: 'c'.repeat(40), modified: 'true' }, 'c'.repeat(40)))?.ok === true + && /含未提交代码/.test(seventh(binRevOf({ rev: 'c'.repeat(40), modified: 'true' }, 'c'.repeat(40)))?.note ?? '') + }, { // 拿不到 revision(-buildvcs=false / 老构建 / 非 git 构建)⇒ **不判**。 // 写成红则合法环境恒红;写成绿又等于没判 ⇒ 只接受"不判",且 note 必须说出来, diff --git a/deploy/redeploy-gateway.sh b/deploy/redeploy-gateway.sh index a3b501c..e2c7f21 100755 --- a/deploy/redeploy-gateway.sh +++ b/deploy/redeploy-gateway.sh @@ -460,7 +460,24 @@ else bad "已装二进制是 ${_binrev%"${_binrev#????????}"},HEAD 是 ${_headrev%"${_headrev#????????}"} —— 装的不是当前代码" CHECK_FAIL=$((CHECK_FAIL+1)) fi -unset _binrev _headrev 2>/dev/null || true + +# ★ 上一句的**假绿洞**(pi 2026-09-25 指出,我实测复现):`revision == HEAD` **不等于** +# "二进制就是那份提交的代码" —— 树脏时 Go 照样写 `vcs.revision=`, +# 只是额外记一个 `vcs.modified=true`。 +# 实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build): +# revision=2931050a == HEAD ⇒ 上面那句判**绿** +# 但二进制里是未提交的代码 ⇒ **假绿** +# 线上那份正是 `vcs.modified=true` ⇒ 它**不对应任何已提交版本**,只比 revision 会漏掉这一点。 +# +# ⚠️ 但**不把它判红**:本仓是多 agent 并发编辑,脏树是常态,而"总在亮的判据会被学会忽略" +# (本仓既有的立场,见 check-deploy-drift 判据⑥:「工作区干净度 —— **WARN,不参与退出码**」)。 +# 同一个事实在本仓已有既定严重度,这里就必须同为 WARN —— 否则同仓同一个量两套红绿。 +# ⇒ 说出来,但不改结论:**"等于 HEAD" 与 "不含未提交代码" 是两个量**,别让前者冒充后者。 +_binmod="$(go version -m "$TARGET" 2>/dev/null | sed -n 's/.*vcs\.modified=//p' | head -1)" +if [ "$_binmod" = "true" ]; then + warn "该二进制记着 vcs.modified=true ⇒ **含未提交代码**,不对应任何已提交版本(revision 相等也不代表就是那份提交)" +fi +unset _binrev _headrev _binmod 2>/dev/null || true echo echo " 仍需人工确认(脚本无法代替):"