diff --git a/example/commondev/go.mod b/example/commondev/go.mod index 3f3124a..9609d78 100644 --- a/example/commondev/go.mod +++ b/example/commondev/go.mod @@ -1,65 +1,7 @@ module commondev -go 1.21.0 +go 1.25.0 -require github.com/JianFeeeee/homeagentsdk v1.4.0 +require github.com/JianFeeeee/homeagentsdk v0.0.0 -// 与同目录其它示例一致:SDK 指向仓库内的 vendored 副本 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -replace github.com/JianFeeeee/homeagentsdk => /root/.homeagent/hmapdev/sdk/v1.4.0 +replace github.com/JianFeeeee/homeagentsdk => ../../ diff --git a/example/commondev/plg.json b/example/commondev/plg.json index 87fe14b..ba89032 100644 --- a/example/commondev/plg.json +++ b/example/commondev/plg.json @@ -6,8 +6,7 @@ "description": "主管式多仓库开发流水线:登记项目、开工程对话、为每条线拉起内置 pi 并注册一对通道,投递任务给 pi 并在同一会话内续投。", "author": "HomeAgent", "entry": "plugin.so", - "sdk": "1.4.0", - "tags": ["commondev", "git", "agent", "develop"], +"tags": ["commondev", "git", "agent", "develop"], "targets": "linux/amd64", "rawfiles": [] } diff --git a/example/editdoc/go.mod b/example/editdoc/go.mod index 21dc80e..a6f704c 100644 --- a/example/editdoc/go.mod +++ b/example/editdoc/go.mod @@ -2,18 +2,6 @@ module editdoc go 1.25.0 -require github.com/JianFeeeee/homeagentsdk v1.4.0 +require github.com/JianFeeeee/homeagentsdk v0.0.0 - - - - - - - - - - - - -replace github.com/JianFeeeee/homeagentsdk => /root/.homeagent/hmapdev/sdk/v1.4.0 +replace github.com/JianFeeeee/homeagentsdk => ../../ diff --git a/example/editdoc/plg.json b/example/editdoc/plg.json index 3121bbb..fc83d7f 100644 --- a/example/editdoc/plg.json +++ b/example/editdoc/plg.json @@ -18,6 +18,5 @@ "outdir": "dist", "bundle": true, "replaces": {}, - "source_dirs": [], - "sdk": "1.4.0" + "source_dirs": [] } \ No newline at end of file diff --git a/tools/hmapdev/example_gomod_test.go b/tools/hmapdev/example_gomod_test.go new file mode 100644 index 0000000..3f78eff --- /dev/null +++ b/tools/hmapdev/example_gomod_test.go @@ -0,0 +1,129 @@ +package main + +// 示例插件的 go.mod 判据(2026-10-17 CI 事故)。 +// +// ## 事故 +// +// 我在一个提交里用 `git add -A` 把本机 `go build` **自动改写**过的 +// go.mod 一并提交了: +// +// require github.com/JianFeeeee/homeagentsdk v1.4.0 +// replace github.com/JianFeeeee/homeagentsdk => /root/.homeagent/hmapdev/sdk/v1.4.0 +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 本机绝对路径 +// +// 后果:CI(干净环境)上 commondev / editdoc 构建**必然失败** +// +// error: 项目声明 sdk=1.4.0,但本地 SDK 存储里没有这个版本 +// +// 而本机能过(那个目录真的存在)⇒ 本地全绿掩盖了问题,PR 卡在 CI 才暴露。 +// +// ## 判据 +// +// 1. 任何示例的 go.mod 都**不得**含本机绝对路径 +// 2. replace 必须是相对路径(`../../`),这是 SDK 仓内示例的约定 +// 3. plg.json 不得声明 `sdk` 版本 —— 一声明就要求本地 SDK store 有该版本, +// 而示例的正确依赖表达是 go.mod 的 v0.0.0 + replace ../../ +// +// 这三条合起来保证「CI 干净环境能构建全部示例」。 + +import ( + "os" + "path/filepath" + "regexp" + "strings" + "testing" +) + +// ★★★ 判据:示例 go.mod 不得含本机绝对路径。 +func TestExampleGoModHasNoAbsoluteReplace(t *testing.T) { + for _, gm := range findExampleGoMods(t) { + b, err := os.ReadFile(gm) + if err != nil { + t.Fatal(err) + } + src := string(b) + for _, bad := range []string{"/root/", "/home/", "/Users/", "C:\\"} { + if strings.Contains(src, bad) { + rel := mustRel(t, gm) + t.Fatalf("★ %s 含本机绝对路径 %q ——\n"+ + " CI(干净环境)会因此构建失败,而本机因为有那个目录反而通过。\n"+ + " 示例必须用相对路径:replace ... => ../../", rel, bad) + } + } + } +} + +// ★★★ 判据:示例的 replace 指向 SDK 仓根(相对路径)。 +func TestExampleGoModUsesRelativeSDKReplace(t *testing.T) { + reReplace := regexp.MustCompile(`(?m)^replace\s+\S*homeagentsdk\S*\s*=>\s*(\S+)`) + for _, gm := range findExampleGoMods(t) { + b, _ := os.ReadFile(gm) + m := reReplace.FindStringSubmatch(string(b)) + if m == nil { + continue // 没有 replace 也算合法(某些示例可能不用) + } + tgt := m[1] + if filepath.IsAbs(tgt) { + t.Fatalf("★ %s 的 replace 是绝对路径 %q(必须相对)", mustRel(t, gm), tgt) + } + if tgt != "../../" { + t.Errorf("%s 的 replace 是 %q,本仓约定为 ../../(与 ai_image/calendar 等一致)", + mustRel(t, gm), tgt) + } + } +} + +// ★★★ 判据:示例 plg.json 不得声明 sdk 版本。 +// +// 为什么:一声明 `"sdk": "1.4.0"`,hmapdev 就会去**本地 SDK store** 找该版本, +// 找不到直接拒绝构建。而示例的正确依赖表达是 go.mod 的 v0.0.0 + replace ../../。 +// 全仓 20+ 个示例都不声明,只有被我改过的两个声明了 ⇒ CI 只在这两个上红。 +func TestExamplePlgJsonDoesNotPinSDKVersion(t *testing.T) { + root := repoRootForTest(t) + matches, _ := filepath.Glob(filepath.Join(root, "example", "*", "plg.json")) + reSDK := regexp.MustCompile(`"sdk"\s*:`) + for _, p := range matches { + b, err := os.ReadFile(p) + if err != nil { + continue + } + if reSDK.Match(b) { + rel := mustRel(t, p) + t.Fatalf("★ %s 声明了 sdk 版本 ——\n"+ + " 这会让 hmapdev 去本地 SDK store 找该版本,干净 CI 上必然失败。\n"+ + " 示例请用 go.mod 的 `require ... v0.0.0` + `replace ... => ../../`", rel) + } + } +} + +func findExampleGoMods(t *testing.T) []string { + t.Helper() + root := repoRootForTest(t) + matches, err := filepath.Glob(filepath.Join(root, "example", "*", "go.mod")) + if err != nil { + t.Fatal(err) + } + if len(matches) == 0 { + t.Fatal("没找到任何示例 go.mod ⇒ 判据会假绿,路径写错了?") + } + return matches +} + +func repoRootForTest(t *testing.T) string { + t.Helper() + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + // tools/hmapdev → 上两级 + return filepath.Dir(filepath.Dir(wd)) +} + +func mustRel(t *testing.T, p string) string { + t.Helper() + r, err := filepath.Rel(repoRootForTest(t), p) + if err != nil { + return p + } + return r +}