]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix server rebuilds when adding a content file on Linux
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 16 Apr 2024 07:32:08 +0000 (09:32 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 16 Apr 2024 10:06:37 +0000 (12:06 +0200)
Fixes #12362

hugolib/hugo_sites_build.go
hugolib/integrationtest_builder.go
hugolib/rebuild_test.go
hugolib/site.go

index a77900e2753e474000de1b93463da8fc0679f215..411f90734e7587ed14ef5a55ebdf019d7ce9171e 100644 (file)
@@ -610,7 +610,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
 
        // For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
        events = h.fileEventsFilter(events)
-       events = h.fileEventsTranslate(events)
+       events = h.fileEventsTrim(events)
        eventInfos := h.fileEventsApplyInfo(events)
 
        logger := h.Log
index be11c18d6921a9a0dffe0d921515942edfd4ffba..8daf7b1caffe77c5f286a767f45b362f3ee39122 100644 (file)
@@ -10,6 +10,7 @@ import (
        "os"
        "path/filepath"
        "regexp"
+       "runtime"
        "sort"
        "strings"
        "sync"
@@ -685,8 +686,17 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error {
        return nil
 }
 
+// We simulate the fsnotify events.
+// See the test output in https://github.com/bep/fsnotifyeventlister for what events gets produced
+// by the different OSes.
 func (s *IntegrationTestBuilder) changeEvents() []fsnotify.Event {
-       var events []fsnotify.Event
+       var (
+               events    []fsnotify.Event
+               isLinux   = runtime.GOOS == "linux"
+               isMacOs   = runtime.GOOS == "darwin"
+               isWindows = runtime.GOOS == "windows"
+       )
+
        for _, v := range s.removedFiles {
                events = append(events, fsnotify.Event{
                        Name: v,
@@ -713,12 +723,32 @@ func (s *IntegrationTestBuilder) changeEvents() []fsnotify.Event {
                        Name: v,
                        Op:   fsnotify.Write,
                })
+               if isLinux || isWindows {
+                       // Duplicate write events, for some reason.
+                       events = append(events, fsnotify.Event{
+                               Name: v,
+                               Op:   fsnotify.Write,
+                       })
+               }
+               if isMacOs {
+                       events = append(events, fsnotify.Event{
+                               Name: v,
+                               Op:   fsnotify.Chmod,
+                       })
+               }
        }
        for _, v := range s.createdFiles {
                events = append(events, fsnotify.Event{
                        Name: v,
                        Op:   fsnotify.Create,
                })
+               if isLinux || isWindows {
+                       events = append(events, fsnotify.Event{
+                               Name: v,
+                               Op:   fsnotify.Write,
+                       })
+               }
+
        }
 
        // Shuffle events.
index 23e3e053208f766e78ee004231eaa4d6f08b9614..ea7efcb6fa672a0efe29a0626b9e9933fabb9d84 100644 (file)
@@ -1553,3 +1553,27 @@ Single: {{ .Title }}|{{ .Content }}|
        b.AssertRenderCountPage(1)
        b.AssertRenderCountContent(1)
 }
+
+func TestRebuildEditSingleListChangeUbuntuIssue12362(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+disableKinds = ['rss','section','sitemap','taxonomy','term']
+disableLiveReload = true
+-- layouts/_default/list.html --
+{{ range .Pages }}{{ .Title }}|{{ end }}
+-- layouts/_default/single.html --
+{{ .Title }}
+-- content/p1.md --
+---
+title: p1
+---
+`
+
+       b := TestRunning(t, files)
+       b.AssertFileContent("public/index.html", "p1|")
+
+       b.AddFiles("content/p2.md", "---\ntitle: p2\n---").Build()
+       b.AssertFileContent("public/index.html", "p1|p2|") // this test passes, which doesn't match reality
+}
index 9ab361722c94629689b9b7fb566d2917c0b0c669..b66a1284b1a66d97d4d7131e98bb137d156f20f9 100644 (file)
@@ -424,7 +424,35 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
                events[n] = ev
                n++
        }
-       return events[:n]
+       events = events[:n]
+
+       eventOrdinal := func(e fsnotify.Event) int {
+               // Pull the structural changes to the top.
+               if e.Op.Has(fsnotify.Create) {
+                       return 1
+               }
+               if e.Op.Has(fsnotify.Remove) {
+                       return 2
+               }
+               if e.Op.Has(fsnotify.Rename) {
+                       return 3
+               }
+               if e.Op.Has(fsnotify.Write) {
+                       return 4
+               }
+               return 5
+       }
+
+       sort.Slice(events, func(i, j int) bool {
+               // First sort by event type.
+               if eventOrdinal(events[i]) != eventOrdinal(events[j]) {
+                       return eventOrdinal(events[i]) < eventOrdinal(events[j])
+               }
+               // Then sort by name.
+               return events[i].Name < events[j].Name
+       })
+
+       return events
 }
 
 type fileEventInfo struct {
@@ -494,41 +522,17 @@ func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo
        return infos
 }
 
-func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
-       eventMap := make(map[string][]fsnotify.Event)
-
-       // We often get a Remove etc. followed by a Create, a Create followed by a Write.
-       // Remove the superfluous events to make the update logic simpler.
-       for _, ev := range events {
-               eventMap[ev.Name] = append(eventMap[ev.Name], ev)
-       }
-
+func (h *HugoSites) fileEventsTrim(events []fsnotify.Event) []fsnotify.Event {
+       seen := make(map[string]bool)
        n := 0
        for _, ev := range events {
-               mapped := eventMap[ev.Name]
-
-               // Keep one
-               found := false
-               var kept fsnotify.Event
-               for i, ev2 := range mapped {
-                       if i == 0 {
-                               kept = ev2
-                       }
-
-                       if ev2.Op&fsnotify.Write == fsnotify.Write {
-                               kept = ev2
-                               found = true
-                       }
-
-                       if !found && ev2.Op&fsnotify.Create == fsnotify.Create {
-                               kept = ev2
-                       }
+               if seen[ev.Name] {
+                       continue
                }
-
-               events[n] = kept
+               seen[ev.Name] = true
+               events[n] = ev
                n++
        }
-
        return events
 }