mesh-build-go: the generated-files compare finds a contracts module in any folder; a folder module's Go packages each publish under their own module path (ADR 0634 §8; issue 1966)

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-05 13:23:00 +01:00
parent 1abae72457
commit 2b2b3e4b8c
4 changed files with 124 additions and 8 deletions
@@ -122,6 +122,30 @@ func generatedNames(dir string) (goModule, tsPackage string, err error) {
return goModule, tsPackage, nil
}
// ModuleDirs is every directory in tree that holds a module.json, in any folder (ADR 0634 §8: a module is found by its
// manifest, whether it lies at the root, under modules/ or in a folder of another module), the tree's own first, then
// sorted. Hidden directories, node_modules and testdata are not entered.
func ModuleDirs(tree string) ([]string, error) {
var out []string
err := filepath.WalkDir(tree, func(p string, e os.DirEntry, err error) error {
if err != nil {
return err
}
if e.IsDir() {
if n := e.Name(); p != tree && (strings.HasPrefix(n, ".") || n == "node_modules" || n == "testdata") {
return filepath.SkipDir
}
return nil
}
if e.Name() == "module.json" {
out = append(out, filepath.Dir(p))
}
return nil
})
sort.Strings(out)
return out, err
}
// CompareStep is AskSteps.Compare for a build module comparing root: it reads {"workspace", "module"}, the workspace's
// tree where tree says it is (the build module's own layout), and answers {"out-of-step": [...]}.
func CompareStep(root string, tree func(workspace string) string) StepFunc {
@@ -231,6 +231,66 @@ func TestAGoPackageIsBuiltAsAModuleZipAndPublishedUnderItsModulePath(t *testing.
}
}
// K-2 (ADR 0634 §8): a module in a folder of the repository, the folder holding more than one Go package, builds and
// publishes each as a root module's would: each artifact its own zip of its own directory under its own module path,
// the sibling package's directory (a Go module of its own) left out of the zip of the folder's.
func TestEachGoPackageOfAModuleInAFolderIsPublishedUnderItsOwnModulePath(t *testing.T) {
manifest := manifestWith(`{"name":"go-aone","kind":"package","language":"go","paths":["go/seats/aone"],"series":"0.1"},
{"name":"go-btwo","kind":"package","language":"go","paths":["go/seats/btwo"],"series":"0.1"},
{"name":"go-whole","kind":"package","language":"go","paths":["go"],"series":"0.1"}`)
m, states, _ := buildModule(t, manifest, "unused")
src := filepath.Join(m.Work, "ws1", "tree", "domain-a", "contracts")
for name, body := range map[string]string{
"module.json": manifest,
"go/seats/aone/go.mod": "module git.example/c/domain-a/go/seats/aone\n\ngo 1.26\n", "go/seats/aone/a.go": "package aone\n",
"go/seats/btwo/go.mod": "module git.example/c/domain-a/go/seats/btwo\n\ngo 1.26\n", "go/seats/btwo/b.go": "package btwo\n",
"go/go.mod": "module git.example/c/domain-a/go\n\ngo 1.26\n", "go/w.go": "package w\n"} {
if err := os.MkdirAll(filepath.Dir(filepath.Join(src, name)), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
t.Fatal(err)
}
}
store := withStore(t, m)
for _, c := range []struct{ artifact, ref string }{
{"go-aone", "git.example/c/domain-a/go/seats/aone@v0.1.3"},
{"go-btwo", "git.example/c/domain-a/go/seats/btwo@v0.1.3"},
{"go-whole", "git.example/c/domain-a/go@v0.1.3"},
} {
for _, tool := range m.Tools(m.StepTools()) {
if tool.Name == "build_go_build" {
args := map[string]any{"workspace": "ws1", "module": "domain-a/contracts", "artifact": c.artifact, "version": "v0.1.3", "seat": GoPackageSeat}
if _, err := tool.Run(args); err != nil {
t.Fatal(err)
}
}
}
st := states.waitEnd(t)
var res BuildResult
if st.State != delivery.StepPassed {
t.Fatalf("%s: %s %q", c.artifact, st.State, st.Reason)
}
_ = json.Unmarshal(st.Result, &res)
if res.Reference != c.ref {
t.Errorf("%s published as %q, want %q", c.artifact, res.Reference, c.ref)
}
}
if len(store.published) != 3 {
t.Fatalf("published %+v", store.published)
}
in := store.published[2]
z, err := zip.NewReader(bytes.NewReader(store.blobs[in.Name+"@"+in.Digest]), int64(len(store.blobs[in.Name+"@"+in.Digest])))
if err != nil {
t.Fatal(err)
}
for _, f := range z.File {
if strings.Contains(f.Name, "/seats/") {
t.Errorf("the zip of the folder's own package holds %s, a package of its own", f.Name)
}
}
}
// CAT-BG2 (design 51 *The versions*): the builder's version.numbered for a bundle or an archive publishes the held
// digest under its version, sending nothing; a digest the holder no longer holds fails the event (offered again),
// and what is not this module's to publish is let pass.
@@ -109,15 +109,14 @@ func (m *Module) test(ctx context.Context, args map[string]any) (any, error) {
return res, nil
}
// compareGenerated regenerates the generated Go files of each contracts module in the tree (the tree's own manifest and
// each modules/<m>/module.json; CAT-BG5) and fails the level on any out of step, Why naming each path.
// compareGenerated regenerates the generated Go files of each contracts module in the tree (every module.json in the tree, in
// any folder: ADR 0634 §8; CAT-BG5) and fails the level on any out of step, Why naming each path.
func compareGenerated(tree string, res *TestResult) error {
manifests, _ := filepath.Glob(filepath.Join(tree, "modules", "*", "module.json"))
if _, err := os.Stat(filepath.Join(tree, "module.json")); err == nil {
manifests = append([]string{filepath.Join(tree, "module.json")}, manifests...)
dirs, err := buildstep.ModuleDirs(tree)
if err != nil {
return err
}
for _, mf := range manifests {
dir := filepath.Dir(mf)
for _, dir := range dirs {
paths, err := buildstep.OutOfStep(dir, buildstep.GeneratedRootGo)
if err != nil {
return err
@@ -186,7 +186,6 @@ func TestUnitLevelOfAContractsModuleOutOfStepFailsNamingEachPath(t *testing.T) {
tree := filepath.Join(m.Work, ws, "tree")
for p, text := range map[string]string{
"module.json": `{"module":"c","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"generated-from":"."}]}}`,
"seats/index.json": `{"format":1,"seats":["one-store"]}`,
"seats/one-store/seat.json": `{"format":1,"seat":"one-store","scope":"mesh","grain":"seat-list","decision":"ADR 1","verbs":[{"name":"query","input":{"type":"object"}}]}`,
"go/go.mod": "module example.org/c/go\n",
"go/stray.go": "package stray\n",
@@ -212,6 +211,40 @@ func TestUnitLevelOfAContractsModuleOutOfStepFailsNamingEachPath(t *testing.T) {
}
}
// ADR 0634 §8: the same for a contracts module in a folder of a tree whose root holds a module of its own (found by its
// manifest, not by lying under modules/); a contracts module (a package artifact naming generated-from) whose generated Go files are out of step with
// the seat generator fails the unit level, each path named in its out-of-step and its why; its TypeScript is not this
// module's to compare.
func TestUnitLevelOfAContractsModuleInAFolderOutOfStepFailsNamingEachPath(t *testing.T) {
m, states, ws, _ := workspaceFor(t, "empty")
tree := filepath.Join(m.Work, ws, "tree")
for p, text := range map[string]string{
"module.json": `{"module":"c","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"generated-from":"."}]}}`,
"seats/one-store/seat.json": `{"format":1,"seat":"one-store","scope":"mesh","grain":"seat-list","decision":"ADR 1","verbs":[{"name":"query","input":{"type":"object"}}]}`,
"go/go.mod": "module example.org/c/go\n",
"go/stray.go": "package stray\n",
"ts/package.json": `{"name":"@example/c"}`,
"ts/stray.ts": "// not this module's\n",
} {
full := filepath.Join(tree, "domain", filepath.FromSlash(p))
_ = os.MkdirAll(filepath.Dir(full), 0o755)
if err := os.WriteFile(full, []byte(text), 0o644); err != nil {
t.Fatal(err)
}
}
res, st := runUnit(t, m, states, ws)
stray := false
for _, p := range res.OutOfStep {
stray = stray || p == "domain/go/stray.go"
if !strings.HasPrefix(p, "domain/go/") {
t.Errorf("%s is not under the folder's Go root", p)
}
}
if st.State != delivery.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "domain/go/stray.go") {
t.Errorf("result = %+v (%s %s)", res, st.State, st.Reason)
}
}
// A package that does not build fails the level with the compiler's words.
func TestUnitLevelOfAPackageThatDoesNotBuildFailsWithTheCompilersLine(t *testing.T) {
m, states, ws, _ := workspaceFor(t, "broken")