feat(deploy): 构建步打印**工作树清单** —— 把不可归因的 modified=true 换成有名字的清单
pi 2026-09-25 提的第三种处置(既不判红、也不只留一个 bool),我采纳。 ## 为什么一个 bool 不够 `go build` 在脏树上**照样**写 `vcs.revision=<HEAD>`,只额外记 `vcs.modified=true`。 那个位只说"含未提交代码",**不说是谁的什么** —— 而本仓是**多 agent 并发**: 未提交改动通常**不是部署者的**,是别人的在飞工作。 ⇒ 这不是理论,本仓**已经付过代价**(`check-deploy-drift` 判据⑥ 注释里那笔账): 2026-09-14 `pool.mjs` 一行未提交的 `let missingSessionCount = 0;` 被"从脏工作区做的" 快照原样带进生产,而当时判据① 报的是「逐字节一致」。 ## 处置:打印清单,不判红 ``` say "3. 构建二进制" 之后、go build 之前: git status --porcelain 非空 ⇒ warn "工作树不干净 —— 下面这份未提交清单会被**编进这次构建**" + 逐行打印 否则 ⇒ ok "工作树干净(构建件不含未提交代码)" ``` ★ 位置贴紧构建步:`go build` 读的就是**此刻**的工作树,所以这份清单才对应"进了这份二进制的东西"。 ★ 判红与否改变不了的事实:**清单有名字,bool 没有** —— 出事时能直接看出"带进去的是谁的那两个文件", 而不只能回答"当时是脏的"。(本会话同族:一个位 vs 一份清单 = 不可归因 vs 可归因。) ## 同时给 ⑤b 记一条**边界**(避免下一个人把它补错) `check-deploy-drift` 的 ⑤b **读不到构建时的工作树** —— 它读的是二进制内嵌位, 而它那里的 `git status` 问的是**此刻**的树(两者可能不同:构建完又提交了、别人的改动进来了)。 ⇒ 注释里写明:清单归**部署脚本**(那里才是同一时刻),⑤b 只回答"含不含未提交代码", **别把 ⑤b 补成 `git status` 然后当成构建清单**。 ## 验证 `bash -n` 通过;`--dry-run` rc=0 且不落地(只留 0 字节锁),清单在 dry-run 下照常打印(只读); 自检 63 项绿、`判据自检失败` 不出现。
This commit is contained in:
@ -1228,6 +1228,10 @@ export function checkLayout(inject = {}) {
|
||||
// 本判据的 R\C 就是下面这两类 —— 它们**只有编译/别的判据能判**:
|
||||
// · 从脏树构建(revision 相等但含未提交代码)—— 本判据只**披露** modified,不判红
|
||||
// · 二进制内容与某次提交一致、但**部署后又被人手改过**(本判据只看内嵌 revision)
|
||||
// ★ 注意本判据**读不到构建时的工作树**:它读的是二进制的内嵌位,而 `git status`
|
||||
// 在这里问的是**此刻**的树 —— 两者可能不同(构建完又提交了 / 别人的改动进来了)。
|
||||
// 所以"这次构建带了哪些未提交文件"由**部署脚本**在构建步打印(那里才是同一时刻),
|
||||
// 本判据只回答"含不含未提交代码"。别把这里补成 `git status` 然后当成构建清单。
|
||||
const negative = '本判据**不覆盖**: ①从脏树构建(含未提交代码,只披露不判红)'
|
||||
+ '②部署后被手工替换/修改(只看内嵌 revision)';
|
||||
binRevNote = `与仓库 HEAD 一致(${got.slice(0, 8)})`
|
||||
|
||||
@ -245,6 +245,25 @@ STAGE="/tmp/agentmail-gateway-build-$TS"
|
||||
# 改标准目录部署是为了"运行时不再依赖源码目录";没有 -trimpath 时这条只做到一半:
|
||||
# 依赖确实没了,但**源仓库位置还印在产物上**。判据在 check-deploy-drift(标准目录 ⑤)。
|
||||
run "rm -f '$STAGE'"
|
||||
# ★ 构建时的**工作树清单** —— 比"一个 modified 位"可归因(pi 2026-09-25 提出,我实测确认)。
|
||||
#
|
||||
# 为什么不是一个 bool:`go build` 在脏树上**照样**写 `vcs.revision=<HEAD>`,只额外记
|
||||
# `vcs.modified=true` ⇒ 那个位只说"含未提交代码",**不说是谁的什么**。而本仓是
|
||||
# **多 agent 并发**:那些未提交改动通常**不是部署者的**,是别人的在飞工作。
|
||||
# ⇒ 这不是理论:本仓为此**已经付过代价**(check-deploy-drift 判据⑥ 注释里那笔账)——
|
||||
# 2026-09-14 `pool.mjs` 一行未提交的 `let missingSessionCount = 0;` 被"从脏工作区做的"
|
||||
# 快照原样带进生产,而当时判据① 报的是「逐字节一致」。
|
||||
# ⇒ 处置取**第三种**:既不判红(脏树在本仓是常态,判红=总在亮),也不只留一个不可归因的位,
|
||||
# 而是**把"这次构建带着哪些未提交文件"打印出来**。
|
||||
# ★ 判红与否两种选择都改变不了这个事实:**清单有名字,bool 没有** ——
|
||||
# 出了事能直接看出"带进去的是谁的那两个文件",而不是只能回答"当时是脏的"。
|
||||
# 位置贴紧构建步:上面那句 `go build` 读的就是此刻的工作树。
|
||||
if [ -n "$(git -C "$REPO" status --porcelain 2>/dev/null)" ]; then
|
||||
warn "工作树不干净 —— 下面这份未提交清单会被**编进这次构建**(多 agent 并发时通常不是你的改动):"
|
||||
git -C "$REPO" status --porcelain 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
ok "工作树干净(构建件不含未提交代码)"
|
||||
fi
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
printf ' [dry-run] (cd server && go build -trimpath -o %s ./cmd/server)\n' "$STAGE"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user