0

I have a config format that looks like below:

type config struct {
   FooBar string `mapstructure:"foo_bar"`
   BazBot string `mapstructure:"baz_bot"`
}

And my Cobra based CLI has flags with dashes (e.g. --foo-bar=value1 --baz-bot=value2).

Viper does not know how to map these properly into the config when they are set at the CLI for the purpose of overriding the config file value. Is there any solution here? Viper config setup looks like this:

func loadConfig(cmd *cobra.Command) (*config, error) {
    res := &config{}

    if err := viper.BindPFlags(cmd.Flags()); err != nil {
        return nil, err
    }

    viper.SetEnvPrefix("K")
    viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
    viper.AutomaticEnv()

    var useConfigFile bool

    if configFile, _ := cmd.Flags().GetString("config"); configFile != "" {
        useConfigFile = true
        viper.SetConfigFile(configFile)
    }

    if useConfigFile {
        if err := viper.ReadInConfig(); err != nil {
            return nil, err
        }

        if err := viper.Unmarshal(res); err != nil {
            return nil, err
        }
    }

    return res, nil
}

Also open to taking recommendations for a library that does support this if viper cannot do it.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Philip Lombardi
  • 1,276
  • 2
  • 17
  • 26

1 Answers1

0

First of all, you should define the flags. I don't know, based on your example if you do it or not, but you should do something similar (based on this):

    cfg := &config{}
    val := reflect.ValueOf(cfg).Elem()
    for i := 0; i < val.NumField(); i++ {
        fieldType := val.Type().Field(i)
        name := fieldType.Tag.Get("mapstructure")
        rootCmd.PersistentFlags().String(strings.ReplaceAll(name, "_", "-"), "default", "usage")
    }

With this the --help command will show you your available flags (--foo-bar and --baz-bot).
Then you should bind the flags in the loadConfig method:

    cmd.Flags().VisitAll(func(flag *pflag.Flag) {
        if err := viper.BindPFlag(strings.ReplaceAll(flag.Name, "-", "_"), flag); err != nil {
            return
        }
    })

This works for me.

Pongi
  • 59
  • 4