hugo/hugolib/config_test.go

525 lines
10 KiB
Go
Raw Normal View History

// 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 (
"bytes"
"fmt"
"path/filepath"
"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"))
}
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",
"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",
Add /config dir support 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
2018-11-15 03:28:02 -05:00
"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",
},
},
"nb": map[string]interface {}{
"languagename": "Norsk",
Add /config dir support 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
2018-11-15 03:28:02 -05:00
"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",
},
},
}
`, got["languages"])
b.AssertObject(`
map[string]interface {}{
Add /config dir support 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
2018-11-15 03:28:02 -05:00
"main": []map[string]interface {}{
map[string]interface {}{
"name": "menu-main-main",
},
},
Add /config dir support 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
2018-11-15 03:28:02 -05:00
"thememenu": []map[string]interface {}{
map[string]interface {}{
"name": "menu-theme",
},
},
Add /config dir support 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
2018-11-15 03:28:02 -05:00
"top": []map[string]interface {}{
map[string]interface {}{
"name": "menu-top-main",
},
},
}
Add /config dir support 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
2018-11-15 03:28:02 -05:00
`, 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)
}
func TestLoadConfigModules(t *testing.T) {
t.Parallel()
assert := require.New(t)
// https://github.com/gohugoio/hugoThemes#themetoml
const (
// Before Hugo 0.56 each theme/component could have its own theme.toml
// with some settings, mostly used on the Hugo themes site.
// To preserve combability we read these files into the new "modules"
// section in config.toml.
o1t = `
name = "Component o1"
license = "MIT"
min_version = 0.38
`
// This is the component's config.toml, using the old theme syntax.
o1c = `
theme = ["n2"]
`
n1 = `
title = "Component n1"
[module]
description = "Component n1 description"
[module.hugoVersion]
min = "0.40.0"
max = "0.50.0"
extended = true
[[module.imports]]
path="o1"
[[module.imports]]
path="n3"
`
n2 = `
title = "Component n2"
`
n3 = `
title = "Component n3"
`
n4 = `
title = "Component n4"
`
)
b := newTestSitesBuilder(t)
writeThemeFiles := func(name, configTOML, themeTOML string) {
b.WithSourceFile(filepath.Join("themes", name, "data", "module.toml"), fmt.Sprintf("name=%q", name))
if configTOML != "" {
b.WithSourceFile(filepath.Join("themes", name, "config.toml"), configTOML)
}
if themeTOML != "" {
b.WithSourceFile(filepath.Join("themes", name, "theme.toml"), themeTOML)
}
}
writeThemeFiles("n1", n1, "")
writeThemeFiles("n2", n2, "")
writeThemeFiles("n3", n3, "")
writeThemeFiles("n4", n4, "")
writeThemeFiles("o1", o1c, o1t)
b.WithConfigFile("toml", `
[module]
[[module.imports]]
path="n1"
[[module.imports]]
path="n4"
`)
b.Build(BuildCfg{})
modulesClient := b.H.Paths.ModulesClient
var graphb bytes.Buffer
modulesClient.Graph(&graphb)
assert.Equal(`project n1
n1 o1
o1 n2
n1 n3
project n4
`, graphb.String())
}
func TestLoadConfigWithOsEnvOverrides(t *testing.T) {
assert := require.New(t)
baseConfig := `
environment = "production"
enableGitInfo = true
intSlice = [5,7,9]
floatSlice = [3.14, 5.19]
stringSlice = ["a", "b"]
[imaging]
anchor = "smart"
quality = 75
resamplefilter = "CatmullRom"
`
b := newTestSitesBuilder(t).WithConfigFile("toml", baseConfig)
b.WithEnviron(
"HUGO_ENVIRONMENT", "test",
"HUGO_NEW", "new", // key not in config.toml
"HUGO_ENABLEGITINFO", "false",
"HUGO_IMAGING_ANCHOR", "top",
"HUGO_STRINGSLICE", `["c", "d"]`,
"HUGO_INTSLICE", `[5, 8, 9]`,
"HUGO_FLOATSLICE", `[5.32]`,
)
b.Build(BuildCfg{})
cfg := b.H.Cfg
assert.Equal("test", cfg.Get("environment"))
assert.Equal(false, cfg.GetBool("enablegitinfo"))
assert.Equal("new", cfg.Get("new"))
assert.Equal("top", cfg.Get("imaging.anchor"))
assert.Equal(int64(75), cfg.Get("imaging.quality"))
assert.Equal([]interface{}{"c", "d"}, cfg.Get("stringSlice"))
assert.Equal([]interface{}{5.32}, cfg.Get("floatSlice"))
assert.Equal([]interface{}{5, 8, 9}, cfg.Get("intSlice"))
}