Files
MailUI4Agents/plugins/homeagent-mail-bridge/manifest_sdk_test.go
JianFeeeee 357662ed08 fix(homeagent): 不再把 SDK 版本钉在源码里;构建期按内核的依赖表校验对齐
崩溃循环的根因不是"忘了重编",是**把 SDK 版本钉死**:

- `plugin.json`/`plg.json` 写死 `"sdk": "1.3.0"`(本机已装 21 个插件里只有 3 个声明该字段);
- `go.mod` 的 replace 指向 `/root/.homeagent/hmapdev/sdk/v1.3.0`(绝对路径 + 具体版本目录)。

于是"照原样重编"只会再造一个装上去就崩的包。实测本机内核链的是**另一条 SDK 血脉**:

    $ go version -m /usr/local/bin/homed
    dep gitcode.com/JianFeeeee/homeagent-sdk v0.9.2
    =>  ./third_party/homeagent-sdk (devel)

所以判据改成**两个二进制的依赖表一致**,而不是"版本号看起来像":

- `build.sh`:读内核的 SDK 依赖(`go version -m`)→ 据此解析 SDK 源码目录 →
  用 `-modfile` 临时替换(不改动受版本管理的 go.mod)→ 构建 → **回头验产物**:
  产物与内核链的 SDK 版本不一致就报错退出,绝不产出"装上去就崩"的包。
- `manifest_sdk_test.go`:① 清单不许写死 `sdk`;② go.mod 的钉子不得偏离构建机指针
  (取不到基准就红,不许默默放过);③ `build.sh` 必须在(它是与内核对齐的硬校验点)。
  三条都做过变异验证(写死 sdk / 钉子漂到 v1.2.0 / 删掉 build.sh → 各自红)。

顺带查出一个比"重编"更根本的事实:本机 `build.sh` 跑到编译就失败 ——

    ./plugin.go:217:18: sett.DataDir undefined (type sdk.SettingsAPI has no field or method DataDir)

即本插件源码用的是 **1.3.0 SDK 的 API**,而本机内核链的是 0.9.x 血脉:**重编解决不了**,
要么内核改成链 1.3.x 的 SDK,要么把插件移植到内核那份 SDK(这是 HomeAgent 侧的决定)。
2026-09-14 16:27:02 +08:00

95 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"os"
"strings"
"testing"
)
/*
* 判据:**插件清单里不许写死 sdk 版本**。
*
* 2026-09-14 的事故与它当天的复发,根因都不是"忘了重编",而是把 SDK 版本钉在源码里:
*
* · 08:09 那次:内核 v1.3.12,插件二进制还是 sdk=1.2.0 → 每次启动 SIGSEGV
* 内核 5 分钟内崩溃 4 次后熔断("停止自动重启,需人工介入"
* · 当天稍晚复发:内核升到 1.4.0,而 `go.mod` 里 replace 指向
* `/root/.homeagent/hmapdev/sdk/v1.3.0` —— **「照原样重编」只会再造一个 1.3.0**。
*
* 写死的那份版本号,和「内核实际用的版本」没有任何机制保证一致,
* 而失配的后果是启动即崩、且要人介入才能恢复。所以:
*
* 1. `plugin.json` / `plg.json` 不得出现 `sdk` 字段 —— 它必须由构建工具
* 按**本次实际编译用的 SDK** 写入(见 `build.sh`:解析本机 current 指针 + 构建后校验);
* 2. `go.mod` 的 `replace` 不得指向带版本号的绝对路径(同样是与内核脱钩的钉子)。
*
* 这两条都是**来源**判据(验形态,不验行为):真正的一致性校验在 `build.sh` 里,
* 它拿内核内嵌的版本号跟本次构建用的比,不一致就拒绝出包。
*/
func TestManifestMustNotPinSDKVersion(t *testing.T) {
for _, name := range []string{"plugin.json", "plg.json"} {
raw, err := os.ReadFile(name)
if err != nil {
t.Fatalf("读不到 %s: %v", name, err)
}
var m map[string]any
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatalf("%s 不是合法 JSON: %v", name, err)
}
if v, ok := m["sdk"]; ok {
t.Errorf("%s 里写死了 sdk=%v —— 这个字段要由构建工具按实际编译用的 SDK 写入,"+
"手写等于把插件钉在某个内核版本上:内核一升就握手失败、启动即崩、"+
"而且重编也修不好2026-09-14 的事故与同日复发都是这个形状)。"+
"删掉它,用 build.sh 构建(它会解析本机 SDK 并与内核版本比对)。", name, v)
}
if _, ok := m["version"]; !ok {
t.Errorf("%s 缺少 version 字段", name)
}
}
}
func TestSDKPinMatchesBuildMachinePointer(t *testing.T) {
raw, err := os.ReadFile("go.mod")
if err != nil {
t.Fatalf("读不到 go.mod: %v", err)
}
pin := ""
for _, line := range strings.Split(string(raw), "\n") {
if strings.Contains(line, "replace") && strings.Contains(line, "homeagent-sdk") {
parts := strings.Split(line, "=>")
if len(parts) == 2 {
pin = strings.TrimSpace(parts[1])
}
}
}
if pin == "" {
t.Fatal("go.mod 里没有指向本机 SDK 的 replace —— 离线环境下 go test/go build 会直接失败," +
"请恢复它(路径取 <SDK_ROOT>/current 指向的目录)")
}
// 基准:构建机的 SDK 指针。取不到就是"判据读不懂",不许默默放过。
root := os.Getenv("HOMEAGENT_SDK_ROOT")
if root == "" {
root = "/root/.homeagent/hmapdev/sdk"
}
curRaw, err := os.ReadFile(root + "/current")
if err != nil {
t.Fatalf("取不到基准:读不到 %s/current%v。"+
"这条判据判的是「钉子有没有偏离构建机的 SDK 指针」,没有基准就无法判断 —— "+
"用 HOMEAGENT_SDK_ROOT 指定 SDK 根目录。", root, err)
}
cur := strings.TrimSpace(string(curRaw))
if !strings.HasSuffix(pin, "/"+cur) {
t.Errorf("go.mod 的 SDK 钉子偏离了构建机指针:\n 钉子: %s\n 指针: %s/%s\n"+
"后果不是「编译不过」,是**照原样重编会再造一个旧版本插件**"+
"内核升级后装上去启动即崩、崩溃循环、要人介入2026-09-14 的形状)。\n"+
"改法:把钉子改成指针指向的目录;内核与 SDK 是否真的对齐由 build.sh 硬校验。",
pin, root, cur)
}
if _, err := os.Stat("build.sh"); err != nil {
t.Error("找不到 build.sh —— 它是「与内核版本一致性」的硬校验点。" +
"只有钉子、没有构建期校验的话「current 与内核不一致」这种状态就没人抓了。")
}
}