hugolib: Fix freeze in invalid front matter error case
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 24 Mar 2018 08:19:49 +0000 (09:19 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 24 Mar 2018 09:06:58 +0000 (10:06 +0100)
Fixes #4526

hugolib/hugo_sites_build_failures_test.go [new file with mode: 0644]
hugolib/page_bundler.go
hugolib/site.go
hugolib/testhelpers_test.go

diff --git a/hugolib/hugo_sites_build_failures_test.go b/hugolib/hugo_sites_build_failures_test.go
new file mode 100644 (file)
index 0000000..b347490
--- /dev/null
@@ -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{})
+
+}
index 2f6b4d0948c2447da3dcbbca6009fb5d27e9b7a5..bedfd58f0217b0c7817939838cbc624f54b4eed4 100644 (file)
@@ -32,6 +32,8 @@ type siteContentProcessor struct {
 
        handleContent contentHandler
 
+       ctx context.Context
+
        // The input file bundles.
        fileBundlesChan chan *bundleDir
 
@@ -51,7 +53,28 @@ type siteContentProcessor struct {
        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
        if n := runtime.NumCPU() * 3; n > numWorkers {
                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))))
 
        return &siteContentProcessor{
+               ctx:             ctx,
                partialBuild:    partialBuild,
                baseDir:         baseDir,
                site:            s,
@@ -80,7 +104,7 @@ func (s *siteContentProcessor) closeInput() {
 
 func (s *siteContentProcessor) process(ctx context.Context) error {
        g1, ctx := errgroup.WithContext(ctx)
-       g2, _ := errgroup.WithContext(ctx)
+       g2, ctx := errgroup.WithContext(ctx)
 
        // There can be only one of these per site.
        g1.Go(func() error {
@@ -161,12 +185,14 @@ func (s *siteContentProcessor) process(ctx context.Context) error {
                })
        }
 
-       if err := g2.Wait(); err != nil {
-               return err
-       }
+       err := g2.Wait()
 
        close(s.pagesChan)
 
+       if err != nil {
+               return err
+       }
+
        if err := g1.Wait(); err != nil {
                return err
        }
index 0ffe153e919a4492f9e0c245d2d7a7fb2cb66115..c3e2d9cb0344345595af3b253d36ee27381dd4f3 100644 (file)
@@ -1281,19 +1281,19 @@ func (c *contentCaptureResultHandler) getContentProcessor(lang string) *siteCont
 func (c *contentCaptureResultHandler) handleSingles(fis ...*fileInfo) {
        for _, fi := range fis {
                proc := c.getContentProcessor(fi.Lang())
-               proc.fileSinglesChan <- fi
+               proc.processSingle(fi)
        }
 }
 func (c *contentCaptureResultHandler) handleBundles(d *bundleDirs) {
        for _, b := range d.bundles {
                proc := c.getContentProcessor(b.fi.Lang())
-               proc.fileBundlesChan <- b
+               proc.processBundle(b)
        }
 }
 
 func (c *contentCaptureResultHandler) handleCopyFiles(filenames ...string) {
        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
        sites := s.owner.langSite()
        for k, v := range sites {
-               proc := newSiteContentProcessor(baseDir, len(filenames) > 0, v)
+               proc := newSiteContentProcessor(ctx, baseDir, len(filenames) > 0, v)
                contentProcessors[k] = proc
                if k == defaultContentLanguage {
                        defaultContentProcessor = proc
@@ -1337,15 +1337,18 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
 
        c := newCapturer(s.Log, sourceSpec, handler, bundleMap, baseDir, filenames...)
 
-       if err := c.capture(); err != nil {
-               return err
-       }
+       err1 := c.capture()
 
        for _, proc := range contentProcessors {
                proc.closeInput()
        }
 
-       return g.Wait()
+       err2 := g.Wait()
+
+       if err1 != nil {
+               return err1
+       }
+       return err2
 }
 
 func (s *Site) buildSiteMeta() (err error) {
index 1f22e428da8d1ec2212ab1a600f038aee8769f25..6f513b3bf04f5da69df7e8162330c2f5294ad548 100644 (file)
@@ -272,12 +272,22 @@ func (s *sitesBuilder) CreateSites() *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 {
                s.CreateSites()
        }
        err := s.H.Build(cfg)
-       if err != nil {
+       if err != nil && !shouldFail {
                s.Fatalf("Build failed: %s", err)
+       } else if err == nil && shouldFail {
+               s.Fatalf("Expected error")
        }
 
        return s