From 9045797d5f24d4c0f04f4f8ec9e15c9722100f2f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 5 Feb 2026 20:11:48 +0100 Subject: [PATCH] server: Fix panic when the server browser error handler tried to use a config in a state of flux I tried and failed to create a test for this, but I have tested this manually., Fixes #14470 --- commands/commandeer.go | 3 +++ commands/hugobuilder.go | 30 +++++++++++++++++++++++++++--- commands/server.go | 2 +- common/loggers/logger.go | 8 ++++++++ main_test.go | 4 +++- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/commands/commandeer.go b/commands/commandeer.go index d7c967c54..97cee7acb 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -331,6 +331,9 @@ func (r *rootCommand) ConfigFromProvider(key configKey, cfg config.Provider) (*c } func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) { + if conf == nil { + return nil, fmt.Errorf("conf must be set") + } k := configKey{counter: r.configVersionID.Load()} h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { depsCfg := r.newDepsConfig(conf) diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index 038b9ad94..779c84e03 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -56,8 +56,9 @@ import ( type hugoBuilder struct { r *rootCommand - confmu sync.Mutex - conf *commonConfig + confmu sync.Mutex + confOld *commonConfig + conf *commonConfig // May be nil. s *serverCommand @@ -94,6 +95,27 @@ func (c *hugoBuilder) withConf(fn func(conf *commonConfig)) { fn(c.conf) } +func (c *hugoBuilder) withConfOrOldConf(fn func(conf *commonConfig)) { + c.confmu.Lock() + defer c.confmu.Unlock() + if c.conf != nil { + fn(c.conf) + } else if c.confOld != nil { + fn(c.confOld) + } +} + +func (c *hugoBuilder) withConfOrOldConfE(fn func(conf *commonConfig) error) error { + c.confmu.Lock() + defer c.confmu.Unlock() + if c.conf != nil { + return fn(c.conf) + } else if c.confOld != nil { + return fn(c.confOld) + } + return errConfigNotSet +} + type hugoBuilderErrState struct { mu sync.Mutex paused bool @@ -1095,6 +1117,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error } c.conf = conf + c.confOld = conf if c.onConfigLoaded != nil { if err := c.onConfigLoaded(false); err != nil { return err @@ -1158,8 +1181,9 @@ func (c *hugoBuilder) reloadConfig() error { c.r.resetLogs() c.r.configVersionID.Add(1) - if err := c.withConfE(func(conf *commonConfig) error { + if err := c.withConfOrOldConfE(func(conf *commonConfig) error { oldConf := conf + c.conf = nil newConf, err := c.r.ConfigFromConfig(configKey{counter: c.r.configVersionID.Load()}, conf) if err != nil { return err diff --git a/commands/server.go b/commands/server.go index 1f5c58463..48e4c79fb 100644 --- a/commands/server.go +++ b/commands/server.go @@ -282,7 +282,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string } port = 1313 - f.c.withConf(func(conf *commonConfig) { + f.c.withConfOrOldConf(func(conf *commonConfig) { if lrport := conf.configs.GetFirstLanguageConfig().BaseURLLiveReload().Port(); lrport != 0 { port = lrport } diff --git a/common/loggers/logger.go b/common/loggers/logger.go index 785405033..b49a0b746 100644 --- a/common/loggers/logger.go +++ b/common/loggers/logger.go @@ -20,6 +20,7 @@ import ( "io" "os" "strings" + "sync" "time" "github.com/bep/logg" @@ -123,8 +124,11 @@ func New(opts Options) Logger { ) l := logger.WithLevel(opts.Level) + logMu := &sync.Mutex{} reset := func() { + logMu.Lock() + defer logMu.Unlock() logCounters.mu.Lock() defer logCounters.mu.Unlock() logCounters.counters = make(map[logg.Level]int) @@ -135,6 +139,7 @@ func New(opts Options) Logger { } return &logAdapter{ + mu: logMu, logCounters: logCounters, errors: errorsw, reset: reset, @@ -204,6 +209,7 @@ type Logger interface { } type logAdapter struct { + mu *sync.Mutex logCounters *logLevelCounter errors *strings.Builder reset func() @@ -328,6 +334,8 @@ func (l *logAdapter) Errorln(v ...any) { } func (l *logAdapter) Errors() string { + l.mu.Lock() + defer l.mu.Unlock() return l.errors.String() } diff --git a/main_test.go b/main_test.go index 79d0c554e..facd8ed46 100644 --- a/main_test.go +++ b/main_test.go @@ -241,7 +241,9 @@ var commonTestScriptsParam = testscript.Params{ if err != nil { ts.Fatalf("failed to read file %v", err) } - newContent := bytes.Replace(oldContent, []byte(args[1]), []byte(args[2]), -1) + old, new := args[1], args[2] + + newContent := bytes.Replace(oldContent, []byte(old), []byte(new), -1) err = os.WriteFile(filename, newContent, 0o644) if err != nil { ts.Fatalf("failed to write file: %v", err) -- 2.39.5