Skip to content
Permalink
Browse files

hugolib: Improve error and reload handling of hook templates in serve…

…r mode

Fixes #6635
  • Loading branch information
bep committed Dec 20, 2019
1 parent 0453683 commit 8a58ebb311fd079f65068e7e37725e4d43f17ab5
@@ -88,6 +88,7 @@ type commandeer struct {
doLiveReload bool
fastRenderMode bool
showErrorInBrowser bool
wasError bool

configured bool
paused bool
@@ -718,6 +718,9 @@ func (c *commandeer) handleBuildErr(err error, msg string) {

func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
defer c.timeTrack(time.Now(), "Total")
defer func() {
c.wasError = false
}()

c.buildErr = nil
visited := c.visitedURLs.PeekAllSet()
@@ -734,16 +737,19 @@ func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
}

}
return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited}, events...)
return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited, ErrRecovery: c.wasError}, events...)
}

func (c *commandeer) partialReRender(urls ...string) error {
defer func() {
c.wasError = false
}()
c.buildErr = nil
visited := make(map[string]bool)
for _, url := range urls {
visited[url] = true
}
return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited, PartialReRender: true})
return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.wasError})
}

func (c *commandeer) fullRebuild(changeType string) {
@@ -334,6 +334,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
// First check the error state
err := f.c.getErrorWithContext()
if err != nil {
f.c.wasError = true
w.WriteHeader(500)
r, err := f.errorTemplate(err)
if err != nil {
@@ -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", `<p>html-render-link</p>`)

b.RemoveFiles(
"layouts/_default/_markup/render-link.html",
)

b.Build(BuildCfg{})
b.AssertFileContent("public/p1/index.html", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)

}

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", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)

b.EditFiles("layouts/_default/_markup/render-link.html", `html-render-link`)

b.Build(BuildCfg{})

b.AssertFileContent("public/p1/index.html", `<p>html-render-link</p>`)

}

func TestRenderHooksRSS(t *testing.T) {

b := newTestSitesBuilder(t)
@@ -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
}
}
@@ -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()
@@ -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
@@ -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

0 comments on commit 8a58ebb

Please sign in to comment.
You can’t perform that action at this time.