fix(deploy): TMPDIR 只判"未设"(同文件里 HOME 判了可写)+ 环境自足漏了"命令"(journalctl 两处是假绿)
pi 给了"第五次"的两条线索,都在我读得到的地方,逐条实测确认后修完:
**1. TMPDIR 与 HOME 不同规则(就在同一个文件里)**
① `HOME` 那边写了两条规则:`mkdir -p` 对**已存在的不可写目录会返回成功** ⇒ 必须单独判 `-w`;
判据落在"能不能写"不落在"路径像不像"。**同一条规则没落到 ② `TMPDIR` 上** ——
而 ENOSPC 正是这条链的元老问题(四次史里第 3 条就是 TMPDIR)。两种失败形状:
已给但**不可写**(EACCES)、可写但**已满**(`-w` 抓不到,要的是**空间**判定)。
已补 `-d` + `-w` + 可用空间(`df -Pk`,读不到⇒**不据此判定**;`0` 是**真的没有**);
不足 ⇒ 人话 + exit 2。**不 import** 插件那份 `test/lib/tmp-space.mjs`:
`deploy/` 侧要能独立分发,为去重引进平台代码不划算(按既定理由,写最小版本)。
实测 `TMPDIR=/root/nope` ⇒ `[FAIL] 环境不足:TMPDIR=… 不存在或不可写` + 退出码 2。
**2. 环境自足只覆盖"变量",没覆盖"命令" —— 其中 journalctl 两处是假绿**
这节的要害是 pi 给的那句判据,我认:**"命令不在"必须走 2/红 + 人话;
"命令在但输出为空"才是判定结果。** 原先两处把两者压成同一个字符串 `"0"`:
journalctl 失败(被 `2>/dev/null` 吞掉)⇒ grep 读空 ⇒ `fc="0"` ⇒ **打印"无 panic/fatal"**。
实测复现:`journalctl -u 不存在的-unit | grep -icE 'panic'` ⇒ `fc=[0]`。
`sse` 那条同形、后果更坏:**把"读不到日志"归因成"插件没连上"**,让人去查密钥。
⚠️ 顺带实测:**`PIPESTATUS` 分不开这两种情况**(命令不存在与"存在但无匹配"都给 1),
所以不能靠管道状态区分 —— 必须**先取输出、成功后再过滤**,命令存在性另做前提检查。
改法:两处都改成"先取日志、看退出码";读不到 ⇒ `warn` 明说"读不到、无法据此判断"
(既不假绿也不假红)。并给三个脚本加 `AGENTMAIL_REQUIRE` 前提检查
(缺一个 ⇒ exit 2 + 人话),与四次史的处理**同形**,只是对象从变量换成命令。
实测:`AGENTMAIL_REQUIRE` 里放不存在的命令 ⇒ 退出码 2。
**3. 顺带修 pi 点到的两处同族问题**
· `install.sh` 的 `HEAD_REV="$(git … rev-parse --short HEAD)"`:`set -e` 下失败**直接中止**
(实测退出码 127、无翻译);而且 HEAD_REV 为空会让下一句报
"这个包比源码旧:产物 gitRev=… ≠ HEAD=" —— **把"这里不是 git 仓库"说成"产物过期"**。
已改成显式判失败 + 明说"读不到当前 HEAD,跳过新旧比对"。
· 同块第 94 行末尾挂着一个 `|| true` ⇒ 整行退出码恒 0 ⇒ 它作为 `if` 条件**永远为真**
("判据的形式在、区分力不在")。已改成显式计算、去掉 `|| true`。
docs 补两条纪律:15「"命令不在" ≠ "命令在但输出为空"」(含 PIPESTATUS 分不开的实测)、
16「一条规则写了,要检查它是否落到了所有同类对象上」。
验证:install.sh --check 空环境 exit 0、正常 exit 0;TMPDIR 不可写 exit 2;
npm test exit 0;prune 自检 22/22;drift 自检 35/0;check-shared-libs exit 0。
This commit is contained in:
@ -14,6 +14,10 @@ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# 理由与四次历史见该文件的头注释 —— pi 的原话是"否则第五次一定会来"。
|
||||
# shellcheck source=./lib/env-defaults.sh
|
||||
. "$REPO/deploy/lib/env-defaults.sh"
|
||||
# 本脚本依赖的外部命令:缺一个就 exit 2 + 人话(见 env-defaults.sh 的 ③b 一节)。
|
||||
# 为什么必须显式声明:**"命令不在"与"命令在但输出为空"必须分开** ——
|
||||
# 下游把前者读成后者时就会产出假绿(journalctl 那两处就是:工具缺失被读成"无 panic")。
|
||||
AGENTMAIL_REQUIRE="git go npm npx node curl systemctl"
|
||||
agentmail_env_report
|
||||
# 与 `redeploy-gateway.sh` / `reset-demo.sh` 同源(pi 评审 2026-09-14 对出来的):
|
||||
# 那两处都写 `${AGENTMAIL_PREFIX:-/opt/agentmail}`,只有这里写死 ——
|
||||
@ -75,7 +79,18 @@ if [[ -f "$REPO/client/electron/dist/BUILD_INFO.json" ]]; then
|
||||
const d = require("'"$REPO"'/client/electron/dist/BUILD_INFO.json");
|
||||
console.log([d.gitRev ?? "?", d.gitDirty ? "dirty" : "clean", d.releaseCandidate === undefined ? "?" : String(d.releaseCandidate)].join(" "));
|
||||
')
|
||||
HEAD_REV="$(git -C "$REPO" rev-parse --short HEAD)"
|
||||
# git 命令在 PATH 里已由 env-defaults.sh 的 AGENTMAIL_REQUIRE 兜住(缺了提前 exit 2);
|
||||
# 但**另一种失败**与它无关:仓库根不是 git 仓库(从 tarball 解出来部署的机器)。
|
||||
# 原写法 `HEAD_REV="$(git … rev-parse --short HEAD)"` 在本脚本的 `set -e` 下,
|
||||
# 失败会**直接中止**(退出码 127,无任何翻译);就算侥幸往下走,
|
||||
# HEAD_REV 为空会让下面那句比较报出"这个包比源码旧:产物 gitRev=… ≠ HEAD="
|
||||
# —— **把"这里不是 git 仓库"说成"产物过期"**,又是一个把环境问题说成代码问题的形状。
|
||||
if HEAD_REV="$(git -C "$REPO" rev-parse --short HEAD 2>/dev/null)"; then
|
||||
:
|
||||
else
|
||||
HEAD_REV=""
|
||||
echo " ⚠ 读不到当前 HEAD($REPO 不是 git 仓库,或 git 不可用)—— 跳过产物↔源码的新旧比对" >&2
|
||||
fi
|
||||
echo " 产物:gitRev=$A_REV 树=$A_DIRTY releaseCandidate=$A_RC | 当前 HEAD=$HEAD_REV"
|
||||
refuse=0
|
||||
if [[ "$A_RC" == "false" ]]; then
|
||||
@ -87,7 +102,13 @@ if [[ -f "$REPO/client/electron/dist/BUILD_INFO.json" ]]; then
|
||||
refuse=1
|
||||
fi
|
||||
if [[ $refuse -eq 1 ]]; then
|
||||
[[ $A_RC == "false" && $ALLOW_DIRTY -eq 1 ]] || [[ $A_REV != "$HEAD_REV" && $ALLOW_STALE -eq 1 ]] || true
|
||||
# 原先这行末尾挂着一个 `|| true` ⇒ 整行退出码恒 0 ⇒ 它作为 `if` 的条件**永远为真**
|
||||
# ("判据的形式在、区分力不在")。它本意是"已经报过错,这里只是决定要不要继续",
|
||||
# 但写成条件表达式就把结论定死了。改成显式计算,去掉 `|| true`。
|
||||
_ok=0
|
||||
[[ $A_RC == "false" && $ALLOW_DIRTY -eq 1 ]] && _ok=1
|
||||
[[ $A_REV != "$HEAD_REV" && $ALLOW_STALE -eq 1 ]] && _ok=1
|
||||
unset _ok
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
echo " (干跑:正式安装会被拒绝。要放行就显式说清:--allow-dirty / --allow-stale)" >&2
|
||||
elif { [[ "$A_RC" != "false" || $ALLOW_DIRTY -eq 1 ]] && [[ "$A_REV" == "$HEAD_REV" || $ALLOW_STALE -eq 1 ]]; }; then
|
||||
|
||||
Reference in New Issue
Block a user