hugolib: Fix freeze in invalid front matter error case

Fixes #4526
This commit is contained in:
Bjørn Erik Pedersen 2018-03-24 09:19:49 +01:00
parent e9c7b6205f
commit 93e24a03ce
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
4 changed files with 95 additions and 14 deletions

View file

@ -0,0 +1,42 @@
package hugolib
import (
"fmt"
"testing"
)
// https://github.com/gohugoio/hugo/issues/4526
func TestSiteBuildFailureInvalidPageMetadata(t *testing.T) {
t.Parallel()
validContentFile := `
---
title = "This is good"
---
Some content.
`
invalidContentFile := `
---
title = "PDF EPUB: Anne Bradstreet: Poems "The Prologue Summary And Analysis EBook Full Text "
---
Some content.
`
var contentFiles []string
for i := 0; i <= 30; i++ {
name := fmt.Sprintf("valid%d.md", i)
contentFiles = append(contentFiles, name, validContentFile)
if i%5 == 0 {
name = fmt.Sprintf("invalid%d.md", i)
contentFiles = append(contentFiles, name, invalidContentFile)
}
}
b := newTestSitesBuilder(t)
b.WithSimpleConfigFile().WithContent(contentFiles...)
b.CreateSites().BuildFail(BuildCfg{})
}

View file

@ -32,6 +32,8 @@ type siteContentProcessor struct {
handleContent contentHandler handleContent contentHandler
ctx context.Context
// The input file bundles. // The input file bundles.
fileBundlesChan chan *bundleDir fileBundlesChan chan *bundleDir
@ -51,7 +53,28 @@ type siteContentProcessor struct {
partialBuild bool partialBuild bool
} }
func newSiteContentProcessor(baseDir string, partialBuild bool, s *Site) *siteContentProcessor { func (s *siteContentProcessor) processBundle(b *bundleDir) {
select {
case s.fileBundlesChan <- b:
case <-s.ctx.Done():
}
}
func (s *siteContentProcessor) processSingle(fi *fileInfo) {
select {
case s.fileSinglesChan <- fi:
case <-s.ctx.Done():
}
}
func (s *siteContentProcessor) processAssets(assets []string) {
select {
case s.fileAssetsChan <- assets:
case <-s.ctx.Done():
}
}
func newSiteContentProcessor(ctx context.Context, baseDir string, partialBuild bool, s *Site) *siteContentProcessor {
numWorkers := 12 numWorkers := 12
if n := runtime.NumCPU() * 3; n > numWorkers { if n := runtime.NumCPU() * 3; n > numWorkers {
numWorkers = n numWorkers = n
@ -60,6 +83,7 @@ func newSiteContentProcessor(baseDir string, partialBuild bool, s *Site) *siteCo
numWorkers = int(math.Ceil(float64(numWorkers) / float64(len(s.owner.Sites)))) numWorkers = int(math.Ceil(float64(numWorkers) / float64(len(s.owner.Sites))))
return &siteContentProcessor{ return &siteContentProcessor{
ctx: ctx,
partialBuild: partialBuild, partialBuild: partialBuild,
baseDir: baseDir, baseDir: baseDir,
site: s, site: s,
@ -80,7 +104,7 @@ func (s *siteContentProcessor) closeInput() {
func (s *siteContentProcessor) process(ctx context.Context) error { func (s *siteContentProcessor) process(ctx context.Context) error {
g1, ctx := errgroup.WithContext(ctx) g1, ctx := errgroup.WithContext(ctx)
g2, _ := errgroup.WithContext(ctx) g2, ctx := errgroup.WithContext(ctx)
// There can be only one of these per site. // There can be only one of these per site.
g1.Go(func() error { g1.Go(func() error {
@ -161,12 +185,14 @@ func (s *siteContentProcessor) process(ctx context.Context) error {
}) })
} }
if err := g2.Wait(); err != nil { err := g2.Wait()
return err
}
close(s.pagesChan) close(s.pagesChan)
if err != nil {
return err
}
if err := g1.Wait(); err != nil { if err := g1.Wait(); err != nil {
return err return err
} }

View file

@ -1281,19 +1281,19 @@ func (c *contentCaptureResultHandler) getContentProcessor(lang string) *siteCont
func (c *contentCaptureResultHandler) handleSingles(fis ...*fileInfo) { func (c *contentCaptureResultHandler) handleSingles(fis ...*fileInfo) {
for _, fi := range fis { for _, fi := range fis {
proc := c.getContentProcessor(fi.Lang()) proc := c.getContentProcessor(fi.Lang())
proc.fileSinglesChan <- fi proc.processSingle(fi)
} }
} }
func (c *contentCaptureResultHandler) handleBundles(d *bundleDirs) { func (c *contentCaptureResultHandler) handleBundles(d *bundleDirs) {
for _, b := range d.bundles { for _, b := range d.bundles {
proc := c.getContentProcessor(b.fi.Lang()) proc := c.getContentProcessor(b.fi.Lang())
proc.fileBundlesChan <- b proc.processBundle(b)
} }
} }
func (c *contentCaptureResultHandler) handleCopyFiles(filenames ...string) { func (c *contentCaptureResultHandler) handleCopyFiles(filenames ...string) {
for _, proc := range c.contentProcessors { for _, proc := range c.contentProcessors {
proc.fileAssetsChan <- filenames proc.processAssets(filenames)
} }
} }
@ -1309,7 +1309,7 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
var defaultContentProcessor *siteContentProcessor var defaultContentProcessor *siteContentProcessor
sites := s.owner.langSite() sites := s.owner.langSite()
for k, v := range sites { for k, v := range sites {
proc := newSiteContentProcessor(baseDir, len(filenames) > 0, v) proc := newSiteContentProcessor(ctx, baseDir, len(filenames) > 0, v)
contentProcessors[k] = proc contentProcessors[k] = proc
if k == defaultContentLanguage { if k == defaultContentLanguage {
defaultContentProcessor = proc defaultContentProcessor = proc
@ -1337,15 +1337,18 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
c := newCapturer(s.Log, sourceSpec, handler, bundleMap, baseDir, filenames...) c := newCapturer(s.Log, sourceSpec, handler, bundleMap, baseDir, filenames...)
if err := c.capture(); err != nil { err1 := c.capture()
return err
}
for _, proc := range contentProcessors { for _, proc := range contentProcessors {
proc.closeInput() proc.closeInput()
} }
return g.Wait() err2 := g.Wait()
if err1 != nil {
return err1
}
return err2
} }
func (s *Site) buildSiteMeta() (err error) { func (s *Site) buildSiteMeta() (err error) {

View file

@ -272,12 +272,22 @@ func (s *sitesBuilder) CreateSites() *sitesBuilder {
} }
func (s *sitesBuilder) Build(cfg BuildCfg) *sitesBuilder { func (s *sitesBuilder) Build(cfg BuildCfg) *sitesBuilder {
return s.build(cfg, false)
}
func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder {
return s.build(cfg, true)
}
func (s *sitesBuilder) build(cfg BuildCfg, shouldFail bool) *sitesBuilder {
if s.H == nil { if s.H == nil {
s.CreateSites() s.CreateSites()
} }
err := s.H.Build(cfg) err := s.H.Build(cfg)
if err != nil { if err != nil && !shouldFail {
s.Fatalf("Build failed: %s", err) s.Fatalf("Build failed: %s", err)
} else if err == nil && shouldFail {
s.Fatalf("Expected error")
} }
return s return s