mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -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
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue