fix(判据/部署): 探针不再假设清单穷尽;broken≠red;到期报文自带"要放行什么";部署加 --check 干跑

pi 2026-09-14(两封)提的六条,能做的都做了。

1. **`[Empty]` 不是"没有设备",是第三态**(pi §1)。改了,而且不是改成"一律红",
   是**去证服务端健康**:探针现在读 `/proc/<pid>/cmdline` 找 `hdc -m`(server 模式),
   服务端在 → 空集才是可判的"确实没有目标"(false);服务端找不到 → `unknown`(红)。
   本机实测:`hdc -m -s ::ffff:127.0.0.1:8710` 在跑 → 空集可信。
   这条用机制而不是用嘴回答"我检查过了"。

2. **硬编码候选清单**(pi §2):候选来源写清(`/opt/huawei/command-line-tools` 是文档安装根),
   `hdc` 也走 PATH;**工具链根在、里面却没有 hdc → `unknown`**("装了但长得不一样"不是"没装")。
   这与 build.sh 那次"第一个命中就算"是同一形状 —— 今天各咬一次。

3. **前提改成"本工作区能装能点"**(pi §2):原前提"设备存在"会让 5 条判据在**我修不了**的时候同时红
   (模拟器要写 /run、/var/log)。现在前提是工作区能力,`need` 逐条写清,
   并且**到期报文会把这些门槛打出来** —— 第一次真红不能被当成噪音消掉。

4. **broken ≠ red**(pi §3):跑不起来(语言级崩痕:SyntaxError/ReferenceError/…)单列
   "跑不起来的判据(N)—— 不是红,也不算过",红是"判据说不成立",broken 是"判据没说话"。
   变异验证:注入未定义标识符 → 报 broken ✓;绿基线 → exit 0 ✓。
   第一版我用"输出里有没有 `not ok`"判,当场误判(node:test 把导入期 ReferenceError 也报成 `not ok`),
   已改成语言级崩痕 —— 判据自己的第一版就得被现实修一次。

5. **标签要有消费点**(pi §4):`release-linux.sh --release` 遇脏树**拒绝**(`--allow-dirty` 才放行),
   不加参数是自用打包(只出声)。"出声≠拒绝"这条说得对。

6. **`deploy/install.sh --check` 干跑**(pi §6):跑全部门禁、不写系统目录,末尾列出正式安装会写什么、
   需要哪里的权限。干跑立刻抓到两个真缺陷:
   · `set -u` 下 `$HOME` 未设 → `HOME: unbound variable`(cron/env -i/某些 sudo 下就是没有),
     而它发生在**所有门禁跑完之后**——最贵的位置(这轮第三次同形状,前两次在 homeagent build.sh)。
   · **packaging 这条门在部署路径上永远过不去**:install.sh 先 `npm test`(含 packaging),
     而 packaging 要求"安装包里的 dist == 当前 dist",部署路径却不重新打包 →
     前端一改,install.sh 就卡在这条门上(第二条"挂在部署路径上却恒红"的门,第一条是 check-shared-libs)。
     这条需要决定:部署路径要么重新打包、要么把 packaging 排除在部署门禁外。**我没有擅自改口径。**
This commit is contained in:
2026-09-14 16:49:08 +08:00
parent 4f0a6e6097
commit f27ad31c91
3 changed files with 187 additions and 18 deletions

View File

@ -12,10 +12,43 @@ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PREFIX=/opt/agentmail
ETC=/etc/agentmail
[[ $EUID -eq 0 ]] || { echo "需要 rootsudo $0" >&2; exit 1; }
#
# ./deploy/install.sh --check # 干跑:只跑所有门禁与前置检查,**不写系统目录**
#
# 为什么要有 --checkpi 2026-09-14发布路径是**最少被跑的门** ——
# `check-shared-libs.sh` 一直红着没人发现,正是因为它只挂在 install.sh 里。
# 而"最后一个能跑这一步的人"(有 root 的那个人)每次都在**改完一堆东西之后**才第一次跑它,
# 于是第一次失败就发生在最贵的时候。干跑把"门自己跑得动吗"提前暴露出来。
CHECK_ONLY=0
for a in "$@"; do
case "$a" in
--check) CHECK_ONLY=1 ;;
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
*) echo "未知参数:$a(支持 --check" >&2; exit 2 ;;
esac
done
if [[ $CHECK_ONLY -eq 0 ]]; then
[[ $EUID -eq 0 ]] || { echo "需要 rootsudo $0(只验证不安装用 ./deploy/install.sh --check" >&2; exit 1; }
else
echo "==> 干跑模式(--check不会写 $PREFIX$ETC"
# 写权限是这一步唯一的硬门槛,先说清楚 —— 下一个拿到权限的人要一眼看到需要什么
for d in "$PREFIX" "$ETC"; do
if [[ -e "$d" && ! -w "$d" ]]; then
echo " 注意:$d 存在但当前用户不可写(正式安装需要 root/sudo 或先放行该目录)"
fi
done
fi
echo "==> 构建前端"
( cd "$REPO/client/electron" && npm ci --no-audit --no-fund 2>/dev/null || npm install --no-audit --no-fund )
# 干跑不装依赖(装依赖要写 node_modules/缓存,干跑的意义是"门能不能跑",不是"装得上装不上")。
if [[ $CHECK_ONLY -eq 1 ]]; then
[[ -d "$REPO/client/electron/node_modules" ]] || {
echo " 干跑需要一个已装好依赖的工作树client/electron/node_modules 不存在)" >&2; exit 1; }
echo " 跳过 npm ci/install干跑不写依赖用的是当前 node_modules"
else
( cd "$REPO/client/electron" && npm ci --no-audit --no-fund 2>/dev/null || npm install --no-audit --no-fund )
fi
( cd "$REPO/client/electron" && npm run typecheck && npm test && npm run build )
echo "==> 校验插件共用模块同源"
@ -71,11 +104,18 @@ fi
# settings.json 的 extensions 数组会被排除(见 SDK resource-loader.js
# 因此守护进程的 worker 里零扩展、本扩展只作用于交互式会话。
if [[ -d "$PI_SDK" ]]; then
# `set -u` 下 `$HOME` 未设会直接杀掉部署(实测:`HOME: unbound variable`
# 而它发生在所有门禁跑完之后 —— 最贵的位置。cron / env -i / 某些 sudo 配置下
# HOME 就是没有的,所以给一个兜底。同一形状我这轮在 homeagent 的 build.sh 里
# 也修过一次cron 里 HOME 未设 → Go 拒绝工作)——**这是这轮第三次**
# 说明"环境变量在部署路径上不存在"这条要当成常态而不是意外。
PI_HOME="${SUDO_USER:+$(getent passwd "$SUDO_USER" | cut -d: -f6)}"
PI_HOME="${PI_HOME:-$HOME}"
PI_HOME="${PI_HOME:-${HOME:-/root}}"
PI_SETTINGS="$PI_HOME/.pi/agent/settings.json"
EXT_PATH="$REPO/plugins/pi-mail-bridge/extension/index.ts"
if [[ -d "$PI_HOME/.pi/agent" || -f "$PI_SETTINGS" ]]; then
if [[ $CHECK_ONLY -eq 1 ]]; then
echo " [干跑] 跳过:把邮件工具扩展注册进 $PI_SETTINGS(要写工作区外的 home 目录)"
elif [[ -d "$PI_HOME/.pi/agent" || -f "$PI_SETTINGS" ]]; then
install -d "$(dirname "$PI_SETTINGS")"
node -e '
const fs = require("fs");
@ -94,6 +134,25 @@ if [[ -d "$PI_SDK" ]]; then
fi
fi
if [[ $CHECK_ONLY -eq 1 ]]; then
cat <<'EOF'
==> 干跑结束:**所有会红的门禁都跑过了**(前端 typecheck/test/build、共用模块同源、
各插件测试、插件构建)。干跑的成功只能说明"门是好的",不能说明"装得上"。
下面这些步骤干跑**没有执行**(它们要写工作区外的目录,正式安装需要 root 或先放行):
· 前端产物 → server/internal/static/static/
· 构建 Gatewaygo vet + go test + go build -o server/agentmail-gateway
· install -m 0755 server/agentmail-gateway → /opt/agentmail/agentmail-gateway
· 生成 /etc/agentmail/*.env已存在的不覆盖+ chmod 0600
· 装 systemd unit → /etc/systemd/system/daemon-reload + enable --now
· 把邮件工具扩展注册进 ~/.pi/agent/settings.json
拿不准就先跑 ./deploy/install.sh --check正式安装用 sudo ./deploy/install.sh。
EOF
exit 0
fi
echo "==> 前端产物嵌入 Gateway"
# 只清构建产物,不能 rm -rf 整个目录:
# placeholder.html 在版本库里(让 go:embed 在新克隆里能编译),