return loggers.New(optsLogger), nil
}
-func (r *rootCommand) Reset() {
+func (r *rootCommand) resetLogs() {
r.logger.Reset()
loggers.Log().Reset()
}
"sync/atomic"
"time"
- "github.com/bep/logg"
"github.com/bep/simplecobra"
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
return e.waserr
}
-func (c *hugoBuilder) errCount() int {
- return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
-}
-
// getDirList provides NewWatcher() with a list of directories to watch for changes.
func (c *hugoBuilder) getDirList() ([]string, error) {
h, err := c.hugo()
for {
select {
case changes := <-c.r.changesFromBuild:
- c.errState.setBuildErr(nil)
unlock, err := h.LockBuild()
if err != nil {
c.r.logger.Errorln("Failed to acquire a build lock: %s", err)
}
if c.s != nil && c.s.doLiveReload {
doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0
- doReload = doReload || c.showErrorInBrowser && c.errCount() > 0
+ doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil
if doReload {
livereload.ForceRefresh()
}
return
}
c.handleEvents(watcher, staticSyncer, evs, configSet)
- if c.showErrorInBrowser && c.errCount() > 0 {
+ if c.showErrorInBrowser && c.errState.buildErr() != nil {
// Need to reload browser to show the error
livereload.ForceRefresh()
}
}
func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) {
- h, err := c.hugo()
+ defer func() {
+ c.errState.setBuildErr(err)
+ }()
+
+ var h *hugolib.HugoSites
+ h, err = c.hugo()
if err != nil {
- return err
+ return
}
- return h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
+ err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
+ return
}
func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
// Set the processing on pause until the state is recovered.
c.errState.setPaused(true)
c.handleBuildErr(err, "Failed to reload config")
+ if c.s.doLiveReload {
+ livereload.ForceRefresh()
+ }
} else {
c.errState.setPaused(false)
}
c.r.logger.Println(htime.Now().Format(layout))
}
-func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
+func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) {
+ defer func() {
+ c.errState.setBuildErr(err)
+ }()
if err := c.errState.buildErr(); err != nil {
ferrs := herrors.UnwrapFileErrorsWithErrorContext(err)
for _, err := range ferrs {
events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
}
}
- c.errState.setBuildErr(nil)
- h, err := c.hugo()
+ var h *hugolib.HugoSites
+ h, err = c.hugo()
if err != nil {
- return err
+ return
}
-
- return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
+ err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
+ return
}
-func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) error {
- c.errState.setBuildErr(nil)
- h, err := c.hugo()
+func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) {
+ defer func() {
+ c.errState.setBuildErr(err)
+ }()
+
+ var h *hugolib.HugoSites
+ h, err = c.hugo()
if err != nil {
- return err
+ return
}
whatChanged := &hugolib.WhatChanged{}
whatChanged.Add(ids...)
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
- c.errState.setBuildErr(err)
- return err
+
+ return
}
func (c *hugoBuilder) reloadConfig() error {
- c.r.Reset()
+ c.r.resetLogs()
c.r.configVersionID.Add(1)
if err := c.withConfE(func(conf *commonConfig) error {
}
func (c *serverCommand) getErrorWithContext() any {
- errCount := c.errCount()
-
- if errCount == 0 {
+ buildErr := c.errState.buildErr()
+ if buildErr == nil {
return nil
}
m["Error"] = cleanErrorLog(c.r.logger.Errors())
m["Version"] = hugo.BuildVersionString()
- ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
+ ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr)
m["Files"] = ferrors
return m
return u.String(), nil
}
-func (c *serverCommand) partialReRender(urls ...string) error {
+func (c *serverCommand) partialReRender(urls ...string) (err error) {
defer func() {
c.errState.setWasErr(false)
}()
- c.errState.setBuildErr(nil)
visited := types.NewEvictingStringQueue(len(urls))
for _, url := range urls {
visited.Add(url)
}
- h, err := c.hugo()
+ var h *hugolib.HugoSites
+ h, err = c.hugo()
if err != nil {
- return err
+ return
}
+
// Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
- return h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
+ err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
+
+ return
}
func (c *serverCommand) serve() error {
// Loads the data from all of the /data folders.
data *lazy.Init
- // Performs late initialization (before render) of the templates.
- layouts *lazy.Init
-
// Loads the Git info and CODEOWNERS for all the pages if enabled.
gitInfo *lazy.Init
}
l = l.WithField("step", "process")
defer loggers.TimeTrackf(l, time.Now(), nil, "")
- if _, err := h.init.layouts.Do(ctx); err != nil {
- return err
- }
-
if len(events) > 0 {
// This is a rebuild triggered from file events.
return h.processPartialFileEvents(ctx, l, config, init, events)
}
if tmplChanged || i18nChanged {
- // TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048.
- h.init.layouts.Reset()
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
// TODO(bep) this could probably be optimized to somehow
// only load the changed templates and its dependencies, but that is non-trivial.
}
func (h *HugoSites) processContentAdaptersOnRebuild(ctx context.Context, buildConfig *BuildCfg) error {
- // Make sure the layouts are initialized.
- if _, err := h.init.layouts.Do(context.Background()); err != nil {
- return err
- }
g := rungroup.Run[*pagesfromdata.PagesFromTemplate](ctx, rungroup.Config[*pagesfromdata.PagesFromTemplate]{
NumWorkers: h.numWorkers,
Handle: func(ctx context.Context, p *pagesfromdata.PagesFromTemplate) error {
s.Assert(s.H.init.gitInfo.InitCount(), qt.Equals, count)
}
-func (s *IntegrationTestBuilder) AssertBuildCountLayouts(count int) {
- s.Helper()
- s.Assert(s.H.init.layouts.InitCount(), qt.Equals, count)
-}
-
func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) {
s.Helper()
fs := s.fs.WorkingDirReadOnly
var pageIDCounter atomic.Uint64
func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
+ p, pth, err := h.doNewPage(m)
+ if err != nil {
+ // Make sure that any partially created page part is marked as stale.
+ m.MarkStale()
+ }
+ return p, pth, err
+}
+
+func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
m.Staler = &resources.AtomicStaler{}
if m.pageMetaParams == nil {
m.pageMetaParams = &pageMetaParams{
}
return ps, nil
}()
- // Make sure to evict any cached and now stale data.
- if err != nil {
- m.MarkStale()
- }
if ps == nil {
return nil, nil, err
skipRebuildForFilenames: make(map[string]bool),
init: &hugoSitesInit{
data: lazy.New(),
- layouts: lazy.New(),
gitInfo: lazy.New(),
},
}
return nil, nil
})
- h.init.layouts.Add(func(context.Context) (any, error) {
- for _, s := range h.Sites {
- if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
- return nil, err
- }
- }
- return nil, nil
- })
-
h.init.gitInfo.Add(func(context.Context) (any, error) {
err := h.loadGitInfo()
if err != nil {
--- /dev/null
+# Test the hugo server command when adding an error to a config file
+# and then fixing it.
+
+hugo server &
+
+waitServer
+
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+replace $WORK/hugo.toml 'title =' 'titlefoo'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
+
+replace $WORK/hugo.toml 'titlefoo' 'title ='
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+stopServer
+
+-- hugo.toml --
+title = "Hugo Server Test"
+baseURL = "https://example.org/"
+disableKinds = ["taxonomy", "term", "sitemap"]
+-- layouts/index.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- layouts/_default/single.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- content/_index.md --
+---
+title: Hugo Home
+---
+-- content/p1/index.md --
+---
+title: P1
+---
+-- content/p2/index.md --
+---
+title: P2
+---
+-- static/staticfiles/static.txt --
+static
+
+
+
--- /dev/null
+# Test the hugo server command when adding a front matter error to a content file
+# and then fixing it.
+
+hugo server &
+
+waitServer
+
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+replace $WORK/content/p1/index.md 'title:' 'titlecolon'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
+
+replace $WORK/content/p1/index.md 'titlecolon' 'title:'
+httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
+
+stopServer
+
+-- hugo.toml --
+title = "Hugo Server Test"
+baseURL = "https://example.org/"
+disableKinds = ["taxonomy", "term", "sitemap"]
+-- layouts/index.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- layouts/_default/single.html --
+Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
+-- content/_index.md --
+---
+title: Hugo Home
+---
+-- content/p1/index.md --
+---
+title: P1
+---
+-- content/p2/index.md --
+---
+title: P2
+---
+-- static/staticfiles/static.txt --
+static
+
+
+
TemplateHandler
TemplateFuncGetter
AddTemplate(name, tpl string) error
- MarkReady() error
}
// TemplateVariants describes the possible variants of a template.
return nil, err
}
+ if err := h.main.createPrototypes(); err != nil {
+ return nil, err
+ }
+
e := &templateExec{
d: d,
executor: exec,
return v, found
}
-func (t *templateExec) MarkReady() error {
- var err error
- t.readyInit.Do(func() {
- // We only need the clones if base templates are in use.
- if len(t.needsBaseof) > 0 {
- err = t.main.createPrototypes()
- if err != nil {
- return
- }
- }
- })
-
- return err
-}
-
type templateHandler struct {
main *templateNamespace
needsBaseof map[string]templateInfo
baseof map[string]templateInfo
- readyInit sync.Once
-
// This is the filesystem to load the templates from. All the templates are
// stored in the root of this filesystem.
layoutsFs afero.Fs