diff --git a/hugolib/image_test.go b/hugolib/image_test.go index 4db57e4a5..a13338afc 100644 --- a/hugolib/image_test.go +++ b/hugolib/image_test.go @@ -211,4 +211,7 @@ SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Ex b.AssertFileContent("resources/_gen/images/sunset_17701188623491591036.json", "DateTimeDigitized|time.Time", "PENTAX") + // TODO(bep) add this as a default assertion after Build()? + b.AssertNoDuplicateWrites() + } diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go index 6e62a2442..6e00a8ee0 100644 --- a/hugolib/testhelpers_test.go +++ b/hugolib/testhelpers_test.go @@ -486,6 +486,8 @@ func (s *sitesBuilder) CreateSitesE() error { return errors.Wrap(err, "failed to load config") } + s.Fs.Destination = hugofs.NewCreateCountingFs(s.Fs.Destination) + depsCfg := s.depsCfg depsCfg.Fs = s.Fs depsCfg.Cfg = s.Cfg @@ -680,6 +682,12 @@ func (s *sitesBuilder) AssertImage(width, height int, filename string) { s.Assert(cfg.Height, qt.Equals, height) } +func (s *sitesBuilder) AssertNoDuplicateWrites() { + s.Helper() + d := s.Fs.Destination.(hugofs.DuplicatesReporter) + s.Assert(d.ReportDuplicates(), qt.Equals, "") +} + func (s *sitesBuilder) FileContent(filename string) string { s.T.Helper() filename = filepath.FromSlash(filename) diff --git a/resources/resource.go b/resources/resource.go index 637f8e8fd..7e755bdbc 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -233,19 +233,26 @@ func (l *genericResource) Permalink() string { } func (l *genericResource) Publish() error { - fr, err := l.ReadSeekCloser() - if err != nil { - return err - } - defer fr.Close() + var err error + l.publishInit.Do(func() { + var fr hugio.ReadSeekCloser + fr, err = l.ReadSeekCloser() + if err != nil { + return + } + defer fr.Close() - fw, err := helpers.OpenFilesForWriting(l.spec.BaseFs.PublishFs, l.getTargetFilenames()...) - if err != nil { - return err - } - defer fw.Close() + var fw io.WriteCloser + fw, err = helpers.OpenFilesForWriting(l.spec.BaseFs.PublishFs, l.getTargetFilenames()...) + if err != nil { + return + } + defer fw.Close() + + _, err = io.Copy(fw, fr) + + }) - _, err = io.Copy(fw, fr) return err } @@ -400,26 +407,34 @@ func (l genericResource) clone() *genericResource { return &l } -// returns an opened file or nil if nothing to write. -func (l *genericResource) openDestinationsForWriting() (io.WriteCloser, error) { - targetFilenames := l.getTargetFilenames() - var changedFilenames []string +// returns an opened file or nil if nothing to write (it may already be published). +func (l *genericResource) openDestinationsForWriting() (w io.WriteCloser, err error) { - // Fast path: - // This is a processed version of the original; - // check if it already existis at the destination. - for _, targetFilename := range targetFilenames { - if _, err := l.getSpec().BaseFs.PublishFs.Stat(targetFilename); err == nil { - continue + l.publishInit.Do(func() { + targetFilenames := l.getTargetFilenames() + var changedFilenames []string + + // Fast path: + // This is a processed version of the original; + // check if it already existis at the destination. + for _, targetFilename := range targetFilenames { + if _, err := l.getSpec().BaseFs.PublishFs.Stat(targetFilename); err == nil { + continue + } + + changedFilenames = append(changedFilenames, targetFilename) } - changedFilenames = append(changedFilenames, targetFilename) - } - if len(changedFilenames) == 0 { - return nil, nil - } + if len(changedFilenames) == 0 { + return + } + + w, err = helpers.OpenFilesForWriting(l.getSpec().BaseFs.PublishFs, changedFilenames...) + + }) + + return - return helpers.OpenFilesForWriting(l.getSpec().BaseFs.PublishFs, changedFilenames...) } func (r *genericResource) openPublishFileForWriting(relTargetPath string) (io.WriteCloser, error) { @@ -524,6 +539,8 @@ type permalinker interface { type resourceContent struct { content string contentInit sync.Once + + publishInit sync.Once } type resourceFileInfo struct {