mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-29 13:52:03 -05:00
7829474088
This commit adds support for a configuration directory (default `config`). The different pieces in this puzzle are: * A new `--environment` (or `-e`) flag. This can also be set with the `HUGO_ENVIRONMENT` OS environment variable. The value for `environment` defaults to `production` when running `hugo` and `development` when running `hugo server`. You can set it to any value you want (e.g. `hugo server -e "Sensible Environment"`), but as it is used to load configuration from the file system, the letter case may be important. You can get this value in your templates with `{{ hugo.Environment }}`. * A new `--configDir` flag (defaults to `config` below your project). This can also be set with `HUGO_CONFIGDIR` OS environment variable. If the `configDir` exists, the configuration files will be read and merged on top of each other from left to right; the right-most value will win on duplicates. Given the example tree below: If `environment` is `production`, the left-most `config.toml` would be the one directly below the project (this can now be omitted if you want), and then `_default/config.toml` and finally `production/config.toml`. And since these will be merged, you can just provide the environment specific configuration setting in you production config, e.g. `enableGitInfo = true`. The order within the directories will be lexical (`config.toml` and then `params.toml`). ```bash config ├── _default │ ├── config.toml │ ├── languages.toml │ ├── menus │ │ ├── menus.en.toml │ │ └── menus.zh.toml │ └── params.toml ├── development │ └── params.toml └── production ├── config.toml └── params.toml ``` Some configuration maps support the language code in the filename (e.g. `menus.en.toml`): `menus` (`menu` also works) and `params`. Also note that the only folders with "a meaning" in the above listing is the top level directories below `config`. The `menus` sub folder is just added for better organization. We use `TOML` in the example above, but Hugo also supports `JSON` and `YAML` as configuration formats. These can be mixed. Fixes #5422
399 lines
8.1 KiB
Go
399 lines
8.1 KiB
Go
// Copyright 2016-present 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 hugolib
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/spf13/afero"
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestLoadConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert := require.New(t)
|
|
|
|
// Add a random config variable for testing.
|
|
// side = page in Norwegian.
|
|
configContent := `
|
|
PaginatePath = "side"
|
|
`
|
|
|
|
mm := afero.NewMemMapFs()
|
|
|
|
writeToFs(t, mm, "hugo.toml", configContent)
|
|
|
|
cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "hugo.toml"})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal("side", cfg.GetString("paginatePath"))
|
|
// default
|
|
assert.Equal("layouts", cfg.GetString("layoutDir"))
|
|
// no themes
|
|
assert.False(cfg.IsSet("allThemes"))
|
|
}
|
|
|
|
func TestLoadMultiConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert := require.New(t)
|
|
|
|
// Add a random config variable for testing.
|
|
// side = page in Norwegian.
|
|
configContentBase := `
|
|
DontChange = "same"
|
|
PaginatePath = "side"
|
|
`
|
|
configContentSub := `
|
|
PaginatePath = "top"
|
|
`
|
|
mm := afero.NewMemMapFs()
|
|
|
|
writeToFs(t, mm, "base.toml", configContentBase)
|
|
|
|
writeToFs(t, mm, "override.toml", configContentSub)
|
|
|
|
cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "base.toml,override.toml"})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal("top", cfg.GetString("paginatePath"))
|
|
assert.Equal("same", cfg.GetString("DontChange"))
|
|
}
|
|
|
|
func TestLoadConfigFromTheme(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert := require.New(t)
|
|
|
|
mainConfigBasic := `
|
|
theme = "test-theme"
|
|
baseURL = "https://example.com/"
|
|
|
|
`
|
|
mainConfig := `
|
|
theme = "test-theme"
|
|
baseURL = "https://example.com/"
|
|
|
|
[frontmatter]
|
|
date = ["date","publishDate"]
|
|
|
|
[params]
|
|
p1 = "p1 main"
|
|
p2 = "p2 main"
|
|
top = "top"
|
|
|
|
[mediaTypes]
|
|
[mediaTypes."text/m1"]
|
|
suffixes = ["m1main"]
|
|
|
|
[outputFormats.o1]
|
|
mediaType = "text/m1"
|
|
baseName = "o1main"
|
|
|
|
[languages]
|
|
[languages.en]
|
|
languageName = "English"
|
|
[languages.en.params]
|
|
pl1 = "p1-en-main"
|
|
[languages.nb]
|
|
languageName = "Norsk"
|
|
[languages.nb.params]
|
|
pl1 = "p1-nb-main"
|
|
|
|
[[menu.main]]
|
|
name = "menu-main-main"
|
|
|
|
[[menu.top]]
|
|
name = "menu-top-main"
|
|
|
|
`
|
|
|
|
themeConfig := `
|
|
baseURL = "http://bep.is/"
|
|
|
|
# Can not be set in theme.
|
|
[frontmatter]
|
|
expiryDate = ["date"]
|
|
|
|
[params]
|
|
p1 = "p1 theme"
|
|
p2 = "p2 theme"
|
|
p3 = "p3 theme"
|
|
|
|
[mediaTypes]
|
|
[mediaTypes."text/m1"]
|
|
suffixes = ["m1theme"]
|
|
[mediaTypes."text/m2"]
|
|
suffixes = ["m2theme"]
|
|
|
|
[outputFormats.o1]
|
|
mediaType = "text/m1"
|
|
baseName = "o1theme"
|
|
[outputFormats.o2]
|
|
mediaType = "text/m2"
|
|
baseName = "o2theme"
|
|
|
|
[languages]
|
|
[languages.en]
|
|
languageName = "English2"
|
|
[languages.en.params]
|
|
pl1 = "p1-en-theme"
|
|
pl2 = "p2-en-theme"
|
|
[[languages.en.menu.main]]
|
|
name = "menu-lang-en-main"
|
|
[[languages.en.menu.theme]]
|
|
name = "menu-lang-en-theme"
|
|
[languages.nb]
|
|
languageName = "Norsk2"
|
|
[languages.nb.params]
|
|
pl1 = "p1-nb-theme"
|
|
pl2 = "p2-nb-theme"
|
|
top = "top-nb-theme"
|
|
[[languages.nb.menu.main]]
|
|
name = "menu-lang-nb-main"
|
|
[[languages.nb.menu.theme]]
|
|
name = "menu-lang-nb-theme"
|
|
[[languages.nb.menu.top]]
|
|
name = "menu-lang-nb-top"
|
|
|
|
[[menu.main]]
|
|
name = "menu-main-theme"
|
|
|
|
[[menu.thememenu]]
|
|
name = "menu-theme"
|
|
|
|
`
|
|
|
|
b := newTestSitesBuilder(t)
|
|
b.WithConfigFile("toml", mainConfig).WithThemeConfigFile("toml", themeConfig)
|
|
b.CreateSites().Build(BuildCfg{})
|
|
|
|
got := b.Cfg.(*viper.Viper).AllSettings()
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"p1": "p1 main",
|
|
"p2": "p2 main",
|
|
"p3": "p3 theme",
|
|
"test-theme": map[string]interface {}{
|
|
"p1": "p1 theme",
|
|
"p2": "p2 theme",
|
|
"p3": "p3 theme",
|
|
},
|
|
"top": "top",
|
|
}`, got["params"])
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"date": []interface {}{
|
|
"date",
|
|
"publishDate",
|
|
},
|
|
}`, got["frontmatter"])
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"text/m1": map[string]interface {}{
|
|
"suffixes": []interface {}{
|
|
"m1main",
|
|
},
|
|
},
|
|
"text/m2": map[string]interface {}{
|
|
"suffixes": []interface {}{
|
|
"m2theme",
|
|
},
|
|
},
|
|
}`, got["mediatypes"])
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"o1": map[string]interface {}{
|
|
"basename": "o1main",
|
|
"mediatype": Type{
|
|
MainType: "text",
|
|
SubType: "m1",
|
|
Delimiter: ".",
|
|
Suffixes: []string{
|
|
"m1main",
|
|
},
|
|
},
|
|
},
|
|
"o2": map[string]interface {}{
|
|
"basename": "o2theme",
|
|
"mediatype": Type{
|
|
MainType: "text",
|
|
SubType: "m2",
|
|
Delimiter: ".",
|
|
Suffixes: []string{
|
|
"m2theme",
|
|
},
|
|
},
|
|
},
|
|
}`, got["outputformats"])
|
|
|
|
b.AssertObject(`map[string]interface {}{
|
|
"en": map[string]interface {}{
|
|
"languagename": "English",
|
|
"menus": map[string]interface {}{
|
|
"theme": []map[string]interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-lang-en-theme",
|
|
},
|
|
},
|
|
},
|
|
"params": map[string]interface {}{
|
|
"pl1": "p1-en-main",
|
|
"pl2": "p2-en-theme",
|
|
"test-theme": map[string]interface {}{
|
|
"pl1": "p1-en-theme",
|
|
"pl2": "p2-en-theme",
|
|
},
|
|
},
|
|
},
|
|
"nb": map[string]interface {}{
|
|
"languagename": "Norsk",
|
|
"menus": map[string]interface {}{
|
|
"theme": []map[string]interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-lang-nb-theme",
|
|
},
|
|
},
|
|
},
|
|
"params": map[string]interface {}{
|
|
"pl1": "p1-nb-main",
|
|
"pl2": "p2-nb-theme",
|
|
"test-theme": map[string]interface {}{
|
|
"pl1": "p1-nb-theme",
|
|
"pl2": "p2-nb-theme",
|
|
"top": "top-nb-theme",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
`, got["languages"])
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"main": []map[string]interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-main-main",
|
|
},
|
|
},
|
|
"thememenu": []map[string]interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-theme",
|
|
},
|
|
},
|
|
"top": []map[string]interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-top-main",
|
|
},
|
|
},
|
|
}
|
|
`, got["menus"])
|
|
|
|
assert.Equal("https://example.com/", got["baseurl"])
|
|
|
|
if true {
|
|
return
|
|
}
|
|
// Test variants with only values from theme
|
|
b = newTestSitesBuilder(t)
|
|
b.WithConfigFile("toml", mainConfigBasic).WithThemeConfigFile("toml", themeConfig)
|
|
b.CreateSites().Build(BuildCfg{})
|
|
|
|
got = b.Cfg.(*viper.Viper).AllSettings()
|
|
|
|
b.AssertObject(`map[string]interface {}{
|
|
"p1": "p1 theme",
|
|
"p2": "p2 theme",
|
|
"p3": "p3 theme",
|
|
"test-theme": map[string]interface {}{
|
|
"p1": "p1 theme",
|
|
"p2": "p2 theme",
|
|
"p3": "p3 theme",
|
|
},
|
|
}`, got["params"])
|
|
|
|
assert.Nil(got["languages"])
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"text/m1": map[string]interface {}{
|
|
"suffix": "m1theme",
|
|
},
|
|
"text/m2": map[string]interface {}{
|
|
"suffix": "m2theme",
|
|
},
|
|
}`, got["mediatypes"])
|
|
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"o1": map[string]interface {}{
|
|
"basename": "o1theme",
|
|
"mediatype": Type{
|
|
MainType: "text",
|
|
SubType: "m1",
|
|
Suffix: "m1theme",
|
|
Delimiter: ".",
|
|
},
|
|
},
|
|
"o2": map[string]interface {}{
|
|
"basename": "o2theme",
|
|
"mediatype": Type{
|
|
MainType: "text",
|
|
SubType: "m2",
|
|
Suffix: "m2theme",
|
|
Delimiter: ".",
|
|
},
|
|
},
|
|
}`, got["outputformats"])
|
|
b.AssertObject(`
|
|
map[string]interface {}{
|
|
"main": []interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-main-theme",
|
|
},
|
|
},
|
|
"thememenu": []interface {}{
|
|
map[string]interface {}{
|
|
"name": "menu-theme",
|
|
},
|
|
},
|
|
}`, got["menu"])
|
|
|
|
}
|
|
|
|
func TestPrivacyConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert := require.New(t)
|
|
|
|
tomlConfig := `
|
|
|
|
someOtherValue = "foo"
|
|
|
|
[privacy]
|
|
[privacy.youtube]
|
|
privacyEnhanced = true
|
|
`
|
|
|
|
b := newTestSitesBuilder(t)
|
|
b.WithConfigFile("toml", tomlConfig)
|
|
b.Build(BuildCfg{SkipRender: true})
|
|
|
|
assert.True(b.H.Sites[0].Info.Config().Privacy.YouTube.PrivacyEnhanced)
|
|
|
|
}
|