Why is Cobra not reading my configuration file?

To combine spf13/cobra and spf13/viper, first define the flag with Cobra:

RootCmd.PersistentFlags().StringP("foo", "", "loaded from config")

Bind it with Viper:

viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))

And get the variable via the Viper's method:

fmt.Println("fooString is: ", viper.GetString("foo"))

For those facing the same issue, the problem is on this call:

cobra.OnInitialize(initConfig)

The function initConfig is not executed directly but append to an array of initializers https://github.com/spf13/cobra/blob/master/cobra.go#L80

So, the values stored in your config file will be loaded when the Run field is executed:

  rootCmd = &cobra.Command{
    Use:   "example",
    Short: "example cmd",
    Run: func(cmd *cobra.Command, args []string) { // OnInitialize is called first
      fmt.Println(viper.AllKeys())
    },  
  }

Since Viper values are somewhat inferior to pflags (e.g. no support for custom data types), I was not satisfied with answer "use Viper to retrieve values", and ended up writing small helper type to put values back in pflags.

type viperPFlagBinding struct {
        configName string
        flagValue  pflag.Value
}

type viperPFlagHelper struct {
        bindings []viperPFlagBinding
}

func (vch *viperPFlagHelper) BindPFlag(configName string, flag *pflag.Flag) (err error) {
        err = viper.BindPFlag(configName, flag)
        if err == nil {
                vch.bindings = append(vch.bindings, viperPFlagBinding{configName, flag.Value})
        }
        return
}

func (vch *viperPFlagHelper) setPFlagsFromViper() {
        for _, v := range vch.bindings {
                v.flagValue.Set(viper.GetString(v.configName))
        }
}


func main() {
        var rootCmd = &cobra.Command{}
        var viperPFlagHelper viperPFlagHelper

        rootCmd.PersistentFlags().StringVar(&config.Password, "password", "", "API server password (remote HTTPS mode only)")
        viperPFlagHelper.BindPFlag("password", rootCmd.Flag("password"))

        rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
                err := viper.ReadInConfig()
                if err != nil {
                        if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
                                return err
                        }
                }

                viperPFlagHelper.setPFlagsFromViper()

                return nil
        }
}

Tags:

Go

Viper Go

Cobra