Files
yellowjacket/backend/events/noemit_test.go
logan b98840ee37 fix(build): keep the index tools free of the Wails application
The v3 migration put application.Get() in backend/events and a
ServiceStartup hook in backend/explore, both of which cmd/indexbuild
reaches. v3's application package is GTK/WebKit bindings on Linux, so
the index-artifact job — a plain golang container with CGO_ENABLED=0,
on the stated grounds that neither command imports the app — stopped
compiling with "undefined: pointer". That job owns the ~205 GB dump
checkpoint, so it is the worst place to learn this.

Both are behind the indexbuild tag now: the one app.Event.Emit lives in
runtime_wails.go, runtime_indexbuild.go answers ErrNoRuntime (what the
app itself returns before Run, so Deliver's callers need no second
path), and explore's ServiceStartup moves to its own tagged file.

TestIndexToolsDoNotImportWails walks `go list -deps -tags indexbuild`
so the claim the workflow makes is checked rather than assumed.
2026-08-16 14:51:01 -04:00

97 lines
2.6 KiB
Go

package events_test
import (
"os"
"path/filepath"
"strings"
"testing"
)
// allowedEmitters are the only files permitted to call the Wails
// runtime's event emitter directly. There is one call and it lives in
// runtime_wails.go rather than emit.go because naming the application
// package is what forces cgo, and cmd/indexbuild builds this package
// without it.
var allowedEmitters = map[string]bool{
filepath.Join("backend", "events", "runtime_wails.go"): true,
}
// TestNoDirectRuntimeEmits fails if anything outside backend/events
// calls the Wails runtime's event emitter directly.
//
// The original reason was survival: v2's runtime.EventsEmit called
// log.Fatalf on a context that did not carry the runtime, so a direct
// call from a background worker could take the process down. v3's
// emit takes no context and cannot do that, and the rule is kept for
// the weaker but still real reason — one emit path is what lets
// emitStatus drop an unchanged payload for every caller at once.
//
// This is a text walk rather than a golangci-lint rule because
// golangci-lint runs once per build configuration, so a call in an
// indexbuild- or dev-tagged file is only seen by the pass that compiles
// it. Walking the tree sees all three, plus anything tagged out
// entirely.
func TestNoDirectRuntimeEmits(t *testing.T) {
// A selector, not a bare name: built at runtime so this file does
// not match itself, and qualified so it catches the call however
// the application value is named (app.Event.Emit, a.Event.Emit,
// application.Get().Event.Emit) without matching an identifier
// that merely ends in the same letters.
needle := ".Event" + ".Emit("
root := filepath.Join("..", "..")
skipDirs := map[string]bool{
".git": true,
"node_modules": true,
"frontend": true,
"build": true,
}
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
if skipDirs[d.Name()] {
return filepath.SkipDir
}
return nil
}
if filepath.Ext(path) != ".go" {
return nil
}
rel, relErr := filepath.Rel(root, path)
if relErr != nil {
return relErr
}
if allowedEmitters[rel] {
return nil
}
src, readErr := os.ReadFile(path)
if readErr != nil {
return readErr
}
for i, line := range strings.Split(string(src), "\n") {
if strings.Contains(line, needle) {
t.Errorf(
"%s:%d calls the Wails emitter directly; use events.Emit\n\t%s",
rel, i+1, strings.TrimSpace(line),
)
}
}
return nil
})
if err != nil {
t.Fatalf("walking %s: %v", root, err)
}
}