]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix "assignment to entry in nil map" on empty YAML config files
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 22 Oct 2025 09:06:54 +0000 (11:06 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 22 Oct 2025 11:40:03 +0000 (13:40 +0200)
Fixes #14074

common/herrors/errors.go
config/allconfig/load.go
hugolib/config_test.go
parser/metadecoders/decoder.go
parser/metadecoders/decoder_integration_test.go [new file with mode: 0644]

index c7ee90dd0ecb823439cdd175f0d8255241f48aae..693ff024c49328c7b7c95adea75dfede54d2c590 100644 (file)
@@ -17,22 +17,13 @@ package herrors
 import (
        "errors"
        "fmt"
-       "io"
        "os"
        "regexp"
-       "runtime"
        "runtime/debug"
        "strings"
        "time"
 )
 
-// PrintStackTrace prints the current stacktrace to w.
-func PrintStackTrace(w io.Writer) {
-       buf := make([]byte, 1<<16)
-       runtime.Stack(buf, true)
-       fmt.Fprintf(w, "%s", buf)
-}
-
 // ErrorSender is a, typically, non-blocking error handler.
 type ErrorSender interface {
        SendError(err error)
index 318a1700a2d4e7a489e026ea632c4f93ce856347..b76c4abde739219c7299928b8fb9184595f16d7a 100644 (file)
@@ -19,6 +19,7 @@ import (
        "fmt"
        "os"
        "path/filepath"
+       "runtime/debug"
        "strings"
 
        "github.com/gobwas/glob"
@@ -44,6 +45,7 @@ func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) {
        defer func() {
                if r := recover(); r != nil {
                        err = fmt.Errorf("failed to load config: %v", r)
+                       debug.PrintStack()
                }
        }()
 
index 27e5f6d41b5d4e709f21c992b699cebd2f1dbadf..be5086f3ed887b23a288523ea43c5c3752772cd2 100644 (file)
@@ -1649,3 +1649,16 @@ Params: {{ site.Params }}|
 
        }
 }
+
+func TestConfigYAMLNilMapIssue14074(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- config/_default/taxonomies.yaml --
+# empty on purpose
+-- hugo.yaml --
+
+`
+
+       Test(t, files)
+}
index 18aaabce1dad462572cbaf520f05a9aaee7cbf66..6443bfdf8d0beff32fc44a28a4976b61febd4918 100644 (file)
@@ -86,6 +86,15 @@ func (d Decoder) UnmarshalToMap(data []byte, f Format) (map[string]any, error) {
 
        err := d.UnmarshalTo(data, f, &m)
 
+       if m == nil {
+               // We migrated to github.com/goccy/go-yaml in v0.152.0,
+               // which produces nil maps for empty YAML files (and empty map nodes), unlike gopkg.in/yaml.v2.
+               //
+               // To prevent crashes when trying to handle empty config files etc., we ensure we always return a non-nil map here.
+               // See issue 14074.
+               m = make(map[string]any)
+       }
+
        return m, err
 }
 
diff --git a/parser/metadecoders/decoder_integration_test.go b/parser/metadecoders/decoder_integration_test.go
new file mode 100644 (file)
index 0000000..2e533c5
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2025 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 metadecoders_test
+
+import (
+       "testing"
+
+       "github.com/gohugoio/hugo/hugolib"
+)
+
+func TestYAMLIntegerSortIssue14078(t *testing.T) {
+       files := `
+-- assets/mydata.yaml --
+a:
+   weight: 1
+x:
+  weight: 2
+c:
+  weight: 3
+t:
+  weight: 4
+
+-- layouts/all.html --
+{{ $mydata := resources.Get "mydata.yaml" | transform.Unmarshal }}
+Sorted: {{ sort $mydata "weight" }}|
+
+`
+
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContent("public/index.html", "Sorted: [map[weight:1] map[weight:2] map[weight:3] map[weight:4]]|")
+}