Create a struct with all of Hugo's config options

Primary motivation is documentation, but it will also hopefully simplify the code.

Also,

* Lower case the default output format names; this is in line with the custom ones (map keys) and how
it's treated all the places. This avoids doing `stringds.EqualFold` everywhere.

Closes #10896
Closes #10620
This commit is contained in:
Bjørn Erik Pedersen
2023-01-04 18:24:36 +01:00
parent 6aededf6b4
commit 241b21b0fd
337 changed files with 13377 additions and 14898 deletions

View File

@@ -21,59 +21,241 @@ import (
"testing"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/media"
"github.com/google/go-cmp/cmp"
"github.com/gohugoio/hugo/config/allconfig"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/maps"
"github.com/spf13/afero"
)
func TestLoadConfigLanguageParamsOverrideIssue10620(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "setion"]
title = "Base Title"
staticDir = "mystatic"
[params]
[params.comments]
color = "blue"
title = "Default Comments Title"
[languages]
[languages.en]
title = "English Title"
[languages.en.params.comments]
title = "English Comments Title"
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
enSite := b.H.Sites[0]
b.Assert(enSite.Title(), qt.Equals, "English Title")
b.Assert(enSite.Home().Title(), qt.Equals, "English Title")
b.Assert(enSite.Params(), qt.DeepEquals, maps.Params{
"comments": maps.Params{
"color": "blue",
"title": "English Comments Title",
},
},
)
}
func TestLoadConfig(t *testing.T) {
c := qt.New(t)
t.Run("2 languages", func(t *testing.T) {
t.Parallel()
loadConfig := func(c *qt.C, configContent string, fromDir bool) config.Provider {
mm := afero.NewMemMapFs()
filename := "config.toml"
descriptor := ConfigSourceDescriptor{Fs: mm}
if fromDir {
filename = filepath.Join("config", "_default", filename)
descriptor.AbsConfigDir = "config"
}
writeToFs(t, mm, filename, configContent)
cfg, _, err := LoadConfig(descriptor)
c.Assert(err, qt.IsNil)
return cfg
}
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "setion"]
title = "Base Title"
staticDir = "mystatic"
[params]
p1 = "p1base"
p2 = "p2base"
[languages]
[languages.en]
title = "English Title"
[languages.en.params]
myparam = "enParamValue"
p1 = "p1en"
weight = 1
[languages.sv]
title = "Svensk Title"
staticDir = "mysvstatic"
weight = 2
[languages.sv.params]
myparam = "svParamValue"
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
enSite := b.H.Sites[0]
svSite := b.H.Sites[1]
b.Assert(enSite.Title(), qt.Equals, "English Title")
b.Assert(enSite.Home().Title(), qt.Equals, "English Title")
b.Assert(enSite.Params()["myparam"], qt.Equals, "enParamValue")
b.Assert(enSite.Params()["p1"], qt.Equals, "p1en")
b.Assert(enSite.Params()["p2"], qt.Equals, "p2base")
b.Assert(svSite.Params()["p1"], qt.Equals, "p1base")
b.Assert(enSite.conf.StaticDir[0], qt.Equals, "mystatic")
b.Assert(svSite.Title(), qt.Equals, "Svensk Title")
b.Assert(svSite.Home().Title(), qt.Equals, "Svensk Title")
b.Assert(svSite.Params()["myparam"], qt.Equals, "svParamValue")
b.Assert(svSite.conf.StaticDir[0], qt.Equals, "mysvstatic")
c.Run("Basic", func(c *qt.C) {
c.Parallel()
// Add a random config variable for testing.
// side = page in Norwegian.
cfg := loadConfig(c, `PaginatePath = "side"`, false)
c.Assert(cfg.GetString("paginatePath"), qt.Equals, "side")
})
// Issue #8763
for _, fromDir := range []bool{false, true} {
testName := "Taxonomy overrides"
if fromDir {
testName += " from dir"
}
c.Run(testName, func(c *qt.C) {
c.Parallel()
cfg := loadConfig(c, `[taxonomies]
appellation = "appellations"
vigneron = "vignerons"`, fromDir)
t.Run("disable default language", func(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "setion"]
title = "Base Title"
defaultContentLanguage = "sv"
disableLanguages = ["sv"]
[languages.en]
weight = 1
[languages.sv]
weight = 2
`
b, err := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).BuildE()
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, "cannot disable default content language")
})
t.Run("no internal config from outside", func(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
[internal]
running = true
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.Assert(b.H.Conf.Running(), qt.Equals, false)
})
t.Run("env overrides", func(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "setion"]
title = "Base Title"
[params]
p1 = "p1base"
p2 = "p2base"
[params.pm2]
pm21 = "pm21base"
pm22 = "pm22base"
-- layouts/index.html --
p1: {{ .Site.Params.p1 }}
p2: {{ .Site.Params.p2 }}
pm21: {{ .Site.Params.pm2.pm21 }}
pm22: {{ .Site.Params.pm2.pm22 }}
pm31: {{ .Site.Params.pm3.pm31 }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Environ: []string{"HUGO_PARAMS_P2=p2env", "HUGO_PARAMS_PM2_PM21=pm21env", "HUGO_PARAMS_PM3_PM31=pm31env"},
},
).Build()
b.AssertFileContent("public/index.html", "p1: p1base\np2: p2env\npm21: pm21env\npm22: pm22base\npm31: pm31env")
})
}
func TestLoadConfigThemeLanguage(t *testing.T) {
t.Parallel()
files := `
-- /hugo.toml --
baseURL = "https://example.com"
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
theme = "mytheme"
[languages]
[languages.en]
title = "English Title"
weight = 1
[languages.sv]
weight = 2
-- themes/mytheme/hugo.toml --
[params]
p1 = "p1base"
[languages]
[languages.en]
title = "English Title Theme"
[languages.en.params]
p2 = "p2en"
[languages.en.params.sub]
sub1 = "sub1en"
[languages.sv]
title = "Svensk Title Theme"
-- layouts/index.html --
title: {{ .Title }}|
p1: {{ .Site.Params.p1 }}|
p2: {{ .Site.Params.p2 }}|
sub: {{ .Site.Params.sub }}|
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertFileContent("public/en/index.html", `
title: English Title|
p1: p1base
p2: p2en
sub: map[sub1:sub1en]
`)
c.Assert(cfg.Get("taxonomies"), qt.DeepEquals, maps.Params{
"appellation": "appellations",
"vigneron": "vignerons",
})
})
}
}
func TestLoadMultiConfig(t *testing.T) {
@@ -84,7 +266,7 @@ func TestLoadMultiConfig(t *testing.T) {
// Add a random config variable for testing.
// side = page in Norwegian.
configContentBase := `
DontChange = "same"
Paginate = 32
PaginatePath = "side"
`
configContentSub := `
@@ -96,11 +278,13 @@ func TestLoadMultiConfig(t *testing.T) {
writeToFs(t, mm, "override.toml", configContentSub)
cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "base.toml,override.toml"})
all, err := allconfig.LoadConfig(allconfig.ConfigSourceDescriptor{Fs: mm, Filename: "base.toml,override.toml"})
c.Assert(err, qt.IsNil)
cfg := all.Base
c.Assert(cfg.PaginatePath, qt.Equals, "top")
c.Assert(cfg.Paginate, qt.Equals, 32)
c.Assert(cfg.GetString("paginatePath"), qt.Equals, "top")
c.Assert(cfg.GetString("DontChange"), qt.Equals, "same")
}
func TestLoadConfigFromThemes(t *testing.T) {
@@ -229,12 +413,9 @@ name = "menu-theme"
c.Run("Merge default", func(c *qt.C) {
b := buildForStrategy(c, "")
got := b.Cfg.Get("").(maps.Params)
got := b.Configs.Base
// Issue #8866
b.Assert(b.Cfg.Get("disableKinds"), qt.IsNil)
b.Assert(got["params"], qt.DeepEquals, maps.Params{
b.Assert(got.Params, qt.DeepEquals, maps.Params{
"b": maps.Params{
"b1": "b1 main",
"c": maps.Params{
@@ -248,100 +429,16 @@ name = "menu-theme"
"p1": "p1 main",
})
b.Assert(got["mediatypes"], qt.DeepEquals, maps.Params{
"text/m2": maps.Params{
"suffixes": []any{
"m2theme",
},
},
"text/m1": maps.Params{
"suffixes": []any{
"m1main",
},
},
})
var eq = qt.CmpEquals(
cmp.Comparer(func(m1, m2 media.Type) bool {
if m1.SubType != m2.SubType {
return false
}
return m1.FirstSuffix == m2.FirstSuffix
}),
)
mediaTypes := b.H.Sites[0].mediaTypesConfig
m1, _ := mediaTypes.GetByType("text/m1")
m2, _ := mediaTypes.GetByType("text/m2")
b.Assert(got["outputformats"], eq, maps.Params{
"o1": maps.Params{
"mediatype": m1,
"basename": "o1main",
},
"o2": maps.Params{
"basename": "o2theme",
"mediatype": m2,
},
})
b.Assert(got["languages"], qt.DeepEquals, maps.Params{
"en": maps.Params{
"languagename": "English",
"params": maps.Params{
"pl2": "p2-en-theme",
"pl1": "p1-en-main",
},
"menus": maps.Params{
"main": []any{
map[string]any{
"name": "menu-lang-en-main",
},
},
"theme": []any{
map[string]any{
"name": "menu-lang-en-theme",
},
},
},
},
"nb": maps.Params{
"languagename": "Norsk",
"params": maps.Params{
"top": "top-nb-theme",
"pl1": "p1-nb-main",
"pl2": "p2-nb-theme",
},
"menus": maps.Params{
"main": []any{
map[string]any{
"name": "menu-lang-nb-main",
},
},
"theme": []any{
map[string]any{
"name": "menu-lang-nb-theme",
},
},
"top": []any{
map[string]any{
"name": "menu-lang-nb-top",
},
},
},
},
})
c.Assert(got["baseurl"], qt.Equals, "https://example.com/")
c.Assert(got.BaseURL, qt.Equals, "https://example.com/")
})
c.Run("Merge shallow", func(c *qt.C) {
b := buildForStrategy(c, fmt.Sprintf("_merge=%q", "shallow"))
got := b.Cfg.Get("").(maps.Params)
got := b.Configs.Base.Params
// Shallow merge, only add new keys to params.
b.Assert(got["params"], qt.DeepEquals, maps.Params{
b.Assert(got, qt.DeepEquals, maps.Params{
"p1": "p1 main",
"b": maps.Params{
"b1": "b1 main",
@@ -360,59 +457,13 @@ name = "menu-theme"
"[params]\np1 = \"p1 theme\"\n",
)
got := b.Cfg.Get("").(maps.Params)
got := b.Configs.Base.Params
b.Assert(got["params"], qt.DeepEquals, maps.Params{
b.Assert(got, qt.DeepEquals, maps.Params{
"p1": "p1 theme",
})
})
c.Run("Merge language no menus or params in project", func(c *qt.C) {
b := buildForConfig(
c,
`
theme = "test-theme"
baseURL = "https://example.com/"
[languages]
[languages.en]
languageName = "English"
`,
`
[languages]
[languages.en]
languageName = "EnglishTheme"
[languages.en.params]
p1="themep1"
[[languages.en.menus.main]]
name = "menu-theme"
`,
)
got := b.Cfg.Get("").(maps.Params)
b.Assert(got["languages"], qt.DeepEquals,
maps.Params{
"en": maps.Params{
"languagename": "English",
"menus": maps.Params{
"main": []any{
map[string]any{
"name": "menu-theme",
},
},
},
"params": maps.Params{
"p1": "themep1",
},
},
},
)
})
// Issue #8724
for _, mergeStrategy := range []string{"none", "shallow"} {
c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) {
@@ -428,22 +479,14 @@ name = "menu-theme"
"baseURL=\"http://example.com\"\n"+fmt.Sprintf(smapConfigTempl, "monthly"),
)
got := b.Cfg.Get("").(maps.Params)
got := b.Configs.Base
if mergeStrategy == "none" {
b.Assert(got["sitemap"], qt.DeepEquals, maps.Params{
"priority": int(-1),
"filename": "sitemap.xml",
})
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Priority: -1, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
} else {
b.Assert(got["sitemap"], qt.DeepEquals, maps.Params{
"priority": int(-1),
"filename": "sitemap.xml",
"changefreq": "monthly",
})
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Priority: -1, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
}
@@ -494,7 +537,7 @@ t3 = "tv3p"
b.Build(BuildCfg{})
got := b.Cfg.Get("params").(maps.Params)
got := b.Configs.Base.Params
b.Assert(got, qt.DeepEquals, maps.Params{
"t3": "tv3p",
@@ -523,7 +566,7 @@ privacyEnhanced = true
b.WithConfigFile("toml", tomlConfig)
b.Build(BuildCfg{SkipRender: true})
c.Assert(b.H.Sites[0].Info.Config().Privacy.YouTube.PrivacyEnhanced, qt.Equals, true)
c.Assert(b.H.Sites[0].Config().Privacy.YouTube.PrivacyEnhanced, qt.Equals, true)
}
func TestLoadConfigModules(t *testing.T) {
@@ -607,7 +650,7 @@ path="n4"
b.Build(BuildCfg{})
modulesClient := b.H.Paths.ModulesClient
modulesClient := b.H.Configs.ModulesClient
var graphb bytes.Buffer
modulesClient.Graph(&graphb)
@@ -621,142 +664,6 @@ project n4
c.Assert(graphb.String(), qt.Equals, expected)
}
func TestLoadConfigWithOsEnvOverrides(t *testing.T) {
c := qt.New(t)
baseConfig := `
theme = "mytheme"
environment = "production"
enableGitInfo = true
intSlice = [5,7,9]
floatSlice = [3.14, 5.19]
stringSlice = ["a", "b"]
[outputFormats]
[outputFormats.ofbase]
mediaType = "text/plain"
[params]
paramWithNoEnvOverride="nooverride"
[params.api_config]
api_key="default_key"
another_key="default another_key"
[imaging]
anchor = "smart"
quality = 75
`
newB := func(t testing.TB) *sitesBuilder {
b := newTestSitesBuilder(t).WithConfigFile("toml", baseConfig)
b.WithSourceFile("themes/mytheme/config.toml", `
[outputFormats]
[outputFormats.oftheme]
mediaType = "text/plain"
[outputFormats.ofbase]
mediaType = "application/xml"
[params]
[params.mytheme_section]
theme_param="themevalue"
theme_param_nooverride="nooverride"
[params.mytheme_section2]
theme_param="themevalue2"
`)
return b
}
c.Run("Variations", func(c *qt.C) {
b := newB(c)
b.WithEnviron(
"HUGO_ENVIRONMENT", "test",
"HUGO_NEW", "new", // key not in config.toml
"HUGO_ENABLEGITINFO", "false",
"HUGO_IMAGING_ANCHOR", "top",
"HUGO_IMAGING_RESAMPLEFILTER", "CatmullRom",
"HUGO_STRINGSLICE", `["c", "d"]`,
"HUGO_INTSLICE", `[5, 8, 9]`,
"HUGO_FLOATSLICE", `[5.32]`,
// Issue #7829
"HUGOxPARAMSxAPI_CONFIGxAPI_KEY", "new_key",
// Delimiters are case sensitive.
"HUGOxPARAMSxAPI_CONFIGXANOTHER_KEY", "another_key",
// Issue #8346
"HUGOxPARAMSxMYTHEME_SECTIONxTHEME_PARAM", "themevalue_changed",
"HUGOxPARAMSxMYTHEME_SECTION2xTHEME_PARAM", "themevalue2_changed",
"HUGO_PARAMS_EMPTY", ``,
"HUGO_PARAMS_HTML", `<a target="_blank" />`,
// Issue #8618
"HUGO_SERVICES_GOOGLEANALYTICS_ID", `gaid`,
"HUGO_PARAMS_A_B_C", "abc",
)
b.Build(BuildCfg{})
cfg := b.H.Cfg
s := b.H.Sites[0]
scfg := s.siteConfigConfig.Services
c.Assert(cfg.Get("environment"), qt.Equals, "test")
c.Assert(cfg.GetBool("enablegitinfo"), qt.Equals, false)
c.Assert(cfg.Get("new"), qt.Equals, "new")
c.Assert(cfg.Get("imaging.anchor"), qt.Equals, "top")
c.Assert(cfg.Get("imaging.quality"), qt.Equals, int64(75))
c.Assert(cfg.Get("imaging.resamplefilter"), qt.Equals, "CatmullRom")
c.Assert(cfg.Get("stringSlice"), qt.DeepEquals, []any{"c", "d"})
c.Assert(cfg.Get("floatSlice"), qt.DeepEquals, []any{5.32})
c.Assert(cfg.Get("intSlice"), qt.DeepEquals, []any{5, 8, 9})
c.Assert(cfg.Get("params.api_config.api_key"), qt.Equals, "new_key")
c.Assert(cfg.Get("params.api_config.another_key"), qt.Equals, "default another_key")
c.Assert(cfg.Get("params.mytheme_section.theme_param"), qt.Equals, "themevalue_changed")
c.Assert(cfg.Get("params.mytheme_section.theme_param_nooverride"), qt.Equals, "nooverride")
c.Assert(cfg.Get("params.mytheme_section2.theme_param"), qt.Equals, "themevalue2_changed")
c.Assert(cfg.Get("params.empty"), qt.Equals, ``)
c.Assert(cfg.Get("params.html"), qt.Equals, `<a target="_blank" />`)
params := cfg.Get("params").(maps.Params)
c.Assert(params["paramwithnoenvoverride"], qt.Equals, "nooverride")
c.Assert(cfg.Get("params.paramwithnoenvoverride"), qt.Equals, "nooverride")
c.Assert(scfg.GoogleAnalytics.ID, qt.Equals, "gaid")
c.Assert(cfg.Get("params.a.b"), qt.DeepEquals, maps.Params{
"c": "abc",
})
ofBase, _ := s.outputFormatsConfig.GetByName("ofbase")
ofTheme, _ := s.outputFormatsConfig.GetByName("oftheme")
c.Assert(ofBase.MediaType, qt.Equals, media.TextType)
c.Assert(ofTheme.MediaType, qt.Equals, media.TextType)
})
// Issue #8709
c.Run("Set in string", func(c *qt.C) {
b := newB(c)
b.WithEnviron(
"HUGO_ENABLEGITINFO", "false",
// imaging.anchor is a string, and it's not possible
// to set a child attribute.
"HUGO_IMAGING_ANCHOR_FOO", "top",
)
b.Build(BuildCfg{})
cfg := b.H.Cfg
c.Assert(cfg.Get("imaging.anchor"), qt.Equals, "smart")
})
}
func TestInvalidDefaultMarkdownHandler(t *testing.T) {
t.Parallel()