summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2024-04-16 09:32:08 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2024-04-16 12:06:37 +0200
commitfa60a2fbc317aa3b1fcfcaf2e842bdb439b8e7f1 (patch)
tree901e4f0d595f5e22908af979f1b2894a0aea95eb
parentfe63de3a83da351fa9b31498486a2f6538a70bde (diff)
Fix server rebuilds when adding a content file on Linux
Fixes #12362
-rw-r--r--hugolib/hugo_sites_build.go2
-rw-r--r--hugolib/integrationtest_builder.go32
-rw-r--r--hugolib/rebuild_test.go24
-rw-r--r--hugolib/site.go66
4 files changed, 91 insertions, 33 deletions
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index a77900e27..411f90734 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -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
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index be11c18d6..8daf7b1ca 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -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.
diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go
index 23e3e0532..ea7efcb6f 100644
--- a/hugolib/rebuild_test.go
+++ b/hugolib/rebuild_test.go
@@ -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
+}
diff --git a/hugolib/site.go b/hugolib/site.go
index 9ab361722..b66a1284b 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -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
}