From 537e2ef31f7df04281ff9cdea34826760929fe7e Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 25 Oct 2026 01:29:21 +0200 Subject: [PATCH] mesh-build-go: check with CGO_ENABLED=0 as production builds do, and let the lint's go vet, a compile alone, warm the shared cache for later checks (issue 1921, try speed S11) --- .../cmd/mesh-build-go/cgo_test.go | 40 +++++++++++++++++++ .../mesh-build-go/cmd/mesh-build-go/fetch.go | 1 + .../cmd/mesh-build-go/isolate_test.go | 3 +- .../mesh-build-go/cmd/mesh-build-go/test.go | 10 ++++- 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 modules/mesh-build-go/cmd/mesh-build-go/cgo_test.go diff --git a/modules/mesh-build-go/cmd/mesh-build-go/cgo_test.go b/modules/mesh-build-go/cmd/mesh-build-go/cgo_test.go new file mode 100644 index 00000000..ba9049c2 --- /dev/null +++ b/modules/mesh-build-go/cmd/mesh-build-go/cgo_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "slices" + "strings" + "testing" + + "git.novox.be/novox/mesh-sdk/go/delivery" +) + +// 1921/S11 (the lead's decision): a check compiles and runs with CGO_ENABLED=0, as a production build does, so it tests +// what ships and the warm cache matches; the lint's go vet, a compile alone, writes the shared cache, while a go test that +// runs a test binary reads it beneath the ask's own layer. +func TestACheckRunsWithoutCgoAndOnlyItsCompileWritesTheSharedCache(t *testing.T) { + m, states, ws, calls := workspaceFor(t, "failing") + var seen []shutOff + m.Run = throughBubblewrap(t, m.Run, &seen) + if _, st := runUnit(t, m, states, ws); st.State != delivery.StepPassed { + t.Fatalf("the step ended %s: %s", st.State, st.Reason) + } + for _, c := range *calls { + if strings.HasPrefix(c.line, "go ") && !slices.Contains(c.env, "CGO_ENABLED=0") { + t.Errorf("%s ran without CGO_ENABLED=0: %v", c.line, c.env) + } + } + vets, tests := 0, 0 + for _, s := range seen { + switch { + case strings.Contains(s.command, " vet "): + vets++ + checkShutOff(t, m, s, true) // the shared caches bound read-write + case strings.Contains(s.command, " test "): + tests++ + checkShutOff(t, m, s, false) + } + } + if vets == 0 || tests == 0 { + t.Errorf("%d vets and %d tests ran shut off, want both", vets, tests) + } +} diff --git a/modules/mesh-build-go/cmd/mesh-build-go/fetch.go b/modules/mesh-build-go/cmd/mesh-build-go/fetch.go index 92b64dd5..f803ca2b 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/fetch.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/fetch.go @@ -373,6 +373,7 @@ func (m *Module) goEnv() []string { modcache = m.GoModCache } return []string{ + "CGO_ENABLED=0", // as a production build compiles (build.go), so a check tests what ships (issue 1921, S11) "GOTOOLCHAIN=local", "GOFLAGS=-mod=mod", "GOMODCACHE=" + modcache, diff --git a/modules/mesh-build-go/cmd/mesh-build-go/isolate_test.go b/modules/mesh-build-go/cmd/mesh-build-go/isolate_test.go index efb19e5d..90f91c0b 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/isolate_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/isolate_test.go @@ -140,7 +140,8 @@ func TestTheUnitLevelRunsEveryGoTestShutOff(t *testing.T) { t.Fatalf("%d go commands ran, %d of them shut off: %v", ran, len(seen), *calls) } for _, s := range seen { - checkShutOff(t, m, s, false) + // the lint's go vet, a compile alone, writes the shared cache (issue 1921, S11): cgo_test.go + checkShutOff(t, m, s, strings.Contains(s.command, " vet ")) } } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/test.go b/modules/mesh-build-go/cmd/mesh-build-go/test.go index abc1194f..d2354b59 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/test.go @@ -229,7 +229,15 @@ func (m *Module) runLevel(ctx context.Context, tree string, o levelOpts) (*TestR } }() linted := m.clock() - lintFailed, lintErr = m.lintAll(ctx, run, tree, todo, func(d string) []string { + // go vet compiles and runs nothing of the change, so it warms the shared cache for every later check, a pull + // request's head included (issue 1921, try speed S11; the operator's decision); the tests run beneath the + // ask's own layer. + compile, err := m.stepRunner(o.workspace, true) + if err != nil { + lintErr = err + return + } + lintFailed, lintErr = m.lintAll(ctx, compile, tree, todo, func(d string) []string { return append(m.goEnvFor(filepath.Join(tree, d)), o.env...) }, byDir, slots.acquire) res.Lint = m.clock().Sub(linted).Seconds()