Skip to content

Commit 179aea1

Browse files
committed
config: Fix _merge issue when key doesn't exist on the left side
Fixes #13643 Fixes #13646
1 parent 61a2865 commit 179aea1

File tree

7 files changed

+162
-87
lines changed

7 files changed

+162
-87
lines changed

‎config/allconfig/allconfig.go‎

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -800,28 +800,56 @@ func (c *Configs) IsZero() bool {
800800

801801
func (c *Configs) Init() error {
802802
var languages langs.Languages
803-
defaultContentLanguage := c.Base.DefaultContentLanguage
804-
for k, v := range c.LanguageConfigMap {
805-
languageConf := v.Languages[k]
806-
language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
807-
if err != nil {
808-
return err
803+
804+
var langKeys []string
805+
var hasEn bool
806+
807+
const en = "en"
808+
809+
for k := range c.LanguageConfigMap {
810+
langKeys = append(langKeys, k)
811+
if k == en {
812+
hasEn = true
809813
}
810-
languages = append(languages, language)
811814
}
812815

813-
// Sort the sites by language weight (if set) or lang.
814-
sort.Slice(languages, func(i, j int) bool {
815-
li := languages[i]
816-
lj := languages[j]
816+
// Sort the LanguageConfigSlice by language weight (if set) or lang.
817+
sort.Slice(langKeys, func(i, j int) bool {
818+
ki := langKeys[i]
819+
kj := langKeys[j]
820+
lki := c.LanguageConfigMap[ki]
821+
lkj := c.LanguageConfigMap[kj]
822+
li := lki.Languages[ki]
823+
lj := lkj.Languages[kj]
817824
if li.Weight != lj.Weight {
818825
return li.Weight < lj.Weight
819826
}
820-
return li.Lang < lj.Lang
827+
return ki < kj
821828
})
822829

823-
for _, l := range languages {
824-
c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang])
830+
// See issue #13646.
831+
defaultConfigLanguageFallback := en
832+
if !hasEn {
833+
// Pick the first one.
834+
defaultConfigLanguageFallback = langKeys[0]
835+
}
836+
837+
if c.Base.DefaultContentLanguage == "" {
838+
c.Base.DefaultContentLanguage = defaultConfigLanguageFallback
839+
}
840+
841+
for _, k := range langKeys {
842+
v := c.LanguageConfigMap[k]
843+
if v.DefaultContentLanguage == "" {
844+
v.DefaultContentLanguage = defaultConfigLanguageFallback
845+
}
846+
c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
847+
languageConf := v.Languages[k]
848+
language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf)
849+
if err != nil {
850+
return err
851+
}
852+
languages = append(languages, language)
825853
}
826854

827855
// Filter out disabled languages.
@@ -836,12 +864,12 @@ func (c *Configs) Init() error {
836864

837865
var languagesDefaultFirst langs.Languages
838866
for _, l := range languages {
839-
if l.Lang == defaultContentLanguage {
867+
if l.Lang == c.Base.DefaultContentLanguage {
840868
languagesDefaultFirst = append(languagesDefaultFirst, l)
841869
}
842870
}
843871
for _, l := range languages {
844-
if l.Lang != defaultContentLanguage {
872+
if l.Lang != c.Base.DefaultContentLanguage {
845873
languagesDefaultFirst = append(languagesDefaultFirst, l)
846874
}
847875
}
@@ -927,17 +955,48 @@ func (c Configs) GetByLang(lang string) config.AllProvider {
927955
return nil
928956
}
929957

958+
func newDefaultConfig() *Config {
959+
return &Config{
960+
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
961+
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
962+
RootConfig: RootConfig{
963+
Environment: hugo.EnvironmentProduction,
964+
TitleCaseStyle: "AP",
965+
PluralizeListTitles: true,
966+
CapitalizeListTitles: true,
967+
StaticDir: []string{"static"},
968+
SummaryLength: 70,
969+
Timeout: "60s",
970+
971+
CommonDirs: config.CommonDirs{
972+
ArcheTypeDir: "archetypes",
973+
ContentDir: "content",
974+
ResourceDir: "resources",
975+
PublishDir: "public",
976+
ThemesDir: "themes",
977+
AssetDir: "assets",
978+
LayoutDir: "layouts",
979+
I18nDir: "i18n",
980+
DataDir: "data",
981+
},
982+
},
983+
}
984+
}
985+
930986
// fromLoadConfigResult creates a new Config from res.
931987
func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) {
932988
if !res.Cfg.IsSet("languages") {
933989
// We need at least one
934990
lang := res.Cfg.GetString("defaultContentLanguage")
991+
if lang == "" {
992+
lang = "en"
993+
}
935994
res.Cfg.Set("languages", maps.Params{lang: maps.Params{}})
936995
}
937996
bcfg := res.BaseConfig
938997
cfg := res.Cfg
939998

940-
all := &Config{}
999+
all := newDefaultConfig()
9411000

9421001
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
9431002
if err != nil {
@@ -947,6 +1006,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
9471006
langConfigMap := make(map[string]*Config)
9481007

9491008
languagesConfig := cfg.GetStringMap("languages")
1009+
9501010
var isMultihost bool
9511011

9521012
if err := all.CompileConfig(logger); err != nil {

‎config/allconfig/allconfig_integration_test.go‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
qt "github.com/frankban/quicktest"
8+
"github.com/gohugoio/hugo/common/hugo"
89
"github.com/gohugoio/hugo/config/allconfig"
910
"github.com/gohugoio/hugo/hugolib"
1011
"github.com/gohugoio/hugo/media"
@@ -234,3 +235,62 @@ baseURL = "https://example.com"
234235
b.Assert(c.IsContentFile("foo.md"), qt.Equals, true)
235236
b.Assert(len(s), qt.Equals, 6)
236237
}
238+
239+
func TestMergeDeep(t *testing.T) {
240+
t.Parallel()
241+
242+
files := `
243+
-- hugo.toml --
244+
baseURL = "https://example.com"
245+
theme = ["theme1", "theme2"]
246+
_merge = "deep"
247+
-- themes/theme1/hugo.toml --
248+
[sitemap]
249+
filename = 'mysitemap.xml'
250+
[services]
251+
[services.googleAnalytics]
252+
id = 'foo bar'
253+
[taxonomies]
254+
foo = 'bars'
255+
-- themes/theme2/config/_default/hugo.toml --
256+
[taxonomies]
257+
bar = 'baz'
258+
-- layouts/home.html --
259+
GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
260+
261+
`
262+
263+
b := hugolib.Test(t, files)
264+
265+
conf := b.H.Configs
266+
base := conf.Base
267+
268+
b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction)
269+
b.Assert(base.BaseURL, qt.Equals, "https://example.com")
270+
b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml")
271+
b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"})
272+
273+
b.AssertFileContent("public/index.html", "GA ID: foo bar.")
274+
}
275+
276+
func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
277+
t.Parallel()
278+
279+
files := `
280+
-- hugo.toml --
281+
baseURL = "https://example.com"
282+
[languages]
283+
[languages.nn]
284+
weight = 20
285+
[languages.sv]
286+
weight = 10
287+
[languages.sv.taxonomies]
288+
tag = "taggar"
289+
-- layouts/all.html --
290+
All.
291+
`
292+
293+
b := hugolib.Test(t, files)
294+
295+
b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
296+
}

‎config/allconfig/alldecoders.go‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,18 @@ var allDecoderSetups = map[string]decodeWeight{
249249
key: "sitemap",
250250
decode: func(d decodeWeight, p decodeConfig) error {
251251
var err error
252-
p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key))
252+
if p.p.IsSet(d.key) {
253+
p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key))
254+
}
253255
return err
254256
},
255257
},
256258
"taxonomies": {
257259
key: "taxonomies",
258260
decode: func(d decodeWeight, p decodeConfig) error {
259-
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
261+
if p.p.IsSet(d.key) {
262+
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
263+
}
260264
return nil
261265
},
262266
},
@@ -306,15 +310,17 @@ var allDecoderSetups = map[string]decodeWeight{
306310
}
307311

308312
// Validate defaultContentLanguage.
309-
var found bool
310-
for lang := range p.c.Languages {
311-
if lang == p.c.DefaultContentLanguage {
312-
found = true
313-
break
313+
if p.c.DefaultContentLanguage != "" {
314+
var found bool
315+
for lang := range p.c.Languages {
316+
if lang == p.c.DefaultContentLanguage {
317+
found = true
318+
break
319+
}
320+
}
321+
if !found {
322+
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
314323
}
315-
}
316-
if !found {
317-
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
318324
}
319325

320326
return nil

‎config/allconfig/load.go‎

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -159,63 +159,9 @@ func (l configLoader) applyConfigAliases() error {
159159

160160
func (l configLoader) applyDefaultConfig() error {
161161
defaultSettings := maps.Params{
162-
"baseURL": "",
163-
"cleanDestinationDir": false,
164-
"watch": false,
165-
"contentDir": "content",
166-
"resourceDir": "resources",
167-
"publishDir": "public",
168-
"publishDirOrig": "public",
169-
"themesDir": "themes",
170-
"assetDir": "assets",
171-
"layoutDir": "layouts",
172-
"i18nDir": "i18n",
173-
"dataDir": "data",
174-
"archetypeDir": "archetypes",
175-
"configDir": "config",
176-
"staticDir": "static",
177-
"buildDrafts": false,
178-
"buildFuture": false,
179-
"buildExpired": false,
180-
"params": maps.Params{},
181-
"environment": hugo.EnvironmentProduction,
182-
"uglyURLs": false,
183-
"verbose": false,
184-
"ignoreCache": false,
185-
"canonifyURLs": false,
186-
"relativeURLs": false,
187-
"removePathAccents": false,
188-
"titleCaseStyle": "AP",
189-
"taxonomies": maps.Params{"tag": "tags", "category": "categories"},
190-
"permalinks": maps.Params{},
191-
"sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"},
192-
"menus": maps.Params{},
193-
"disableLiveReload": false,
194-
"pluralizeListTitles": true,
195-
"capitalizeListTitles": true,
196-
"forceSyncStatic": false,
197-
"footnoteAnchorPrefix": "",
198-
"footnoteReturnLinkContents": "",
199-
"newContentEditor": "",
200-
"paginate": 0, // Moved into the paginator struct in Hugo v0.128.0.
201-
"paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0.
202-
"summaryLength": 70,
203-
"rssLimit": -1,
204-
"sectionPagesMenu": "",
205-
"disablePathToLower": false,
206-
"hasCJKLanguage": false,
207-
"enableEmoji": false,
208-
"defaultContentLanguage": "en",
209-
"defaultContentLanguageInSubdir": false,
210-
"enableMissingTranslationPlaceholders": false,
211-
"enableGitInfo": false,
212-
"ignoreFiles": make([]string, 0),
213-
"disableAliases": false,
214-
"debug": false,
215-
"disableFastRender": false,
216-
"timeout": "60s",
217-
"timeZone": "",
218-
"enableInlineShortcodes": false,
162+
// These dirs are used early/before we build the config struct.
163+
"themesDir": "themes",
164+
"configDir": "config",
219165
}
220166

221167
l.cfg.SetDefaults(defaultSettings)

‎config/commonConfig.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"fmt"
1818
"net/http"
1919
"regexp"
20+
"slices"
2021
"sort"
2122
"strings"
2223

@@ -28,7 +29,6 @@ import (
2829
"github.com/gohugoio/hugo/common/herrors"
2930
"github.com/mitchellh/mapstructure"
3031
"github.com/spf13/cast"
31-
"slices"
3232
)
3333

3434
type BaseConfig struct {

‎config/services/servicesConfig.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ func DecodeConfig(cfg config.Provider) (c Config, err error) {
101101

102102
if c.RSS.Limit == 0 {
103103
c.RSS.Limit = cfg.GetInt(rssLimitKey)
104+
if c.RSS.Limit == 0 {
105+
c.RSS.Limit = -1
106+
}
104107
}
105108

106109
return

‎hugolib/config_test.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ name = "menu-theme"
475475
})
476476
})
477477

478-
// Issue #8724
478+
// Issue #8724 ##13643
479479
for _, mergeStrategy := range []string{"none", "shallow"} {
480480
c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) {
481481
smapConfigTempl := `[sitemap]
@@ -495,7 +495,7 @@ name = "menu-theme"
495495
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"})
496496
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
497497
} else {
498-
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"})
498+
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: 0.5, Filename: "sitemap.xml"})
499499
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
500500
}
501501
})

0 commit comments

Comments
 (0)