Separate out PrepareBaseCmd, try to set env vars
This commit is contained in:
parent
f7c872981f
commit
b92bd8f6a8
|
@ -0,0 +1,136 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
data "github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/go-wire/data/base58"
|
||||
)
|
||||
|
||||
const (
|
||||
RootFlag = "root"
|
||||
OutputFlag = "output"
|
||||
EncodingFlag = "encoding"
|
||||
)
|
||||
|
||||
// PrepareBaseCmd is meant for tendermint and other servers
|
||||
func PrepareBaseCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() {
|
||||
cobra.OnInitialize(func() { initEnv(envPrefix) })
|
||||
cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data")
|
||||
cmd.PersistentPreRunE = multiE(bindFlags, cmd.PersistentPreRunE)
|
||||
return func() { execute(cmd) }
|
||||
}
|
||||
|
||||
// PrepareMainCmd is meant for client side libs that want some more flags
|
||||
func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() {
|
||||
cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)")
|
||||
cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)")
|
||||
cmd.PersistentPreRunE = multiE(setEncoding, validateOutput, cmd.PersistentPreRunE)
|
||||
return PrepareBaseCmd(cmd, envPrefix, defautRoot)
|
||||
}
|
||||
|
||||
// initEnv sets to use ENV variables if set.
|
||||
func initEnv(prefix string) {
|
||||
copyEnvVars(prefix)
|
||||
|
||||
// env variables with TM prefix (eg. TM_ROOT)
|
||||
viper.SetEnvPrefix(prefix)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
// This copies all variables like TMROOT to TM_ROOT,
|
||||
// so we can support both formats for the user
|
||||
func copyEnvVars(prefix string) {
|
||||
prefix = strings.ToUpper(prefix)
|
||||
ps := prefix + "_"
|
||||
for _, e := range os.Environ() {
|
||||
kv := strings.SplitN(e, "=", 1)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// execute adds all child commands to the root command sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func execute(cmd *cobra.Command) {
|
||||
// TODO: this can do something cooler with debug and log-levels
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
type wrapE func(cmd *cobra.Command, args []string) error
|
||||
|
||||
func multiE(fs ...wrapE) wrapE {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func bindFlags(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
|
||||
}
|
||||
|
||||
// rootDir is command line flag, env variable, or default $HOME/.tlc
|
||||
rootDir := viper.GetString("root")
|
||||
viper.SetConfigName("config") // name of config file (without extension)
|
||||
viper.AddConfigPath(rootDir) // search root directory
|
||||
|
||||
// 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 {
|
||||
// we ignore not found error, only parse error
|
||||
// stderr, so if we redirect output to json file, this doesn't appear
|
||||
fmt.Fprintf(os.Stderr, "%#v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setEncoding reads the encoding flag
|
||||
func setEncoding(cmd *cobra.Command, args []string) error {
|
||||
// validate and set encoding
|
||||
enc := viper.GetString("encoding")
|
||||
switch enc {
|
||||
case "hex":
|
||||
data.Encoder = data.HexEncoder
|
||||
case "b64":
|
||||
data.Encoder = data.B64Encoder
|
||||
case "btc":
|
||||
data.Encoder = base58.BTCEncoder
|
||||
default:
|
||||
return errors.Errorf("Unsupported encoding: %s", enc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateOutput(cmd *cobra.Command, args []string) error {
|
||||
// validate output format
|
||||
output := viper.GetString(OutputFlag)
|
||||
switch output {
|
||||
case "text", "json":
|
||||
default:
|
||||
return errors.Errorf("Unsupported output format: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue