]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Make sure that unreferenced but changed bundle resources gets republished
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 28 May 2025 07:18:16 +0000 (09:18 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 28 May 2025 17:32:28 +0000 (19:32 +0200)
Fixes #13748

hugolib/content_map_page.go
hugolib/hugo_sites_build.go
hugolib/page.go
hugolib/page__new.go
hugolib/rebuild_test.go
resources/resource.go
resources/resource_spec.go
resources/transform.go

index ef2daca74b76d6cb53b8226098f94479757c59b2..47637d3e8dbb748c69f9ce8289e37aa37b93aea9 100644 (file)
@@ -21,7 +21,6 @@ import (
        "sort"
        "strconv"
        "strings"
-       "sync"
        "sync/atomic"
        "time"
 
@@ -1310,7 +1309,6 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
                }
 
                po.renderState = 0
-               po.p.resourcesPublishInit = &sync.Once{}
                if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatus404HTMLFormat.Name {
                        // Will force a re-render even in fast render mode.
                        po.renderOnce = false
index ce8ddd14365edcb541a1d4b2eec226173d0bf72a..b1fe279f4afc9cc151ebec8b195fa160351b5bde 100644 (file)
@@ -905,7 +905,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
        handleChange := func(pathInfo *paths.Path, delete, isDir bool) {
                switch pathInfo.Component() {
                case files.ComponentFolderContent:
-                       logger.Println("Source changed", pathInfo.Path())
                        isContentDataFile := pathInfo.IsContentData()
                        if !isContentDataFile {
                                if ids := h.pageTrees.collectAndMarkStaleIdentities(pathInfo); len(ids) > 0 {
index 84d4d1ea883577f54b3ca9a5ad3e21f047898358..bdfc66e87f7179174b44371291086235ea8386ed 100644 (file)
@@ -19,7 +19,6 @@ import (
        "path/filepath"
        "strconv"
        "strings"
-       "sync"
        "sync/atomic"
 
        "github.com/gohugoio/hugo/hugofs"
@@ -29,6 +28,7 @@ import (
        "github.com/gohugoio/hugo/media"
        "github.com/gohugoio/hugo/output"
        "github.com/gohugoio/hugo/related"
+       "github.com/gohugoio/hugo/resources"
        "github.com/gohugoio/hugo/tpl/tplimpl"
        "github.com/spf13/afero"
 
@@ -110,8 +110,7 @@ type pageState struct {
        *pageCommon
 
        resource.Staler
-       dependencyManager    identity.Manager
-       resourcesPublishInit *sync.Once
+       dependencyManager identity.Manager
 }
 
 func (p *pageState) incrPageOutputTemplateVariation() {
@@ -522,39 +521,41 @@ func (p *pageState) initPage() error {
 }
 
 func (p *pageState) renderResources() error {
-       var initErr error
+       for _, r := range p.Resources() {
 
-       p.resourcesPublishInit.Do(func() {
-               for _, r := range p.Resources() {
-                       if _, ok := r.(page.Page); ok {
+               if _, ok := r.(page.Page); ok {
+                       if p.s.h.buildCounter.Load() == 0 {
                                // Pages gets rendered with the owning page but we count them here.
                                p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Pages)
-                               continue
                        }
+                       continue
+               }
 
-                       if _, isWrapper := r.(resource.ResourceWrapper); isWrapper {
-                               // Skip resources that are wrapped.
-                               // These gets published on its own.
-                               continue
-                       }
+               if resources.IsPublished(r) {
+                       continue
+               }
 
-                       src, ok := r.(resource.Source)
-                       if !ok {
-                               initErr = fmt.Errorf("resource %T does not support resource.Source", r)
-                               return
-                       }
+               if _, isWrapper := r.(resource.ResourceWrapper); isWrapper {
+                       // Skip resources that are wrapped.
+                       // These gets published on its own.
+                       continue
+               }
 
-                       if err := src.Publish(); err != nil {
-                               if !herrors.IsNotExist(err) {
-                                       p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
-                               }
-                       } else {
-                               p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
+               src, ok := r.(resource.Source)
+               if !ok {
+                       return fmt.Errorf("resource %T does not support resource.Source", r)
+               }
+
+               if err := src.Publish(); err != nil {
+                       if !herrors.IsNotExist(err) {
+                               p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
                        }
+               } else {
+                       p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
                }
-       })
+       }
 
-       return initErr
+       return nil
 }
 
 func (p *pageState) AlternativeOutputFormats() page.OutputFormats {
index 10336e255832d1a0f31a4966904ae4c7eafe4002..9d05abea6b5eb2c2b7d14e18cf2205003ab677ca 100644 (file)
@@ -16,7 +16,6 @@ package hugolib
 import (
        "fmt"
        "strings"
-       "sync"
        "sync/atomic"
 
        "github.com/gohugoio/hugo/hugofs/files"
@@ -190,7 +189,6 @@ func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
                        pid:                               pid,
                        pageOutput:                        nopPageOutput,
                        pageOutputTemplateVariationsState: &atomic.Uint32{},
-                       resourcesPublishInit:              &sync.Once{},
                        Staler:                            m,
                        dependencyManager:                 m.s.Conf.NewIdentityManager(m.Path()),
                        pageCommon: &pageCommon{
index 0b22b9aa7c627bd3a55a8dd5cb9823be44701e6b..d4a15fb5bf14b12c531dab519a63208e86191713 100644 (file)
@@ -1946,3 +1946,23 @@ tags: ["tag1"]
        // But that is a harder problem to tackle.
        b.AssertFileContent("public/tags/index.html", "All. Tag1|Tag2|")
 }
+
+func TestRebuildEditNonReferencedResourceIssue13748(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+disableLiveReload = true
+-- content/mybundle/index.md --
+-- content/mybundle/resource.txt --
+This is a resource file.
+-- layouts/all.html --
+All.
+`
+       b := TestRunning(t, files)
+
+       b.AssertFileContent("public/mybundle/resource.txt", "This is a resource file.")
+       b.EditFileReplaceAll("content/mybundle/resource.txt", "This is a resource file.", "This is an edited resource file.").Build()
+       b.AssertFileContent("public/mybundle/resource.txt", "This is an edited resource file.")
+}
index 6ef9bdae050b1529dc727b436399d98c0a86a163..f6e5b9d7353409bf8bff82a87267ce566d5af893 100644 (file)
@@ -24,6 +24,7 @@ import (
        "sync/atomic"
 
        "github.com/gohugoio/hugo/identity"
+       "github.com/gohugoio/hugo/lazy"
        "github.com/gohugoio/hugo/resources/internal"
 
        "github.com/gohugoio/hugo/common/hashing"
@@ -54,6 +55,7 @@ var (
        _ identity.DependencyManagerProvider = (*genericResource)(nil)
        _ identity.Identity                  = (*genericResource)(nil)
        _ fileInfo                           = (*genericResource)(nil)
+       _ isPublishedProvider                = (*genericResource)(nil)
 )
 
 type ResourceSourceDescriptor struct {
@@ -242,6 +244,7 @@ type baseResourceInternal interface {
        fileInfo
        mediaTypeAssigner
        targetPather
+       isPublishedProvider
 
        ReadSeekCloser() (hugio.ReadSeekCloser, error)
 
@@ -355,7 +358,7 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo {
 
 // genericResource represents a generic linkable resource.
 type genericResource struct {
-       publishInit *sync.Once
+       publishInit *lazy.OnceMore
 
        key     string
        keyInit *sync.Once
@@ -536,6 +539,10 @@ func (l *genericResource) Publish() error {
        return err
 }
 
+func (l *genericResource) isPublished() bool {
+       return l.publishInit.Done()
+}
+
 func (l *genericResource) RelPermalink() string {
        return l.spec.PathSpec.GetBasePath(false) + paths.PathEscape(l.paths.TargetLink())
 }
@@ -629,7 +636,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour
 }
 
 func (l genericResource) clone() *genericResource {
-       l.publishInit = &sync.Once{}
+       l.publishInit = &lazy.OnceMore{}
        l.keyInit = &sync.Once{}
        return &l
 }
@@ -643,6 +650,10 @@ type targetPather interface {
        TargetPath() string
 }
 
+type isPublishedProvider interface {
+       isPublished() bool
+}
+
 type resourceHash struct {
        value    uint64
        size     int64
@@ -702,6 +713,11 @@ func InternalResourceSourcePathBestEffort(r resource.Resource) string {
        return InternalResourceTargetPath(r)
 }
 
+// isPublished returns true if the resource is published.
+func IsPublished(r resource.Resource) bool {
+       return r.(isPublishedProvider).isPublished()
+}
+
 type targetPathProvider interface {
        // targetPath is the relative path to this resource.
        // In most cases this will be the same as the RelPermalink(),
index 56ad9a27d6fd35d353b5c563c567b3853de3ce2f..806a5ff8d4b70b6b027a6de5b98e6626268d3286 100644 (file)
@@ -20,6 +20,7 @@ import (
 
        "github.com/gohugoio/hugo/config"
        "github.com/gohugoio/hugo/config/allconfig"
+       "github.com/gohugoio/hugo/lazy"
        "github.com/gohugoio/hugo/output"
        "github.com/gohugoio/hugo/resources/internal"
        "github.com/gohugoio/hugo/resources/jsconfig"
@@ -189,7 +190,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro
        gr := &genericResource{
                Staler:           &AtomicStaler{},
                h:                &resourceHash{},
-               publishInit:      &sync.Once{},
+               publishInit:      &lazy.OnceMore{},
                keyInit:          &sync.Once{},
                includeHashInKey: isImage,
                paths:            rp,
index abb55f3ccdd0ee932e85efcc68dbadd329cc5acd..572143d49faf5562a4e1667e63118e2bfd2d97bf 100644 (file)
@@ -61,6 +61,7 @@ var (
        _ identity.DependencyManagerProvider = (*resourceAdapter)(nil)
        _ identity.IdentityGroupProvider     = (*resourceAdapter)(nil)
        _ resource.NameNormalizedProvider    = (*resourceAdapter)(nil)
+       _ isPublishedProvider                = (*resourceAdapter)(nil)
 )
 
 // These are transformations that need special support in Hugo that may not
@@ -325,6 +326,11 @@ func (r *resourceAdapter) Publish() error {
        return r.target.Publish()
 }
 
+func (r *resourceAdapter) isPublished() bool {
+       r.init(false, false)
+       return r.target.isPublished()
+}
+
 func (r *resourceAdapter) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
        r.init(false, false)
        return r.target.ReadSeekCloser()