mesh-build-go: let a check's go vet warm the shared Go build cache with cgo off, and say each linted directory as it ends

A check's lint recompiled every package from a cold layer on each ask (g14 linted
mesh-catalog's 8 directories in 141 s where ace, warm, took 17 s), and said nothing
until it ended. ADR 0541 §2 and ADR 0542 §1 decided this; the built speed-up on
feat/try-speed (S11) was parked. Ported here alone: CGO_ENABLED=0 in every check
command's go environment, vet runs with the shared caches writable, and the lint
says a phase per finished directory (issue 1921).
This commit is contained in:
2026-10-29 00:46:19 +01:00
parent 158348ede7
commit 31c860bc92
5 changed files with 41 additions and 3 deletions
@@ -296,6 +296,7 @@ func (m *Module) goEnv() []string {
modcache = m.GoModCache
}
return append([]string{
"CGO_ENABLED=0", // as a production build compiles (build.go), so a check tests what ships and the warm cache matches (ADR 0542 §1)
"GOTOOLCHAIN=local",
"GOFLAGS=-mod=mod",
"GOMODCACHE=" + modcache,
@@ -45,7 +45,7 @@ func (m *Module) isolation(workspace string, write bool) buildstep.Isolation {
}
// stepRunner is the runner of a run step of the workspace: buildstep.Isolated over m.Run, so every command it runs is
// shut off; write is true for a trunk build alone (the shared caches are written by trunk builds only). Inside is the
// shut off; write is true for a trunk build and for a check's go vet, compile steps that run nothing of the change (ADR 0541 §2). Inside is the
// integration level's own process, already running shut off (level.go): it runs its commands as they are.
func (m *Module) stepRunner(workspace string, write bool) (forgekey.Runner, error) {
if m.Inside {
@@ -130,8 +130,23 @@ func TestTheUnitLevelRunsEveryGoTestShutOff(t *testing.T) {
if ran == 0 || len(seen) != ran {
t.Fatalf("%d go commands ran, %d of them shut off: %v", ran, len(seen), *calls)
}
// A check compiles with cgo off as a build does (ADR 0542 §1), and the lint's go vet, a compile that runs nothing
// of the change, writes the shared caches; every other command reads them beneath its own layer (ADR 0541 §2).
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 := 0
for _, s := range seen {
checkShutOff(t, m, s, false)
vet := strings.Contains(s.command, " vet ")
if vet {
vets++
}
checkShutOff(t, m, s, vet)
}
if vets == 0 {
t.Error("no go vet ran shut off")
}
}
@@ -13,6 +13,7 @@ import (
"sort"
"strings"
"sync"
"time"
"git.novox.be/novox/mesh-sdk/go/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
@@ -144,6 +145,18 @@ func (m *Module) lintAll(ctx context.Context, run forgekey.Runner, tree string,
var wg sync.WaitGroup
var crash any // a panic of a lint goes on in the caller, as a panic of a test run does
var crashMu sync.Mutex
// Each directory's end is said as a phase, so a long lint is readable while it runs (issue 1921): which directories
// are done and how long the last one took.
var doneMu sync.Mutex
done := 0
said := func(d string, began time.Time) {
doneMu.Lock()
defer doneMu.Unlock()
done++
if done < len(dirs) {
buildstep.Phase(ctx, fmt.Sprintf("unit: lint of %d directories, %d done (%s in %s)", len(dirs), done, d, m.clock().Sub(began).Round(time.Second)))
}
}
for i, d := range dirs {
wg.Add(1)
slots <- struct{}{}
@@ -157,7 +170,9 @@ func (m *Module) lintAll(ctx context.Context, run forgekey.Runner, tree string,
crashMu.Unlock()
}
}()
began := m.clock()
answers[i].failed, answers[i].err = m.lint(ctx, run, tree, d, env(d))
said(d, began)
}()
}
wg.Wait()
@@ -251,8 +251,15 @@ func (m *Module) runLevel(ctx context.Context, tree string, o levelOpts) (*TestR
if o.level == LevelUnit {
// gofmt and go vet of each directory the level runs come before its tests (ADR 0529 §1; issue 1887 D7-go): a
// directory that fails them fails the level at once, and their time is the result's Lint.
// go vet compiles and runs nothing of the change, with cgo off (goEnv), so it writes the shared cache and warms it
// for every later check, a pull request's head included (ADR 0541 §2, ADR 0542 §1); the tests run beneath their
// own throwaway layer.
compile, err := m.stepRunner(o.workspace, true)
if err != nil {
return nil, err
}
linted := m.clock()
failed, err := m.lintAll(ctx, run, tree, todo, func(d string) []string {
failed, err := m.lintAll(ctx, compile, tree, todo, func(d string) []string {
return append(m.goEnvFor(filepath.Join(tree, d)), o.env...)
})
if err != nil {