package cli import ( "os" "path/filepath" "strings" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( HomeFlag = "home" TraceFlag = "trace" OutputFlag = "output" // used in the cli ) // PrepareBaseCmd is meant for tendermint and other servers func PrepareBaseCmd(cmd *cobra.Command, envPrefix, defaultHome string) *cobra.Command { // the primary caller of this command is in the SDK and // returning the cobra.Command object avoids breaking that // code. In the long term, the SDK could avoid this entirely. cobra.OnInitialize(func() { InitEnv(envPrefix) }) cmd.PersistentFlags().StringP(HomeFlag, "", defaultHome, "directory for config and data") cmd.PersistentFlags().Bool(TraceFlag, false, "print out full stack trace on errors") cmd.PersistentPreRunE = concatCobraCmdFuncs(BindFlagsLoadViper, cmd.PersistentPreRunE) return cmd } // InitEnv sets to use ENV variables if set. func InitEnv(prefix string) { // This copies all variables like TMROOT to TM_ROOT, // so we can support both formats for the user prefix = strings.ToUpper(prefix) ps := prefix + "_" for _, e := range os.Environ() { kv := strings.SplitN(e, "=", 2) if len(kv) == 2 { k, v := kv[0], kv[1] if strings.HasPrefix(k, prefix) && !strings.HasPrefix(k, ps) { k2 := strings.Replace(k, prefix, ps, 1) os.Setenv(k2, v) } } } // env variables with TM prefix (eg. TM_ROOT) viper.SetEnvPrefix(prefix) viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) viper.AutomaticEnv() } type cobraCmdFunc func(cmd *cobra.Command, args []string) error // Returns a single function that calls each argument function in sequence // RunE, PreRunE, PersistentPreRunE, etc. all have this same signature func concatCobraCmdFuncs(fs ...cobraCmdFunc) cobraCmdFunc { return func(cmd *cobra.Command, args []string) error { for _, f := range fs { if f != nil { if err := f(cmd, args); err != nil { return err } } } return nil } } // Bind all flags and read the config into viper func BindFlagsLoadViper(cmd *cobra.Command, args []string) error { // cmd.Flags() includes flags from this command and all persistent flags from the parent if err := viper.BindPFlags(cmd.Flags()); err != nil { return err } homeDir := viper.GetString(HomeFlag) viper.Set(HomeFlag, homeDir) viper.SetConfigName("config") // name of config file (without extension) viper.AddConfigPath(homeDir) // search root directory viper.AddConfigPath(filepath.Join(homeDir, "config")) // search root directory /config // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { // stderr, so if we redirect output to json file, this doesn't appear // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok { // ignore not found error, return other errors return err } return nil }