diff options
Diffstat (limited to 'config')
-rw-r--r-- | config/allconfig/allconfig.go | 136 | ||||
-rw-r--r-- | config/allconfig/alldecoders.go | 2 | ||||
-rw-r--r-- | config/allconfig/configlanguage.go | 12 | ||||
-rw-r--r-- | config/allconfig/docshelper.go | 3 | ||||
-rw-r--r-- | config/allconfig/integration_test.go | 7 | ||||
-rw-r--r-- | config/allconfig/load.go | 12 | ||||
-rw-r--r-- | config/commonConfig.go | 23 | ||||
-rw-r--r-- | config/commonConfig_test.go | 14 | ||||
-rw-r--r-- | config/configProvider.go | 3 | ||||
-rw-r--r-- | config/env.go | 37 | ||||
-rw-r--r-- | config/namespace.go | 3 | ||||
-rw-r--r-- | config/namespace_test.go | 12 | ||||
-rw-r--r-- | config/testconfig/testconfig.go | 5 |
13 files changed, 149 insertions, 120 deletions
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 9f0d73ecd..5788e792b 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -30,6 +30,7 @@ import ( "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/urls" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/privacy" @@ -283,12 +284,13 @@ func (c *Config) CompileConfig(logger loggers.Logger) error { disabledLangs := make(map[string]bool) for _, lang := range c.DisableLanguages { - if lang == c.DefaultContentLanguage { - return fmt.Errorf("cannot disable default content language %q", lang) - } disabledLangs[lang] = true } for lang, language := range c.Languages { + if !language.Disabled && disabledLangs[lang] { + language.Disabled = true + c.Languages[lang] = language + } if language.Disabled { disabledLangs[lang] = true if lang == c.DefaultContentLanguage { @@ -408,15 +410,19 @@ type ConfigCompiled struct { } // This may be set after the config is compiled. -func (c *ConfigCompiled) SetMainSectionsIfNotSet(sections []string) { +func (c *ConfigCompiled) SetMainSections(sections []string) { c.mu.Lock() defer c.mu.Unlock() - if c.MainSections != nil { - return - } c.MainSections = sections } +// IsMainSectionsSet returns whether the main sections have been set. +func (c *ConfigCompiled) IsMainSectionsSet() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.MainSections != nil +} + // This is set after the config is compiled by the server command. func (c *ConfigCompiled) SetBaseURL(baseURL, baseURLLiveReload urls.BaseURL) { c.BaseURL = baseURL @@ -425,7 +431,6 @@ func (c *ConfigCompiled) SetBaseURL(baseURL, baseURLLiveReload urls.BaseURL) { // RootConfig holds all the top-level configuration options in Hugo type RootConfig struct { - // The base URL of the site. // Note that the default value is empty, but Hugo requires a valid URL (e.g. "https://example.com/") to work properly. // <docsmeta>{"identifiers": ["URL"] }</docsmeta> @@ -648,13 +653,16 @@ type Configs struct { LanguageConfigMap map[string]*Config LanguageConfigSlice []*Config - IsMultihost bool - Languages langs.Languages - LanguagesDefaultFirst langs.Languages + IsMultihost bool Modules modules.Modules ModulesClient *modules.Client + // All below is set in Init. + Languages langs.Languages + LanguagesDefaultFirst langs.Languages + ContentPathParser paths.PathParser + configLangs []config.AllProvider } @@ -674,6 +682,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 { + c.LanguageConfigSlice = append(c.LanguageConfigSlice, v) + languageConf := v.Languages[k] + language, err := langs.NewLanguage(k, 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 { + if !l.Disabled { + languages[n] = l + n++ + } + } + languages = languages[:n] + + var languagesDefaultFirst langs.Languages + for _, l := range languages { + if l.Lang == defaultContentLanguage { + languagesDefaultFirst = append(languagesDefaultFirst, l) + } + } + for _, l := range languages { + if l.Lang != defaultContentLanguage { + languagesDefaultFirst = append(languagesDefaultFirst, l) + } + } + + c.Languages = languages + c.LanguagesDefaultFirst = languagesDefaultFirst + c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet()} + c.configLangs = make([]config.AllProvider, len(c.Languages)) for i, l := range c.LanguagesDefaultFirst { c.configLangs[i] = ConfigLanguage{ @@ -751,7 +811,6 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon } langConfigMap := make(map[string]*Config) - var langConfigs []*Config languagesConfig := cfg.GetStringMap("languages") var isMultiHost bool @@ -848,65 +907,24 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon } } - var languages langs.Languages - defaultContentLanguage := all.DefaultContentLanguage - for k, v := range langConfigMap { - languageConf := v.Languages[k] - language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf) - if err != nil { - return nil, 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 { - langConfigs = append(langConfigs, langConfigMap[l.Lang]) - } - - var languagesDefaultFirst langs.Languages - for _, l := range languages { - if l.Lang == defaultContentLanguage { - languagesDefaultFirst = append(languagesDefaultFirst, l) - } - } - for _, l := range languages { - if l.Lang != defaultContentLanguage { - languagesDefaultFirst = append(languagesDefaultFirst, l) - } - } - bcfg.PublishDir = all.PublishDir res.BaseConfig = bcfg all.CommonDirs.CacheDir = bcfg.CacheDir - for _, l := range langConfigs { + for _, l := range langConfigMap { l.CommonDirs.CacheDir = bcfg.CacheDir } cm := &Configs{ - Base: all, - LanguageConfigMap: langConfigMap, - LanguageConfigSlice: langConfigs, - LoadingInfo: res, - IsMultihost: isMultiHost, - Languages: languages, - LanguagesDefaultFirst: languagesDefaultFirst, + Base: all, + LanguageConfigMap: langConfigMap, + LoadingInfo: res, + IsMultihost: isMultiHost, } return cm, nil } func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error { - var decoderSetups []decodeWeight if len(keys) == 0 { diff --git a/config/allconfig/alldecoders.go b/config/allconfig/alldecoders.go index dc58882f3..f96c19cfc 100644 --- a/config/allconfig/alldecoders.go +++ b/config/allconfig/alldecoders.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go index 2c5a116f4..71bd232de 100644 --- a/config/allconfig/configlanguage.go +++ b/config/allconfig/configlanguage.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -16,6 +16,7 @@ package allconfig import ( "time" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/urls" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/langs" @@ -41,10 +42,15 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages { return c.m.LanguagesDefaultFirst } +func (c ConfigLanguage) PathParser() paths.PathParser { + return c.m.ContentPathParser +} + func (c ConfigLanguage) LanguagePrefix() string { if c.DefaultContentLanguageInSubdir() && c.DefaultContentLanguage() == c.Language().Lang { return c.Language().Lang } + if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang { return "" } @@ -119,6 +125,10 @@ func (c ConfigLanguage) Quiet() bool { return c.m.Base.Internal.Quiet } +func (c ConfigLanguage) Watching() bool { + return c.m.Base.Internal.Watch +} + // GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use. func (c ConfigLanguage) GetConfigSection(s string) any { switch s { diff --git a/config/allconfig/docshelper.go b/config/allconfig/docshelper.go index 48a09de51..1a5fb6153 100644 --- a/config/allconfig/docshelper.go +++ b/config/allconfig/docshelper.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -22,7 +22,6 @@ import ( // This is is just some helpers used to create some JSON used in the Hugo docs. func init() { docsProvider := func() docshelper.DocProvider { - cfg := config.New() for configRoot, v := range allDecoderSetups { if v.internalOrDeprecated { diff --git a/config/allconfig/integration_test.go b/config/allconfig/integration_test.go index fcb92e71d..4f2f1a06e 100644 --- a/config/allconfig/integration_test.go +++ b/config/allconfig/integration_test.go @@ -10,7 +10,6 @@ import ( ) func TestDirsMount(t *testing.T) { - files := ` -- hugo.toml -- baseURL = "https://example.com" @@ -44,7 +43,7 @@ Title: {{ .Title }} hugolib.IntegrationTestConfig{T: t, TxtarString: files}, ).Build() - //b.AssertFileContent("public/p1/index.html", "Title: p1") + // b.AssertFileContent("public/p1/index.html", "Title: p1") sites := b.H.Sites b.Assert(len(sites), qt.Equals, 2) @@ -58,7 +57,7 @@ Title: {{ .Title }} enConcp := sites[0].Conf enConf := enConcp.GetConfig().(*allconfig.Config) - b.Assert(enConcp.BaseURL().String(), qt.Equals, "https://example.com") + b.Assert(enConcp.BaseURL().String(), qt.Equals, "https://example.com/") modConf := enConf.Module b.Assert(modConf.Mounts, qt.HasLen, 8) b.Assert(modConf.Mounts[0].Source, qt.Equals, filepath.FromSlash("content/en")) @@ -67,11 +66,9 @@ Title: {{ .Title }} b.Assert(modConf.Mounts[1].Source, qt.Equals, filepath.FromSlash("content/sv")) b.Assert(modConf.Mounts[1].Target, qt.Equals, "content") b.Assert(modConf.Mounts[1].Lang, qt.Equals, "sv") - } func TestConfigAliases(t *testing.T) { - files := ` -- hugo.toml -- baseURL = "https://example.com" diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 7d706c7e3..eceed31f4 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -37,6 +37,7 @@ import ( "github.com/spf13/afero" ) +//lint:ignore ST1005 end user message. 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") func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) { @@ -566,15 +567,6 @@ func (l configLoader) deleteMergeStrategies() { }) } -func (l configLoader) loadModulesConfig() (modules.Config, error) { - modConfig, err := modules.DecodeConfig(l.cfg) - if err != nil { - return modules.Config{}, err - } - - return modConfig, nil -} - func (l configLoader) wrapFileError(err error, filename string) error { fe := herrors.UnwrapFileError(err) if fe != nil { diff --git a/config/commonConfig.go b/config/commonConfig.go index ef9d47553..6ca061093 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -86,28 +86,21 @@ var defaultBuild = BuildConfig{ CacheBusters: []CacheBuster{ { - Source: `assets/.*\.(js|ts|jsx|tsx)`, - Target: `(js|scripts|javascript)`, - }, - { - Source: `assets/.*\.(css|sass|scss)$`, - Target: cssTargetCachebusterRe, - }, - { Source: `(postcss|tailwind)\.config\.js`, Target: cssTargetCachebusterRe, }, - // This is deliberately coarse grained; it will cache bust resources with "json" in the cache key when js files changes, which is good. - { - Source: `assets/.*\.(.*)$`, - Target: `$1`, - }, }, } // BuildConfig holds some build related configuration. type BuildConfig struct { - UseResourceCacheWhen string // never, fallback, always. Default is fallback + // When to use the resource file cache. + // One of never, fallback, always. Default is fallback + UseResourceCacheWhen string + + // When enabled, will duplicate bundled resource files across languages that + // doesn't have a translated version. + DuplicateResourceFiles bool // When enabled, will collect and write a hugo_stats.json with some build // related aggregated data (e.g. CSS class names). @@ -373,7 +366,6 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error { return match } - } return compileErr } @@ -416,7 +408,6 @@ func DecodeServer(cfg Provider) (Server, error) { Status: 404, }, } - } return *s, nil diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go index 8aa1318dd..425d3e970 100644 --- a/config/commonConfig_test.go +++ b/config/commonConfig_test.go @@ -148,21 +148,13 @@ func TestBuildConfigCacheBusters(t *testing.T) { l := loggers.NewDefault() c.Assert(conf.CompileConfig(l), qt.IsNil) - m, err := conf.MatchCacheBuster(l, "assets/foo/main.js") - c.Assert(err, qt.IsNil) + m, _ := conf.MatchCacheBuster(l, "tailwind.config.js") c.Assert(m, qt.IsNotNil) - c.Assert(m("scripts"), qt.IsTrue) - c.Assert(m("asdf"), qt.IsFalse) - - m, _ = conf.MatchCacheBuster(l, "tailwind.config.js") c.Assert(m("css"), qt.IsTrue) c.Assert(m("js"), qt.IsFalse) - m, err = conf.MatchCacheBuster(l, "assets/foo.json") - c.Assert(err, qt.IsNil) - c.Assert(m, qt.IsNotNil) - c.Assert(m("json"), qt.IsTrue) - + m, _ = conf.MatchCacheBuster(l, "foo.bar") + c.Assert(m, qt.IsNil) } func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) { diff --git a/config/configProvider.go b/config/configProvider.go index 11099e407..2536639ea 100644 --- a/config/configProvider.go +++ b/config/configProvider.go @@ -17,6 +17,7 @@ import ( "time" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/urls" "github.com/gohugoio/hugo/langs" @@ -30,6 +31,7 @@ type AllProvider interface { LanguagePrefix() string BaseURL() urls.BaseURL BaseURLLiveReload() urls.BaseURL + PathParser() paths.PathParser Environment() string IsMultihost() bool IsMultiLingual() bool @@ -54,6 +56,7 @@ type AllProvider interface { BuildFuture() bool BuildDrafts() bool Running() bool + Watching() bool PrintUnusedTemplates() bool EnableMissingTranslationPlaceholders() bool TemplateMetrics() bool diff --git a/config/env.go b/config/env.go index 1e9266b17..0ad5ecaea 100644 --- a/config/env.go +++ b/config/env.go @@ -18,6 +18,12 @@ import ( "runtime" "strconv" "strings" + + "github.com/pbnjay/memory" +) + +const ( + gigabyte = 1 << 30 ) // GetNumWorkerMultiplier returns the base value used to calculate the number @@ -33,6 +39,37 @@ func GetNumWorkerMultiplier() int { return runtime.NumCPU() } +// GetMemoryLimit returns the upper memory limit in bytes for Hugo's in-memory caches. +// Note that this does not represent "all of the memory" that Hugo will use, +// so it needs to be set to a lower number than the available system memory. +// It will read from the HUGO_MEMORYLIMIT (in Gigabytes) environment variable. +// If that is not set, it will set aside a quarter of the total system memory. +func GetMemoryLimit() uint64 { + if mem := os.Getenv("HUGO_MEMORYLIMIT"); mem != "" { + if v := stringToGibabyte(mem); v > 0 { + return v + } + + } + + // There is a FreeMemory function, but as the kernel in most situations + // will take whatever memory that is left and use for caching etc., + // that value is not something that we can use. + m := memory.TotalMemory() + if m != 0 { + return uint64(m / 4) + } + + return 2 * gigabyte +} + +func stringToGibabyte(f string) uint64 { + if v, err := strconv.ParseFloat(f, 32); err == nil && v > 0 { + return uint64(v * gigabyte) + } + return 0 +} + // SetEnvVars sets vars on the form key=value in the oldVars slice. func SetEnvVars(oldVars *[]string, keyValues ...string) { for i := 0; i < len(keyValues); i += 2 { diff --git a/config/namespace.go b/config/namespace.go index 3ecd01014..b518c6c01 100644 --- a/config/namespace.go +++ b/config/namespace.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -20,7 +20,6 @@ import ( ) func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) { - // Calculate the hash of the input (not including any defaults applied later). // This allows us to introduce new config options without breaking the hash. h := identity.HashString(configSource) diff --git a/config/namespace_test.go b/config/namespace_test.go index 008237c13..9bd23e08e 100644 --- a/config/namespace_test.go +++ b/config/namespace_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -26,7 +26,7 @@ func TestNamespace(t *testing.T) { c := qt.New(t) c.Assert(true, qt.Equals, true) - //ns, err := config.DecodeNamespace[map[string]DocsMediaTypeConfig](in, defaultMediaTypesConfig, buildConfig) + // ns, err := config.DecodeNamespace[map[string]DocsMediaTypeConfig](in, defaultMediaTypesConfig, buildConfig) ns, err := DecodeNamespace[[]*tstNsExt]( map[string]interface{}{"foo": "bar"}, @@ -46,23 +46,15 @@ func TestNamespace(t *testing.T) { c.Assert(ns.SourceHash, qt.Equals, "14368731254619220105") c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"}) c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil)) - } type ( tstNsExt struct { Foo string } - tstNsInt struct { - Foo string - } ) func (t *tstNsExt) Init() error { t.Foo = strings.ToUpper(t.Foo) return nil } -func (t *tstNsInt) Compile(ext *tstNsExt) error { - t.Foo = ext.Foo + " qux" - return nil -} diff --git a/config/testconfig/testconfig.go b/config/testconfig/testconfig.go index 4aafd69f0..8f70e6cb7 100644 --- a/config/testconfig/testconfig.go +++ b/config/testconfig/testconfig.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Hugo Authors. All rights reserved. +// Copyright 2024 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. @@ -36,7 +36,7 @@ func GetTestConfigs(fs afero.Fs, cfg config.Provider) *allconfig.Configs { // Make sure that the workingDir exists. workingDir := cfg.GetString("workingDir") if workingDir != "" { - if err := fs.MkdirAll(workingDir, 0777); err != nil { + if err := fs.MkdirAll(workingDir, 0o777); err != nil { panic(err) } } @@ -46,7 +46,6 @@ func GetTestConfigs(fs afero.Fs, cfg config.Provider) *allconfig.Configs { panic(err) } return configs - } func GetTestConfig(fs afero.Fs, cfg config.Provider) config.AllProvider { |