From 8a58ebb311fd079f65068e7e37725e4d43f17ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 20 Dec 2019 08:11:36 +0100 Subject: hugolib: Improve error and reload handling of hook templates in server mode Fixes #6635 --- hugolib/content_render_hooks_test.go | 54 ++++++++++++++++++++++++++++++++++++ hugolib/hugo_sites.go | 19 +++++++++++-- hugolib/page.go | 7 +++-- hugolib/site.go | 17 ++++++++++-- hugolib/testhelpers_test.go | 32 +++++++++++++-------- 5 files changed, 111 insertions(+), 18 deletions(-) (limited to 'hugolib') diff --git a/hugolib/content_render_hooks_test.go b/hugolib/content_render_hooks_test.go index d206013ba..ee7a02074 100644 --- a/hugolib/content_render_hooks_test.go +++ b/hugolib/content_render_hooks_test.go @@ -158,6 +158,60 @@ SHORT3| } +func TestRenderHooksDeleteTemplate(t *testing.T) { + config := ` +baseURL="https://example.org" +workingDir="/mywork" +` + b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running() + b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`) + b.WithTemplatesAdded("_default/_markup/render-link.html", `html-render-link`) + + b.WithContent("p1.md", `--- +title: P1 +--- +[First Link](https://www.google.com "Google's Homepage") + +`) + b.Build(BuildCfg{}) + + b.AssertFileContent("public/p1/index.html", `

html-render-link

`) + + b.RemoveFiles( + "layouts/_default/_markup/render-link.html", + ) + + b.Build(BuildCfg{}) + b.AssertFileContent("public/p1/index.html", `

First Link

`) + +} + +func TestRenderHookAddTemplate(t *testing.T) { + config := ` +baseURL="https://example.org" +workingDir="/mywork" +` + b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running() + b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`) + + b.WithContent("p1.md", `--- +title: P1 +--- +[First Link](https://www.google.com "Google's Homepage") + +`) + b.Build(BuildCfg{}) + + b.AssertFileContent("public/p1/index.html", `

First Link

`) + + b.EditFiles("layouts/_default/_markup/render-link.html", `html-render-link`) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/p1/index.html", `

html-render-link

`) + +} + func TestRenderHooksRSS(t *testing.T) { b := newTestSitesBuilder(t) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 526f39fca..8c29e2a88 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -564,6 +564,9 @@ type BuildCfg struct { // we should skip most of the processing. PartialReRender bool + // Set in server mode when the last build failed for some reason. + ErrRecovery bool + // Recently visited URLs. This is used for partial re-rendering. RecentlyVisited map[string]bool } @@ -807,8 +810,20 @@ func (h *HugoSites) findPagesByKindIn(kind string, inPages page.Pages) page.Page return h.Sites[0].findPagesByKindIn(kind, inPages) } -func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) { +func (h *HugoSites) resetPageState() { + for _, s := range h.Sites { + for _, p := range s.rawAllPages { + for _, po := range p.pageOutputs { + if po.cp == nil { + continue + } + po.cp.Reset() + } + } + } +} +func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) { for _, s := range h.Sites { PAGES: for _, p := range s.rawAllPages { @@ -820,7 +835,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) { for id, _ := range idset { if po.cp.dependencyTracker.Search(id) != nil { po.cp.Reset() - p.forceRender = true continue OUTPUTS } } @@ -834,7 +848,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) { po.cp.Reset() } } - p.forceRender = true continue PAGES } } diff --git a/hugolib/page.go b/hugolib/page.go index fb3b597be..af3deb59f 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -629,9 +629,12 @@ func (p *pageState) Render(layout ...string) (template.HTML, error) { } -// wrapError adds some more context to the given error if possible +// wrapError adds some more context to the given error if possible/needed func (p *pageState) wrapError(err error) error { - + if _, ok := err.(*herrors.ErrorWithFileContext); ok { + // Preserve the first file context. + return err + } var filename string if !p.File().IsZero() { filename = p.File().Filename() diff --git a/hugolib/site.go b/hugolib/site.go index 866ff5624..eb232c629 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -909,6 +909,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro contentFilesChanged []string tmplChanged bool + tmplAdded bool dataChanged bool i18nChanged bool @@ -934,8 +935,16 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro logger.Println("Source changed", ev) sourceChanged = append(sourceChanged, ev) case files.ComponentFolderLayouts: - logger.Println("Template changed", ev) tmplChanged = true + if _, found := s.Tmpl.Lookup(id.Path); !found { + tmplAdded = true + } + if tmplAdded { + logger.Println("Template added", ev) + } else { + logger.Println("Template changed", ev) + } + case files.ComponentFolderData: logger.Println("Data changed", ev) dataChanged = true @@ -1021,7 +1030,11 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro sourceFilesChanged[ev.Name] = true } - h.resetPageStateFromEvents(changeIdentities) + if config.ErrRecovery || tmplAdded { + h.resetPageState() + } else { + h.resetPageStateFromEvents(changeIdentities) + } if len(sourceReallyChanged) > 0 || len(contentFilesChanged) > 0 { var filenamesChanged []string diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go index 80aafe052..93ea9477c 100644 --- a/hugolib/testhelpers_test.go +++ b/hugolib/testhelpers_test.go @@ -68,6 +68,7 @@ type sitesBuilder struct { // Used to test partial rebuilds. changedFiles []string + removedFiles []string // Aka the Hugo server mode. running bool @@ -386,16 +387,22 @@ func (s *sitesBuilder) WithI18nAdded(filenameContent ...string) *sitesBuilder { } func (s *sitesBuilder) EditFiles(filenameContent ...string) *sitesBuilder { - var changedFiles []string for i := 0; i < len(filenameContent); i += 2 { filename, content := filepath.FromSlash(filenameContent[i]), filenameContent[i+1] absFilename := s.absFilename(filename) - changedFiles = append(changedFiles, absFilename) + s.changedFiles = append(s.changedFiles, absFilename) writeSource(s.T, s.Fs, absFilename, content) } - s.changedFiles = changedFiles + return s +} +func (s *sitesBuilder) RemoveFiles(filenames ...string) *sitesBuilder { + for _, filename := range filenames { + absFilename := s.absFilename(filename) + s.removedFiles = append(s.removedFiles, absFilename) + s.Assert(s.Fs.Source.Remove(absFilename), qt.IsNil) + } return s } @@ -523,17 +530,20 @@ func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder { } func (s *sitesBuilder) changeEvents() []fsnotify.Event { - if len(s.changedFiles) == 0 { - return nil - } - events := make([]fsnotify.Event, len(s.changedFiles)) - // TODO(bep) remove? - for i, v := range s.changedFiles { - events[i] = fsnotify.Event{ + var events []fsnotify.Event + + for _, v := range s.changedFiles { + events = append(events, fsnotify.Event{ Name: v, Op: fsnotify.Write, - } + }) + } + for _, v := range s.removedFiles { + events = append(events, fsnotify.Event{ + Name: v, + Op: fsnotify.Remove, + }) } return events -- cgit v1.2.3