mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
hugolib: Improve error and reload handling of hook templates in server mode
Fixes #6635
This commit is contained in:
parent
0453683816
commit
8a58ebb311
8 changed files with 121 additions and 20 deletions
|
@ -88,6 +88,7 @@ type commandeer struct {
|
||||||
doLiveReload bool
|
doLiveReload bool
|
||||||
fastRenderMode bool
|
fastRenderMode bool
|
||||||
showErrorInBrowser bool
|
showErrorInBrowser bool
|
||||||
|
wasError bool
|
||||||
|
|
||||||
configured bool
|
configured bool
|
||||||
paused bool
|
paused bool
|
||||||
|
|
|
@ -718,6 +718,9 @@ func (c *commandeer) handleBuildErr(err error, msg string) {
|
||||||
|
|
||||||
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
||||||
defer c.timeTrack(time.Now(), "Total")
|
defer c.timeTrack(time.Now(), "Total")
|
||||||
|
defer func() {
|
||||||
|
c.wasError = false
|
||||||
|
}()
|
||||||
|
|
||||||
c.buildErr = nil
|
c.buildErr = nil
|
||||||
visited := c.visitedURLs.PeekAllSet()
|
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 {
|
func (c *commandeer) partialReRender(urls ...string) error {
|
||||||
|
defer func() {
|
||||||
|
c.wasError = false
|
||||||
|
}()
|
||||||
c.buildErr = nil
|
c.buildErr = nil
|
||||||
visited := make(map[string]bool)
|
visited := make(map[string]bool)
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
visited[url] = true
|
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) {
|
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
|
// First check the error state
|
||||||
err := f.c.getErrorWithContext()
|
err := f.c.getErrorWithContext()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
f.c.wasError = true
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
r, err := f.errorTemplate(err)
|
r, err := f.errorTemplate(err)
|
||||||
if err != nil {
|
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) {
|
func TestRenderHooksRSS(t *testing.T) {
|
||||||
|
|
||||||
b := newTestSitesBuilder(t)
|
b := newTestSitesBuilder(t)
|
||||||
|
|
|
@ -564,6 +564,9 @@ type BuildCfg struct {
|
||||||
// we should skip most of the processing.
|
// we should skip most of the processing.
|
||||||
PartialReRender bool
|
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.
|
// Recently visited URLs. This is used for partial re-rendering.
|
||||||
RecentlyVisited map[string]bool
|
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)
|
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 {
|
for _, s := range h.Sites {
|
||||||
PAGES:
|
PAGES:
|
||||||
for _, p := range s.rawAllPages {
|
for _, p := range s.rawAllPages {
|
||||||
|
@ -820,7 +835,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
|
||||||
for id, _ := range idset {
|
for id, _ := range idset {
|
||||||
if po.cp.dependencyTracker.Search(id) != nil {
|
if po.cp.dependencyTracker.Search(id) != nil {
|
||||||
po.cp.Reset()
|
po.cp.Reset()
|
||||||
p.forceRender = true
|
|
||||||
continue OUTPUTS
|
continue OUTPUTS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,7 +848,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
|
||||||
po.cp.Reset()
|
po.cp.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.forceRender = true
|
|
||||||
continue PAGES
|
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 {
|
func (p *pageState) wrapError(err error) error {
|
||||||
|
if _, ok := err.(*herrors.ErrorWithFileContext); ok {
|
||||||
|
// Preserve the first file context.
|
||||||
|
return err
|
||||||
|
}
|
||||||
var filename string
|
var filename string
|
||||||
if !p.File().IsZero() {
|
if !p.File().IsZero() {
|
||||||
filename = p.File().Filename()
|
filename = p.File().Filename()
|
||||||
|
|
|
@ -909,6 +909,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
|
||||||
contentFilesChanged []string
|
contentFilesChanged []string
|
||||||
|
|
||||||
tmplChanged bool
|
tmplChanged bool
|
||||||
|
tmplAdded bool
|
||||||
dataChanged bool
|
dataChanged bool
|
||||||
i18nChanged bool
|
i18nChanged bool
|
||||||
|
|
||||||
|
@ -934,8 +935,16 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
|
||||||
logger.Println("Source changed", ev)
|
logger.Println("Source changed", ev)
|
||||||
sourceChanged = append(sourceChanged, ev)
|
sourceChanged = append(sourceChanged, ev)
|
||||||
case files.ComponentFolderLayouts:
|
case files.ComponentFolderLayouts:
|
||||||
logger.Println("Template changed", ev)
|
|
||||||
tmplChanged = true
|
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:
|
case files.ComponentFolderData:
|
||||||
logger.Println("Data changed", ev)
|
logger.Println("Data changed", ev)
|
||||||
dataChanged = true
|
dataChanged = true
|
||||||
|
@ -1021,7 +1030,11 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
|
||||||
sourceFilesChanged[ev.Name] = true
|
sourceFilesChanged[ev.Name] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.ErrRecovery || tmplAdded {
|
||||||
|
h.resetPageState()
|
||||||
|
} else {
|
||||||
h.resetPageStateFromEvents(changeIdentities)
|
h.resetPageStateFromEvents(changeIdentities)
|
||||||
|
}
|
||||||
|
|
||||||
if len(sourceReallyChanged) > 0 || len(contentFilesChanged) > 0 {
|
if len(sourceReallyChanged) > 0 || len(contentFilesChanged) > 0 {
|
||||||
var filenamesChanged []string
|
var filenamesChanged []string
|
||||||
|
|
|
@ -68,6 +68,7 @@ type sitesBuilder struct {
|
||||||
|
|
||||||
// Used to test partial rebuilds.
|
// Used to test partial rebuilds.
|
||||||
changedFiles []string
|
changedFiles []string
|
||||||
|
removedFiles []string
|
||||||
|
|
||||||
// Aka the Hugo server mode.
|
// Aka the Hugo server mode.
|
||||||
running bool
|
running bool
|
||||||
|
@ -386,16 +387,22 @@ func (s *sitesBuilder) WithI18nAdded(filenameContent ...string) *sitesBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sitesBuilder) EditFiles(filenameContent ...string) *sitesBuilder {
|
func (s *sitesBuilder) EditFiles(filenameContent ...string) *sitesBuilder {
|
||||||
var changedFiles []string
|
|
||||||
for i := 0; i < len(filenameContent); i += 2 {
|
for i := 0; i < len(filenameContent); i += 2 {
|
||||||
filename, content := filepath.FromSlash(filenameContent[i]), filenameContent[i+1]
|
filename, content := filepath.FromSlash(filenameContent[i]), filenameContent[i+1]
|
||||||
absFilename := s.absFilename(filename)
|
absFilename := s.absFilename(filename)
|
||||||
changedFiles = append(changedFiles, absFilename)
|
s.changedFiles = append(s.changedFiles, absFilename)
|
||||||
writeSource(s.T, s.Fs, absFilename, content)
|
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
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,17 +530,20 @@ func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sitesBuilder) changeEvents() []fsnotify.Event {
|
func (s *sitesBuilder) changeEvents() []fsnotify.Event {
|
||||||
if len(s.changedFiles) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
events := make([]fsnotify.Event, len(s.changedFiles))
|
var events []fsnotify.Event
|
||||||
// TODO(bep) remove?
|
|
||||||
for i, v := range s.changedFiles {
|
for _, v := range s.changedFiles {
|
||||||
events[i] = fsnotify.Event{
|
events = append(events, fsnotify.Event{
|
||||||
Name: v,
|
Name: v,
|
||||||
Op: fsnotify.Write,
|
Op: fsnotify.Write,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
for _, v := range s.removedFiles {
|
||||||
|
events = append(events, fsnotify.Event{
|
||||||
|
Name: v,
|
||||||
|
Op: fsnotify.Remove,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return events
|
return events
|
||||||
|
|
Loading…
Reference in a new issue