Reputation: 91660
I'm trying to complete a very simple example wherein I use viper
to load configuration from environment variables. I have installed it in my project via go get github.com/spf13/viper
, and the dependency is in my go.mod
:
module github.com/naftulikay/viper-demo
go 1.16
require github.com/spf13/viper v1.7.1 // indirect
Here is my code, which does compile and run:
package main
import (
"fmt"
"github.com/spf13/viper"
)
var config Config
type Config struct {
MyString string //`mapstructure:"MY_STRING"`
}
func main() {
v := viper.New()
v.AutomaticEnv()
var err error
err = v.Unmarshal(&config)
if err != nil {
fmt.Printf("Unable to load config: %s\n", err)
}
fmt.Printf("%+v\n", &config)
}
I am setting environment variables to try to test different permutations of the environment variable name
MY_STRING=a my_string=b MyString=c ./viper-demo
I've tried a lot of different things, removing the mapstructure
annotation, changing its value, etc., but all I get when I execute is:
&{MyString:}
I'm following the instructions from the README as well as other demos I've found online and it just seems like viper
does not load environment variables.
I feel like this is a very simple example, my requirements are mapping config to a struct, and fetching that config from environment variables. I may eventually start doing CLI flags and a config file, but environment variables are make-or-break for me, I need them to work.
What am I missing?
Upvotes: 5
Views: 10926
Reputation: 1
You could do something like this
package main
import (
"reflect"
"github.com/spf13/viper"
)
func automaticBindEnv() {
v := reflect.ValueOf(&Config{})
t := v.Elem().Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
env := field.Tag.Get("mapstructure")
if env == "" {
continue
}
viper.BindEnv(env)
}
}
Upvotes: 0
Reputation: 8186
A fix has been merged since Dec 2023. You have to enable the feature though [1], e.g.:
go build -tags=viper_bind_struct ...
Upvotes: 1
Reputation: 1
This seems to be a long running issue, for several years now [1]. However, in
that thread it is suggested that you can use BindEnv
[2]:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
var c struct {
TMP string
}
v := viper.New()
v.AutomaticEnv()
v.BindEnv("TMP")
v.Unmarshal(&c)
fmt.Printf("%+v\n", &c)
}
For another approach, you can use os.Environ
[3]:
package main
import (
"fmt"
"os"
"strings"
)
func environ() map[string]string {
m := make(map[string]string)
for _, s := range os.Environ() {
a := strings.Split(s, "=")
m[a[0]] = a[1]
}
return m
}
func main() {
m := environ()
fmt.Printf("%q\n", m)
}
Upvotes: 3