mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-25 13:10:38 +03:00
config: Fix _merge issue when key doesn't exist on the left side
Fixes #13643 Fixes #13646
This commit is contained in:
parent
61a286595e
commit
179aea11ac
7 changed files with 165 additions and 90 deletions
|
@ -800,30 +800,58 @@ func (c *Configs) IsZero() bool {
|
|||
|
||||
func (c *Configs) Init() error {
|
||||
var languages langs.Languages
|
||||
defaultContentLanguage := c.Base.DefaultContentLanguage
|
||||
for k, v := range c.LanguageConfigMap {
|
||||
|
||||
var langKeys []string
|
||||
var hasEn bool
|
||||
|
||||
const en = "en"
|
||||
|
||||
for k := range c.LanguageConfigMap {
|
||||
langKeys = append(langKeys, k)
|
||||
if k == en {
|
||||
hasEn = true
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the LanguageConfigSlice by language weight (if set) or lang.
|
||||
sort.Slice(langKeys, func(i, j int) bool {
|
||||
ki := langKeys[i]
|
||||
kj := langKeys[j]
|
||||
lki := c.LanguageConfigMap[ki]
|
||||
lkj := c.LanguageConfigMap[kj]
|
||||
li := lki.Languages[ki]
|
||||
lj := lkj.Languages[kj]
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return ki < kj
|
||||
})
|
||||
|
||||
// See issue #13646.
|
||||
defaultConfigLanguageFallback := en
|
||||
if !hasEn {
|
||||
// Pick the first one.
|
||||
defaultConfigLanguageFallback = langKeys[0]
|
||||
}
|
||||
|
||||
if c.Base.DefaultContentLanguage == "" {
|
||||
c.Base.DefaultContentLanguage = defaultConfigLanguageFallback
|
||||
}
|
||||
|
||||
for _, k := range langKeys {
|
||||
v := c.LanguageConfigMap[k]
|
||||
if v.DefaultContentLanguage == "" {
|
||||
v.DefaultContentLanguage = defaultConfigLanguageFallback
|
||||
}
|
||||
c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
|
||||
languageConf := v.Languages[k]
|
||||
language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
|
||||
language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
languages = append(languages, language)
|
||||
}
|
||||
|
||||
// Sort the sites by language weight (if set) or lang.
|
||||
sort.Slice(languages, func(i, j int) bool {
|
||||
li := languages[i]
|
||||
lj := languages[j]
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return li.Lang < lj.Lang
|
||||
})
|
||||
|
||||
for _, l := range languages {
|
||||
c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang])
|
||||
}
|
||||
|
||||
// Filter out disabled languages.
|
||||
var n int
|
||||
for _, l := range languages {
|
||||
|
@ -836,12 +864,12 @@ func (c *Configs) Init() error {
|
|||
|
||||
var languagesDefaultFirst langs.Languages
|
||||
for _, l := range languages {
|
||||
if l.Lang == defaultContentLanguage {
|
||||
if l.Lang == c.Base.DefaultContentLanguage {
|
||||
languagesDefaultFirst = append(languagesDefaultFirst, l)
|
||||
}
|
||||
}
|
||||
for _, l := range languages {
|
||||
if l.Lang != defaultContentLanguage {
|
||||
if l.Lang != c.Base.DefaultContentLanguage {
|
||||
languagesDefaultFirst = append(languagesDefaultFirst, l)
|
||||
}
|
||||
}
|
||||
|
@ -927,17 +955,48 @@ func (c Configs) GetByLang(lang string) config.AllProvider {
|
|||
return nil
|
||||
}
|
||||
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
|
||||
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
|
||||
RootConfig: RootConfig{
|
||||
Environment: hugo.EnvironmentProduction,
|
||||
TitleCaseStyle: "AP",
|
||||
PluralizeListTitles: true,
|
||||
CapitalizeListTitles: true,
|
||||
StaticDir: []string{"static"},
|
||||
SummaryLength: 70,
|
||||
Timeout: "60s",
|
||||
|
||||
CommonDirs: config.CommonDirs{
|
||||
ArcheTypeDir: "archetypes",
|
||||
ContentDir: "content",
|
||||
ResourceDir: "resources",
|
||||
PublishDir: "public",
|
||||
ThemesDir: "themes",
|
||||
AssetDir: "assets",
|
||||
LayoutDir: "layouts",
|
||||
I18nDir: "i18n",
|
||||
DataDir: "data",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// fromLoadConfigResult creates a new Config from res.
|
||||
func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) {
|
||||
if !res.Cfg.IsSet("languages") {
|
||||
// We need at least one
|
||||
lang := res.Cfg.GetString("defaultContentLanguage")
|
||||
if lang == "" {
|
||||
lang = "en"
|
||||
}
|
||||
res.Cfg.Set("languages", maps.Params{lang: maps.Params{}})
|
||||
}
|
||||
bcfg := res.BaseConfig
|
||||
cfg := res.Cfg
|
||||
|
||||
all := &Config{}
|
||||
all := newDefaultConfig()
|
||||
|
||||
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
|
||||
if err != nil {
|
||||
|
@ -947,6 +1006,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
|||
langConfigMap := make(map[string]*Config)
|
||||
|
||||
languagesConfig := cfg.GetStringMap("languages")
|
||||
|
||||
var isMultihost bool
|
||||
|
||||
if err := all.CompileConfig(logger); err != nil {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
|
@ -234,3 +235,62 @@ baseURL = "https://example.com"
|
|||
b.Assert(c.IsContentFile("foo.md"), qt.Equals, true)
|
||||
b.Assert(len(s), qt.Equals, 6)
|
||||
}
|
||||
|
||||
func TestMergeDeep(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
theme = ["theme1", "theme2"]
|
||||
_merge = "deep"
|
||||
-- themes/theme1/hugo.toml --
|
||||
[sitemap]
|
||||
filename = 'mysitemap.xml'
|
||||
[services]
|
||||
[services.googleAnalytics]
|
||||
id = 'foo bar'
|
||||
[taxonomies]
|
||||
foo = 'bars'
|
||||
-- themes/theme2/config/_default/hugo.toml --
|
||||
[taxonomies]
|
||||
bar = 'baz'
|
||||
-- layouts/home.html --
|
||||
GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction)
|
||||
b.Assert(base.BaseURL, qt.Equals, "https://example.com")
|
||||
b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml")
|
||||
b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"})
|
||||
|
||||
b.AssertFileContent("public/index.html", "GA ID: foo bar.")
|
||||
}
|
||||
|
||||
func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
[languages]
|
||||
[languages.nn]
|
||||
weight = 20
|
||||
[languages.sv]
|
||||
weight = 10
|
||||
[languages.sv.taxonomies]
|
||||
tag = "taggar"
|
||||
-- layouts/all.html --
|
||||
All.
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
|
||||
}
|
||||
|
|
|
@ -249,14 +249,18 @@ var allDecoderSetups = map[string]decodeWeight{
|
|||
key: "sitemap",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key))
|
||||
if p.p.IsSet(d.key) {
|
||||
p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key))
|
||||
}
|
||||
return err
|
||||
},
|
||||
},
|
||||
"taxonomies": {
|
||||
key: "taxonomies",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
if p.p.IsSet(d.key) {
|
||||
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
@ -306,15 +310,17 @@ var allDecoderSetups = map[string]decodeWeight{
|
|||
}
|
||||
|
||||
// Validate defaultContentLanguage.
|
||||
var found bool
|
||||
for lang := range p.c.Languages {
|
||||
if lang == p.c.DefaultContentLanguage {
|
||||
found = true
|
||||
break
|
||||
if p.c.DefaultContentLanguage != "" {
|
||||
var found bool
|
||||
for lang := range p.c.Languages {
|
||||
if lang == p.c.DefaultContentLanguage {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -159,63 +159,9 @@ func (l configLoader) applyConfigAliases() error {
|
|||
|
||||
func (l configLoader) applyDefaultConfig() error {
|
||||
defaultSettings := maps.Params{
|
||||
"baseURL": "",
|
||||
"cleanDestinationDir": false,
|
||||
"watch": false,
|
||||
"contentDir": "content",
|
||||
"resourceDir": "resources",
|
||||
"publishDir": "public",
|
||||
"publishDirOrig": "public",
|
||||
"themesDir": "themes",
|
||||
"assetDir": "assets",
|
||||
"layoutDir": "layouts",
|
||||
"i18nDir": "i18n",
|
||||
"dataDir": "data",
|
||||
"archetypeDir": "archetypes",
|
||||
"configDir": "config",
|
||||
"staticDir": "static",
|
||||
"buildDrafts": false,
|
||||
"buildFuture": false,
|
||||
"buildExpired": false,
|
||||
"params": maps.Params{},
|
||||
"environment": hugo.EnvironmentProduction,
|
||||
"uglyURLs": false,
|
||||
"verbose": false,
|
||||
"ignoreCache": false,
|
||||
"canonifyURLs": false,
|
||||
"relativeURLs": false,
|
||||
"removePathAccents": false,
|
||||
"titleCaseStyle": "AP",
|
||||
"taxonomies": maps.Params{"tag": "tags", "category": "categories"},
|
||||
"permalinks": maps.Params{},
|
||||
"sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"},
|
||||
"menus": maps.Params{},
|
||||
"disableLiveReload": false,
|
||||
"pluralizeListTitles": true,
|
||||
"capitalizeListTitles": true,
|
||||
"forceSyncStatic": false,
|
||||
"footnoteAnchorPrefix": "",
|
||||
"footnoteReturnLinkContents": "",
|
||||
"newContentEditor": "",
|
||||
"paginate": 0, // Moved into the paginator struct in Hugo v0.128.0.
|
||||
"paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0.
|
||||
"summaryLength": 70,
|
||||
"rssLimit": -1,
|
||||
"sectionPagesMenu": "",
|
||||
"disablePathToLower": false,
|
||||
"hasCJKLanguage": false,
|
||||
"enableEmoji": false,
|
||||
"defaultContentLanguage": "en",
|
||||
"defaultContentLanguageInSubdir": false,
|
||||
"enableMissingTranslationPlaceholders": false,
|
||||
"enableGitInfo": false,
|
||||
"ignoreFiles": make([]string, 0),
|
||||
"disableAliases": false,
|
||||
"debug": false,
|
||||
"disableFastRender": false,
|
||||
"timeout": "60s",
|
||||
"timeZone": "",
|
||||
"enableInlineShortcodes": false,
|
||||
// These dirs are used early/before we build the config struct.
|
||||
"themesDir": "themes",
|
||||
"configDir": "config",
|
||||
}
|
||||
|
||||
l.cfg.SetDefaults(defaultSettings)
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cast"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type BaseConfig struct {
|
||||
|
|
|
@ -101,6 +101,9 @@ func DecodeConfig(cfg config.Provider) (c Config, err error) {
|
|||
|
||||
if c.RSS.Limit == 0 {
|
||||
c.RSS.Limit = cfg.GetInt(rssLimitKey)
|
||||
if c.RSS.Limit == 0 {
|
||||
c.RSS.Limit = -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -475,7 +475,7 @@ name = "menu-theme"
|
|||
})
|
||||
})
|
||||
|
||||
// Issue #8724
|
||||
// Issue #8724 ##13643
|
||||
for _, mergeStrategy := range []string{"none", "shallow"} {
|
||||
c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) {
|
||||
smapConfigTempl := `[sitemap]
|
||||
|
@ -495,7 +495,7 @@ name = "menu-theme"
|
|||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"})
|
||||
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
|
||||
} else {
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"})
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: 0.5, Filename: "sitemap.xml"})
|
||||
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue