diff options
author | Bjørn Erik Pedersen <[email protected]> | 2024-04-16 09:32:08 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2024-04-16 12:06:37 +0200 |
commit | fa60a2fbc317aa3b1fcfcaf2e842bdb439b8e7f1 (patch) | |
tree | 901e4f0d595f5e22908af979f1b2894a0aea95eb | |
parent | fe63de3a83da351fa9b31498486a2f6538a70bde (diff) | |
download | hugo-fa60a2fbc317aa3b1fcfcaf2e842bdb439b8e7f1.tar.gz hugo-fa60a2fbc317aa3b1fcfcaf2e842bdb439b8e7f1.zip |
Fix server rebuilds when adding a content file on Linux
Fixes #12362
-rw-r--r-- | hugolib/hugo_sites_build.go | 2 | ||||
-rw-r--r-- | hugolib/integrationtest_builder.go | 32 | ||||
-rw-r--r-- | hugolib/rebuild_test.go | 24 | ||||
-rw-r--r-- | hugolib/site.go | 66 |
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 } |