From 5fb69502d6b79f3f3e38e855132c84c690f9eb7c Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 15 Jan 2025 18:02:07 +1100 Subject: [PATCH] fix: modifying modules should not break stub list (#4053) The build engine was not synchronising the list of stubbed modules correctly if a new build was triggered (such as by a module being added, or a dependency changing). Steps to repro: - `ftl dev examples/go` - Wait for all modules to build - Look at any module's `go.work` file, it'll contain each other module's stub path - Move the `echo` module from `examples/go` to somewhere else - FTL dev will remove the module - Move the `echo` module back to its original location - Look at any `go.work` file now. It'll only contain the stub paths for `builtin` and `echo` --- internal/buildengine/engine.go | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/internal/buildengine/engine.go b/internal/buildengine/engine.go index 51134c713..075b48f69 100644 --- a/internal/buildengine/engine.go +++ b/internal/buildengine/engine.go @@ -8,6 +8,7 @@ import ( "fmt" "net/url" "runtime" + "sort" "strings" "time" @@ -594,12 +595,13 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration }) err = GenerateStubs(ctx, e.projectConfig.Root(), maps.Values(modulesToStub), metasMap) if err != nil { - logger.Errorf(err, "failed to generate stubs") + logger.Errorf(err, "Failed to generate stubs") } - err = SyncStubReferences(ctx, e.projectConfig.Root(), maps.Keys(metasMap), metasMap, &schema.Schema{Modules: maps.Values(modulesToStub)}) + // Sync references to stubs if needed by the runtime + err = e.syncNewStubReferences(ctx, modulesToStub, metasMap) if err != nil { - return err + logger.Errorf(err, "Failed to sync stub references") } } @@ -949,13 +951,8 @@ func (e *Engine) buildWithCallback(ctx context.Context, callback buildCallback, return err } - moduleNames := []string{} - for _, module := range knownSchemas { - moduleNames = append(moduleNames, module.Name) - } - // Sync references to stubs if needed by the runtime - err = SyncStubReferences(ctx, e.projectConfig.Root(), moduleNames, metasMap, &schema.Schema{Modules: maps.Values(builtModules)}) + err = e.syncNewStubReferences(ctx, builtModules, metasMap) if err != nil { return err } @@ -1110,6 +1107,22 @@ func (e *Engine) gatherSchemas( return nil } +func (e *Engine) syncNewStubReferences(ctx context.Context, newModules map[string]*schema.Module, metasMap map[string]moduleMeta) error { + fullSchema := &schema.Schema{Modules: maps.Values(newModules)} + for _, module := range e.schemaSource.View().Modules { + if _, ok := newModules[module.Name]; !ok { + fullSchema.Modules = append(fullSchema.Modules, module) + } + } + sort.SliceStable(fullSchema.Modules, func(i, j int) bool { return fullSchema.Modules[i].Name < fullSchema.Modules[j].Name }) + + return SyncStubReferences(ctx, + e.projectConfig.Root(), + slices.Map(fullSchema.Modules, func(m *schema.Module) string { return m.Name }), + metasMap, + fullSchema) +} + func (e *Engine) newModuleMeta(ctx context.Context, config moduleconfig.UnvalidatedModuleConfig) (moduleMeta, error) { plugin, err := languageplugin.New(ctx, config.Dir, config.Language, config.Module) if err != nil {