Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backport/v0.41.x]: fix: bind values from env variables to flags #8488

Merged
merged 1 commit into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/core/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ Flags are added to commands directly (generally in the [module's CLI file](../bu

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L118

## Environment variables

Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows to reduce amount of flags typed for routine operations. For example instead of:
```sh
gaia --home=./ --node=<node address> --chain-id="testchain-1" --keyring-backend=test tx ... --from=<key name>
```
this will be more convinient:
```sh
# define env variables in .env, .envrc etc
GAIA_HOME=<path to home>
GAIA_NODE=<node address>
GAIA_CHAIN_ID="testchain-1"
GAIA_KEYRING_BACKEND="test"

# and later just use
gaia tx ... --from=<key name>
```

## Configurations

It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary.
Expand Down
35 changes: 35 additions & 0 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
tmcfg "github.com/tendermint/tendermint/config"
tmlog "github.com/tendermint/tendermint/libs/log"
Expand Down Expand Up @@ -63,6 +64,37 @@ func NewContext(v *viper.Viper, config *tmcfg.Config, logger tmlog.Logger) *Cont
return &Context{v, config, logger}
}

func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) {
defer func() {
recover()
}()

cmd.Flags().VisitAll(func(f *pflag.Flag) {
// Environment variables can't have dashes in them, so bind them to their equivalent
// keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR
err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))))
if err != nil {
panic(err)
}

err = v.BindPFlag(f.Name, f)
if err != nil {
panic(err)
}

// Apply the viper config value to the flag when the flag is not set and viper has a value
if !f.Changed && v.IsSet(f.Name) {
val := v.Get(f.Name)
err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
if err != nil {
panic(err)
}
}
})

return
}

// InterceptConfigsPreRunHandler performs a pre-run function for the root daemon
// application command. It will create a Viper literal and a default server
// Context. The server Tendermint configuration will either be read and parsed
Expand Down Expand Up @@ -98,6 +130,9 @@ func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {

// return value is a tendermint configuration object
serverCtx.Config = config
if err = bindFlags(basename, cmd, serverCtx.Viper); err != nil {
return err
}

var logWriter io.Writer
if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain {
Expand Down