Skip to content

Commit cb6e27b

Browse files
committed
hugolib/commands: Fix stuck server error issues
Fixes gohugoio#11378
1 parent 5bbe95f commit cb6e27b

File tree

12 files changed

+143
-84
lines changed

12 files changed

+143
-84
lines changed

‎commands/commandeer.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
507507
return loggers.New(optsLogger), nil
508508
}
509509

510-
func (r *rootCommand) Reset() {
510+
func (r *rootCommand) resetLogs() {
511511
r.logger.Reset()
512512
loggers.Log().Reset()
513513
}

‎commands/hugobuilder.go‎

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"sync/atomic"
2828
"time"
2929

30-
"github.com/bep/logg"
3130
"github.com/bep/simplecobra"
3231
"github.com/fsnotify/fsnotify"
3332
"github.com/gohugoio/hugo/common/herrors"
@@ -136,10 +135,6 @@ func (e *hugoBuilderErrState) wasErr() bool {
136135
return e.waserr
137136
}
138137

139-
func (c *hugoBuilder) errCount() int {
140-
return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
141-
}
142-
143138
// getDirList provides NewWatcher() with a list of directories to watch for changes.
144139
func (c *hugoBuilder) getDirList() ([]string, error) {
145140
h, err := c.hugo()
@@ -345,7 +340,6 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
345340
for {
346341
select {
347342
case changes := <-c.r.changesFromBuild:
348-
c.errState.setBuildErr(nil)
349343
unlock, err := h.LockBuild()
350344
if err != nil {
351345
c.r.logger.Errorln("Failed to acquire a build lock: %s", err)
@@ -358,7 +352,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
358352
}
359353
if c.s != nil && c.s.doLiveReload {
360354
doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0
361-
doReload = doReload || c.showErrorInBrowser && c.errCount() > 0
355+
doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil
362356
if doReload {
363357
livereload.ForceRefresh()
364358
}
@@ -372,7 +366,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
372366
return
373367
}
374368
c.handleEvents(watcher, staticSyncer, evs, configSet)
375-
if c.showErrorInBrowser && c.errCount() > 0 {
369+
if c.showErrorInBrowser && c.errState.buildErr() != nil {
376370
// Need to reload browser to show the error
377371
livereload.ForceRefresh()
378372
}
@@ -419,11 +413,17 @@ func (c *hugoBuilder) build() error {
419413
}
420414

421415
func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) {
422-
h, err := c.hugo()
416+
defer func() {
417+
c.errState.setBuildErr(err)
418+
}()
419+
420+
var h *hugolib.HugoSites
421+
h, err = c.hugo()
423422
if err != nil {
424-
return err
423+
return
425424
}
426-
return h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
425+
err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
426+
return
427427
}
428428

429429
func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
@@ -619,6 +619,9 @@ func (c *hugoBuilder) fullRebuild(changeType string) {
619619
// Set the processing on pause until the state is recovered.
620620
c.errState.setPaused(true)
621621
c.handleBuildErr(err, "Failed to reload config")
622+
if c.s.doLiveReload {
623+
livereload.ForceRefresh()
624+
}
622625
} else {
623626
c.errState.setPaused(false)
624627
}
@@ -1081,37 +1084,44 @@ func (c *hugoBuilder) printChangeDetected(typ string) {
10811084
c.r.logger.Println(htime.Now().Format(layout))
10821085
}
10831086

1084-
func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
1087+
func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) {
1088+
defer func() {
1089+
c.errState.setBuildErr(err)
1090+
}()
10851091
if err := c.errState.buildErr(); err != nil {
10861092
ferrs := herrors.UnwrapFileErrorsWithErrorContext(err)
10871093
for _, err := range ferrs {
10881094
events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
10891095
}
10901096
}
1091-
c.errState.setBuildErr(nil)
1092-
h, err := c.hugo()
1097+
var h *hugolib.HugoSites
1098+
h, err = c.hugo()
10931099
if err != nil {
1094-
return err
1100+
return
10951101
}
1096-
1097-
return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
1102+
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
1103+
return
10981104
}
10991105

1100-
func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) error {
1101-
c.errState.setBuildErr(nil)
1102-
h, err := c.hugo()
1106+
func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) {
1107+
defer func() {
1108+
c.errState.setBuildErr(err)
1109+
}()
1110+
1111+
var h *hugolib.HugoSites
1112+
h, err = c.hugo()
11031113
if err != nil {
1104-
return err
1114+
return
11051115
}
11061116
whatChanged := &hugolib.WhatChanged{}
11071117
whatChanged.Add(ids...)
11081118
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
1109-
c.errState.setBuildErr(err)
1110-
return err
1119+
1120+
return
11111121
}
11121122

11131123
func (c *hugoBuilder) reloadConfig() error {
1114-
c.r.Reset()
1124+
c.r.resetLogs()
11151125
c.r.configVersionID.Add(1)
11161126

11171127
if err := c.withConfE(func(conf *commonConfig) error {

‎commands/server.go‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -648,9 +648,8 @@ func (c *serverCommand) setServerInfoInConfig() error {
648648
}
649649

650650
func (c *serverCommand) getErrorWithContext() any {
651-
errCount := c.errCount()
652-
653-
if errCount == 0 {
651+
buildErr := c.errState.buildErr()
652+
if buildErr == nil {
654653
return nil
655654
}
656655

@@ -659,7 +658,7 @@ func (c *serverCommand) getErrorWithContext() any {
659658
m["Error"] = cleanErrorLog(c.r.logger.Errors())
660659

661660
m["Version"] = hugo.BuildVersionString()
662-
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
661+
ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr)
663662
m["Files"] = ferrors
664663

665664
return m
@@ -830,22 +829,25 @@ func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port i
830829
return u.String(), nil
831830
}
832831

833-
func (c *serverCommand) partialReRender(urls ...string) error {
832+
func (c *serverCommand) partialReRender(urls ...string) (err error) {
834833
defer func() {
835834
c.errState.setWasErr(false)
836835
}()
837-
c.errState.setBuildErr(nil)
838836
visited := types.NewEvictingStringQueue(len(urls))
839837
for _, url := range urls {
840838
visited.Add(url)
841839
}
842840

843-
h, err := c.hugo()
841+
var h *hugolib.HugoSites
842+
h, err = c.hugo()
844843
if err != nil {
845-
return err
844+
return
846845
}
846+
847847
// Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
848-
return h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
848+
err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
849+
850+
return
849851
}
850852

851853
func (c *serverCommand) serve() error {

‎hugolib/hugo_sites.go‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,6 @@ type hugoSitesInit struct {
179179
// Loads the data from all of the /data folders.
180180
data *lazy.Init
181181

182-
// Performs late initialization (before render) of the templates.
183-
layouts *lazy.Init
184-
185182
// Loads the Git info and CODEOWNERS for all the pages if enabled.
186183
gitInfo *lazy.Init
187184
}

‎hugolib/hugo_sites_build.go‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,6 @@ func (h *HugoSites) process(ctx context.Context, l logg.LevelLogger, config *Bui
250250
l = l.WithField("step", "process")
251251
defer loggers.TimeTrackf(l, time.Now(), nil, "")
252252

253-
if _, err := h.init.layouts.Do(ctx); err != nil {
254-
return err
255-
}
256-
257253
if len(events) > 0 {
258254
// This is a rebuild triggered from file events.
259255
return h.processPartialFileEvents(ctx, l, config, init, events)
@@ -1067,8 +1063,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
10671063
}
10681064

10691065
if tmplChanged || i18nChanged {
1070-
// TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048.
1071-
h.init.layouts.Reset()
10721066
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
10731067
// TODO(bep) this could probably be optimized to somehow
10741068
// only load the changed templates and its dependencies, but that is non-trivial.
@@ -1141,10 +1135,6 @@ func (s *Site) handleContentAdapterChanges(bi pagesfromdata.BuildInfo, buildConf
11411135
}
11421136

11431137
func (h *HugoSites) processContentAdaptersOnRebuild(ctx context.Context, buildConfig *BuildCfg) error {
1144-
// Make sure the layouts are initialized.
1145-
if _, err := h.init.layouts.Do(context.Background()); err != nil {
1146-
return err
1147-
}
11481138
g := rungroup.Run[*pagesfromdata.PagesFromTemplate](ctx, rungroup.Config[*pagesfromdata.PagesFromTemplate]{
11491139
NumWorkers: h.numWorkers,
11501140
Handle: func(ctx context.Context, p *pagesfromdata.PagesFromTemplate) error {

‎hugolib/integrationtest_builder.go‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,6 @@ func (s *IntegrationTestBuilder) AssertBuildCountGitInfo(count int) {
246246
s.Assert(s.H.init.gitInfo.InitCount(), qt.Equals, count)
247247
}
248248

249-
func (s *IntegrationTestBuilder) AssertBuildCountLayouts(count int) {
250-
s.Helper()
251-
s.Assert(s.H.init.layouts.InitCount(), qt.Equals, count)
252-
}
253-
254249
func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) {
255250
s.Helper()
256251
fs := s.fs.WorkingDirReadOnly

‎hugolib/page__new.go‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ import (
3434
var pageIDCounter atomic.Uint64
3535

3636
func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
37+
p, pth, err := h.doNewPage(m)
38+
if err != nil {
39+
// Make sure that any partially created page part is marked as stale.
40+
m.MarkStale()
41+
}
42+
return p, pth, err
43+
}
44+
45+
func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
3746
m.Staler = &resources.AtomicStaler{}
3847
if m.pageMetaParams == nil {
3948
m.pageMetaParams = &pageMetaParams{
@@ -231,10 +240,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
231240
}
232241
return ps, nil
233242
}()
234-
// Make sure to evict any cached and now stale data.
235-
if err != nil {
236-
m.MarkStale()
237-
}
238243

239244
if ps == nil {
240245
return nil, nil, err

‎hugolib/site.go‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
344344
skipRebuildForFilenames: make(map[string]bool),
345345
init: &hugoSitesInit{
346346
data: lazy.New(),
347-
layouts: lazy.New(),
348347
gitInfo: lazy.New(),
349348
},
350349
}
@@ -400,15 +399,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
400399
return nil, nil
401400
})
402401

403-
h.init.layouts.Add(func(context.Context) (any, error) {
404-
for _, s := range h.Sites {
405-
if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
406-
return nil, err
407-
}
408-
}
409-
return nil, nil
410-
})
411-
412402
h.init.gitInfo.Add(func(context.Context) (any, error) {
413403
err := h.loadGitInfo()
414404
if err != nil {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Test the hugo server command when adding an error to a config file
2+
# and then fixing it.
3+
4+
hugo server &
5+
6+
waitServer
7+
8+
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
9+
10+
replace $WORK/hugo.toml 'title =' 'titlefoo'
11+
httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
12+
13+
replace $WORK/hugo.toml 'titlefoo' 'title ='
14+
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
15+
16+
stopServer
17+
18+
-- hugo.toml --
19+
title = "Hugo Server Test"
20+
baseURL = "https://example.org/"
21+
disableKinds = ["taxonomy", "term", "sitemap"]
22+
-- layouts/index.html --
23+
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
24+
-- layouts/_default/single.html --
25+
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
26+
-- content/_index.md --
27+
---
28+
title: Hugo Home
29+
---
30+
-- content/p1/index.md --
31+
---
32+
title: P1
33+
---
34+
-- content/p2/index.md --
35+
---
36+
title: P2
37+
---
38+
-- static/staticfiles/static.txt --
39+
static
40+
41+
42+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Test the hugo server command when adding a front matter error to a content file
2+
# and then fixing it.
3+
4+
hugo server &
5+
6+
waitServer
7+
8+
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
9+
10+
replace $WORK/content/p1/index.md 'title:' 'titlecolon'
11+
httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
12+
13+
replace $WORK/content/p1/index.md 'titlecolon' 'title:'
14+
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
15+
16+
stopServer
17+
18+
-- hugo.toml --
19+
title = "Hugo Server Test"
20+
baseURL = "https://example.org/"
21+
disableKinds = ["taxonomy", "term", "sitemap"]
22+
-- layouts/index.html --
23+
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
24+
-- layouts/_default/single.html --
25+
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
26+
-- content/_index.md --
27+
---
28+
title: Hugo Home
29+
---
30+
-- content/p1/index.md --
31+
---
32+
title: P1
33+
---
34+
-- content/p2/index.md --
35+
---
36+
title: P2
37+
---
38+
-- static/staticfiles/static.txt --
39+
static
40+
41+
42+

0 commit comments

Comments
 (0)