]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
hugolib/commands: Fix stuck server error issues
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 23 Oct 2024 17:26:13 +0000 (19:26 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 24 Oct 2024 11:34:11 +0000 (13:34 +0200)
Fixes #11378

12 files changed:
commands/commandeer.go
commands/hugobuilder.go
commands/server.go
hugolib/hugo_sites.go
hugolib/hugo_sites_build.go
hugolib/integrationtest_builder.go
hugolib/page__new.go
hugolib/site.go
testscripts/commands/server__error_recovery_edit_config.txt [new file with mode: 0644]
testscripts/commands/server__error_recovery_edit_content.txt [new file with mode: 0644]
tpl/template.go
tpl/tplimpl/template.go

index ad2adf3a289b3a41b271f03b1c78ae044569f7bd..69077ad73cdbb7de03906ce21a141ebe396cb96b 100644 (file)
@@ -507,7 +507,7 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
        return loggers.New(optsLogger), nil
 }
 
-func (r *rootCommand) Reset() {
+func (r *rootCommand) resetLogs() {
        r.logger.Reset()
        loggers.Log().Reset()
 }
index 42bf68a3726a36a7779032cc6f5f5d247e3d822c..95129018f1ae5304ddff0d7237bc9240b5d94349 100644 (file)
@@ -27,7 +27,6 @@ import (
        "sync/atomic"
        "time"
 
-       "github.com/bep/logg"
        "github.com/bep/simplecobra"
        "github.com/fsnotify/fsnotify"
        "github.com/gohugoio/hugo/common/herrors"
@@ -136,10 +135,6 @@ func (e *hugoBuilderErrState) wasErr() bool {
        return e.waserr
 }
 
-func (c *hugoBuilder) errCount() int {
-       return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
-}
-
 // getDirList provides NewWatcher() with a list of directories to watch for changes.
 func (c *hugoBuilder) getDirList() ([]string, error) {
        h, err := c.hugo()
@@ -345,7 +340,6 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
                for {
                        select {
                        case changes := <-c.r.changesFromBuild:
-                               c.errState.setBuildErr(nil)
                                unlock, err := h.LockBuild()
                                if err != nil {
                                        c.r.logger.Errorln("Failed to acquire a build lock: %s", err)
@@ -358,7 +352,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
                                }
                                if c.s != nil && c.s.doLiveReload {
                                        doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0
-                                       doReload = doReload || c.showErrorInBrowser && c.errCount() > 0
+                                       doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil
                                        if doReload {
                                                livereload.ForceRefresh()
                                        }
@@ -372,7 +366,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
                                        return
                                }
                                c.handleEvents(watcher, staticSyncer, evs, configSet)
-                               if c.showErrorInBrowser && c.errCount() > 0 {
+                               if c.showErrorInBrowser && c.errState.buildErr() != nil {
                                        // Need to reload browser to show the error
                                        livereload.ForceRefresh()
                                }
@@ -419,11 +413,17 @@ func (c *hugoBuilder) build() error {
 }
 
 func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) {
-       h, err := c.hugo()
+       defer func() {
+               c.errState.setBuildErr(err)
+       }()
+
+       var h *hugolib.HugoSites
+       h, err = c.hugo()
        if err != nil {
-               return err
+               return
        }
-       return h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
+       err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
+       return
 }
 
 func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
@@ -619,6 +619,9 @@ func (c *hugoBuilder) fullRebuild(changeType string) {
                        // Set the processing on pause until the state is recovered.
                        c.errState.setPaused(true)
                        c.handleBuildErr(err, "Failed to reload config")
+                       if c.s.doLiveReload {
+                               livereload.ForceRefresh()
+                       }
                } else {
                        c.errState.setPaused(false)
                }
@@ -1081,37 +1084,44 @@ func (c *hugoBuilder) printChangeDetected(typ string) {
        c.r.logger.Println(htime.Now().Format(layout))
 }
 
-func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
+func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) {
+       defer func() {
+               c.errState.setBuildErr(err)
+       }()
        if err := c.errState.buildErr(); err != nil {
                ferrs := herrors.UnwrapFileErrorsWithErrorContext(err)
                for _, err := range ferrs {
                        events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
                }
        }
-       c.errState.setBuildErr(nil)
-       h, err := c.hugo()
+       var h *hugolib.HugoSites
+       h, err = c.hugo()
        if err != nil {
-               return err
+               return
        }
-
-       return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
+       err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
+       return
 }
 
-func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) error {
-       c.errState.setBuildErr(nil)
-       h, err := c.hugo()
+func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) {
+       defer func() {
+               c.errState.setBuildErr(err)
+       }()
+
+       var h *hugolib.HugoSites
+       h, err = c.hugo()
        if err != nil {
-               return err
+               return
        }
        whatChanged := &hugolib.WhatChanged{}
        whatChanged.Add(ids...)
        err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
-       c.errState.setBuildErr(err)
-       return err
+
+       return
 }
 
 func (c *hugoBuilder) reloadConfig() error {
-       c.r.Reset()
+       c.r.resetLogs()
        c.r.configVersionID.Add(1)
 
        if err := c.withConfE(func(conf *commonConfig) error {
index 84d4165f0cc4f5b31963e05c21a30f44e909f641..6b801b158c834daabd60be91decbec3d4b2adaec 100644 (file)
@@ -648,9 +648,8 @@ func (c *serverCommand) setServerInfoInConfig() error {
 }
 
 func (c *serverCommand) getErrorWithContext() any {
-       errCount := c.errCount()
-
-       if errCount == 0 {
+       buildErr := c.errState.buildErr()
+       if buildErr == nil {
                return nil
        }
 
@@ -659,7 +658,7 @@ func (c *serverCommand) getErrorWithContext() any {
        m["Error"] = cleanErrorLog(c.r.logger.Errors())
 
        m["Version"] = hugo.BuildVersionString()
-       ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
+       ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr)
        m["Files"] = ferrors
 
        return m
@@ -830,22 +829,25 @@ func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port i
        return u.String(), nil
 }
 
-func (c *serverCommand) partialReRender(urls ...string) error {
+func (c *serverCommand) partialReRender(urls ...string) (err error) {
        defer func() {
                c.errState.setWasErr(false)
        }()
-       c.errState.setBuildErr(nil)
        visited := types.NewEvictingStringQueue(len(urls))
        for _, url := range urls {
                visited.Add(url)
        }
 
-       h, err := c.hugo()
+       var h *hugolib.HugoSites
+       h, err = c.hugo()
        if err != nil {
-               return err
+               return
        }
+
        // Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
-       return h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
+       err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
+
+       return
 }
 
 func (c *serverCommand) serve() error {
index 659a772f22250c3de5a492f47f8929900bbc30c2..a5186fd441fc30296e1b58fe0e57fd4420ea1989 100644 (file)
@@ -179,9 +179,6 @@ type hugoSitesInit struct {
        // Loads the data from all of the /data folders.
        data *lazy.Init
 
-       // Performs late initialization (before render) of the templates.
-       layouts *lazy.Init
-
        // Loads the Git info and CODEOWNERS for all the pages if enabled.
        gitInfo *lazy.Init
 }
index 65ce946e97713dfd0f000dd6204a0d55132b9457..dd548be515d93dd4458fd9231d4e56976e4cb82c 100644 (file)
@@ -250,10 +250,6 @@ func (h *HugoSites) process(ctx context.Context, l logg.LevelLogger, config *Bui
        l = l.WithField("step", "process")
        defer loggers.TimeTrackf(l, time.Now(), nil, "")
 
-       if _, err := h.init.layouts.Do(ctx); err != nil {
-               return err
-       }
-
        if len(events) > 0 {
                // This is a rebuild triggered from file events.
                return h.processPartialFileEvents(ctx, l, config, init, events)
@@ -1067,8 +1063,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
        }
 
        if tmplChanged || i18nChanged {
-               // TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048.
-               h.init.layouts.Reset()
                if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
                        // TODO(bep) this could probably be optimized to somehow
                        // only load the changed templates and its dependencies, but that is non-trivial.
@@ -1141,10 +1135,6 @@ func (s *Site) handleContentAdapterChanges(bi pagesfromdata.BuildInfo, buildConf
 }
 
 func (h *HugoSites) processContentAdaptersOnRebuild(ctx context.Context, buildConfig *BuildCfg) error {
-       // Make sure the layouts are initialized.
-       if _, err := h.init.layouts.Do(context.Background()); err != nil {
-               return err
-       }
        g := rungroup.Run[*pagesfromdata.PagesFromTemplate](ctx, rungroup.Config[*pagesfromdata.PagesFromTemplate]{
                NumWorkers: h.numWorkers,
                Handle: func(ctx context.Context, p *pagesfromdata.PagesFromTemplate) error {
index b45defb42d29fcfd3b8c8c2fa6d371f3737efc90..b806ad7c143d42404d92ecf4b7b17781fd2fdab3 100644 (file)
@@ -246,11 +246,6 @@ func (s *IntegrationTestBuilder) AssertBuildCountGitInfo(count int) {
        s.Assert(s.H.init.gitInfo.InitCount(), qt.Equals, count)
 }
 
-func (s *IntegrationTestBuilder) AssertBuildCountLayouts(count int) {
-       s.Helper()
-       s.Assert(s.H.init.layouts.InitCount(), qt.Equals, count)
-}
-
 func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) {
        s.Helper()
        fs := s.fs.WorkingDirReadOnly
index b7d9b10f279964ae842b70305169e209641bc5a7..9a4972d07fdb03987ae11557a3f43afd700f3406 100644 (file)
@@ -34,6 +34,15 @@ import (
 var pageIDCounter atomic.Uint64
 
 func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
+       p, pth, err := h.doNewPage(m)
+       if err != nil {
+               // Make sure that any partially created page part is marked as stale.
+               m.MarkStale()
+       }
+       return p, pth, err
+}
+
+func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
        m.Staler = &resources.AtomicStaler{}
        if m.pageMetaParams == nil {
                m.pageMetaParams = &pageMetaParams{
@@ -231,10 +240,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
                }
                return ps, nil
        }()
-       // Make sure to evict any cached and now stale data.
-       if err != nil {
-               m.MarkStale()
-       }
 
        if ps == nil {
                return nil, nil, err
index 08031390ba19836d789acf9ffc24a0ec58fd342e..24ee5dcc506c48ddae635331b13b9ae1788f4a34 100644 (file)
@@ -344,7 +344,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
                skipRebuildForFilenames: make(map[string]bool),
                init: &hugoSitesInit{
                        data:    lazy.New(),
-                       layouts: lazy.New(),
                        gitInfo: lazy.New(),
                },
        }
@@ -400,15 +399,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
                return nil, nil
        })
 
-       h.init.layouts.Add(func(context.Context) (any, error) {
-               for _, s := range h.Sites {
-                       if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
-                               return nil, err
-                       }
-               }
-               return nil, nil
-       })
-
        h.init.gitInfo.Add(func(context.Context) (any, error) {
                err := h.loadGitInfo()
                if err != nil {
diff --git a/testscripts/commands/server__error_recovery_edit_config.txt b/testscripts/commands/server__error_recovery_edit_config.txt
new file mode 100644 (file)
index 0000000..664d992
--- /dev/null
@@ -0,0 +1,42 @@
+# Test the hugo server command when adding an error to a config file
+# and then fixing it.
+
+hugo server &
+
+waitServer
+
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+replace $WORK/hugo.toml 'title =' 'titlefoo'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
+
+replace $WORK/hugo.toml 'titlefoo' 'title ='
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+stopServer
+
+-- hugo.toml --
+title = "Hugo Server Test"
+baseURL = "https://example.org/"
+disableKinds = ["taxonomy", "term", "sitemap"]
+-- layouts/index.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- layouts/_default/single.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- content/_index.md --
+---
+title: Hugo Home
+---
+-- content/p1/index.md --
+---
+title: P1
+---
+-- content/p2/index.md --
+---
+title: P2
+---
+-- static/staticfiles/static.txt --
+static
+
+
+
diff --git a/testscripts/commands/server__error_recovery_edit_content.txt b/testscripts/commands/server__error_recovery_edit_content.txt
new file mode 100644 (file)
index 0000000..f5ea7e9
--- /dev/null
@@ -0,0 +1,42 @@
+# Test the hugo server command when adding a front matter error to a content file
+# and then fixing it.
+
+hugo server &
+
+waitServer
+
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+replace $WORK/content/p1/index.md 'title:' 'titlecolon'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
+
+replace $WORK/content/p1/index.md 'titlecolon' 'title:'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+stopServer
+
+-- hugo.toml --
+title = "Hugo Server Test"
+baseURL = "https://example.org/"
+disableKinds = ["taxonomy", "term", "sitemap"]
+-- layouts/index.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- layouts/_default/single.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- content/_index.md --
+---
+title: Hugo Home
+---
+-- content/p1/index.md --
+---
+title: P1
+---
+-- content/p2/index.md --
+---
+title: P2
+---
+-- static/staticfiles/static.txt --
+static
+
+
+
index cb8d2b321ba4683c407b55e9b798272bbc494d37..18a31e2314cbfb68cdad547bd783afc4a574d395 100644 (file)
@@ -40,7 +40,6 @@ type TemplateManager interface {
        TemplateHandler
        TemplateFuncGetter
        AddTemplate(name, tpl string) error
-       MarkReady() error
 }
 
 // TemplateVariants describes the possible variants of a template.
index 04ccdaad22bdbb8ed3ae95e0520475ad912c7063..fd07db68e1e3d5afa64a1db90fda7e09484c918b 100644 (file)
@@ -168,6 +168,10 @@ func newTemplateHandlers(d *deps.Deps) (*tpl.TemplateHandlers, error) {
                return nil, err
        }
 
+       if err := h.main.createPrototypes(); err != nil {
+               return nil, err
+       }
+
        e := &templateExec{
                d:               d,
                executor:        exec,
@@ -312,28 +316,11 @@ func (t *templateExec) GetFunc(name string) (reflect.Value, bool) {
        return v, found
 }
 
-func (t *templateExec) MarkReady() error {
-       var err error
-       t.readyInit.Do(func() {
-               // We only need the clones if base templates are in use.
-               if len(t.needsBaseof) > 0 {
-                       err = t.main.createPrototypes()
-                       if err != nil {
-                               return
-                       }
-               }
-       })
-
-       return err
-}
-
 type templateHandler struct {
        main        *templateNamespace
        needsBaseof map[string]templateInfo
        baseof      map[string]templateInfo
 
-       readyInit sync.Once
-
        // This is the filesystem to load the templates from. All the templates are
        // stored in the root of this filesystem.
        layoutsFs afero.Fs