diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-21 12:20:21 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-22 20:46:14 +0200 |
commit | d1661b823af25c50d3bbe5366ea40a3cdd52e237 (patch) | |
tree | cd84d18229fb9c294ff1be56d7c0ce92a8f46761 /hugolib | |
parent | 7930d2132a3c36c1aaca20f16f56978c84656b0a (diff) |
hugolib: Continue the file context/line number errors work
See #5324
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/hugo_sites.go | 36 | ||||
-rw-r--r-- | hugolib/hugo_sites_build.go | 75 | ||||
-rw-r--r-- | hugolib/hugo_sites_build_errors_test.go | 87 | ||||
-rw-r--r-- | hugolib/page.go | 53 | ||||
-rw-r--r-- | hugolib/page_content.go | 35 | ||||
-rw-r--r-- | hugolib/page_errors.go | 47 | ||||
-rw-r--r-- | hugolib/shortcode.go | 30 | ||||
-rw-r--r-- | hugolib/site.go | 39 | ||||
-rw-r--r-- | hugolib/testhelpers_test.go | 6 |
9 files changed, 280 insertions, 128 deletions
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 7f70967d6..a184e8877 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -21,6 +21,7 @@ import ( "strings" "sync" + "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/helpers" @@ -53,6 +54,40 @@ type HugoSites struct { gitInfo *gitInfo } +func (h *HugoSites) pickOneAndLogTheRest(errors []error) error { + if len(errors) == 0 { + return nil + } + + var i int + + for j, err := range errors { + // If this is in server mode, we want to return an error to the client + // with a file context, if possible. + if herrors.UnwrapErrorWithFileContext(err) != nil { + i = j + break + } + } + + // Log the rest, but add a threshold to avoid flooding the log. + const errLogThreshold = 5 + + for j, err := range errors { + if j == i || err == nil { + continue + } + + if j >= errLogThreshold { + break + } + + h.Log.ERROR.Println(err) + } + + return errors[i] +} + func (h *HugoSites) IsMultihost() bool { return h != nil && h.multihost } @@ -636,6 +671,7 @@ func handleShortcodes(p *PageWithoutContent, rawContentCopy []byte) ([]byte, err err := p.shortcodeState.executeShortcodesForDelta(p) if err != nil { + return rawContentCopy, err } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 13fbfd57e..4c275f55b 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -26,13 +26,29 @@ import ( // Build builds all sites. If filesystem events are provided, // this is considered to be a potential partial rebuild. func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { + errCollector := h.StartErrorCollector() + errs := make(chan error) + + go func(from, to chan error) { + var errors []error + i := 0 + for e := range from { + i++ + if i > 50 { + break + } + errors = append(errors, e) + } + to <- h.pickOneAndLogTheRest(errors) + + close(to) + + }(errCollector, errs) if h.Metrics != nil { h.Metrics.Reset() } - //t0 := time.Now() - // Need a pointer as this may be modified. conf := &config @@ -41,33 +57,46 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { conf.whatChanged = &whatChanged{source: true, other: true} } + var prepareErr error + if !config.PartialReRender { - for _, s := range h.Sites { - s.Deps.BuildStartListeners.Notify() - } + prepare := func() error { + for _, s := range h.Sites { + s.Deps.BuildStartListeners.Notify() + } + + if len(events) > 0 { + // Rebuild + if err := h.initRebuild(conf); err != nil { + return err + } + } else { + if err := h.init(conf); err != nil { + return err + } + } - if len(events) > 0 { - // Rebuild - if err := h.initRebuild(conf); err != nil { + if err := h.process(conf, events...); err != nil { return err } - } else { - if err := h.init(conf); err != nil { + + if err := h.assemble(conf); err != nil { return err } + return nil } - if err := h.process(conf, events...); err != nil { - return err + prepareErr = prepare() + if prepareErr != nil { + h.SendError(prepareErr) } - if err := h.assemble(conf); err != nil { - return err - } } - if err := h.render(conf); err != nil { - return err + if prepareErr == nil { + if err := h.render(conf); err != nil { + h.SendError(err) + } } if h.Metrics != nil { @@ -79,6 +108,18 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { h.Log.FEEDBACK.Println() } + select { + // Make sure the channel always gets something. + case errCollector <- nil: + default: + } + close(errCollector) + + err := <-errs + if err != nil { + return err + } + errorCount := h.Log.ErrorCounter.Count() if errorCount > 0 { return fmt.Errorf("logged %d error(s)", errorCount) diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 6b44bea88..2e8eb99ea 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -2,6 +2,7 @@ package hugolib import ( "fmt" + "path/filepath" "strings" "testing" @@ -17,13 +18,20 @@ type testSiteBuildErrorAsserter struct { func (t testSiteBuildErrorAsserter) getFileError(err error) *herrors.ErrorWithFileContext { t.assert.NotNil(err, t.name) ferr := herrors.UnwrapErrorWithFileContext(err) - t.assert.NotNil(ferr, fmt.Sprintf("[%s] got %T: %+v", t.name, err, err)) + t.assert.NotNil(ferr, fmt.Sprintf("[%s] got %T: %+v\n%s", t.name, err, err, trace())) return ferr } func (t testSiteBuildErrorAsserter) assertLineNumber(lineNumber int, err error) { fe := t.getFileError(err) - t.assert.Equal(lineNumber, fe.LineNumber, fmt.Sprintf("[%s] got => %s", t.name, fe)) + t.assert.Equal(lineNumber, fe.LineNumber, fmt.Sprintf("[%s] got => %s\n%s", t.name, fe, trace())) +} + +func (t testSiteBuildErrorAsserter) assertErrorMessage(e1, e2 string) { + // The error message will contain filenames with OS slashes. Normalize before compare. + e1, e2 = filepath.ToSlash(e1), filepath.ToSlash(e2) + t.assert.Equal(e1, e2, trace()) + } func TestSiteBuildErrors(t *testing.T) { @@ -32,6 +40,7 @@ func TestSiteBuildErrors(t *testing.T) { const ( yamlcontent = "yamlcontent" + tomlcontent = "tomlcontent" shortcode = "shortcode" base = "base" single = "single" @@ -55,7 +64,7 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title }}", ".Title }", 1) }, assertCreateError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(2, err) + a.assertLineNumber(4, err) }, }, { @@ -65,7 +74,7 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title", ".Titles", 1) }, assertBuildError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(2, err) + a.assertLineNumber(4, err) }, }, { @@ -75,7 +84,12 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title }}", ".Title }", 1) }, assertCreateError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(3, err) + fe := a.getFileError(err) + assert.Equal(5, fe.LineNumber) + assert.Equal(1, fe.ColumnNumber) + assert.Equal("go-html-template", fe.ChromaLexer) + a.assertErrorMessage("\"layouts/_default/single.html:5:1\": parse failed: template: _default/single.html:5: unexpected \"}\" in operand", fe.Error()) + }, }, { @@ -85,7 +99,12 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title", ".Titles", 1) }, assertBuildError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(3, err) + fe := a.getFileError(err) + assert.Equal(5, fe.LineNumber) + assert.Equal(14, fe.ColumnNumber) + assert.Equal("md", fe.ChromaLexer) + a.assertErrorMessage("asdfadf", fe.Error()) + }, }, { @@ -95,7 +114,7 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title }}", ".Title }", 1) }, assertCreateError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(2, err) + a.assertLineNumber(4, err) }, }, { @@ -105,10 +124,47 @@ func TestSiteBuildErrors(t *testing.T) { return strings.Replace(content, ".Title", ".Titles", 1) }, assertBuildError: func(a testSiteBuildErrorAsserter, err error) { - a.assertLineNumber(25, err) + a.assertLineNumber(4, err) }, }, + { + name: "Shortode does not exist", + fileType: yamlcontent, + fileFixer: func(content string) string { + return strings.Replace(content, "{{< sc >}}", "{{< nono >}}", 1) + }, + assertBuildError: func(a testSiteBuildErrorAsserter, err error) { + fe := a.getFileError(err) + assert.Equal(7, fe.LineNumber) + assert.Equal(14, fe.ColumnNumber) + assert.Equal("md", fe.ChromaLexer) + a.assertErrorMessage("\"content/myyaml.md:7:14\": failed to extract shortcode: template for shortcode \"nono\" not found", fe.Error()) + }, + }, + { + name: "Invalid YAML front matter", + fileType: yamlcontent, + fileFixer: func(content string) string { + // TODO(bep) 2errors YAML line numbers seems to be off by one for > 1 line. + return strings.Replace(content, "title:", "title", 1) + }, + assertBuildError: func(a testSiteBuildErrorAsserter, err error) { + a.assertLineNumber(2, err) + }, + }, + { + name: "Invalid TOML front matter", + fileType: tomlcontent, + fileFixer: func(content string) string { + return strings.Replace(content, "description = ", "description &", 1) + }, + assertBuildError: func(a testSiteBuildErrorAsserter, err error) { + fe := a.getFileError(err) + assert.Equal(6, fe.LineNumber) + assert.Equal("toml", fe.ErrorContext.ChromaLexer) + }, + }, { name: "Panic in template Execute", fileType: single, @@ -166,7 +222,7 @@ title: "The YAML" Some content. -{{< sc >}} + {{< sc >}} Some more text. @@ -174,6 +230,19 @@ The end. `)) + b.WithContent("mytoml.md", f(tomlcontent, `+++ +title = "The TOML" +p1 = "v" +p2 = "v" +p3 = "v" +description = "Descriptioon" ++++ + +Some content. + + +`)) + createErr := b.CreateSitesE() if test.assertCreateError != nil { test.assertCreateError(errorAsserter, createErr) diff --git a/hugolib/page.go b/hugolib/page.go index 74005e5a8..df6f88b01 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -20,11 +20,10 @@ import ( "fmt" "reflect" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/media" _errors "github.com/pkg/errors" - "github.com/gohugoio/hugo/common/maps" - "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/related" @@ -304,7 +303,7 @@ func (p *Page) initContent() { if len(p.summary) == 0 { if err = p.setAutoSummary(); err != nil { - err = _errors.Wrapf(err, "Failed to set user auto summary for page %q:", p.pathOrTitle()) + err = p.errorf(err, "failed to set auto summary") } } c <- err @@ -315,11 +314,11 @@ func (p *Page) initContent() { p.s.Log.WARN.Printf("WARNING: Timed out creating content for page %q (.Content will be empty). This is most likely a circular shortcode content loop that should be fixed. If this is just a shortcode calling a slow remote service, try to set \"timeout=20000\" (or higher, value is in milliseconds) in config.toml.\n", p.pathOrTitle()) case err := <-c: if err != nil { - // TODO(bep) 2errors needs to be transported to the caller. - p.s.Log.ERROR.Println(err) + p.s.SendError(err) } } }) + } // This is sent to the shortcodes for this page. Not doing that will create an infinite regress. So, @@ -560,11 +559,6 @@ func (ps Pages) findPagePos(page *Page) int { return -1 } -func (p *Page) createWorkContentCopy() { - p.workContent = make([]byte, len(p.rawContent)) - copy(p.workContent, p.rawContent) -} - func (p *Page) Plain() string { p.initContent() p.initPlain(true) @@ -697,12 +691,6 @@ func (p *Page) UniqueID() string { return p.File.UniqueID() } -// for logging -// TODO(bep) 2errors remove -func (p *Page) lineNumRawContentStart() int { - return bytes.Count(p.frontmatter, []byte("\n")) + 1 -} - // Returns the page as summary and main. func (p *Page) setUserDefinedSummary(rawContentCopy []byte) (*summaryContent, error) { @@ -936,31 +924,18 @@ func (s *Site) NewPage(name string) (*Page, error) { return p, nil } -func (p *Page) errorf(err error, format string, a ...interface{}) error { - args := append([]interface{}{p.Lang(), p.pathOrTitle()}, a...) - format = "[%s] Page %q: " + format - if err == nil { - return fmt.Errorf(format, args...) - } - return _errors.Wrapf(err, format, args...) -} - func (p *Page) ReadFrom(buf io.Reader) (int64, error) { // Parse for metadata & body if err := p.parse(buf); err != nil { - return 0, p.errorf(err, "parse failed") + return 0, p.errWithFileContext(err) } - // Work on a copy of the raw content from now on. - // TODO(bep) 2errors - //p.createWorkContentCopy() - if err := p.mapContent(); err != nil { - return 0, err + return 0, p.errWithFileContext(err) } - return int64(len(p.rawContent)), nil + return int64(len(p.source.parsed.Input())), nil } func (p *Page) WordCount() int { @@ -1169,7 +1144,7 @@ func (p *Page) initMainOutputFormat() error { pageOutput, err := newPageOutput(p, false, false, outFormat) if err != nil { - return _errors.Wrapf(err, "Failed to create output page for type %q for page %q:", outFormat.Name, p.pathOrTitle()) + return p.errorf(err, "failed to create output page for type %q", outFormat.Name) } p.mainPageOutput = pageOutput @@ -1485,7 +1460,7 @@ func (p *Page) updateMetaData(frontmatter map[string]interface{}) error { if isCJKLanguage != nil { p.isCJKLanguage = *isCJKLanguage } else if p.s.Cfg.GetBool("hasCJKLanguage") { - if cjk.Match(p.rawContent) { + if cjk.Match(p.source.parsed.Input()) { p.isCJKLanguage = true } else { p.isCJKLanguage = false @@ -1711,7 +1686,8 @@ func (p *Page) shouldRenderTo(f output.Format) bool { } func (p *Page) RawContent() string { - return string(p.rawContent) + // TODO(bep) 2errors + return string(p.source.parsed.Input()) } func (p *Page) FullFilePath() string { @@ -2145,12 +2121,7 @@ func (p *Page) setValuesForKind(s *Site) { // Used in error logs. func (p *Page) pathOrTitle() string { if p.Filename() != "" { - // Make a path relative to the working dir if possible. - filename := strings.TrimPrefix(p.Filename(), p.s.WorkingDir) - if filename != p.Filename() { - filename = strings.TrimPrefix(filename, helpers.FilePathSeparator) - } - return filename + return p.Filename() } return p.title } diff --git a/hugolib/page_content.go b/hugolib/page_content.go index 39abd0981..8c20db761 100644 --- a/hugolib/page_content.go +++ b/hugolib/page_content.go @@ -14,11 +14,14 @@ package hugolib import ( - "fmt" + "bytes" "io" + errors "github.com/pkg/errors" + bp "github.com/gohugoio/hugo/bufferpool" + "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/parser/metadecoders" "github.com/gohugoio/hugo/parser/pageparser" ) @@ -31,11 +34,6 @@ var ( type pageContent struct { renderable bool - frontmatter []byte - - // rawContent is the raw content read from the content file. - rawContent []byte - // workContent is a copy of rawContent that may be mutated during site build. workContent []byte @@ -66,6 +64,10 @@ func (p *Page) mapContent() error { iter := p.source.parsed.Iterator() + fail := func(err error, i pageparser.Item) error { + return parseError(err, iter.Input(), i.Pos) + } + // the parser is guaranteed to return items in proper order or fail, so … // … it's safe to keep some "global" state var currShortcode shortcode @@ -87,7 +89,7 @@ Loop: f := metadecoders.FormatFromFrontMatterType(it.Type) m, err := metadecoders.UnmarshalToMap(it.Val, f) if err != nil { - return err + return herrors.ToFileErrorWithOffset(string(f), err, iter.LineNumber()-1) } if err := p.updateMetaData(m); err != nil { return err @@ -125,7 +127,7 @@ Loop: } if err != nil { - return err + return fail(errors.Wrap(err, "failed to extract shortcode"), it) } if currShortcode.params == nil { @@ -139,10 +141,10 @@ Loop: case it.IsEOF(): break Loop case it.IsError(): - err := fmt.Errorf("%s:shortcode:%d: %s", - p.pathOrTitle(), iter.LineNumber(), it) + err := fail(errors.WithStack(errors.New(it.ValStr())), it) currShortcode.err = err return err + default: result.Write(it.Val) } @@ -180,3 +182,16 @@ func (p *Page) parse(reader io.Reader) error { return nil } + +func parseError(err error, input []byte, pos int) error { + if herrors.UnwrapFileError(err) != nil { + // Use the most specific location. + return err + } + lf := []byte("\n") + input = input[:pos] + lineNumber := bytes.Count(input, lf) + 1 + endOfLastLine := bytes.LastIndex(input, lf) + return herrors.NewFileError("md", lineNumber, pos-endOfLastLine, err) + +} diff --git a/hugolib/page_errors.go b/hugolib/page_errors.go new file mode 100644 index 000000000..42e2a8835 --- /dev/null +++ b/hugolib/page_errors.go @@ -0,0 +1,47 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hugolib + +import ( + "fmt" + + "github.com/gohugoio/hugo/common/herrors" + errors "github.com/pkg/errors" +) + +func (p *Page) errorf(err error, format string, a ...interface{}) error { + if herrors.UnwrapErrorWithFileContext(err) != nil { + // More isn't always better. + return err + } + args := append([]interface{}{p.Lang(), p.pathOrTitle()}, a...) + format = "[%s] page %q: " + format + if err == nil { + errors.Errorf(format, args...) + return fmt.Errorf(format, args...) + } + return errors.Wrapf(err, format, args...) +} + +func (p *Page) errWithFileContext(err error) error { + + err, _ = herrors.WithFileContextForFile( + err, + p.Filename(), + p.Filename(), + p.s.SourceSpec.Fs.Source, + herrors.SimpleLineMatcher) + + return err +} diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 749730236..024a919ed 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -18,7 +18,9 @@ import ( "errors" "fmt" "html/template" + "reflect" + "regexp" "sort" @@ -139,6 +141,7 @@ type shortcode struct { ordinal int err error doMarkup bool + pos int // the position in bytes in the source file } func (sc shortcode) String() string { @@ -458,7 +461,13 @@ func (s *shortcodeHandler) executeShortcodesForDelta(p *PageWithoutContent) erro render := s.contentShortcodesDelta.getShortcodeRenderer(k) renderedShortcode, err := render() if err != nil { - return _errors.Wrapf(err, "Failed to execute shortcode in page %q:", p.Path()) + sc := s.shortcodes.getShortcode(k.(scKey).ShortcodePlaceholder) + if sc != nil { + err = p.errWithFileContext(parseError(_errors.Wrapf(err, "failed to render shortcode %q", sc.name), p.source.parsed.Input(), sc.pos)) + } + + p.s.SendError(err) + continue } s.renderedShortcodes[k.(scKey).ShortcodePlaceholder] = renderedShortcode @@ -495,15 +504,8 @@ func (s *shortcodeHandler) extractShortcode(ordinal int, pt *pageparser.Iterator var cnt = 0 var nestedOrdinal = 0 - // TODO(bep) 2errors revisit after https://github.com/gohugoio/hugo/issues/5324 - msgf := func(i pageparser.Item, format string, args ...interface{}) string { - format = format + ":%d:" - // TODO(bep) 2errors - c1 := 32 // strings.Count(pt.lexer.input[:i.pos], "\n") + 1 - c2 := bytes.Count(p.frontmatter, []byte{'\n'}) - args = append(args, c1+c2) - return fmt.Sprintf(format, args...) - + fail := func(err error, i pageparser.Item) error { + return parseError(err, pt.Input(), i.Pos) } Loop: @@ -511,6 +513,7 @@ Loop: currItem := pt.Next() switch { case currItem.IsLeftShortcodeDelim(): + sc.pos = currItem.Pos next := pt.Peek() if next.IsShortcodeClose() { continue @@ -550,7 +553,8 @@ Loop: // return that error, more specific continue } - return sc, errors.New(msgf(next, "shortcode %q has no .Inner, yet a closing tag was provided", next.Val)) + + return sc, fail(_errors.Errorf("shortcode %q has no .Inner, yet a closing tag was provided", next.Val), next) } if next.IsRightShortcodeDelim() { // self-closing @@ -568,13 +572,13 @@ Loop: // if more than one. It is "all inner or no inner". tmpl := getShortcodeTemplateForTemplateKey(scKey{}, sc.name, p.s.Tmpl) if tmpl == nil { - return sc, errors.New(msgf(currItem, "unable to locate template for shortcode %q", sc.name)) + return sc, fail(_errors.Errorf("template for shortcode %q not found", sc.name), currItem) } var err error isInner, err = isInnerShortcode(tmpl.(tpl.TemplateExecutor)) if err != nil { - return sc, _errors.Wrap(err, msgf(currItem, "failed to handle template for shortcode %q", sc.name)) + return sc, fail(_errors.Wrapf(err, "failed to handle template for shortcode %q", sc.name), currItem) } case currItem.IsShortcodeParam(): diff --git a/hugolib/site.go b/hugolib/site.go index 8358cf610..78a0070ee 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -30,7 +30,6 @@ import ( _errors "github.com/pkg/errors" - "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/resource" @@ -1552,7 +1551,7 @@ func (s *Site) preparePages() error { } } - return s.pickOneAndLogTheRest(errors) + return s.owner.pickOneAndLogTheRest(errors) } func (s *Site) errorCollator(results <-chan error, errs chan<- error) { @@ -1561,45 +1560,11 @@ func (s *Site) errorCollator(results <-chan error, errs chan<- error) { errors = append(errors, e) } - errs <- s.pickOneAndLogTheRest(errors) + errs <- s.owner.pickOneAndLogTheRest(errors) close(errs) } -func (s *Site) pickOneAndLogTheRest(errors []error) error { - if len(errors) == 0 { - return nil - } - - var i int - - for j, err := range errors { - // If this is in server mode, we want to return an error to the client - // with a file context, if possible. - if herrors.UnwrapErrorWithFileContext(err) != nil { - i = j - break - } - } - - // Log the rest, but add a threshold to avoid flooding the log. - const errLogThreshold = 5 - - for j, err := range errors { - if j == i { - continue - } - - if j >= errLogThreshold { - break - } - - s.Log.ERROR.Println(err) - } - - return errors[i] -} - func (s *Site) appendThemeTemplates(in []string) []string { if !s.PathSpec.ThemeSet() { return in diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go index 70c9263b3..d37d83ed3 100644 --- a/hugolib/testhelpers_test.go +++ b/hugolib/testhelpers_test.go @@ -465,12 +465,16 @@ func (s *sitesBuilder) Fatalf(format string, args ...interface{}) { } func Fatalf(t testing.TB, format string, args ...interface{}) { - trace := strings.Join(assert.CallerInfo(), "\n\r\t\t\t") + trace := trace() format = format + "\n%s" args = append(args, trace) t.Fatalf(format, args...) } +func trace() string { + return strings.Join(assert.CallerInfo(), "\n\r\t\t\t") +} + func (s *sitesBuilder) AssertFileContent(filename string, matches ...string) { content := readDestination(s.T, s.Fs, filename) for _, match := range matches { |