You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
2.9 KiB

  1. package cli
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. "github.com/spf13/cobra"
  7. "github.com/spf13/viper"
  8. )
  9. const (
  10. HomeFlag = "home"
  11. TraceFlag = "trace"
  12. OutputFlag = "output" // used in the cli
  13. )
  14. // PrepareBaseCmd is meant for tendermint and other servers
  15. func PrepareBaseCmd(cmd *cobra.Command, envPrefix, defaultHome string) *cobra.Command {
  16. // the primary caller of this command is in the SDK and
  17. // returning the cobra.Command object avoids breaking that
  18. // code. In the long term, the SDK could avoid this entirely.
  19. cobra.OnInitialize(func() { InitEnv(envPrefix) })
  20. cmd.PersistentFlags().StringP(HomeFlag, "", defaultHome, "directory for config and data")
  21. cmd.PersistentFlags().Bool(TraceFlag, false, "print out full stack trace on errors")
  22. cmd.PersistentPreRunE = concatCobraCmdFuncs(BindFlagsLoadViper, cmd.PersistentPreRunE)
  23. return cmd
  24. }
  25. // InitEnv sets to use ENV variables if set.
  26. func InitEnv(prefix string) {
  27. // This copies all variables like TMROOT to TM_ROOT,
  28. // so we can support both formats for the user
  29. prefix = strings.ToUpper(prefix)
  30. ps := prefix + "_"
  31. for _, e := range os.Environ() {
  32. kv := strings.SplitN(e, "=", 2)
  33. if len(kv) == 2 {
  34. k, v := kv[0], kv[1]
  35. if strings.HasPrefix(k, prefix) && !strings.HasPrefix(k, ps) {
  36. k2 := strings.Replace(k, prefix, ps, 1)
  37. os.Setenv(k2, v)
  38. }
  39. }
  40. }
  41. // env variables with TM prefix (eg. TM_ROOT)
  42. viper.SetEnvPrefix(prefix)
  43. viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
  44. viper.AutomaticEnv()
  45. }
  46. type cobraCmdFunc func(cmd *cobra.Command, args []string) error
  47. // Returns a single function that calls each argument function in sequence
  48. // RunE, PreRunE, PersistentPreRunE, etc. all have this same signature
  49. func concatCobraCmdFuncs(fs ...cobraCmdFunc) cobraCmdFunc {
  50. return func(cmd *cobra.Command, args []string) error {
  51. for _, f := range fs {
  52. if f != nil {
  53. if err := f(cmd, args); err != nil {
  54. return err
  55. }
  56. }
  57. }
  58. return nil
  59. }
  60. }
  61. // Bind all flags and read the config into viper
  62. func BindFlagsLoadViper(cmd *cobra.Command, args []string) error {
  63. // cmd.Flags() includes flags from this command and all persistent flags from the parent
  64. if err := viper.BindPFlags(cmd.Flags()); err != nil {
  65. return err
  66. }
  67. homeDir := viper.GetString(HomeFlag)
  68. viper.Set(HomeFlag, homeDir)
  69. viper.SetConfigName("config") // name of config file (without extension)
  70. viper.AddConfigPath(homeDir) // search root directory
  71. viper.AddConfigPath(filepath.Join(homeDir, "config")) // search root directory /config
  72. // If a config file is found, read it in.
  73. if err := viper.ReadInConfig(); err == nil {
  74. // stderr, so if we redirect output to json file, this doesn't appear
  75. // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
  76. } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
  77. // ignore not found error, return other errors
  78. return err
  79. }
  80. return nil
  81. }