commands, hugolib: Get file context in "config parse failed" errors
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 22 Oct 2018 18:20:48 +0000 (20:20 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 23 Oct 2018 06:09:41 +0000 (08:09 +0200)
Fixes #5325

commands/hugo.go
go.mod
go.sum
hugolib/config.go
hugolib/hugo_sites_build_errors_test.go
hugolib/page_bundler_test.go
hugolib/page_test.go
hugolib/shortcode_test.go

index acc48539d252df4195a02c85072e8b9e74b6797c..1cfbdcf7c1ff6d5aea3aae83487274b1acede4bc 100644 (file)
@@ -662,9 +662,10 @@ func (c *commandeer) fullRebuild() {
        c.commandeerHugoState = &commandeerHugoState{}
        err := c.loadConfig(true, true)
        if err != nil {
-               c.logger.ERROR.Println("Failed to reload config:", err)
                // Set the processing on pause until the state is recovered.
                c.paused = true
+               c.handleBuildErr(err, "Failed to reload config")
+
        } else {
                c.paused = false
        }
diff --git a/go.mod b/go.mod
index b931009767e5914a6dd3ffc47cfc54c066759d0c..8060a887633a9c8cba78f19d5b20f7d637200e57 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -63,7 +63,6 @@ require (
        golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e // indirect
        golang.org/x/text v0.3.0
        gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
-       gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
        gopkg.in/yaml.v2 v2.2.1
 )
 
diff --git a/go.sum b/go.sum
index 806cdc98a98d8df9fcf0a65a4983f52925323a47..23e14989e7db717ae633d811cec7c0f8f1308e67 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -65,7 +65,6 @@ github.com/magefile/mage v1.4.0 h1:RI7B1CgnPAuu2O9lWszwya61RLmfL0KCdo+QyyI/Bhk=
 github.com/magefile/mage v1.4.0/go.mod h1:IUDi13rsHje59lecXokTfGX0QIzO45uVPlXnJYsXepA=
 github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/markbates/inflect v0.0.0-20171215194931-a12c3aec81a6 h1:LZhVjIISSbj8qLf2qDPP0D8z0uvOWAW5C85ly5mJW6c=
 github.com/markbates/inflect v0.0.0-20171215194931-a12c3aec81a6/go.mod h1:oTeZL2KHA7CUX6X+fovmK9OvIOFuqu0TwdQrZjLTh88=
 github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -146,7 +145,5 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU=
-gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
 gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
index b21981304faa9eb13ee4e012899eda6954ebcd3d..32fa2dfca71d644a36cb04afd2dfea69030059bc 100644 (file)
@@ -16,10 +16,11 @@ package hugolib
 import (
        "errors"
        "fmt"
-
        "io"
        "strings"
 
+       "github.com/gohugoio/hugo/common/herrors"
+
        "github.com/gohugoio/hugo/hugolib/paths"
        _errors "github.com/pkg/errors"
 
@@ -106,12 +107,23 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
        v.SetConfigFile(configFilenames[0])
        v.AddConfigPath(d.Path)
 
+       applyFileContext := func(filename string, err error) error {
+               err, _ = herrors.WithFileContextForFile(
+                       err,
+                       filename,
+                       filename,
+                       fs,
+                       herrors.SimpleLineMatcher)
+
+               return err
+       }
+
        var configFileErr error
 
        err := v.ReadInConfig()
        if err != nil {
                if _, ok := err.(viper.ConfigParseError); ok {
-                       return nil, configFiles, err
+                       return nil, configFiles, applyFileContext(v.ConfigFileUsed(), err)
                }
                configFileErr = ErrNoConfigFile
        }
@@ -129,7 +141,7 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
                                return nil, configFiles, fmt.Errorf("Unable to open Config file.\n (%s)\n", err)
                        }
                        if err = v.MergeConfig(r); err != nil {
-                               return nil, configFiles, fmt.Errorf("Unable to parse/merge Config file (%s).\n (%s)\n", configFile, err)
+                               return nil, configFiles, applyFileContext(configFile, err)
                        }
                        configFiles = append(configFiles, configFile)
                }
index 2e8eb99eae3cc46c7b4e1ad9beb0fb3ecc9feb41..f290022e041a2af44fb9dc44fe1432b582094e8f 100644 (file)
@@ -30,7 +30,7 @@ func (t testSiteBuildErrorAsserter) assertLineNumber(lineNumber int, err error)
 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())
+       t.assert.Contains(e2, e1, trace())
 
 }
 
@@ -102,8 +102,8 @@ func TestSiteBuildErrors(t *testing.T) {
                                fe := a.getFileError(err)
                                assert.Equal(5, fe.LineNumber)
                                assert.Equal(14, fe.ColumnNumber)
-                               assert.Equal("md", fe.ChromaLexer)
-                               a.assertErrorMessage("asdfadf", fe.Error())
+                               assert.Equal("go-html-template", fe.ChromaLexer)
+                               a.assertErrorMessage("\"layouts/_default/single.html:5:14\": execute of template failed", fe.Error())
 
                        },
                },
@@ -124,7 +124,12 @@ func TestSiteBuildErrors(t *testing.T) {
                                return strings.Replace(content, ".Title", ".Titles", 1)
                        },
                        assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
-                               a.assertLineNumber(4, err)
+                               fe := a.getFileError(err)
+                               assert.Equal(7, fe.LineNumber)
+                               assert.Equal("md", fe.ChromaLexer)
+                               // Make sure that it contains both the content file and template
+                               a.assertErrorMessage(`content/myyaml.md:7:10": failed to render shortcode "sc"`, fe.Error())
+                               a.assertErrorMessage(`shortcodes/sc.html:4:22: executing "shortcodes/sc.html" at <.Page.Titles>: can't evaluate`, fe.Error())
                        },
                },
                {
@@ -173,7 +178,7 @@ func TestSiteBuildErrors(t *testing.T) {
                        },
                        assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
                                assert.Error(err)
-                               assert.Contains(err.Error(), "single.html")
+                               assert.Contains(err.Error(), `"content/mytoml.md": render of "page" failed: execute of template failed: panic in Execute`)
                        },
                },
        }
index 39de49663ebf1358afed38108347d72f551cc01a..1eb5aacdd52d6666d774b7bf6ca12ef785a39a8e 100644 (file)
@@ -132,7 +132,7 @@ func TestPageBundlerSiteRegular(t *testing.T) {
                                assert.Len(pageResources, 2)
                                firstPage := pageResources[0].(*Page)
                                secondPage := pageResources[1].(*Page)
-                               assert.Equal(filepath.FromSlash("base/b/my-bundle/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle())
+                               assert.Equal(filepath.FromSlash("/work/base/b/my-bundle/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle())
                                assert.Contains(firstPage.content(), "TheContent")
                                assert.Equal(6, len(leafBundle1.Resources))
 
index 7359140fcf7f5c6100790260604098dc774a4349..ced7e78d88296168bf58b0571a7523b5ad6fd3d1 100644 (file)
@@ -1145,7 +1145,7 @@ func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) {
                r   string
                err string
        }{
-               {invalidFrontmatterShortDelimEnding, ":2: EOF looking for end YAML front matter delimiter"},
+               {invalidFrontmatterShortDelimEnding, "EOF looking for end YAML front matter delimiter"},
        }
        for _, test := range tests {
                s := newTestSite(t)
index 6e250ed21fb5a6c6057d914ecc40510a2ef8158d..0d397f9eeee6d4b1b8444ea9d3d31b4d1a82ae05 100644 (file)
@@ -368,11 +368,11 @@ func TestExtractShortcodes(t *testing.T) {
                expectErrorMsg   string
        }{
                {"text", "Some text.", "map[]", "Some text.", ""},
-               {"invalid right delim", "{{< tag }}", "", false, ":5:.*unrecognized character.*}"},
-               {"invalid close", "\n{{< /tag >}}", "", false, ":6:.*got closing shortcode, but none is open"},
-               {"invalid close2", "\n\n{{< tag >}}{{< /anotherTag >}}", "", false, ":7: closing tag for shortcode 'anotherTag' does not match start tag"},
-               {"unterminated quote 1", `{{< figure src="im caption="S" >}}`, "", false, ":5:.got pos.*"},
-               {"unterminated quote 1", `{{< figure src="im" caption="S >}}`, "", false, ":5:.*unterm.*}"},
+               {"invalid right delim", "{{< tag }}", "", false, "unrecognized character"},
+               {"invalid close", "\n{{< /tag >}}", "", false, "got closing shortcode, but none is open"},
+               {"invalid close2", "\n\n{{< tag >}}{{< /anotherTag >}}", "", false, "closing tag for shortcode 'anotherTag' does not match start tag"},
+               {"unterminated quote 1", `{{< figure src="im caption="S" >}}`, "", false, "got pos"},
+               {"unterminated quote 1", `{{< figure src="im" caption="S >}}`, "", false, "unterm"},
                {"one shortcode, no markup", "{{< tag >}}", "", testScPlaceholderRegexp, ""},
                {"one shortcode, markup", "{{% tag %}}", "", testScPlaceholderRegexp, ""},
                {"one pos param", "{{% tag param1 %}}", `tag([\"param1\"], true){[]}"]`, testScPlaceholderRegexp, ""},
@@ -430,7 +430,7 @@ func TestExtractShortcodes(t *testing.T) {
                        if err == nil {
                                t.Fatalf("[%d] %s: ExtractShortcodes didn't return an expected error", i, this.name)
                        } else {
-                               r, _ := regexp.Compile(this.expectErrorMsg)
+                               r := regexp.MustCompile(this.expectErrorMsg)
                                if !r.MatchString(err.Error()) {
                                        t.Fatalf("[%d] %s: ExtractShortcodes didn't return an expected error message, got\n%s but expected\n%s",
                                                i, this.name, err.Error(), this.expectErrorMsg)