Repository: spf13/viper (go)

Issue:
Allow multiple env. var. keys in BindEnv
It sometimes can happen that a project updates its conventions for environment variables, deprecating others while still wanting to offer support for them. For this reason it would be nice if `BindEnv` would allow this. The good news is that `BindEnv`'s function signature takes a variadic input so adding even more arguments to it would not be a breaking change.

An example of how it would work:

```go
viper.BindEnv("my.key", "APP_MY_KEY", "MY_KEY")
```

Then, when running `viper.Get("my.key")`, the first environment variable from the list would win. Basically the first entry is the latest valid form and the subsequent ones are the deprecated ones we want to keep support for.

Would it be ok to open a PR for this? It's a fairly small change with full backwards compatibility.

Pull request description:
Allow BindEnv to register multiple environment variables.
This change modifies BindEnv to permit a list of environment variable
names in order to support multiple env. vars. for the same config key.
When this form is used, env. keys take precedence in the written order.

Closes #971 

Patch:
diff --git a/.github/workflows/feedback_issue.yml b/.github/workflows/feedback_issue.yml
new file mode 100644
index 0000000..5730d86
--- /dev/null
+++ b/.github/workflows/feedback_issue.yml
@@ -0,0 +1,32 @@
+on:
+  issues:
+    types: [opened]
+
+jobs:
+  comment:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/github-script@v3
+        with:
+          github-token: ${{secrets.GITHUB_TOKEN}}
+          script: |
+            github.issues.createComment({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              body: `👋 Thanks for reporting!
+
+            A maintainer will take a look at your issue shortly. 👀
+
+            In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
+
+            ⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
+
+            📣 If you've already given us your feedback, you can still help by spreading the news,
+            either by sharing the above link or telling people about this on Twitter:
+
+            https://twitter.com/sagikazarmark/status/1306904078967074816
+
+            **Thank you!** ❤️
+            `,
+            })
diff --git a/.github/workflows/feedback_pull_request.yml b/.github/workflows/feedback_pull_request.yml
new file mode 100644
index 0000000..60e6808
--- /dev/null
+++ b/.github/workflows/feedback_pull_request.yml
@@ -0,0 +1,32 @@
+on:
+  pull_request:
+    types: [opened]
+
+jobs:
+  comment:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/github-script@v3
+        with:
+          github-token: ${{secrets.GITHUB_TOKEN}}
+          script: |
+            github.issues.createComment({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              body: `👋 Thanks for contributing to Viper! You are awesome! 🎉
+
+            A maintainer will take a look at your pull request shortly. 👀
+
+            In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
+
+            ⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
+
+            📣 If you've already given us your feedback, you can still help by spreading the news,
+            either by sharing the above link or telling people about this on Twitter:
+
+            https://twitter.com/sagikazarmark/status/1306904078967074816
+
+            **Thank you!** ❤️
+            `,
+            })
diff --git a/.golangci.yml b/.golangci.yml
index a0755ce..a7b7381 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,27 +1,76 @@
+run:
+    timeout: 5m
+
 linters-settings:
+    gci:
+        local-prefixes: github.com/spf13/viper
     golint:
-        min-confidence: 0.1
+        min-confidence: 0
     goimports:
         local-prefixes: github.com/spf13/viper
 
 linters:
-    enable-all: true
-    disable:
-        - funlen
-        - maligned
+    disable-all: true
+    enable:
+        - bodyclose
+        - deadcode
+        - dogsled
+        - dupl
+        - exhaustive
+        - exportloopref
+        - gci
+        - goconst
+        - gofmt
+        - gofumpt
+        - goimports
+        - golint
+        - goprintffuncname
+        - govet
+        - ineffassign
+        - misspell
+        - nakedret
+        - noctx
+        - nolintlint
+        - prealloc
+        - rowserrcheck
+        - sqlclosecheck
+        - staticcheck
+        - structcheck
+        - stylecheck
+        - typecheck
+        - unconvert
+        - unparam
+        - unused
+        - varcheck
+        - whitespace
+
+        # fixme
+        # - errcheck
+        # - gochecknoglobals
+        # - gochecknoinits
+        # - gocognit
+        # - gocritic
+        # - gocyclo
+        # - godot
+        # - gosec
+        # - gosimple
+        # - lll
+        # - nlreturn
+        # - scopelint
 
-        # TODO: fix me
-        - wsl
-        - gochecknoinits
-        - gosimple
-        - gochecknoglobals
-        - errcheck
-        - lll
-        - godox
-        - scopelint
-        - gocyclo
-        - gocognit
-        - gocritic
+        # unused
+        # - depguard
+        # - goheader
+        # - gomodguard
 
-service:
-    golangci-lint-version: 1.21.x
+        # don't enable:
+        # - asciicheck
+        # - funlen
+        # - godox
+        # - goerr113
+        # - gomnd
+        # - interfacer
+        # - maligned
+        # - nestif
+        # - testpackage
+        # - wsl
diff --git a/Makefile b/Makefile
index 1c2cab0..dd487eb 100644
--- a/Makefile
+++ b/Makefile
@@ -15,8 +15,8 @@ TEST_FORMAT = short-verbose
 endif
 
 # Dependency versions
-GOTESTSUM_VERSION = 0.4.0
-GOLANGCI_VERSION = 1.21.0
+GOTESTSUM_VERSION = 0.5.3
+GOLANGCI_VERSION = 1.31.0
 
 # Add the ability to override some variables
 # Use with care
diff --git a/README.md b/README.md
index dfd8034..e7b50cf 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,8 @@
+> ## Viper v2 feedback
+> Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9
+>
+> **Thank you!**
+
 ![Viper](.github/logo.png?raw=true)
 
 [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
@@ -245,9 +250,10 @@ using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
 the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
 prefix.
 
-`BindEnv` takes one or two parameters. The first parameter is the key name, the
-second is the name of the environment variable. The name of the environment
-variable is case sensitive. If the ENV variable name is not provided, then
+`BindEnv` takes one or more parameters. The first parameter is the key name, the
+rest are the name of the environment variables to bind to this key. If more than
+one are provided, they will take precedence in the specified order. The name of
+the environment variable is case sensitive. If the ENV variable name is not provided, then
 Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter),
 it **does not** automatically add the prefix. For example if the second parameter is "id",
 Viper will look for the ENV variable "ID".
@@ -607,14 +613,15 @@ will be returned instead. E.g.
 GetString("datastore.metric.host") // returns "0.0.0.0"
 ```
 
-### Extract sub-tree
+### Extracting a sub-tree
 
-Extract sub-tree from Viper.
+When developing reusable modules, it's often useful to extract a subset of the configuration
+and pass it to a module. This way the module can be instantiated more than once, with different configurations.
 
-For example, `viper` represents:
+For example, an application might use multiple different cache stores for different purposes:
 
-```json
-app:
+```yaml
+cache:
   cache1:
     max-items: 100
     item-size: 64
@@ -623,35 +630,36 @@ app:
     item-size: 80
 ```
 
-After executing:
+We could pass the cache name to a module (eg. `NewCache("cache1")`),
+but it would require weird concatenation for accessing config keys and would be less separated from the global config.
 
-```go
-subv := viper.Sub("app.cache1")
-```
+So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration:
 
-`subv` represents:
+```go
+cache1Config := viper.Sub("cache.cache1")
+if cache1Config == nil { // Sub returns nil if the key cannot be found
+    panic("cache configuration not found")
+}
 
-```json
-max-items: 100
-item-size: 64
+cache1 := NewCache(cache1Config)
 ```
 
-Suppose we have:
+**Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found.
+
+Internally, the `NewCache` function can address `max-items` and `item-size` keys directly:
 
 ```go
-func NewCache(cfg *Viper) *Cache {...}
+func NewCache(v *Viper) *Cache {
+    return &Cache{
+        MaxItems: c.GetInt("max-items"),
+        ItemSize: c.GetInt("item-size"),
+    }
+}
 ```
 
-which creates a cache based on config information formatted as `subv`.
-Now it’s easy to create these 2 caches separately as:
+The resulting code is easy to test, since it's decoupled from the main config structure,
+and easier to reuse (for the same reason).
 
-```go
-cfg1 := viper.Sub("app.cache1")
-cache1 := NewCache(cfg1)
-
-cfg2 := viper.Sub("app.cache2")
-cache2 := NewCache(cfg2)
-```
 
 ### Unmarshaling
 
diff --git a/remote/remote.go b/remote/remote.go
index 6bdaa9a..ac3ef26 100644
--- a/remote/remote.go
+++ b/remote/remote.go
@@ -11,9 +11,9 @@ import (
 	"io"
 	"os"
 
-	"github.com/spf13/viper"
-
 	crypt "github.com/bketelsen/crypt/config"
+
+	"github.com/spf13/viper"
 )
 
 type remoteConfigProvider struct{}
diff --git a/viper.go b/viper.go
index 50fd66e..abd9a38 100644
--- a/viper.go
+++ b/viper.go
@@ -205,7 +205,7 @@ type Viper struct {
 	defaults       map[string]interface{}
 	kvstore        map[string]interface{}
 	pflags         map[string]FlagValue
-	env            map[string]string
+	env            map[string][]string
 	aliases        map[string]string
 	typeByDefValue bool
 
@@ -228,7 +228,7 @@ func New() *Viper {
 	v.defaults = make(map[string]interface{})
 	v.kvstore = make(map[string]interface{})
 	v.pflags = make(map[string]FlagValue)
-	v.env = make(map[string]string)
+	v.env = make(map[string][]string)
 	v.aliases = make(map[string]string)
 	v.typeByDefValue = false
 
@@ -409,6 +409,7 @@ func (v *Viper) WatchConfig() {
 // SetConfigFile explicitly defines the path, name and extension of the config file.
 // Viper will use this and not check any of the config paths.
 func SetConfigFile(in string) { v.SetConfigFile(in) }
+
 func (v *Viper) SetConfigFile(in string) {
 	if in != "" {
 		v.configFile = in
@@ -419,6 +420,7 @@ func (v *Viper) SetConfigFile(in string) {
 // E.g. if your prefix is "spf", the env registry will look for env
 // variables that start with "SPF_".
 func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
+
 func (v *Viper) SetEnvPrefix(in string) {
 	if in != "" {
 		v.envPrefix = in
@@ -437,6 +439,7 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
 // but empty environment variables as valid values instead of falling back.
 // For backward compatibility reasons this is false by default.
 func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
+
 func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
 	v.allowEmptyEnv = allowEmptyEnv
 }
@@ -465,6 +468,7 @@ func (v *Viper) ConfigFileUsed() string { return v.configFile }
 // AddConfigPath adds a path for Viper to search for the config file in.
 // Can be called multiple times to define multiple search paths.
 func AddConfigPath(in string) { v.AddConfigPath(in) }
+
 func (v *Viper) AddConfigPath(in string) {
 	if in != "" {
 		absin := absPathify(in)
@@ -486,6 +490,7 @@ func (v *Viper) AddConfigPath(in string) {
 func AddRemoteProvider(provider, endpoint, path string) error {
 	return v.AddRemoteProvider(provider, endpoint, path)
 }
+
 func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
 	if !stringInSlice(provider, SupportedRemoteProviders) {
 		return UnsupportedRemoteProviderError(provider)
@@ -706,6 +711,7 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
 //
 //   "a b c"
 func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
+
 func (v *Viper) SetTypeByDefaultValue(enable bool) {
 	v.typeByDefValue = enable
 }
@@ -723,6 +729,7 @@ func GetViper() *Viper {
 //
 // Get returns an interface. For a specific value use one of the Get____ methods.
 func Get(key string) interface{} { return v.Get(key) }
+
 func (v *Viper) Get(key string) interface{} {
 	lcaseKey := strings.ToLower(key)
 	val := v.find(lcaseKey, true)
@@ -773,6 +780,7 @@ func (v *Viper) Get(key string) interface{} {
 // Sub returns new Viper instance representing a sub tree of this instance.
 // Sub is case-insensitive for a key.
 func Sub(key string) *Viper { return v.Sub(key) }
+
 func (v *Viper) Sub(key string) *Viper {
 	subv := New()
 	data := v.Get(key)
@@ -789,96 +797,112 @@ func (v *Viper) Sub(key string) *Viper {
 
 // GetString returns the value associated with the key as a string.
 func GetString(key string) string { return v.GetString(key) }
+
 func (v *Viper) GetString(key string) string {
 	return cast.ToString(v.Get(key))
 }
 
 // GetBool returns the value associated with the key as a boolean.
 func GetBool(key string) bool { return v.GetBool(key) }
+
 func (v *Viper) GetBool(key string) bool {
 	return cast.ToBool(v.Get(key))
 }
 
 // GetInt returns the value associated with the key as an integer.
 func GetInt(key string) int { return v.GetInt(key) }
+
 func (v *Viper) GetInt(key string) int {
 	return cast.ToInt(v.Get(key))
 }
 
 // GetInt32 returns the value associated with the key as an integer.
 func GetInt32(key string) int32 { return v.GetInt32(key) }
+
 func (v *Viper) GetInt32(key string) int32 {
 	return cast.ToInt32(v.Get(key))
 }
 
 // GetInt64 returns the value associated with the key as an integer.
 func GetInt64(key string) int64 { return v.GetInt64(key) }
+
 func (v *Viper) GetInt64(key string) int64 {
 	return cast.ToInt64(v.Get(key))
 }
 
 // GetUint returns the value associated with the key as an unsigned integer.
 func GetUint(key string) uint { return v.GetUint(key) }
+
 func (v *Viper) GetUint(key string) uint {
 	return cast.ToUint(v.Get(key))
 }
 
 // GetUint32 returns the value associated with the key as an unsigned integer.
 func GetUint32(key string) uint32 { return v.GetUint32(key) }
+
 func (v *Viper) GetUint32(key string) uint32 {
 	return cast.ToUint32(v.Get(key))
 }
 
 // GetUint64 returns the value associated with the key as an unsigned integer.
 func GetUint64(key string) uint64 { return v.GetUint64(key) }
+
 func (v *Viper) GetUint64(key string) uint64 {
 	return cast.ToUint64(v.Get(key))
 }
 
 // GetFloat64 returns the value associated with the key as a float64.
 func GetFloat64(key string) float64 { return v.GetFloat64(key) }
+
 func (v *Viper) GetFloat64(key string) float64 {
 	return cast.ToFloat64(v.Get(key))
 }
 
 // GetTime returns the value associated with the key as time.
 func GetTime(key string) time.Time { return v.GetTime(key) }
+
 func (v *Viper) GetTime(key string) time.Time {
 	return cast.ToTime(v.Get(key))
 }
 
 // GetDuration returns the value associated with the key as a duration.
 func GetDuration(key string) time.Duration { return v.GetDuration(key) }
+
 func (v *Viper) GetDuration(key string) time.Duration {
 	return cast.ToDuration(v.Get(key))
 }
 
 // GetIntSlice returns the value associated with the key as a slice of int values.
 func GetIntSlice(key string) []int { return v.GetIntSlice(key) }
+
 func (v *Viper) GetIntSlice(key string) []int {
 	return cast.ToIntSlice(v.Get(key))
 }
 
 // GetStringSlice returns the value associated with the key as a slice of strings.
 func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
+
 func (v *Viper) GetStringSlice(key string) []string {
 	return cast.ToStringSlice(v.Get(key))
 }
 
 // GetStringMap returns the value associated with the key as a map of interfaces.
 func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
+
 func (v *Viper) GetStringMap(key string) map[string]interface{} {
 	return cast.ToStringMap(v.Get(key))
 }
 
 // GetStringMapString returns the value associated with the key as a map of strings.
 func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
+
 func (v *Viper) GetStringMapString(key string) map[string]string {
 	return cast.ToStringMapString(v.Get(key))
 }
 
 // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
 func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
+
 func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
 	return cast.ToStringMapStringSlice(v.Get(key))
 }
@@ -886,6 +910,7 @@ func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
 // GetSizeInBytes returns the size of the value associated with the given key
 // in bytes.
 func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
+
 func (v *Viper) GetSizeInBytes(key string) uint {
 	sizeStr := cast.ToString(v.Get(key))
 	return parseSizeInBytes(sizeStr)
@@ -895,6 +920,7 @@ func (v *Viper) GetSizeInBytes(key string) uint {
 func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
 	return v.UnmarshalKey(key, rawVal, opts...)
 }
+
 func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
 	return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
 }
@@ -904,6 +930,7 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConf
 func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
 	return v.Unmarshal(rawVal, opts...)
 }
+
 func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
 	return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
 }
@@ -940,6 +967,7 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error {
 func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
 	return v.UnmarshalExact(rawVal, opts...)
 }
+
 func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
 	config := defaultDecoderConfig(rawVal, opts...)
 	config.ErrorUnused = true
@@ -950,6 +978,7 @@ func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption)
 // BindPFlags binds a full flag set to the configuration, using each flag's long
 // name as the config key.
 func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
+
 func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
 	return v.BindFlagValues(pflagValueSet{flags})
 }
@@ -961,13 +990,18 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
 //	 Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
 //
 func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
+
 func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
+	if flag == nil {
+		return fmt.Errorf("flag for %q is nil", key)
+	}
 	return v.BindFlagValue(key, pflagValue{flag})
 }
 
 // BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
 // name as the config key.
 func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
+
 func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
 	flags.VisitAll(func(flag FlagValue) {
 		if err = v.BindFlagValue(flag.Name(), flag); err != nil {
@@ -979,6 +1013,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
 
 // BindFlagValue binds a specific key to a FlagValue.
 func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
+
 func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
 	if flag == nil {
 		return fmt.Errorf("flag for %q is nil", key)
@@ -990,24 +1025,24 @@ func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
 // BindEnv binds a Viper key to a ENV variable.
 // ENV variables are case sensitive.
 // If only a key is provided, it will use the env key matching the key, uppercased.
+// If more arguments are provided, they will represent the env variable names that
+// should bind to this key and will be taken in the specified order.
 // EnvPrefix will be used when set when env name is not provided.
 func BindEnv(input ...string) error { return v.BindEnv(input...) }
+
 func (v *Viper) BindEnv(input ...string) error {
-	var key, envkey string
 	if len(input) == 0 {
 		return fmt.Errorf("missing key to bind to")
 	}
 
-	key = strings.ToLower(input[0])
+	key := strings.ToLower(input[0])
 
 	if len(input) == 1 {
-		envkey = v.mergeWithEnvPrefix(key)
+		v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
 	} else {
-		envkey = input[1]
+		v.env[key] = append(v.env[key], input[1:]...)
 	}
 
-	v.env[key] = envkey
-
 	return nil
 }
 
@@ -1086,10 +1121,12 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
 			return nil
 		}
 	}
-	envkey, exists := v.env[lcaseKey]
+	envkeys, exists := v.env[lcaseKey]
 	if exists {
-		if val, ok := v.getEnv(envkey); ok {
-			return val
+		for _, envkey := range envkeys {
+			if val, ok := v.getEnv(envkey); ok {
+				return val
+			}
 		}
 	}
 	if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
@@ -1190,6 +1227,7 @@ func stringToStringConv(val string) interface{} {
 // IsSet checks to see if the key has been set in any of the data locations.
 // IsSet is case-insensitive for a key.
 func IsSet(key string) bool { return v.IsSet(key) }
+
 func (v *Viper) IsSet(key string) bool {
 	lcaseKey := strings.ToLower(key)
 	val := v.find(lcaseKey, false)
@@ -1199,6 +1237,7 @@ func (v *Viper) IsSet(key string) bool {
 // AutomaticEnv has Viper check ENV variables for all.
 // keys set in config, default & flags
 func AutomaticEnv() { v.AutomaticEnv() }
+
 func (v *Viper) AutomaticEnv() {
 	v.automaticEnvApplied = true
 }
@@ -1207,6 +1246,7 @@ func (v *Viper) AutomaticEnv() {
 // Useful for mapping an environmental variable to a key that does
 // not match it.
 func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
+
 func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
 	v.envKeyReplacer = r
 }
@@ -1214,6 +1254,7 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
 // RegisterAlias creates an alias that provides another accessor for the same key.
 // This enables one to change a name without breaking the application.
 func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
+
 func (v *Viper) RegisterAlias(alias string, key string) {
 	v.registerAlias(alias, strings.ToLower(key))
 }
@@ -1261,6 +1302,7 @@ func (v *Viper) realKey(key string) string {
 
 // InConfig checks to see if the given key (or an alias) is in the config file.
 func InConfig(key string) bool { return v.InConfig(key) }
+
 func (v *Viper) InConfig(key string) bool {
 	// if the requested key is an alias, then return the proper key
 	key = v.realKey(key)
@@ -1273,6 +1315,7 @@ func (v *Viper) InConfig(key string) bool {
 // SetDefault is case-insensitive for a key.
 // Default only used when no value is provided by the user via flag, config or ENV.
 func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
+
 func (v *Viper) SetDefault(key string, value interface{}) {
 	// If alias passed in, then set the proper default
 	key = v.realKey(strings.ToLower(key))
@@ -1291,6 +1334,7 @@ func (v *Viper) SetDefault(key string, value interface{}) {
 // Will be used instead of values obtained via
 // flags, config file, ENV, default, or key/value store.
 func Set(key string, value interface{}) { v.Set(key, value) }
+
 func (v *Viper) Set(key string, value interface{}) {
 	// If alias passed in, then set the proper override
 	key = v.realKey(strings.ToLower(key))
@@ -1307,6 +1351,7 @@ func (v *Viper) Set(key string, value interface{}) {
 // ReadInConfig will discover and load the configuration file from disk
 // and key/value stores, searching in one of the defined paths.
 func ReadInConfig() error { return v.ReadInConfig() }
+
 func (v *Viper) ReadInConfig() error {
 	jww.INFO.Println("Attempting to read in config file")
 	filename, err := v.getConfigFile()
@@ -1337,6 +1382,7 @@ func (v *Viper) ReadInConfig() error {
 
 // MergeInConfig merges a new configuration with an existing config.
 func MergeInConfig() error { return v.MergeInConfig() }
+
 func (v *Viper) MergeInConfig() error {
 	jww.INFO.Println("Attempting to merge in config file")
 	filename, err := v.getConfigFile()
@@ -1359,6 +1405,7 @@ func (v *Viper) MergeInConfig() error {
 // ReadConfig will read a configuration file, setting existing keys to nil if the
 // key does not exist in the file.
 func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
+
 func (v *Viper) ReadConfig(in io.Reader) error {
 	v.config = make(map[string]interface{})
 	return v.unmarshalReader(in, v.config)
@@ -1366,6 +1413,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
 
 // MergeConfig merges a new configuration with an existing config.
 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
+
 func (v *Viper) MergeConfig(in io.Reader) error {
 	cfg := make(map[string]interface{})
 	if err := v.unmarshalReader(in, cfg); err != nil {
@@ -1377,6 +1425,7 @@ func (v *Viper) MergeConfig(in io.Reader) error {
 // MergeConfigMap merges the configuration from the map given with an existing config.
 // Note that the map given may be modified.
 func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
+
 func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
 	if v.config == nil {
 		v.config = make(map[string]interface{})
@@ -1388,6 +1437,7 @@ func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
 
 // WriteConfig writes the current configuration to a file.
 func WriteConfig() error { return v.WriteConfig() }
+
 func (v *Viper) WriteConfig() error {
 	filename, err := v.getConfigFile()
 	if err != nil {
@@ -1398,6 +1448,7 @@ func (v *Viper) WriteConfig() error {
 
 // SafeWriteConfig writes current configuration to file only if the file does not exist.
 func SafeWriteConfig() error { return v.SafeWriteConfig() }
+
 func (v *Viper) SafeWriteConfig() error {
 	if len(v.configPaths) < 1 {
 		return errors.New("missing configuration for 'configPath'")
@@ -1407,12 +1458,14 @@ func (v *Viper) SafeWriteConfig() error {
 
 // WriteConfigAs writes current configuration to a given filename.
 func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
+
 func (v *Viper) WriteConfigAs(filename string) error {
 	return v.writeConfig(filename, true)
 }
 
 // SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
 func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }
+
 func (v *Viper) SafeWriteConfigAs(filename string) error {
 	alreadyExists, err := afero.Exists(v.fs, filename)
 	if alreadyExists && err == nil {
@@ -1463,6 +1516,7 @@ func (v *Viper) writeConfig(filename string, force bool) error {
 func unmarshalReader(in io.Reader, c map[string]interface{}) error {
 	return v.unmarshalReader(in, c)
 }
+
 func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
 	buf := new(bytes.Buffer)
 	buf.ReadFrom(in)
@@ -1658,6 +1712,14 @@ func castToMapStringInterface(
 	return tgt
 }
 
+func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} {
+	tgt := map[string]interface{}{}
+	for k, v := range src {
+		tgt[k] = v
+	}
+	return tgt
+}
+
 func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
 	tgt := map[string]interface{}{}
 	for k, v := range src {
@@ -1737,6 +1799,7 @@ func mergeMaps(
 // ReadRemoteConfig attempts to get configuration from a remote source
 // and read it in the remote configuration registry.
 func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
+
 func (v *Viper) ReadRemoteConfig() error {
 	return v.getKeyValueConfig()
 }
@@ -1822,13 +1885,14 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface
 // AllKeys returns all keys holding a value, regardless of where they are set.
 // Nested keys are returned with a v.keyDelim separator
 func AllKeys() []string { return v.AllKeys() }
+
 func (v *Viper) AllKeys() []string {
 	m := map[string]bool{}
 	// add all paths, by order of descending priority to ensure correct shadowing
 	m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
 	m = v.flattenAndMergeMap(m, v.override, "")
 	m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
-	m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env))
+	m = v.mergeFlatMap(m, castMapStringSliceToMapInterface(v.env))
 	m = v.flattenAndMergeMap(m, v.config, "")
 	m = v.flattenAndMergeMap(m, v.kvstore, "")
 	m = v.flattenAndMergeMap(m, v.defaults, "")
@@ -1902,6 +1966,7 @@ outer:
 
 // AllSettings merges all settings and returns them as a map[string]interface{}.
 func AllSettings() map[string]interface{} { return v.AllSettings() }
+
 func (v *Viper) AllSettings() map[string]interface{} {
 	m := map[string]interface{}{}
 	// start from the list of keys, and construct the map one value at a time
@@ -1923,6 +1988,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
 
 // SetFs sets the filesystem to use to read configuration.
 func SetFs(fs afero.Fs) { v.SetFs(fs) }
+
 func (v *Viper) SetFs(fs afero.Fs) {
 	v.fs = fs
 }
@@ -1930,6 +1996,7 @@ func (v *Viper) SetFs(fs afero.Fs) {
 // SetConfigName sets name for the config file.
 // Does not include extension.
 func SetConfigName(in string) { v.SetConfigName(in) }
+
 func (v *Viper) SetConfigName(in string) {
 	if in != "" {
 		v.configName = in
@@ -1940,6 +2007,7 @@ func (v *Viper) SetConfigName(in string) {
 // SetConfigType sets the type of the configuration returned by the
 // remote source, e.g. "json".
 func SetConfigType(in string) { v.SetConfigType(in) }
+
 func (v *Viper) SetConfigType(in string) {
 	if in != "" {
 		v.configType = in
@@ -1948,6 +2016,7 @@ func (v *Viper) SetConfigType(in string) {
 
 // SetConfigPermissions sets the permissions for the config file.
 func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) }
+
 func (v *Viper) SetConfigPermissions(perm os.FileMode) {
 	v.configPermissions = perm.Perm()
 }
@@ -2018,6 +2087,7 @@ func (v *Viper) findConfigFile() (string, error) {
 // Debug prints all configuration registries for debugging
 // purposes.
 func Debug() { v.Debug() }
+
 func (v *Viper) Debug() {
 	fmt.Printf("Aliases:\n%#v\n", v.aliases)
 	fmt.Printf("Override:\n%#v\n", v.override)


Test patch:
diff --git a/flags_test.go b/flags_test.go
index 0b976b6..a22e31b 100644
--- a/flags_test.go
+++ b/flags_test.go
@@ -10,13 +10,13 @@ import (
 func TestBindFlagValueSet(t *testing.T) {
 	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
-	var testValues = map[string]*string{
+	testValues := map[string]*string{
 		"host":     nil,
 		"port":     nil,
 		"endpoint": nil,
 	}
 
-	var mutatedTestValues = map[string]string{
+	mutatedTestValues := map[string]string{
 		"host":     "localhost",
 		"port":     "6060",
 		"endpoint": "/public",
@@ -44,8 +44,8 @@ func TestBindFlagValueSet(t *testing.T) {
 }
 
 func TestBindFlagValue(t *testing.T) {
-	var testString = "testing"
-	var testValue = newStringValue(testString, &testString)
+	testString := "testing"
+	testValue := newStringValue(testString, &testString)
 
 	flag := &pflag.Flag{
 		Name:    "testflag",
@@ -59,7 +59,7 @@ func TestBindFlagValue(t *testing.T) {
 	assert.Equal(t, testString, Get("testvalue"))
 
 	flag.Value.Set("testing_mutate")
-	flag.Changed = true //hack for pflag usage
+	flag.Changed = true // hack for pflag usage
 
 	assert.Equal(t, "testing_mutate", Get("testvalue"))
 }
diff --git a/overrides_test.go b/overrides_test.go
index dd2aa9b..8048204 100644
--- a/overrides_test.go
+++ b/overrides_test.go
@@ -78,6 +78,7 @@ func TestNestedOverrides(t *testing.T) {
 func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
 	return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
 }
+
 func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
 	return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
 }
diff --git a/util_test.go b/util_test.go
index 01d932d..8c98704 100644
--- a/util_test.go
+++ b/util_test.go
@@ -24,14 +24,16 @@ func TestCopyAndInsensitiviseMap(t *testing.T) {
 			"Bar": map[interface{}]interface {
 			}{
 				"ABc": "A",
-				"cDE": "B"},
+				"cDE": "B",
+			},
 		}
 		expected = map[string]interface{}{
 			"foo": 32,
 			"bar": map[string]interface {
 			}{
 				"abc": "A",
-				"cde": "B"},
+				"cde": "B",
+			},
 		}
 	)
 
diff --git a/viper_test.go b/viper_test.go
index fe942de..28c7f13 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -26,7 +26,6 @@ import (
 	"github.com/mitchellh/mapstructure"
 	"github.com/spf13/afero"
 	"github.com/spf13/cast"
-
 	"github.com/spf13/pflag"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -487,10 +486,11 @@ func TestEnv(t *testing.T) {
 	initJSON()
 
 	BindEnv("id")
-	BindEnv("f", "FOOD")
+	BindEnv("f", "FOOD", "OLD_FOOD")
 
 	os.Setenv("ID", "13")
 	os.Setenv("FOOD", "apple")
+	os.Setenv("OLD_FOOD", "banana")
 	os.Setenv("NAME", "crunk")
 
 	assert.Equal(t, "13", Get("id"))
@@ -502,6 +502,17 @@ func TestEnv(t *testing.T) {
 	assert.Equal(t, "crunk", Get("name"))
 }
 
+func TestMultipleEnv(t *testing.T) {
+	initJSON()
+
+	BindEnv("f", "FOOD", "OLD_FOOD")
+
+	os.Unsetenv("FOOD")
+	os.Setenv("OLD_FOOD", "banana")
+
+	assert.Equal(t, "banana", Get("f"))
+}
+
 func TestEmptyEnv(t *testing.T) {
 	initJSON()
 
@@ -694,7 +705,8 @@ func TestAllKeys(t *testing.T) {
 					{"key": 1},
 					{"key": 2},
 					{"key": 3},
-					{"key": 4}},
+					{"key": 4},
+				},
 			},
 		},
 		"title_dotenv": "DotEnv Example",
@@ -828,13 +840,13 @@ func TestBindPFlags(t *testing.T) {
 	v := New() // create independent Viper object
 	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
-	var testValues = map[string]*string{
+	testValues := map[string]*string{
 		"host":     nil,
 		"port":     nil,
 		"endpoint": nil,
 	}
 
-	var mutatedTestValues = map[string]string{
+	mutatedTestValues := map[string]string{
 		"host":     "localhost",
 		"port":     "6060",
 		"endpoint": "/public",
@@ -951,8 +963,8 @@ func TestBindPFlagsIntSlice(t *testing.T) {
 }
 
 func TestBindPFlag(t *testing.T) {
-	var testString = "testing"
-	var testValue = newStringValue(testString, &testString)
+	testString := "testing"
+	testValue := newStringValue(testString, &testString)
 
 	flag := &pflag.Flag{
 		Name:    "testflag",
@@ -970,6 +982,11 @@ func TestBindPFlag(t *testing.T) {
 	assert.Equal(t, "testing_mutate", Get("testvalue"))
 }
 
+func TestBindPFlagDetectNilFlag(t *testing.T) {
+	result := BindPFlag("testvalue", nil)
+	assert.Error(t, result)
+}
+
 func TestBindPFlagStringToString(t *testing.T) {
 	tests := []struct {
 		Expected map[string]string
@@ -1025,8 +1042,8 @@ func TestBoundCaseSensitivity(t *testing.T) {
 
 	assert.Equal(t, "blue", Get("eyes"))
 
-	var testString = "green"
-	var testValue = newStringValue(testString, &testString)
+	testString := "green"
+	testValue := newStringValue(testString, &testString)
 
 	flag := &pflag.Flag{
 		Name:    "eyeballs",
@@ -1105,9 +1122,11 @@ func TestFindsNestedKeys(t *testing.T) {
 				},
 				map[string]interface{}{
 					"type": "Chocolate",
-				}, map[string]interface{}{
+				},
+				map[string]interface{}{
 					"type": "Blueberry",
-				}, map[string]interface{}{
+				},
+				map[string]interface{}{
 					"type": "Devil's Food",
 				},
 			},
@@ -1972,7 +1991,8 @@ func TestCaseInsensitiveSet(t *testing.T) {
 		"Bar": map[interface{}]interface {
 		}{
 			"ABc": "A",
-			"cDE": "B"},
+			"cDE": "B",
+		},
 	}
 
 	m2 := map[string]interface{}{
@@ -1980,7 +2000,8 @@ func TestCaseInsensitiveSet(t *testing.T) {
 		"Bar": map[interface{}]interface {
 		}{
 			"bCd": "A",
-			"eFG": "B"},
+			"eFG": "B",
+		},
 	}
 
 	Set("Given1", m1)