From 437be525529fe340843541e8bc28f2ce7b70fc51 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 21 Sep 2026 06:27:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE:=20=E8=87=AA=E6=A3=80=E5=A4=B9?= =?UTF-8?q?=E5=85=B7=20`$T2`=20=E4=B8=80=E7=9B=B4=E6=B2=A1=E8=A2=AB?= =?UTF-8?q?=E6=B8=85=E7=90=86=EF=BC=88=E6=AF=8F=E8=B7=91=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=BC=8F=E4=B8=80=E4=B8=AA=20/tmp=20=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=EF=BC=89+=20=E8=A1=A5=E4=B8=80=E6=9D=A1=E5=AE=88=E5=AE=83?= =?UTF-8?q?=E7=9A=84=E5=88=A4=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 病 `mkdtree_fail` 建的是 `$T2`,而清理语句是 `743e397` **之前**写的、只提了当时存在的 `$T` 和 `$B` ⇒ **加了新夹具没加清理**。每跑一次 `--self-check` 就往 `/tmp` 漏一个目录 (实测:一轮里跑 6 次 → 6 个残留;历次累计清出 **40** 个)。 ★ 但真正的病是**第二层**:`$T2` 用的是**裸 `mktemp -d`**,于是它叫 `/tmp/tmp.XXXXXXXX` —— 和任何 `mktemp -d` 的产物**长得一样**。后果: **认不出来是谁的,就没法清理、也没法写判据。** ## 我第一版判据是假绿(变异测试当场抓住) 我按 `find -name 'am-prune-selftest-*'` 数残留。但 `$T2` 根本不匹配这个前缀 ⇒ **把 `$T2` 从清理列表里删掉(复原 bug),判据照样绿 23/0。** 这就是"判据在,但走不到"—— 它守的是另一个前缀。 ## 修 1. `mkdtree_fail` 改用 `mktemp -d "$TMPD/am-prune-selftest-XXXXXX"`,与 `mktree` 同前缀 ⇒ 夹具**可识别**; 2. 清理列表补上 `$T2`; 3. 新增判据「自检夹具清干净(本次新建的残留 = N 个,须为 0)」, 用**集合差**(跑前快照 vs 跑后快照)而不是"有没有"或"最近 N 分钟" ⇒ 上次的残留不会误伤,也不依赖时钟。 ## 变异 + 对照(都实跑) 变异:`rm -rf "$T" "$B"`(去掉 $T2) ⇒ rc=1,22 通过/1 失败,**点名残留目录** ✓ 基线: ⇒ rc=0,23 通过/0 失败,/tmp 残留 0 ✓ 对照:预置一个 STALE 残留 ⇒ rc=0 通过(集合差不误伤)✓ (单次自检耗时 ~39s,不是挂起 —— 我第一次用 2 次循环跑,误撞了 60s 上限。) --- deploy/prune-deploy-artifacts.sh | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/deploy/prune-deploy-artifacts.sh b/deploy/prune-deploy-artifacts.sh index b1e29da..7b3652c 100755 --- a/deploy/prune-deploy-artifacts.sh +++ b/deploy/prune-deploy-artifacts.sh @@ -190,7 +190,17 @@ if [ "${1:-}" = "--self-check" ]; then } # 给"删除失败"那条样本用的最小树:两个构建暂存(窗口 1 份 ⇒ 旧的应被删) mkdtree_fail() { - local t; t="$(mktemp -d)" + # 前缀必须和 `mktree` 一致(`$TMPD/am-prune-selftest-XXXXXX`)。 + # + # ★ 2026-09-21:这里原来是裸 `mktemp -d`,于是 `$T2` 叫 `/tmp/tmp.XXXXXXXX`。 + # 后果有两层,第二层才是要命的: + # ① 每跑一次 `--self-check` 就往 `/tmp` 漏一个目录(清理语句里漏了 `$T2`); + # ② 漏出来的目录**认不出来是谁的** —— `/tmp/tmp.*` 是任何 `mktemp -d` 的通用形态, + # 既没法安全清理,也没法写出一条"残留必须为 0"的判据 + # (我第一版判据就栽在这:按 `am-prune-selftest-*` 数,永远数不到 `$T2` + # ⇒ 变异测试把 `$T2` 从清理列表里删掉,判据**照样绿**)。 + # **夹具认不出来,就没有判据守得住它。** + local t; t="$(mktemp -d "$TMPD/am-prune-selftest-XXXXXX")" mkdir -p "$t/data" "$t/tmp" for ts in 20260101-000000 20260105-000000; do mkdir -p "$t/tmp/agentmail-gateway-build-$ts" @@ -228,6 +238,11 @@ if [ "${1:-}" = "--self-check" ]; then } SIG_BEFORE="$(_tree_sig "$ROOT")" + # 自检**之前**已存在的夹具目录(正常情况下是空的,但并发跑或上次崩溃会留下)。 + # 后面按**集合差**判"本次是否清干净"—— 比对"有没有新的", + # 而不是"有没有"或"最近 10 分钟内的":前者会被上一次的残留误伤,后者依赖时钟。 + _FIX_BEFORE="$(find "${TMPDIR:-/tmp}" -maxdepth 1 -name 'am-prune-selftest-*' 2>/dev/null | sort)" + T="$(mktree)" out="$(run "$T")"; rc=$? # 干跑:看报告 ck "干净样本:干跑成功(退出码 $rc)" "$([ "$rc" = 0 ] && echo 1 || echo 0)" @@ -297,7 +312,21 @@ FAKERM ck "自检没有碰生产根($ROOT 清单不变)" "$([ "$(_tree_sig "$ROOT")" = "$SIG_BEFORE" ] && echo 1 || echo 0)" - rm -rf "$T" "$B" + # ⚠️ `$T2` 一度漏在这里(2026-09-21 发现)。夹具是三个(`T` / `B` / `T2`), + # 而清理语句是 `743e397` **之前**写的、只提了当时存在的两个 ⇒ 加了新夹具没加清理。 + # 症状:每跑一次 `--self-check`,`/tmp` 就多留一个 `am-prune-selftest-*`。 + # **一个自检脚本自己不干净,比它测出来的红更值得先修。** + # 判据:跑完 `--self-check` 后按前缀数目录,必须**一个都不剩**(见下面的自检后置)。 + rm -rf "$T" "$B" "$T2" + # 后置:按**集合差**确证本次的夹具真被清掉(不是"我以为清了")。 + # 只关心"本次新出现的",所以上次的残留不会把它打成假红; + # 这条判据正好能抓住"新夹具忘了加进清理列表"—— 那正是 `$T2` 的病史。 + _FIX_AFTER="$(find "${TMPDIR:-/tmp}" -maxdepth 1 -name 'am-prune-selftest-*' 2>/dev/null | sort)" + _FIX_LEFT="$(comm -13 <(printf '%s\n' "$_FIX_BEFORE") <(printf '%s\n' "$_FIX_AFTER") | grep -c . || true)" + ck "自检夹具清干净(本次新建的残留 = $_FIX_LEFT 个,须为 0)" "$([ "$_FIX_LEFT" = 0 ] && echo 1 || echo 0)" + if [ "$_FIX_LEFT" != 0 ]; then + printf '%s\n' "$_FIX_AFTER" | comm -13 <(printf '%s\n' "$_FIX_BEFORE") - | sed 's/^/ 残留: /' >&2 + fi echo if [ "$fails" != 0 ]; then echo " 判据自检失败 $fails/$total 项 —— 清理器本身不可信,不要用它删东西" >&2