diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-11-15 09:28:02 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-12-11 13:08:36 +0100 |
commit | 7829474088f835251f04caa1121d47e35fe89f7e (patch) | |
tree | f9b91d601befc966162036559e4418bebf46d643 /common | |
parent | 256418917c6642f7e5b3d3206ff4b6fa03b1cb28 (diff) |
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
Diffstat (limited to 'common')
-rw-r--r-- | common/herrors/error_locator.go | 8 | ||||
-rw-r--r-- | common/hugo/hugo.go | 44 | ||||
-rw-r--r-- | common/hugo/hugo_test.go | 13 | ||||
-rw-r--r-- | common/hugo/version.go | 14 | ||||
-rw-r--r-- | common/maps/maps.go | 72 | ||||
-rw-r--r-- | common/maps/maps_test.go | 51 |
6 files changed, 175 insertions, 27 deletions
diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go index 88cb06c8c..3a72f4790 100644 --- a/common/herrors/error_locator.go +++ b/common/herrors/error_locator.go @@ -17,10 +17,10 @@ package herrors import ( "io" "io/ioutil" + "path/filepath" "strings" "github.com/gohugoio/hugo/common/text" - "github.com/gohugoio/hugo/helpers" "github.com/spf13/afero" ) @@ -172,12 +172,16 @@ func chromaLexerFromType(fileType string) string { return fileType } +func extNoDelimiter(filename string) string { + return strings.TrimPrefix(".", filepath.Ext(filename)) +} + func chromaLexerFromFilename(filename string) string { if strings.Contains(filename, "layouts") { return "go-html-template" } - ext := helpers.ExtNoDelimiter(filename) + ext := extNoDelimiter(filename) return chromaLexerFromType(ext) } diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index b93b10bf1..62d923bf0 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -18,28 +18,50 @@ import ( "html/template" ) +const ( + EnvironmentDevelopment = "development" + EnvironmentProduction = "production" +) + var ( - // CommitHash contains the current Git revision. Use make to build to make + // commitHash contains the current Git revision. Use make to build to make // sure this gets set. - CommitHash string + commitHash string - // BuildDate contains the date of the current build. - BuildDate string + // buildDate contains the date of the current build. + buildDate string ) // Info contains information about the current Hugo environment type Info struct { - Version VersionString - Generator template.HTML CommitHash string BuildDate string + + // The build environment. + // Defaults are "production" (hugo) and "development" (hugo server). + // This can also be set by the user. + // It can be any string, but it will be all lower case. + Environment string } -func NewInfo() Info { +// Version returns the current version as a comparable version string. +func (i Info) Version() VersionString { + return CurrentVersion.Version() +} + +// Generator a Hugo meta generator HTML tag. +func (i Info) Generator() template.HTML { + return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, CurrentVersion.String())) +} + +// NewInfo creates a new Hugo Info object. +func NewInfo(environment string) Info { + if environment == "" { + environment = EnvironmentProduction + } return Info{ - Version: CurrentVersion.Version(), - CommitHash: CommitHash, - BuildDate: BuildDate, - Generator: template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, CurrentVersion.String())), + CommitHash: commitHash, + BuildDate: buildDate, + Environment: environment, } } diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go index 18a9b594f..1769db587 100644 --- a/common/hugo/hugo_test.go +++ b/common/hugo/hugo_test.go @@ -23,12 +23,13 @@ import ( func TestHugoInfo(t *testing.T) { assert := require.New(t) - hugoInfo := NewInfo() + hugoInfo := NewInfo("") - assert.Equal(CurrentVersion.Version(), hugoInfo.Version) - assert.IsType(VersionString(""), hugoInfo.Version) - assert.Equal(CommitHash, hugoInfo.CommitHash) - assert.Equal(BuildDate, hugoInfo.BuildDate) - assert.Contains(hugoInfo.Generator, fmt.Sprintf("Hugo %s", hugoInfo.Version)) + assert.Equal(CurrentVersion.Version(), hugoInfo.Version()) + assert.IsType(VersionString(""), hugoInfo.Version()) + assert.Equal(commitHash, hugoInfo.CommitHash) + assert.Equal(buildDate, hugoInfo.BuildDate) + assert.Equal("production", hugoInfo.Environment) + assert.Contains(hugoInfo.Generator(), fmt.Sprintf("Hugo %s", hugoInfo.Version())) } diff --git a/common/hugo/version.go b/common/hugo/version.go index 204f8f747..e9deb6acf 100644 --- a/common/hugo/version.go +++ b/common/hugo/version.go @@ -130,8 +130,8 @@ func BuildVersionString() string { program := "Hugo Static Site Generator" version := "v" + CurrentVersion.String() - if CommitHash != "" { - version += "-" + strings.ToUpper(CommitHash) + if commitHash != "" { + version += "-" + strings.ToUpper(commitHash) } if isExtended { version += "/extended" @@ -139,14 +139,12 @@ func BuildVersionString() string { osArch := runtime.GOOS + "/" + runtime.GOARCH - var buildDate string - if BuildDate != "" { - buildDate = BuildDate - } else { - buildDate = "unknown" + date := buildDate + if date == "" { + date = "unknown" } - return fmt.Sprintf("%s %s %s BuildDate: %s", program, version, osArch, buildDate) + return fmt.Sprintf("%s %s %s BuildDate: %s", program, version, osArch, date) } diff --git a/common/maps/maps.go b/common/maps/maps.go index a114b557c..e0d4f964d 100644 --- a/common/maps/maps.go +++ b/common/maps/maps.go @@ -16,6 +16,8 @@ package maps import ( "strings" + "github.com/gobwas/glob" + "github.com/spf13/cast" ) @@ -42,3 +44,73 @@ func ToLower(m map[string]interface{}) { } } + +type keyRename struct { + pattern glob.Glob + newKey string +} + +// KeyRenamer supports renaming of keys in a map. +type KeyRenamer struct { + renames []keyRename +} + +// NewKeyRenamer creates a new KeyRenamer given a list of pattern and new key +// value pairs. +func NewKeyRenamer(patternKeys ...string) (KeyRenamer, error) { + var renames []keyRename + for i := 0; i < len(patternKeys); i += 2 { + g, err := glob.Compile(strings.ToLower(patternKeys[i]), '/') + if err != nil { + return KeyRenamer{}, err + } + renames = append(renames, keyRename{pattern: g, newKey: patternKeys[i+1]}) + } + + return KeyRenamer{renames: renames}, nil +} + +func (r KeyRenamer) getNewKey(keyPath string) string { + for _, matcher := range r.renames { + if matcher.pattern.Match(keyPath) { + return matcher.newKey + } + } + + return "" +} + +// Rename renames the keys in the given map according +// to the patterns in the current KeyRenamer. +func (r KeyRenamer) Rename(m map[string]interface{}) { + r.renamePath("", m) +} + +func (KeyRenamer) keyPath(k1, k2 string) string { + k1, k2 = strings.ToLower(k1), strings.ToLower(k2) + if k1 == "" { + return k2 + } else { + return k1 + "/" + k2 + } +} + +func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) { + for key, val := range m { + keyPath := r.keyPath(parentKeyPath, key) + switch val.(type) { + case map[interface{}]interface{}: + val = cast.ToStringMap(val) + r.renamePath(keyPath, val.(map[string]interface{})) + case map[string]interface{}: + r.renamePath(keyPath, val.(map[string]interface{})) + } + + newKey := r.getNewKey(keyPath) + + if newKey != "" { + delete(m, key) + m[newKey] = val + } + } +} diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index 37add5dc5..29bffa6bc 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -16,6 +16,8 @@ package maps import ( "reflect" "testing" + + "github.com/stretchr/testify/require" ) func TestToLower(t *testing.T) { @@ -70,3 +72,52 @@ func TestToLower(t *testing.T) { } } } + +func TestRenameKeys(t *testing.T) { + assert := require.New(t) + + m := map[string]interface{}{ + "a": 32, + "ren1": "m1", + "ren2": "m1_2", + "sub": map[string]interface{}{ + "subsub": map[string]interface{}{ + "REN1": "m2", + "ren2": "m2_2", + }, + }, + "no": map[string]interface{}{ + "ren1": "m2", + "ren2": "m2_2", + }, + } + + expected := map[string]interface{}{ + "a": 32, + "new1": "m1", + "new2": "m1_2", + "sub": map[string]interface{}{ + "subsub": map[string]interface{}{ + "new1": "m2", + "ren2": "m2_2", + }, + }, + "no": map[string]interface{}{ + "ren1": "m2", + "ren2": "m2_2", + }, + } + + renamer, err := NewKeyRenamer( + "{ren1,sub/*/ren1}", "new1", + "{Ren2,sub/ren2}", "new2", + ) + assert.NoError(err) + + renamer.Rename(m) + + if !reflect.DeepEqual(expected, m) { + t.Errorf("Expected\n%#v, got\n%#v\n", expected, m) + } + +} |