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 e9d2f57f..78ec70b5 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/fetch.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/fetch.go @@ -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, diff --git a/modules/mesh-build-go/cmd/mesh-build-go/isolate.go b/modules/mesh-build-go/cmd/mesh-build-go/isolate.go index b47538e9..48e416f3 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/isolate.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/isolate.go @@ -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 { 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 9d6615c5..bd11ede0 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 @@ -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") } } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/lint.go b/modules/mesh-build-go/cmd/mesh-build-go/lint.go index 32366378..badae201 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/lint.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/lint.go @@ -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() 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 d165124c..5f992d8f 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/test.go @@ -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 {