Fix stale pages on rebuilds in GetPage with short refs

Fixes #13004
This commit is contained in:
Bjørn Erik Pedersen 2024-11-03 10:41:34 +01:00
parent 1f23b4949c
commit 30d9aea860
5 changed files with 51 additions and 11 deletions

View file

@ -37,6 +37,7 @@ import (
"github.com/gohugoio/hugo/hugolib/doctree" "github.com/gohugoio/hugo/hugolib/doctree"
"github.com/gohugoio/hugo/hugolib/pagesfromdata" "github.com/gohugoio/hugo/hugolib/pagesfromdata"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/lazy"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources"
@ -109,6 +110,11 @@ type pageMap struct {
cfg contentMapConfig cfg contentMapConfig
} }
// Invoked on rebuilds.
func (m *pageMap) Reset() {
m.pageReverseIndex.Reset()
}
// pageTrees holds pages and resources in a tree structure for all sites/languages. // pageTrees holds pages and resources in a tree structure for all sites/languages.
// Each site gets its own tree set via the Shape method. // Each site gets its own tree set via the Shape method.
type pageTrees struct { type pageTrees struct {
@ -958,9 +964,7 @@ type contentTreeReverseIndex struct {
} }
func (c *contentTreeReverseIndex) Reset() { func (c *contentTreeReverseIndex) Reset() {
c.contentTreeReverseIndexMap = &contentTreeReverseIndexMap{ c.init.ResetWithLock().Unlock()
m: make(map[any]contentNodeI),
}
} }
func (c *contentTreeReverseIndex) Get(key any) contentNodeI { func (c *contentTreeReverseIndex) Get(key any) contentNodeI {
@ -972,7 +976,7 @@ func (c *contentTreeReverseIndex) Get(key any) contentNodeI {
} }
type contentTreeReverseIndexMap struct { type contentTreeReverseIndexMap struct {
init sync.Once init lazy.OnceMore
m map[any]contentNodeI m map[any]contentNodeI
} }

View file

@ -445,3 +445,38 @@ code_p3
b.AssertNoRenderShortcodesArtifacts() b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3_edited</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>") b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3_edited</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
} }
// Issue 13004.
func TestRenderShortcodesIncludeShortRefEdit(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableLiveReload = true
disableKinds = ["home", "taxonomy", "term", "section", "rss", "sitemap", "robotsTXT", "404"]
-- content/first/p1.md --
---
title: "p1"
---
## p1-h1
{{% include "p2" %}}
-- content/second/p2.md --
---
title: "p2"
---
### p2-h1
This is some **markup**.
-- layouts/shortcodes/include.html --
{{ $p := site.GetPage (.Get 0) -}}
{{ $p.RenderShortcodes -}}
-- layouts/_default/single.html --
{{ .Content }}
`
b := TestRunning(t, files)
b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContentEquals("public/first/p1/index.html", "<h2 id=\"p1-h1\">p1-h1</h2>\n<p></p>\n<h3 id=\"p2-h1\">p2-h1</h3>\n<p>This is some <strong>markup</strong>.\n</p>\n")
b.EditFileReplaceAll("content/second/p2.md", "p2-h1", "p2-h1-edited").Build()
b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContentEquals("public/first/p1/index.html", "<h2 id=\"p1-h1\">p1-h1</h2>\n<p></p>\n<h3 id=\"p2-h1-edited\">p2-h1-edited</h3>\n<p>This is some <strong>markup</strong>.\n</p>\n")
}

View file

@ -1351,6 +1351,7 @@ func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string {
func (s *Site) resetBuildState(sourceChanged bool) { func (s *Site) resetBuildState(sourceChanged bool) {
s.relatedDocsHandler = s.relatedDocsHandler.Clone() s.relatedDocsHandler = s.relatedDocsHandler.Clone()
s.init.Reset() s.init.Reset()
s.pageMap.Reset()
} }
func (s *Site) errorCollator(results <-chan error, errs chan<- error) { func (s *Site) errorCollator(results <-chan error, errs chan<- error) {

View file

@ -36,7 +36,7 @@ type Init struct {
prev *Init prev *Init
children []*Init children []*Init
init onceMore init OnceMore
out any out any
err error err error
f func(context.Context) (any, error) f func(context.Context) (any, error)

View file

@ -18,19 +18,19 @@ import (
"sync/atomic" "sync/atomic"
) )
// onceMore is similar to sync.Once. // OnceMore is similar to sync.Once.
// //
// Additional features are: // Additional features are:
// * it can be reset, so the action can be repeated if needed // * it can be reset, so the action can be repeated if needed
// * it has methods to check if it's done or in progress // * it has methods to check if it's done or in progress
type onceMore struct { type OnceMore struct {
mu sync.Mutex mu sync.Mutex
lock uint32 lock uint32
done uint32 done uint32
} }
func (t *onceMore) Do(f func()) { func (t *OnceMore) Do(f func()) {
if atomic.LoadUint32(&t.done) == 1 { if atomic.LoadUint32(&t.done) == 1 {
return return
} }
@ -53,15 +53,15 @@ func (t *onceMore) Do(f func()) {
f() f()
} }
func (t *onceMore) InProgress() bool { func (t *OnceMore) InProgress() bool {
return atomic.LoadUint32(&t.lock) == 1 return atomic.LoadUint32(&t.lock) == 1
} }
func (t *onceMore) Done() bool { func (t *OnceMore) Done() bool {
return atomic.LoadUint32(&t.done) == 1 return atomic.LoadUint32(&t.done) == 1
} }
func (t *onceMore) ResetWithLock() *sync.Mutex { func (t *OnceMore) ResetWithLock() *sync.Mutex {
t.mu.Lock() t.mu.Lock()
defer atomic.StoreUint32(&t.done, 0) defer atomic.StoreUint32(&t.done, 0)
return &t.mu return &t.mu