From 68f67c9aebfeb63aa12d69e86b1d652d6ce63ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 16 Feb 2024 14:24:35 +0100 Subject: Fix rebuild regression on non-default content language edits Fixes #12043 --- hugolib/content_map_page.go | 45 ++++++++++++++++++++++--------- hugolib/hugo_sites_build.go | 2 +- hugolib/rebuild_test.go | 53 +++++++++++++++++++++++++++++++++++++ resources/resource/resourcetypes.go | 6 ++++- 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 576ee5e08..70e8a0bb6 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -125,37 +125,52 @@ type pageTrees struct { resourceTrees doctree.MutableTrees } -// collectIdentities collects all identities from in all trees matching the given key. -// This will at most match in one tree, but may give identies from multiple dimensions (e.g. language). -func (t *pageTrees) collectIdentities(p *paths.Path) []identity.Identity { - ids := t.collectIdentitiesFor(p.Base()) +// collectAndMarkStaleIdentities collects all identities from in all trees matching the given key. +// We currently re-read all page/resources for all languages that share the same path, +// so we mark all entries as stale (which will trigger cache invalidation), then +// return the first. +func (t *pageTrees) collectAndMarkStaleIdentities(p *paths.Path) []identity.Identity { + ids := t.collectAndMarkStaleIdentitiesFor(p.Base()) if p.Component() == files.ComponentFolderContent { // It may also be a bundled content resource. - if n := t.treeResources.Get(p.ForBundleType(paths.PathTypeContentResource).Base()); n != nil { + key := p.ForBundleType(paths.PathTypeContentResource).Base() + tree := t.treeResources + if n := tree.Get(key); n != nil { n.ForEeachIdentity(func(id identity.Identity) bool { ids = append(ids, id) return false }) + if n, ok := tree.GetRaw(key); ok { + n.MarkStale() + } } } return ids } -func (t *pageTrees) collectIdentitiesFor(key string) []identity.Identity { +func (t *pageTrees) collectAndMarkStaleIdentitiesFor(key string) []identity.Identity { var ids []identity.Identity - if n := t.treePages.Get(key); n != nil { + tree := t.treePages + if n := tree.Get(key); n != nil { n.ForEeachIdentity(func(id identity.Identity) bool { ids = append(ids, id) return false }) + if n, ok := tree.GetRaw(key); ok { + n.MarkStale() + } } - if n := t.treeResources.Get(key); n != nil { + tree = t.treeResources + if n := tree.Get(key); n != nil { n.ForEeachIdentity(func(id identity.Identity) bool { ids = append(ids, id) return false }) + if n, ok := tree.GetRaw(key); ok { + n.MarkStale() + } } return ids @@ -626,9 +641,7 @@ func (n contentNodeIs) resetBuildState() { func (n contentNodeIs) MarkStale() { for _, nn := range n { - if nn != nil { - nn.MarkStale() - } + resource.MarkStale(nn) } } @@ -799,6 +812,7 @@ func (s *contentNodeShifter) Insert(old, new contentNodeI) contentNodeI { if !ok { panic(fmt.Sprintf("unknown type %T", new)) } + resource.MarkStale(vv[newp.s.languagei]) vv[newp.s.languagei] = new return vv case *resourceSource: @@ -818,6 +832,7 @@ func (s *contentNodeShifter) Insert(old, new contentNodeI) contentNodeI { if !ok { panic(fmt.Sprintf("unknown type %T", new)) } + resource.MarkStale(vv[newp.LangIndex()]) vv[newp.LangIndex()] = newp return vv default: @@ -1014,8 +1029,12 @@ func (h *HugoSites) resolveAndClearStateForIdentities( ) for _, id := range changes { - if staler, ok := id.(resource.Staler); ok { - h.Log.Trace(logg.StringFunc(func() string { return fmt.Sprintf("Marking stale: %s (%T)\n", id, id) })) + if staler, ok := id.(resource.Staler); ok && !staler.IsStale() { + var msgDetail string + if p, ok := id.(*pageState); ok && p.File() != nil { + msgDetail = fmt.Sprintf(" (%s)", p.File().Filename()) + } + h.Log.Trace(logg.StringFunc(func() string { return fmt.Sprintf("Marking stale: %s (%T)%s\n", id, id, msgDetail) })) staler.MarkStale() } } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 17a3f0056..aa43d541a 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -702,7 +702,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf switch pathInfo.Component() { case files.ComponentFolderContent: logger.Println("Source changed", pathInfo.Path()) - if ids := h.pageTrees.collectIdentities(pathInfo); len(ids) > 0 { + if ids := h.pageTrees.collectAndMarkStaleIdentities(pathInfo); len(ids) > 0 { changes = append(changes, ids...) } diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go index 06a6083f5..6e43a4570 100644 --- a/hugolib/rebuild_test.go +++ b/hugolib/rebuild_test.go @@ -1171,6 +1171,59 @@ Hello: {{ i18n "hello" }} b.AssertFileContent("public/index.html", "Hello: Hugo") } +func TestRebuildEditContentNonDefaultLanguage(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +defaultContentLanguage = "en" +defaultContentLanguageInSubdir = true +[languages] +[languages.en] +weight = 1 +[languages.nn] +weight = 2 +-- content/p1/index.en.md -- +--- +title: "P1 en" +--- +P1 en. +-- content/p1/b.en.md -- +--- +title: "B en" +--- +B en. +-- content/p1/f1.en.txt -- +F1 en +-- content/p1/index.nn.md -- +--- +title: "P1 nn" +--- +P1 nn. +-- content/p1/b.nn.md -- +--- +title: "B nn" +--- +B nn. +-- content/p1/f1.nn.txt -- +F1 nn +-- layouts/_default/single.html -- +Single: {{ .Title }}|{{ .Content }}|Bundled File: {{ with .Resources.GetMatch "f1.*" }}{{ .Content }}{{ end }}|Bundled Page: {{ with .Resources.GetMatch "b.*" }}{{ .Content }}{{ end }}| +` + + b := TestRunning(t, files) + + b.AssertFileContent("public/nn/p1/index.html", "Single: P1 nn|

P1 nn.

", "F1 nn|") + b.EditFileReplaceAll("content/p1/index.nn.md", "P1 nn.", "P1 nn edit.").Build() + b.AssertFileContent("public/nn/p1/index.html", "Single: P1 nn|

P1 nn edit.

\n|") + b.EditFileReplaceAll("content/p1/f1.nn.txt", "F1 nn", "F1 nn edit.").Build() + b.AssertFileContent("public/nn/p1/index.html", "Bundled File: F1 nn edit.") + b.EditFileReplaceAll("content/p1/b.nn.md", "B nn.", "B nn edit.").Build() + b.AssertFileContent("public/nn/p1/index.html", "B nn edit.") +} + func TestRebuildVariationsAssetsSassImport(t *testing.T) { if !htesting.IsCI() { t.Skip("skip CI only") diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go index 43d0aa786..7a735dca5 100644 --- a/resources/resource/resourcetypes.go +++ b/resources/resource/resourcetypes.go @@ -17,6 +17,7 @@ import ( "context" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/media" @@ -250,7 +251,10 @@ func IsStaleAny(os ...any) bool { // MarkStale will mark any of the oses as stale, if possible. func MarkStale(os ...any) { for _, o := range os { - if s, ok := o.(Staler); ok { + if types.IsNil(o) { + continue + } + if s, ok := o.(StaleMarker); ok { s.MarkStale() } } -- cgit v1.2.3