summaryrefslogtreecommitdiffstats
path: root/hugolib
diff options
context:
space:
mode:
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/config.go292
-rw-r--r--hugolib/config_test.go16
-rw-r--r--hugolib/configdir_test.go152
-rw-r--r--hugolib/hugo_sites.go10
-rw-r--r--hugolib/hugo_sites_build.go8
-rw-r--r--hugolib/hugo_sites_build_test.go15
-rw-r--r--hugolib/page.go7
-rw-r--r--hugolib/page_test.go2
-rw-r--r--hugolib/paths/themes.go14
-rw-r--r--hugolib/site.go4
-rw-r--r--hugolib/testhelpers_test.go20
11 files changed, 425 insertions, 115 deletions
diff --git a/hugolib/config.go b/hugolib/config.go
index 77ebb42ae..3a452d5fd 100644
--- a/hugolib/config.go
+++ b/hugolib/config.go
@@ -14,14 +14,19 @@
package hugolib
import (
- "errors"
"fmt"
- "io"
+
+ "os"
+ "path/filepath"
"strings"
- "github.com/gohugoio/hugo/common/herrors"
+ "github.com/gohugoio/hugo/parser/metadecoders"
+ "github.com/gohugoio/hugo/common/herrors"
+ "github.com/gohugoio/hugo/common/hugo"
+ "github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib/paths"
+ "github.com/pkg/errors"
_errors "github.com/pkg/errors"
"github.com/gohugoio/hugo/langs"
@@ -65,96 +70,84 @@ func loadSiteConfig(cfg config.Provider) (scfg SiteConfig, err error) {
type ConfigSourceDescriptor struct {
Fs afero.Fs
- // Full path to the config file to use, i.e. /my/project/config.toml
+ // Path to the config file to use, e.g. /my/project/config.toml
Filename string
// The path to the directory to look for configuration. Is used if Filename is not
- // set.
+ // set or if it is set to a relative filename.
Path string
// The project's working dir. Is used to look for additional theme config.
WorkingDir string
+
+ // The (optional) directory for additional configuration files.
+ AbsConfigDir string
+
+ // production, development
+ Environment string
}
func (d ConfigSourceDescriptor) configFilenames() []string {
+ if d.Filename == "" {
+ return []string{"config"}
+ }
return strings.Split(d.Filename, ",")
}
+func (d ConfigSourceDescriptor) configFileDir() string {
+ if d.Path != "" {
+ return d.Path
+ }
+ return d.WorkingDir
+}
+
// LoadConfigDefault is a convenience method to load the default "config.toml" config.
func LoadConfigDefault(fs afero.Fs) (*viper.Viper, error) {
v, _, err := LoadConfig(ConfigSourceDescriptor{Fs: fs, Filename: "config.toml"})
return v, err
}
-var ErrNoConfigFile = errors.New("Unable to locate Config file. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
+var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
// LoadConfig loads Hugo configuration into a new Viper and then adds
// a set of defaults.
func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provider) error) (*viper.Viper, []string, error) {
+ if d.Environment == "" {
+ d.Environment = hugo.EnvironmentProduction
+ }
+
var configFiles []string
- fs := d.Fs
v := viper.New()
- v.SetFs(fs)
-
- if d.Path == "" {
- d.Path = "."
- }
+ l := configLoader{ConfigSourceDescriptor: d}
- configFilenames := d.configFilenames()
v.AutomaticEnv()
v.SetEnvPrefix("hugo")
- v.SetConfigFile(configFilenames[0])
- v.AddConfigPath(d.Path)
- applyFileContext := func(filename string, err error) error {
- err, _ = herrors.WithFileContextForFile(
- err,
- filename,
- filename,
- fs,
- herrors.SimpleLineMatcher)
+ var cerr error
- return err
- }
-
- var configFileErr error
-
- err := v.ReadInConfig()
- if err != nil {
- if _, ok := err.(viper.ConfigParseError); ok {
- return nil, configFiles, applyFileContext(v.ConfigFileUsed(), err)
+ for _, name := range d.configFilenames() {
+ var filename string
+ if filename, cerr = l.loadConfig(name, v); cerr != nil && cerr != ErrNoConfigFile {
+ return nil, nil, cerr
}
- configFileErr = ErrNoConfigFile
+ configFiles = append(configFiles, filename)
}
- if configFileErr == nil {
-
- if cf := v.ConfigFileUsed(); cf != "" {
- configFiles = append(configFiles, cf)
+ if d.AbsConfigDir != "" {
+ dirnames, err := l.loadConfigFromConfigDir(v)
+ if err == nil {
+ configFiles = append(configFiles, dirnames...)
}
-
- for _, configFile := range configFilenames[1:] {
- var r io.Reader
- var err error
- if r, err = fs.Open(configFile); err != nil {
- return nil, configFiles, fmt.Errorf("Unable to open Config file.\n (%s)\n", err)
- }
- if err = v.MergeConfig(r); err != nil {
- return nil, configFiles, applyFileContext(configFile, err)
- }
- configFiles = append(configFiles, configFile)
- }
-
+ cerr = err
}
if err := loadDefaultSettingsFor(v); err != nil {
return v, configFiles, err
}
- if configFileErr == nil {
-
- themeConfigFiles, err := loadThemeConfig(d, v)
+ if cerr == nil {
+ themeConfigFiles, err := l.loadThemeConfig(v)
if err != nil {
return v, configFiles, err
}
@@ -176,8 +169,179 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
return v, configFiles, err
}
- return v, configFiles, configFileErr
+ return v, configFiles, cerr
+
+}
+
+type configLoader struct {
+ ConfigSourceDescriptor
+}
+
+func (l configLoader) wrapFileInfoError(err error, fi os.FileInfo) error {
+ rfi, ok := fi.(hugofs.RealFilenameInfo)
+ if !ok {
+ return err
+ }
+ return l.wrapFileError(err, rfi.RealFilename())
+}
+
+func (l configLoader) loadConfig(configName string, v *viper.Viper) (string, error) {
+ baseDir := l.configFileDir()
+ var baseFilename string
+ if filepath.IsAbs(configName) {
+ baseFilename = configName
+ } else {
+ baseFilename = filepath.Join(baseDir, configName)
+ }
+
+ var filename string
+ fileExt := helpers.ExtNoDelimiter(configName)
+ if fileExt != "" {
+ exists, _ := helpers.Exists(baseFilename, l.Fs)
+ if exists {
+ filename = baseFilename
+ }
+ } else {
+ for _, ext := range []string{"toml", "yaml", "yml", "json"} {
+ filenameToCheck := baseFilename + "." + ext
+ exists, _ := helpers.Exists(filenameToCheck, l.Fs)
+ if exists {
+ filename = filenameToCheck
+ fileExt = ext
+ break
+ }
+ }
+ }
+
+ if filename == "" {
+ return "", ErrNoConfigFile
+ }
+
+ m, err := config.FromFileToMap(l.Fs, filename)
+ if err != nil {
+ return "", l.wrapFileError(err, filename)
+ }
+
+ if err = v.MergeConfigMap(m); err != nil {
+ return "", l.wrapFileError(err, filename)
+ }
+
+ return filename, nil
+
+}
+
+func (l configLoader) wrapFileError(err error, filename string) error {
+ err, _ = herrors.WithFileContextForFile(
+ err,
+ filename,
+ filename,
+ l.Fs,
+ herrors.SimpleLineMatcher)
+ return err
+}
+
+func (l configLoader) newRealBaseFs(path string) afero.Fs {
+ return hugofs.NewBasePathRealFilenameFs(afero.NewBasePathFs(l.Fs, path).(*afero.BasePathFs))
+
+}
+
+func (l configLoader) loadConfigFromConfigDir(v *viper.Viper) ([]string, error) {
+ sourceFs := l.Fs
+ configDir := l.AbsConfigDir
+
+ if _, err := sourceFs.Stat(configDir); err != nil {
+ // Config dir does not exist.
+ return nil, nil
+ }
+
+ defaultConfigDir := filepath.Join(configDir, "_default")
+ environmentConfigDir := filepath.Join(configDir, l.Environment)
+
+ var configDirs []string
+ // Merge from least to most specific.
+ for _, dir := range []string{defaultConfigDir, environmentConfigDir} {
+ if _, err := sourceFs.Stat(dir); err == nil {
+ configDirs = append(configDirs, dir)
+ }
+ }
+
+ if len(configDirs) == 0 {
+ return nil, nil
+ }
+
+ // Keep track of these so we can watch them for changes.
+ var dirnames []string
+
+ for _, configDir := range configDirs {
+ err := afero.Walk(sourceFs, configDir, func(path string, fi os.FileInfo, err error) error {
+ if fi == nil {
+ return nil
+ }
+
+ if fi.IsDir() {
+ dirnames = append(dirnames, path)
+ return nil
+ }
+
+ name := helpers.Filename(filepath.Base(path))
+
+ item, err := metadecoders.UnmarshalFileToMap(sourceFs, path)
+ if err != nil {
+ return l.wrapFileError(err, path)
+ }
+
+ var keyPath []string
+
+ if name != "config" {
+ // Can be params.jp, menus.en etc.
+ name, lang := helpers.FileAndExtNoDelimiter(name)
+
+ keyPath = []string{name}
+
+ if lang != "" {
+ keyPath = []string{"languages", lang}
+ switch name {
+ case "menu", "menus":
+ keyPath = append(keyPath, "menus")
+ case "params":
+ keyPath = append(keyPath, "params")
+ }
+ }
+ }
+
+ root := item
+ if len(keyPath) > 0 {
+ root = make(map[string]interface{})
+ m := root
+ for i, key := range keyPath {
+ if i >= len(keyPath)-1 {
+ m[key] = item
+ } else {
+ nm := make(map[string]interface{})
+ m[key] = nm
+ m = nm
+ }
+ }
+ }
+
+ // Migrate menu => menus etc.
+ config.RenameKeys(root)
+
+ if err := v.MergeConfigMap(root); err != nil {
+ return l.wrapFileError(err, path)
+ }
+
+ return nil
+
+ })
+
+ if err != nil {
+ return nil, err
+ }
+
+ }
+ return dirnames, nil
}
func loadLanguageSettings(cfg config.Provider, oldLangs langs.Languages) error {
@@ -289,12 +453,11 @@ func loadLanguageSettings(cfg config.Provider, oldLangs langs.Languages) error {
return nil
}
-func loadThemeConfig(d ConfigSourceDescriptor, v1 *viper.Viper) ([]string, error) {
- themesDir := paths.AbsPathify(d.WorkingDir, v1.GetString("themesDir"))
+func (l configLoader) loadThemeConfig(v1 *viper.Viper) ([]string, error) {
+ themesDir := paths.AbsPathify(l.WorkingDir, v1.GetString("themesDir"))
themes := config.GetStringSlicePreserveString(v1, "theme")
- // CollectThemes(fs afero.Fs, themesDir string, themes []strin
- themeConfigs, err := paths.CollectThemes(d.Fs, themesDir, themes)
+ themeConfigs, err := paths.CollectThemes(l.Fs, themesDir, themes)
if err != nil {
return nil, err
}
@@ -309,7 +472,7 @@ func loadThemeConfig(d ConfigSourceDescriptor, v1 *viper.Viper) ([]string, error
for _, tc := range themeConfigs {
if tc.ConfigFilename != "" {
configFilenames = append(configFilenames, tc.ConfigFilename)
- if err := applyThemeConfig(v1, tc); err != nil {
+ if err := l.applyThemeConfig(v1, tc); err != nil {
return nil, err
}
}
@@ -319,18 +482,18 @@ func loadThemeConfig(d ConfigSourceDescriptor, v1 *viper.Viper) ([]string, error
}
-func applyThemeConfig(v1 *viper.Viper, theme paths.ThemeConfig) error {
+func (l configLoader) applyThemeConfig(v1 *viper.Viper, theme paths.ThemeConfig) error {
const (
paramsKey = "params"
languagesKey = "languages"
- menuKey = "menu"
+ menuKey = "menus"
)
v2 := theme.Cfg
for _, key := range []string{paramsKey, "outputformats", "mediatypes"} {
- mergeStringMapKeepLeft("", key, v1, v2)
+ l.mergeStringMapKeepLeft("", key, v1, v2)
}
themeLower := strings.ToLower(theme.Name)
@@ -348,7 +511,7 @@ func applyThemeConfig(v1 *viper.Viper, theme paths.ThemeConfig) error {
v1Langs := v1.GetStringMap(languagesKey)
for k := range v1Langs {
langParamsKey := languagesKey + "." + k + "." + paramsKey
- mergeStringMapKeepLeft(paramsKey, langParamsKey, v1, v2)
+ l.mergeStringMapKeepLeft(paramsKey, langParamsKey, v1, v2)
}
v2Langs := v2.GetStringMap(languagesKey)
for k := range v2Langs {
@@ -378,7 +541,7 @@ func applyThemeConfig(v1 *viper.Viper, theme paths.ThemeConfig) error {
}
// Add menu definitions from theme not found in project
- if v2.IsSet("menu") {
+ if v2.IsSet(menuKey) {
v2menus := v2.GetStringMap(menuKey)
for k, v := range v2menus {
menuEntry := menuKey + "." + k
@@ -392,7 +555,7 @@ func applyThemeConfig(v1 *viper.Viper, theme paths.ThemeConfig) error {
}
-func mergeStringMapKeepLeft(rootKey, key string, v1, v2 config.Provider) {
+func (configLoader) mergeStringMapKeepLeft(rootKey, key string, v1, v2 config.Provider) {
if !v2.IsSet(key) {
return
}
@@ -440,6 +603,7 @@ func loadDefaultSettingsFor(v *viper.Viper) error {
v.SetDefault("buildDrafts", false)
v.SetDefault("buildFuture", false)
v.SetDefault("buildExpired", false)
+ v.SetDefault("environment", hugo.EnvironmentProduction)
v.SetDefault("uglyURLs", false)
v.SetDefault("verbose", false)
v.SetDefault("ignoreCache", false)
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index 1f9e7377c..885a07ee9 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -247,8 +247,8 @@ map[string]interface {}{
b.AssertObject(`map[string]interface {}{
"en": map[string]interface {}{
"languagename": "English",
- "menu": map[string]interface {}{
- "theme": []interface {}{
+ "menus": map[string]interface {}{
+ "theme": []map[string]interface {}{
map[string]interface {}{
"name": "menu-lang-en-theme",
},
@@ -265,8 +265,8 @@ map[string]interface {}{
},
"nb": map[string]interface {}{
"languagename": "Norsk",
- "menu": map[string]interface {}{
- "theme": []interface {}{
+ "menus": map[string]interface {}{
+ "theme": []map[string]interface {}{
map[string]interface {}{
"name": "menu-lang-nb-theme",
},
@@ -287,23 +287,23 @@ map[string]interface {}{
b.AssertObject(`
map[string]interface {}{
- "main": []interface {}{
+ "main": []map[string]interface {}{
map[string]interface {}{
"name": "menu-main-main",
},
},
- "thememenu": []interface {}{
+ "thememenu": []map[string]interface {}{
map[string]interface {}{
"name": "menu-theme",
},
},
- "top": []interface {}{
+ "top": []map[string]interface {}{
map[string]interface {}{
"name": "menu-top-main",
},
},
}
-`, got["menu"])
+`, got["menus"])
assert.Equal("https://example.com/", got["baseurl"])
diff --git a/hugolib/configdir_test.go b/hugolib/configdir_test.go
new file mode 100644
index 000000000..80fcda61f
--- /dev/null
+++ b/hugolib/configdir_test.go
@@ -0,0 +1,152 @@
+// Copyright 2018 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 (
+ "path/filepath"
+ "testing"
+
+ "github.com/gohugoio/hugo/common/herrors"
+
+ "github.com/gohugoio/hugo/htesting"
+ "github.com/spf13/afero"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoadConfigDir(t *testing.T) {
+ t.Parallel()
+
+ assert := require.New(t)
+
+ configContent := `
+baseURL = "https://example.org"
+paginagePath = "pag_root"
+
+[languages.en]
+weight = 0
+languageName = "English"
+
+[languages.no]
+weight = 10
+languageName = "FOO"
+
+[params]
+p1 = "p1_base"
+
+`
+
+ mm := afero.NewMemMapFs()
+
+ writeToFs(t, mm, "hugo.toml", configContent)
+
+ fb := htesting.NewTestdataBuilder(mm, "config/_default", t)
+
+ fb.Add("config.toml", `paginatePath = "pag_default"`)
+
+ fb.Add("params.yaml", `
+p2: "p2params_default"
+p3: "p3params_default"
+p4: "p4params_default"
+`)
+ fb.Add("menus.toml", `
+[[docs]]
+name = "About Hugo"
+weight = 1
+[[docs]]
+name = "Home"
+weight = 2
+ `)
+
+ fb.Add("menus.no.toml", `
+ [[docs]]
+ name = "Om Hugo"
+ weight = 1
+ `)
+
+ fb.Add("params.no.toml",
+ `
+p3 = "p3params_no_default"
+p4 = "p4params_no_default"`,
+ )
+ fb.Add("languages.no.toml", `languageName = "Norsk_no_default"`)
+
+ fb.Build()
+
+ fb = fb.WithWorkingDir("config/production")
+
+ fb.Add("config.toml", `paginatePath = "pag_production"`)
+
+ fb.Add("params.no.toml", `
+p2 = "p2params_no_production"
+p3 = "p3params_no_production"
+`)
+
+ fb.Build()
+
+ fb = fb.WithWorkingDir("config/development")
+
+ // This is set in all the config.toml variants above, but this will win.
+ fb.Add("config.toml", `paginatePath = "pag_development"`)
+
+ fb.Add("params.no.toml", `p3 = "p3params_no_development"`)
+ fb.Add("params.toml", `p3 = "p3params_development"`)
+
+ fb.Build()
+
+ cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Environment: "development", Filename: "hugo.toml", AbsConfigDir: "config"})
+ assert.NoError(err)
+
+ assert.Equal("pag_development", cfg.GetString("paginatePath")) // /config/development/config.toml
+
+ assert.Equal(10, cfg.GetInt("languages.no.weight")) // /config.toml
+ assert.Equal("Norsk_no_default", cfg.GetString("languages.no.languageName")) // /config/_default/languages.no.toml
+
+ assert.Equal("p1_base", cfg.GetString("params.p1"))
+ assert.Equal("p2params_default", cfg.GetString("params.p2")) // Is in both _default and production
+ assert.Equal("p3params_development", cfg.GetString("params.p3"))
+ assert.Equal("p3params_no_development", cfg.GetString("languages.no.params.p3"))
+
+ assert.Equal(2, len(cfg.Get("menus.docs").(([]map[string]interface{}))))
+ noMenus := cfg.Get("languages.no.menus.docs")
+ assert.NotNil(noMenus)
+ assert.Equal(1, len(noMenus.(([]map[string]interface{}))))
+
+}
+
+func TestLoadConfigDirError(t *testing.T) {
+ t.Parallel()
+
+ assert := require.New(t)
+
+ configContent := `
+baseURL = "https://example.org"
+
+`
+
+ mm := afero.NewMemMapFs()
+
+ writeToFs(t, mm, "hugo.toml", configContent)
+
+ fb := htesting.NewTestdataBuilder(mm, "config/development", t)
+
+ fb.Add("config.toml", `invalid & syntax`).Build()
+
+ _, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Environment: "development", Filename: "hugo.toml", AbsConfigDir: "config"})
+ assert.Error(err)
+
+ fe := herrors.UnwrapErrorWithFileContext(err)
+ assert.NotNil(fe)
+ assert.Equal(filepath.FromSlash("config/development/config.toml"), fe.Position().Filename)
+
+}
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 0bb3b4362..5e75135c0 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -21,6 +21,8 @@ import (
"strings"
"sync"
+ "github.com/gohugoio/hugo/config"
+
"github.com/gohugoio/hugo/publisher"
"github.com/gohugoio/hugo/common/herrors"
@@ -361,14 +363,14 @@ func (h *HugoSites) resetLogs() {
}
}
-func (h *HugoSites) createSitesFromConfig() error {
+func (h *HugoSites) createSitesFromConfig(cfg config.Provider) error {
oldLangs, _ := h.Cfg.Get("languagesSorted").(langs.Languages)
if err := loadLanguageSettings(h.Cfg, oldLangs); err != nil {
return err
}
- depsCfg := deps.DepsCfg{Fs: h.Fs, Cfg: h.Cfg}
+ depsCfg := deps.DepsCfg{Fs: h.Fs, Cfg: cfg}
sites, err := createSitesFromConfig(depsCfg)
@@ -412,9 +414,9 @@ func (h *HugoSites) toSiteInfos() []*SiteInfo {
type BuildCfg struct {
// Reset site state before build. Use to force full rebuilds.
ResetState bool
- // Re-creates the sites from configuration before a build.
+ // If set, we re-create the sites from the given configuration before a build.
// This is needed if new languages are added.
- CreateSitesFromConfig bool
+ NewConfig config.Provider
// Skip rendering. Useful for testing.
SkipRender bool
// Use this to indicate what changed (for rebuilds).
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index 4c275f55b..ec5070fa8 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -144,8 +144,8 @@ func (h *HugoSites) init(config *BuildCfg) error {
h.reset()
}
- if config.CreateSitesFromConfig {
- if err := h.createSitesFromConfig(); err != nil {
+ if config.NewConfig != nil {
+ if err := h.createSitesFromConfig(config.NewConfig); err != nil {
return err
}
}
@@ -154,8 +154,8 @@ func (h *HugoSites) init(config *BuildCfg) error {
}
func (h *HugoSites) initRebuild(config *BuildCfg) error {
- if config.CreateSitesFromConfig {
- return errors.New("Rebuild does not support 'CreateSitesFromConfig'.")
+ if config.NewConfig != nil {
+ return errors.New("Rebuild does not support 'NewConfig'.")
}
if config.ResetState {
diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go
index f1e317f59..91ae8434d 100644
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -11,14 +11,11 @@ import (
"path/filepath"
"time"
- "github.com/gohugoio/hugo/langs"
-
"github.com/fortytw2/leaktest"
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
- "github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -661,9 +658,8 @@ title = "Svenska"
sites := b.H
- // Watching does not work with in-memory fs, so we trigger a reload manually
- assert.NoError(sites.Cfg.(*langs.Language).Cfg.(*viper.Viper).ReadInConfig())
- err := b.H.Build(BuildCfg{CreateSitesFromConfig: true})
+ assert.NoError(b.LoadConfig())
+ err := b.H.Build(BuildCfg{NewConfig: b.Cfg})
if err != nil {
t.Fatalf("Failed to rebuild sites: %s", err)
@@ -723,10 +719,9 @@ func TestChangeDefaultLanguage(t *testing.T) {
"DefaultContentLanguageInSubdir": false,
})
- // Watching does not work with in-memory fs, so we trigger a reload manually
- // This does not look pretty, so we should think of something else.
- assert.NoError(b.H.Cfg.(*langs.Language).Cfg.(*viper.Viper).ReadInConfig())
- err := b.H.Build(BuildCfg{CreateSitesFromConfig: true})
+ assert.NoError(b.LoadConfig())
+ err := b.H.Build(BuildCfg{NewConfig: b.Cfg})
+
if err != nil {
t.Fatalf("Failed to rebuild sites: %s", err)
}
diff --git a/hugolib/page.go b/hugolib/page.go
index 81880023a..15ed631c1 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -1647,7 +1647,12 @@ func (p *Page) Menus() PageMenus {
p.pageMenusInit.Do(func() {
p.pageMenus = PageMenus{}
- if ms, ok := p.params["menu"]; ok {
+ ms, ok := p.params["menus"]
+ if !ok {
+ ms, ok = p.params["menu"]
+ }
+
+ if ok {
link := p.RelPermalink()
me := MenuEntry{Page: p, Name: p.LinkTitle(), Weight: p.Weight, URL: link}
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 177ed7fb1..b8273ce28 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1436,7 +1436,7 @@ func TestIndexPageSimpleMethods(t *testing.T) {
{func(n *Page) bool { return n.IsNode() }},
{func(n *Page) bool { return !n.IsPage() }},
{func(n *Page) bool { return n.Scratch() != nil }},
- {func(n *Page) bool { return n.Hugo().Version != "" }},
+ {func(n *Page) bool { return n.Hugo().Version() != "" }},
} {
n := s.newHomePage()
diff --git a/hugolib/paths/themes.go b/hugolib/paths/themes.go
index c6dae5e62..1ce8e7997 100644
--- a/hugolib/paths/themes.go
+++ b/hugolib/paths/themes.go
@@ -20,7 +20,6 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/spf13/afero"
"github.com/spf13/cast"
- "github.com/spf13/viper"
)
type ThemeConfig struct {
@@ -73,18 +72,11 @@ func (c *themesCollector) add(name, configFilename string) (ThemeConfig, error)
var tc ThemeConfig
if configFilename != "" {
- v := viper.New()
- v.SetFs(c.fs)
- v.AutomaticEnv()
- v.SetEnvPrefix("hugo")
- v.SetConfigFile(configFilename)
-
- err := v.ReadInConfig()
+ var err error
+ cfg, err = config.FromFile(c.fs, configFilename)
if err != nil {
- return tc, err
+ return tc, nil
}
- cfg = v
-
}
tc = ThemeConfig{Name: name, ConfigFilename: configFilename, Cfg: cfg}
diff --git a/hugolib/site.go b/hugolib/site.go
index 0579edf6e..7882d517f 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1226,7 +1226,7 @@ func (s *Site) initializeSiteInfo() error {
Data: &s.Data,
owner: s.owner,
s: s,
- hugoInfo: hugo.NewInfo(),
+ hugoInfo: hugo.NewInfo(s.Cfg.GetString("environment")),
// TODO(bep) make this Menu and similar into delegate methods on SiteInfo
Taxonomies: s.Taxonomies,
}
@@ -1370,7 +1370,7 @@ func (s *Site) getMenusFromConfig() Menus {
ret := Menus{}
- if menus := s.Language.GetStringMap("menu"); menus != nil {
+ if menus := s.Language.GetStringMap("menus"); menus != nil {
for name, menu := range menus {
m, err := cast.ToSliceE(menu)
if err != nil {
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 33f973e96..a60ca2905 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -322,6 +322,15 @@ func (s *sitesBuilder) CreateSites() *sitesBuilder {
return s
}
+func (s *sitesBuilder) LoadConfig() error {
+ cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: s.Fs.Source, Filename: "config." + s.configFormat})
+ if err != nil {
+ return err
+ }
+ s.Cfg = cfg
+ return nil
+}
+
func (s *sitesBuilder) CreateSitesE() error {
s.addDefaults()
s.writeFilePairs("content", s.contentFilePairs)
@@ -334,18 +343,9 @@ func (s *sitesBuilder) CreateSitesE() error {
s.writeFilePairs("i18n", s.i18nFilePairsAdded)
if s.Cfg == nil {
- cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: s.Fs.Source, Filename: "config." + s.configFormat})
- if err != nil {
+ if err := s.LoadConfig(); err != nil {
return err
}
- // TODO(bep)
- /* expectedConfigs := 1
- if s.theme != "" {
- expectedConfigs = 2
- }
- require.Equal(s.T, expectedConfigs, len(configFiles), fmt.Sprintf("Configs: %v", configFiles))
- */
- s.Cfg = cfg
}
sites, err := NewHugoSites(deps.DepsCfg{Fs: s.Fs, Cfg: s.Cfg, Logger: s.logger, Running: s.running})