From b5644829d3ed1abb81424ebcd5eac396118ea5c6 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 17:18:45 +0100 Subject: [PATCH] F-catalog-B: the catalogue's Go is as gofmt writes it, and checks/gofmt holds it so buildstep's generated.go and generated_test.go and claude-code's config_test.go reached the join unformatted: the second of a kind, which no check caught since each module's tests hold only themselves. All three are formatted, and checks/gofmt refuses a Go file of the catalogue gofmt would change. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- checks/gofmt/go.mod | 3 + checks/gofmt/gofmt.go | 58 +++++++++++++++++++ checks/gofmt/gofmt_test.go | 53 +++++++++++++++++ .../cmd/claude-code/config_test.go | 2 +- modules/mesh-build-go/buildstep/generated.go | 2 +- .../mesh-build-go/buildstep/generated_test.go | 2 +- 6 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 checks/gofmt/go.mod create mode 100644 checks/gofmt/gofmt.go create mode 100644 checks/gofmt/gofmt_test.go diff --git a/checks/gofmt/go.mod b/checks/gofmt/go.mod new file mode 100644 index 000000000..5d9098aa8 --- /dev/null +++ b/checks/gofmt/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-catalog/checks/gofmt + +go 1.22 diff --git a/checks/gofmt/gofmt.go b/checks/gofmt/gofmt.go new file mode 100644 index 000000000..275adb24a --- /dev/null +++ b/checks/gofmt/gofmt.go @@ -0,0 +1,58 @@ +// Package gofmt refuses a Go file of the catalogue that gofmt would change (the second of a kind in one milestone: +// buildstep's generated.go and claude-code's config_test.go reached a join unformatted). A check here holds every +// module to it, where a module's own tests hold only themselves. +package gofmt + +import ( + "bytes" + "go/format" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" +) + +// Files are the Go files under root, relative to it: every .go file but those in .git, node_modules and a testdata +// directory (a test's fixture may be anything). +func Files(root string) ([]string, error) { + var out []string + err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + switch d.Name() { + case ".git", "node_modules", "testdata": + return filepath.SkipDir + } + return nil + } + if strings.HasSuffix(d.Name(), ".go") && d.Type().IsRegular() { + rel, _ := filepath.Rel(root, p) + out = append(out, filepath.ToSlash(rel)) + } + return nil + }) + sort.Strings(out) + return out, err +} + +// Unformatted are the files gofmt would change, and why for one that does not parse. +func Unformatted(root string, files []string) (map[string]string, error) { + out := map[string]string{} + for _, f := range files { + src, err := os.ReadFile(filepath.Join(root, f)) + if err != nil { + return nil, err + } + got, err := format.Source(src) + switch { + case err != nil: + out[f] = "it does not parse: " + err.Error() + case !bytes.Equal(got, src): + out[f] = "gofmt changes it: run gofmt -w " + f + } + } + return out, nil +} diff --git a/checks/gofmt/gofmt_test.go b/checks/gofmt/gofmt_test.go new file mode 100644 index 000000000..89470ab76 --- /dev/null +++ b/checks/gofmt/gofmt_test.go @@ -0,0 +1,53 @@ +package gofmt + +import ( + "os" + "path/filepath" + "testing" +) + +func TestUnformattedNamesWhatGofmtChanges(t *testing.T) { + root := t.TempDir() + for f, src := range map[string]string{ + "modules/a/a.go": "package a\n", + "modules/b/b.go": "package b\nimport (\n\t\"os\"\n\t\"fmt\"\n)\nvar _, _ = fmt.Sprint, os.Exit\n", + "modules/c/testdata/c.go": "package c", + "modules/d/node_modules/d.go": "package d", + } { + p := filepath.Join(root, f) + _ = os.MkdirAll(filepath.Dir(p), 0o755) + if err := os.WriteFile(p, []byte(src), 0o644); err != nil { + t.Fatal(err) + } + } + files, err := Files(root) + if err != nil { + t.Fatal(err) + } + got, err := Unformatted(root, files) + if err != nil { + t.Fatal(err) + } + if len(files) != 2 || len(got) != 1 || got["modules/b/b.go"] == "" { + t.Errorf("files %v, unformatted %v; want a.go and b.go read, b.go named", files, got) + } +} + +// The catalogue's own Go files are as gofmt writes them. +func TestTheCataloguesGoIsFormatted(t *testing.T) { + root := filepath.Join("..", "..") + files, err := Files(root) + if err != nil { + t.Fatal(err) + } + if len(files) == 0 { + t.Fatal("no Go file found: the check reads nothing") + } + got, err := Unformatted(root, files) + if err != nil { + t.Fatal(err) + } + for f, why := range got { + t.Errorf("%s: %s", f, why) + } +} diff --git a/modules/claude-code/cmd/claude-code/config_test.go b/modules/claude-code/cmd/claude-code/config_test.go index f443507ea..3c219d049 100644 --- a/modules/claude-code/cmd/claude-code/config_test.go +++ b/modules/claude-code/cmd/claude-code/config_test.go @@ -269,7 +269,7 @@ func TestTheManifestListsEveryToolServed(t *testing.T) { t.Fatal(err) } var m struct { - Tools []string `json:"tools"` + Tools []string `json:"tools"` Invokes []string `json:"invokes"` Reads []string `json:"reads"` Consumes []string `json:"consumes"` diff --git a/modules/mesh-build-go/buildstep/generated.go b/modules/mesh-build-go/buildstep/generated.go index 488dc1ec9..c164951de 100644 --- a/modules/mesh-build-go/buildstep/generated.go +++ b/modules/mesh-build-go/buildstep/generated.go @@ -20,8 +20,8 @@ import ( "sort" "strings" - "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/seatgen" + "git.novox.be/novox/mesh-sdk/go/catalogue" ) // GeneratedRootGo and GeneratedRootTS are the generated roots of a contracts module, one per build module. diff --git a/modules/mesh-build-go/buildstep/generated_test.go b/modules/mesh-build-go/buildstep/generated_test.go index 369466e32..bce50b660 100644 --- a/modules/mesh-build-go/buildstep/generated_test.go +++ b/modules/mesh-build-go/buildstep/generated_test.go @@ -10,8 +10,8 @@ import ( "strings" "testing" - "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/seatgen" + "git.novox.be/novox/mesh-sdk/go/delivery" ) // contracts writes a contracts module of one seat, its generated files as the generator writes them, and answers its