Skip to content

Commit

Permalink
Fix so temporary images do not get published
Browse files Browse the repository at this point in the history
Fixes #10255
  • Loading branch information
bep committed Jul 30, 2023
1 parent 87d9bff commit 3fb8d25
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 49 deletions.
18 changes: 18 additions & 0 deletions hugolib/integrationtest_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,24 @@ func (s *IntegrationTestBuilder) AssertBuildCountTranslations(count int) {
s.Assert(s.H.init.translations.InitCount(), qt.Equals, count)
}

func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) {
s.Helper()
fs := s.fs.WorkingDirReadOnly
count := 0
afero.Walk(fs, dirname, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
count++
return nil
})
s.Assert(count, qt.Equals, expected)

}

func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...string) {
s.Helper()
content := strings.TrimSpace(s.FileContent(filename))
Expand Down
22 changes: 2 additions & 20 deletions resources/image_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,28 +106,10 @@ func (c *ImageCache) getOrCreate(
rp := img.getResourcePaths()
rp.relTargetDirFile.file = relTarget.file
img.setSourceFilename(info.Name)
img.setSourfeFilenameIsHash(true)
img.setMediaType(conf.TargetFormat.MediaType())

if err := img.InitConfig(r); err != nil {
return err
}

r.Seek(0, 0)

w, err := img.openDestinationsForWriting()
if err != nil {
return err
}

if w == nil {
// Nothing to write.
return nil
}

defer w.Close()
_, err = io.Copy(w, r)

return err
return img.InitConfig(r)
}

// create creates the image and encodes it to the cache (w).
Expand Down
39 changes: 39 additions & 0 deletions resources/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,42 @@ Width: {{ $svg.Width }}
b.Assert(err.Error(), qt.Contains, `error calling Width: this method is only available for raster images. To determine if an image is SVG, you can do {{ if eq .MediaType.SubType "svg" }}{{ end }}`)

}

// Issue 10255.
func TestNoPublishOfUnusedProcessedImage(t *testing.T) {
t.Parallel()

workingDir := t.TempDir()

files := `
-- assets/images/pixel.png --
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
-- layouts/index.html --
{{ $image := resources.Get "images/pixel.png" }}
{{ $image = $image.Resize "400x" }}
{{ $image = $image.Resize "300x" }}
{{ $image = $image.Resize "200x" }}
{{ $image = $image.Resize "100x" }}
{{ $image = $image.Crop "50x50" }}
{{ $image = $image.Filter (images.GaussianBlur 6) }}
{{ ($image | fingerprint).Permalink }}
`

for i := 0; i < 3; i++ {

b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
NeedsOsFS: true,
WorkingDir: workingDir,
}).Build()

b.AssertFileCount("resources/_gen/images", 6)
b.AssertFileCount("public/images", 1)
b.Build()
}

}
53 changes: 24 additions & 29 deletions resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ type baseResourceInternal interface {
specProvider
getResourcePaths() *resourcePathDescriptor
getTargetFilenames() []string
openDestinationsForWriting() (io.WriteCloser, error)
openPublishFileForWriting(relTargetPath string) (io.WriteCloser, error)

relTargetPathForRel(rel string, addBaseTargetPath, isAbs, isURL bool) string
Expand Down Expand Up @@ -216,6 +215,7 @@ func (d dirFile) path() string {
type fileInfo interface {
getSourceFilename() string
setSourceFilename(string)
setSourfeFilenameIsHash(bool)
setSourceFs(afero.Fs)
getFileInfo() hugofs.FileMetaInfo
hash() (string, error)
Expand Down Expand Up @@ -304,6 +304,21 @@ func (l *genericResource) Permalink() string {
func (l *genericResource) Publish() error {
var err error
l.publishInit.Do(func() {
targetFilenames := l.getTargetFilenames()
if l.sourceFilenameIsHash {
// This is a processed image. We want to avoid copying it if it hasn't changed.
var changedFilenames []string
for _, targetFilename := range targetFilenames {
if _, err := l.getSpec().BaseFs.PublishFs.Stat(targetFilename); err == nil {
continue
}
changedFilenames = append(changedFilenames, targetFilename)
}
if len(changedFilenames) == 0 {
return
}
targetFilenames = changedFilenames
}
var fr hugio.ReadSeekCloser
fr, err = l.ReadSeekCloser()
if err != nil {
Expand All @@ -312,7 +327,7 @@ func (l *genericResource) Publish() error {
defer fr.Close()

var fw io.WriteCloser
fw, err = helpers.OpenFilesForWriting(l.spec.BaseFs.PublishFs, l.getTargetFilenames()...)
fw, err = helpers.OpenFilesForWriting(l.spec.BaseFs.PublishFs, targetFilenames...)
if err != nil {
return
}
Expand Down Expand Up @@ -475,33 +490,6 @@ func (l genericResource) clone() *genericResource {
return &l
}

// returns an opened file or nil if nothing to write (it may already be published).
func (l *genericResource) openDestinationsForWriting() (w io.WriteCloser, err error) {
l.publishInit.Do(func() {
targetFilenames := l.getTargetFilenames()
var changedFilenames []string

// Fast path:
// This is a processed version of the original;
// check if it already exists at the destination.
for _, targetFilename := range targetFilenames {
if _, err := l.getSpec().BaseFs.PublishFs.Stat(targetFilename); err == nil {
continue
}

changedFilenames = append(changedFilenames, targetFilename)
}

if len(changedFilenames) == 0 {
return
}

w, err = helpers.OpenFilesForWriting(l.getSpec().BaseFs.PublishFs, changedFilenames...)
})

return
}

func (r *genericResource) openPublishFileForWriting(relTargetPath string) (io.WriteCloser, error) {
return helpers.OpenFilesForWriting(r.spec.BaseFs.PublishFs, r.relTargetPathsFor(relTargetPath)...)
}
Expand Down Expand Up @@ -622,6 +610,9 @@ type resourceFileInfo struct {
// the path to the file on the real filesystem.
sourceFilename string

// For performance. This means that whenever the content changes, the filename changes.
sourceFilenameIsHash bool

fi hugofs.FileMetaInfo

// A hash of the source content. Is only calculated in caching situations.
Expand Down Expand Up @@ -654,6 +645,10 @@ func (fi *resourceFileInfo) setSourceFilename(s string) {
fi.sourceFilename = s
}

func (fi *resourceFileInfo) setSourfeFilenameIsHash(b bool) {
fi.sourceFilenameIsHash = b
}

func (fi *resourceFileInfo) getSourceFs() afero.Fs {
return fi.sourceFs
}
Expand Down

0 comments on commit 3fb8d25

Please sign in to comment.