package cmd import ( "context" "errors" "fmt" "path/filepath" "strings" "github.com/creachadair/tomledit" "github.com/creachadair/tomledit/parser" "github.com/creachadair/tomledit/transform" "github.com/spf13/cobra" "cosmossdk.io/tools/confix" "github.com/cosmos/cosmos-sdk/client" ) // SetCommand returns a CLI command to interactively update an application config value. func SetCommand() *cobra.Command { cmd := &cobra.Command{ Use: "set ", Short: "Set an application config value", Long: "Set an application config value. The [config] argument must be the path of the file when using the `confix` tool standalone, otherwise it must be the name of the config file without the .toml extension.", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { filename, inputValue := args[0], args[2] // parse key e.g mempool.size -> [mempool, size] key := strings.Split(args[1], ".") clientCtx := client.GetClientContextFromCmd(cmd) if clientCtx.HomeDir != "" { filename = filepath.Join(clientCtx.HomeDir, "config", filename+tomlSuffix) } plan := transform.Plan{ { Desc: fmt.Sprintf("update %q=%q in %s", key, inputValue, filename), T: transform.Func(func(_ context.Context, doc *tomledit.Document) error { results := doc.Find(key...) if len(results) == 0 { return fmt.Errorf("key %q not found", key) } else if len(results) > 1 { return fmt.Errorf("key %q is ambiguous", key) } value, err := parser.ParseValue(inputValue) if err != nil { value = parser.MustValue(`"` + inputValue + `"`) } if ok := transform.InsertMapping(results[0].Section, &parser.KeyValue{ Block: results[0].Block, Name: results[0].Name, Value: value, }, true); !ok { return errors.New("failed to set value") } return nil }), }, } outputPath := filename if FlagStdOut { outputPath = "" } ctx := cmd.Context() if FlagVerbose { ctx = confix.WithLogWriter(ctx, cmd.ErrOrStderr()) } doc, err := confix.LoadConfig(filename) if err != nil { return err } return confix.Upgrade(ctx, plan, doc, filename, outputPath, FlagSkipValidate) }, } cmd.Flags().BoolVar(&FlagStdOut, "stdout", false, "print the updated config to stdout") cmd.Flags().BoolVarP(&FlagVerbose, "verbose", "v", false, "log changes to stderr") cmd.Flags().BoolVarP(&FlagSkipValidate, "skip-validate", "s", false, "skip configuration validation (allows to mutate unknown configurations)") return cmd } // GetCommand returns a CLI command to interactively get an application config value. func GetCommand() *cobra.Command { cmd := &cobra.Command{ Use: "get ", Short: "Get an application config value", Long: "Get an application config value. The [config] argument must be the path of the file when using the `confix` tool standalone, otherwise it must be the name of the config file without the .toml extension.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { filename, key := args[0], args[1] // parse key e.g mempool.size -> [mempool, size] keys := strings.Split(key, ".") clientCtx := client.GetClientContextFromCmd(cmd) if clientCtx.HomeDir != "" { filename = filepath.Join(clientCtx.HomeDir, "config", filename+tomlSuffix) } doc, err := confix.LoadConfig(filename) if err != nil { return fmt.Errorf("failed to load config: %w", err) } results := doc.Find(keys...) if len(results) == 0 { return fmt.Errorf("key %q not found", key) } else if len(results) > 1 { return fmt.Errorf("key %q is ambiguous", key) } return clientCtx.PrintString(fmt.Sprintf("%s\n", results[0].Value.String())) }, } return cmd }