Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3c980c0

Browse files
committed
resources: Re-publish on transformation cache hit
When a resource transformation result was served from cache (same options as a previous build), the output file was not re-written to disk. This caused incorrect output when toggling transformation options (e.g. minify) back to a previously seen value in server mode. Fixes #14629
1 parent 404ac00 commit 3c980c0

10 files changed

Lines changed: 105 additions & 9 deletions

File tree

deps/deps.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,9 @@ type DepsCfg struct {
441441
type BuildState struct {
442442
counter uint64
443443

444+
// Tracks invocations of the Build method.
445+
BuildCounter atomic.Uint64
446+
444447
mu sync.Mutex // protects state below.
445448

446449
OnSignalRebuild func(ids ...identity.Identity)
@@ -472,6 +475,11 @@ type DeferredExecutions struct {
472475

473476
var _ identity.SignalRebuilder = (*BuildState)(nil)
474477

478+
// IsRebuild reports whether this is a rebuild.
479+
func (b *BuildState) IsRebuild() bool {
480+
return b.BuildCounter.Load() > 0
481+
}
482+
475483
// StartStageRender will be called before a stage is rendered.
476484
func (b *BuildState) StartStageRender(stage tpl.RenderingContext) {
477485
}

hugolib/hugo_sites.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ type HugoSites struct {
123123
*progressReporter
124124
*fatalErrorHandler
125125
*buildCounters
126-
// Tracks invocations of the Build method.
127-
buildCounter atomic.Uint64
128126
}
129127

130128
// hugoSitesSitesProvider is a wrapper that implements page.SitesProvider.
@@ -253,7 +251,7 @@ func (h *HugoSites) Close() error {
253251
}
254252

255253
func (h *HugoSites) isRebuild() bool {
256-
return h.buildCounter.Load() > 0
254+
return h.BuildState.IsRebuild()
257255
}
258256

259257
func (h *HugoSites) resolveFirstSite(matrix sitesmatrix.VectorStore) *Site {
@@ -650,7 +648,7 @@ func (cfg *BuildCfg) shouldRender(infol logg.LevelLogger, p *pageState) bool {
650648

651649
fastRenderMode := p.s.Conf.FastRenderMode()
652650

653-
if !fastRenderMode || p.s.h.buildCounter.Load() == 0 {
651+
if !fastRenderMode || !p.s.h.BuildState.IsRebuild() {
654652
return shouldRender
655653
}
656654

hugolib/hugo_sites_build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
8383
h.reportProgress(func() (state terminal.ProgressState, progress float64) {
8484
return terminal.ProgressHidden, 1.0
8585
})
86-
h.buildCounter.Add(1)
86+
h.BuildState.BuildCounter.Add(1)
8787
}()
8888

8989
if h.Deps == nil {

hugolib/page.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ func (ps *pageState) initPage() error {
721721
func (ps *pageState) renderResources() error {
722722
for _, r := range ps.Resources() {
723723
if _, ok := r.(page.Page); ok {
724-
if ps.s.h.buildCounter.Load() == 0 {
724+
if !ps.s.h.BuildState.IsRebuild() {
725725
// Pages gets rendered with the owning page but we count them here.
726726
ps.s.PathSpec.ProcessingStats.Incr(&ps.s.PathSpec.ProcessingStats.Pages)
727727
}

hugolib/site_render.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func pageRenderer(
154154
}
155155
}
156156

157-
if !s.conf.DisableAliases && s.h.buildCounter.Load() == 0 {
157+
if !s.conf.DisableAliases && !s.h.BuildState.IsRebuild() {
158158
of := p.outputFormat()
159159
if of.IsHTML && of.Permalinkable {
160160
// Render any aliases for this page.

identity/identity.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ type SignalRebuilder interface {
245245
SignalRebuild(ids ...Identity)
246246
}
247247

248+
// IsRebuildProvider signals if we're in a rebuild or not.
249+
type IsRebuildProvider interface {
250+
IsRebuild() bool
251+
}
252+
248253
// IncrementByOne implements Incrementer adding 1 every time Incr is called.
249254
type IncrementByOne struct {
250255
counter uint64

resources/resource_cache.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"strings"
2323
"sync"
2424

25+
"github.com/gohugoio/hugo/common/hmaps"
2526
"github.com/gohugoio/hugo/resources/resource"
2627

2728
"github.com/gohugoio/hugo/cache/dynacache"
@@ -56,6 +57,8 @@ func newResourceCache(rs *Spec, memCache *dynacache.Cache) *ResourceCache {
5657
"/res1/tra",
5758
dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
5859
),
60+
61+
cacheResourceTransformationPublished: hmaps.NewMap[string, string](),
5962
}
6063
}
6164

@@ -68,6 +71,9 @@ type ResourceCache struct {
6871
cacheResources *dynacache.Partition[string, resource.Resources]
6972
cacheResourceTransformation *dynacache.Partition[string, *resourceAdapterInner]
7073

74+
// Used in rebuilds. Maps the target path to the last published transformation key.
75+
cacheResourceTransformationPublished *hmaps.Map[string, string]
76+
7177
fileCache *filecache.Cache
7278
}
7379

resources/resource_spec.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func NewSpec(
5757
errorHandler herrors.ErrorSender,
5858
execHelper *hexec.Exec,
5959
buildClosers types.CloseAdder,
60-
rebuilder identity.SignalRebuilder,
60+
rebuilder Rebuilder,
6161
) (*Spec, error) {
6262
conf := s.Cfg.GetConfig().(*allconfig.Config)
6363
imgConfig := conf.Imaging
@@ -118,13 +118,18 @@ func NewSpec(
118118
return rs, nil
119119
}
120120

121+
type Rebuilder interface {
122+
identity.SignalRebuilder
123+
identity.IsRebuildProvider
124+
}
125+
121126
type Spec struct {
122127
*helpers.PathSpec
123128

124129
Logger loggers.Logger
125130
ErrorSender herrors.ErrorSender
126131
BuildClosers types.CloseAdder
127-
Rebuilder identity.SignalRebuilder
132+
Rebuilder Rebuilder
128133

129134
Permalinks page.PermalinkExpander
130135

resources/transform.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,51 @@ func (r *resourceAdapter) TransformationKey() string {
455455

456456
func (r *resourceAdapter) getOrTransform(publish, setContent bool) error {
457457
key := r.TransformationKey()
458+
459+
var created bool
458460
res, err := r.spec.ResourceCache.cacheResourceTransformation.GetOrCreate(key, func(string) (*resourceAdapterInner, error) {
461+
created = true
459462
return r.transform(key, publish, setContent)
460463
})
461464
if err != nil {
462465
return err
463466
}
464467

465468
r.resourceAdapterInner = res
469+
470+
if publish && r.spec.Rebuilder.IsRebuild() {
471+
targetPath := r.target.TargetPath()
472+
var republish bool
473+
474+
r.spec.ResourceCache.cacheResourceTransformationPublished.WithWriteLock(func(m map[string]string) error {
475+
if created {
476+
m[targetPath] = key
477+
} else {
478+
key2, found := m[targetPath]
479+
republish = !found || key2 != key
480+
m[targetPath] = key
481+
}
482+
return nil
483+
})
484+
485+
if !created && republish {
486+
src, err := contentReadSeekerCloser(r.target)
487+
if err != nil {
488+
return err
489+
}
490+
defer src.Close()
491+
dest, err := r.target.openPublishFileForWriting(targetPath)
492+
if err != nil {
493+
return err
494+
}
495+
defer dest.Close()
496+
_, err = io.Copy(dest, src)
497+
if err != nil {
498+
return err
499+
}
500+
}
501+
}
502+
466503
return nil
467504
}
468505

tpl/css/build_integration_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,43 @@ Home.
169169
b.AssertFileContent("public/css/main.css", `{background:red}`)
170170
}
171171

172+
func TestCSSBuildEditOptionsMultiple(t *testing.T) {
173+
t.Parallel()
174+
175+
files := `
176+
-- hugo.toml --
177+
disableKinds = ["taxonomy", "term", "rss"]
178+
disableLiveReload = true
179+
-- assets/css/main.css --
180+
body {
181+
background: red;
182+
}
183+
-- layouts/_partials/css.html --
184+
{{ with resources.Get "css/main.css" }}
185+
{{ $opts := dict "minify" false }}
186+
{{ with . | css.Build $opts }}
187+
<link rel="stylesheet" href="{{ .RelPermalink }}" />
188+
{{ end }}
189+
{{ end }}
190+
-- layouts/all.html --
191+
All. {{ partial "css.html" . }}
192+
-- content/p1.md --
193+
-- content/p2.md --
194+
-- content/p3.md --
195+
196+
197+
`
198+
199+
b := hugolib.TestRunning(t, files, hugolib.TestOptOsFs())
200+
201+
for range 3 {
202+
b.AssertFileContent("public/css/main.css", ` background: red;`)
203+
b.EditFileReplaceAll("layouts/_partials/css.html", `"minify" false`, `"minify" true`).Build()
204+
b.AssertFileContent("public/css/main.css", `{background:red}`)
205+
b.EditFileReplaceAll("layouts/_partials/css.html", `"minify" true`, `"minify" false`).Build()
206+
}
207+
}
208+
172209
func TestCSSBuildSourceMaps(t *testing.T) {
173210
t.Parallel()
174211

0 commit comments

Comments
 (0)