Prevent stale content in Fast Render Mode - hugo - [fork] hugo port for 9front
 (HTM) git clone git@git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 4a366fcfee24b3a5a5045b16c3b87b76147adf5e
 (DIR) parent 083311d0336ced35909b3375950f7817ecf95ed0
 (HTM) Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Wed, 17 Oct 2018 09:28:04 +0200
       
       Prevent stale content in Fast Render Mode
       
       We do that by re-render visited pages that is not already in the stack. This may potentially do some double work, but that small penalty should be well worth it.
       
       Fixes #5281
       
       Diffstat:
         M commands/commandeer.go              |       5 +----
         M commands/hugo.go                    |      27 +++++++++++++++++++--------
         M commands/server.go                  |      14 +++++++++++++-
         M common/types/evictingqueue.go       |       7 +++++++
         M common/types/evictingqueue_test.go  |       3 +++
         M hugolib/hugo_sites.go               |       5 +++++
         M hugolib/hugo_sites_build.go         |      56 +++++++++++++++++++------------
         M hugolib/site_render.go              |       2 +-
       
       8 files changed, 83 insertions(+), 36 deletions(-)
       ---
 (DIR) diff --git a/commands/commandeer.go b/commands/commandeer.go
       @@ -324,10 +324,7 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error {
                                fs.Destination = new(afero.MemMapFs)
                        }
        
       -                doLiveReload := !c.h.buildWatch && !config.GetBool("disableLiveReload")
       -                fastRenderMode := doLiveReload && !config.GetBool("disableFastRender")
       -
       -                if fastRenderMode {
       +                if c.fastRenderMode {
                                // For now, fast render mode only. It should, however, be fast enough
                                // for the full variant, too.
                                changeDetector := &fileChangeDetector{
 (DIR) diff --git a/commands/hugo.go b/commands/hugo.go
       @@ -618,13 +618,20 @@ func (c *commandeer) buildSites() (err error) {
                return c.hugo.Build(hugolib.BuildCfg{})
        }
        
       +func (c *commandeer) handleBuildErr(err error, msg string) {
       +        c.buildErr = err
       +        c.logger.ERROR.Printf("%s: %s", msg, err)
       +        if !c.h.quiet && c.h.verbose {
       +                herrors.PrintStackTrace(err)
       +        }
       +}
       +
        func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
                defer c.timeTrack(time.Now(), "Total")
        
                c.buildErr = nil
                visited := c.visitedURLs.PeekAllSet()
       -        doLiveReload := !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload")
       -        if doLiveReload && !c.Cfg.GetBool("disableFastRender") {
       +        if c.fastRenderMode {
        
                        // Make sure we always render the home pages
                        for _, l := range c.languages {
       @@ -640,6 +647,15 @@ func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
                return c.hugo.Build(hugolib.BuildCfg{RecentlyVisited: visited}, events...)
        }
        
       +func (c *commandeer) partialReRender(urls ...string) error {
       +        c.buildErr = nil
       +        visited := make(map[string]bool)
       +        for _, url := range urls {
       +                visited[url] = true
       +        }
       +        return c.hugo.Build(hugolib.BuildCfg{RecentlyVisited: visited, PartialReRender: true})
       +}
       +
        func (c *commandeer) fullRebuild() {
                c.commandeerHugoState = &commandeerHugoState{}
                err := c.loadConfig(true, true)
       @@ -907,15 +923,10 @@ func (c *commandeer) handleEvents(watcher *watcher.Batcher,
        
                        c.changeDetector.PrepareNew()
                        if err := c.rebuildSites(dynamicEvents); err != nil {
       -                        c.buildErr = err
       -                        c.logger.ERROR.Printf("Rebuild failed: %s", err)
       -                        if !c.h.quiet && c.h.verbose {
       -                                herrors.PrintStackTrace(err)
       -                        }
       +                        c.handleBuildErr(err, "Rebuild failed")
                        }
        
                        if doLiveReload {
       -
                                if len(partitionedEvents.ContentEvents) == 0 && len(partitionedEvents.AssetEvents) > 0 {
                                        changed := c.changeDetector.changed()
                                        if c.changeDetector != nil && len(changed) == 0 {
 (DIR) diff --git a/commands/server.go b/commands/server.go
       @@ -345,10 +345,22 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
                                        w.Header().Set("Pragma", "no-cache")
                                }
        
       -                        if f.c.fastRenderMode {
       +                        if f.c.fastRenderMode && f.c.buildErr == nil {
                                        p := r.RequestURI
                                        if strings.HasSuffix(p, "/") || strings.HasSuffix(p, "html") || strings.HasSuffix(p, "htm") {
       +                                        if !f.c.visitedURLs.Contains(p) {
       +                                                // If not already on stack, re-render that single page.
       +                                                if err := f.c.partialReRender(p); err != nil {
       +                                                        f.c.handleBuildErr(err, fmt.Sprintf("Failed to render %q", p))
       +                                                        if f.c.showErrorInBrowser {
       +                                                                http.Redirect(w, r, p, 301)
       +                                                                return
       +                                                        }
       +                                                }
       +                                        }
       +
                                                f.c.visitedURLs.Add(p)
       +
                                        }
                                }
                                h.ServeHTTP(w, r)
 (DIR) diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go
       @@ -52,6 +52,13 @@ func (q *EvictingStringQueue) Add(v string) {
                q.mu.Unlock()
        }
        
       +// Contains returns whether the queue contains v.
       +func (q *EvictingStringQueue) Contains(v string) bool {
       +        q.mu.Lock()
       +        defer q.mu.Unlock()
       +        return q.set[v]
       +}
       +
        // Peek looks at the last element added to the queue.
        func (q *EvictingStringQueue) Peek() string {
                q.mu.Lock()
 (DIR) diff --git a/common/types/evictingqueue_test.go b/common/types/evictingqueue_test.go
       @@ -36,6 +36,9 @@ func TestEvictingStringQueue(t *testing.T) {
                queue.Add("a")
                queue.Add("b")
        
       +        assert.True(queue.Contains("a"))
       +        assert.False(queue.Contains("foo"))
       +
                assert.Equal([]string{"b", "a"}, queue.PeekAll())
                assert.Equal("b", queue.Peek())
                queue.Add("c")
 (DIR) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
       @@ -368,6 +368,11 @@ type BuildCfg struct {
                SkipRender bool
                // Use this to indicate what changed (for rebuilds).
                whatChanged *whatChanged
       +
       +        // This is a partial re-render of some selected pages. This means
       +        // we should skip most of the processing.
       +        PartialReRender bool
       +
                // Recently visited URLs. This is used for partial re-rendering.
                RecentlyVisited map[string]bool
        }
 (DIR) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
       @@ -41,27 +41,29 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
                        conf.whatChanged = &whatChanged{source: true, other: true}
                }
        
       -        for _, s := range h.Sites {
       -                s.Deps.BuildStartListeners.Notify()
       -        }
       +        if !config.PartialReRender {
       +                for _, s := range h.Sites {
       +                        s.Deps.BuildStartListeners.Notify()
       +                }
        
       -        if len(events) > 0 {
       -                // Rebuild
       -                if err := h.initRebuild(conf); err != nil {
       -                        return err
       +                if len(events) > 0 {
       +                        // Rebuild
       +                        if err := h.initRebuild(conf); err != nil {
       +                                return err
       +                        }
       +                } else {
       +                        if err := h.init(conf); err != nil {
       +                                return err
       +                        }
                        }
       -        } else {
       -                if err := h.init(conf); err != nil {
       +
       +                if err := h.process(conf, events...); err != nil {
                                return err
                        }
       -        }
        
       -        if err := h.process(conf, events...); err != nil {
       -                return err
       -        }
       -
       -        if err := h.assemble(conf); err != nil {
       -                return err
       +                if err := h.assemble(conf); err != nil {
       +                        return err
       +                }
                }
        
                if err := h.render(conf); err != nil {
       @@ -226,8 +228,10 @@ func (h *HugoSites) assemble(config *BuildCfg) error {
        }
        
        func (h *HugoSites) render(config *BuildCfg) error {
       -        for _, s := range h.Sites {
       -                s.initRenderFormats()
       +        if !config.PartialReRender {
       +                for _, s := range h.Sites {
       +                        s.initRenderFormats()
       +                }
                }
        
                for _, s := range h.Sites {
       @@ -240,15 +244,23 @@ func (h *HugoSites) render(config *BuildCfg) error {
        
                                        isRenderingSite := s == s2
        
       -                                if err := s2.preparePagesForRender(isRenderingSite && i == 0); err != nil {
       -                                        return err
       +                                if !config.PartialReRender {
       +                                        if err := s2.preparePagesForRender(isRenderingSite && i == 0); err != nil {
       +                                                return err
       +                                        }
                                        }
        
                                }
        
                                if !config.SkipRender {
       -                                if err := s.render(config, i); err != nil {
       -                                        return err
       +                                if config.PartialReRender {
       +                                        if err := s.renderPages(config); err != nil {
       +                                                return err
       +                                        }
       +                                } else {
       +                                        if err := s.render(config, i); err != nil {
       +                                                return err
       +                                        }
                                        }
                                }
                        }
 (DIR) diff --git a/hugolib/site_render.go b/hugolib/site_render.go
       @@ -43,7 +43,7 @@ func (s *Site) renderPages(cfg *BuildCfg) error {
                        go pageRenderer(s, pages, results, wg)
                }
        
       -        if len(s.headlessPages) > 0 {
       +        if !cfg.PartialReRender && len(s.headlessPages) > 0 {
                        wg.Add(1)
                        go headlessPagesPublisher(s, wg)
                }