forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
gnoland config
command suite (gnolang#1605)
## Description Based on discussions in gnolang#1593, this PR introduces the CLI suite for manipulating the `config.toml`. Using this suite, we can build better workflows for power users. This PR is a series of lego blocks that are required for us to get a normal user chain connection going: - gnolang#1252 - solved `genesis.json` management and manipulation - gnolang#1593 - solved node secrets management and manipulation - this PR - solves `config.toml` management and manipulation All of these PRs get us to a point where the user can run: - `gnoland init` - `gnoland start` to get up and running with any Gno network. The added middle step is fine-tuning the configuration and genesis, but it's worth noting this step is optional. New commands: ```shell gnoland config --help USAGE config <subcommand> [flags] Gno config manipulation suite, for editing base and module configurations SUBCOMMANDS init Initializes the Gno node configuration set Edits the Gno node configuration get Shows the Gno node configuration ``` In short, the `gnoland config init` command initializes a default `config.toml`, while other subcommands allow editing viewing any field in the specific configuration. <details><summary>Contributors' checklist...</summary> - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details>
- Loading branch information
1 parent
764364a
commit 4c9d10c
Showing
13 changed files
with
2,027 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/gnolang/gno/tm2/pkg/commands" | ||
) | ||
|
||
type configCfg struct { | ||
configPath string | ||
} | ||
|
||
// newConfigCmd creates the config root command | ||
func newConfigCmd(io commands.IO) *commands.Command { | ||
cmd := commands.NewCommand( | ||
commands.Metadata{ | ||
Name: "config", | ||
ShortUsage: "config <subcommand> [flags]", | ||
ShortHelp: "gno config manipulation suite", | ||
LongHelp: "Gno config manipulation suite, for editing base and module configurations", | ||
}, | ||
commands.NewEmptyConfig(), | ||
commands.HelpExec, | ||
) | ||
|
||
cmd.AddSubCommands( | ||
newConfigInitCmd(io), | ||
newConfigSetCmd(io), | ||
newConfigGetCmd(io), | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func (c *configCfg) RegisterFlags(fs *flag.FlagSet) { | ||
fs.StringVar( | ||
&c.configPath, | ||
"config-path", | ||
"./config.toml", | ||
"the path for the config.toml", | ||
) | ||
} | ||
|
||
// getFieldAtPath fetches the given field from the given path | ||
func getFieldAtPath(currentValue reflect.Value, path []string) (*reflect.Value, error) { | ||
// Look at the current section, and figure out if | ||
// it's a part of the current struct | ||
field := currentValue.FieldByName(path[0]) | ||
if !field.IsValid() || !field.CanSet() { | ||
return nil, newInvalidFieldError(path[0], currentValue) | ||
} | ||
|
||
// Dereference the field if needed | ||
if field.Kind() == reflect.Ptr { | ||
field = field.Elem() | ||
} | ||
|
||
// Check if this is not the end of the path | ||
// ex: x.y.field | ||
if len(path) > 1 { | ||
// Recursively try to traverse the path and return the given field | ||
return getFieldAtPath(field, path[1:]) | ||
} | ||
|
||
return &field, nil | ||
} | ||
|
||
// newInvalidFieldError creates an error for non-existent struct fields | ||
// being passed as arguments to [getFieldAtPath] | ||
func newInvalidFieldError(field string, value reflect.Value) error { | ||
var ( | ||
valueType = value.Type() | ||
numFields = value.NumField() | ||
) | ||
|
||
fields := make([]string, 0, numFields) | ||
|
||
for i := 0; i < numFields; i++ { | ||
valueField := valueType.Field(i) | ||
if !valueField.IsExported() { | ||
continue | ||
} | ||
|
||
fields = append(fields, valueField.Name) | ||
} | ||
|
||
return fmt.Errorf( | ||
"field %q, is not a valid configuration key, available keys: %s", | ||
field, | ||
fields, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
|
||
"github.com/gnolang/gno/tm2/pkg/bft/config" | ||
"github.com/gnolang/gno/tm2/pkg/commands" | ||
) | ||
|
||
var errInvalidConfigGetArgs = errors.New("invalid number of config get arguments provided") | ||
|
||
// newConfigGetCmd creates the config get command | ||
func newConfigGetCmd(io commands.IO) *commands.Command { | ||
cfg := &configCfg{} | ||
|
||
cmd := commands.NewCommand( | ||
commands.Metadata{ | ||
Name: "get", | ||
ShortUsage: "config get <key>", | ||
ShortHelp: "shows the Gno node configuration", | ||
LongHelp: "Shows the Gno node configuration at the given path " + | ||
"by fetching the option specified at <key>", | ||
}, | ||
cfg, | ||
func(_ context.Context, args []string) error { | ||
return execConfigGet(cfg, io, args) | ||
}, | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func execConfigGet(cfg *configCfg, io commands.IO, args []string) error { | ||
// Load the config | ||
loadedCfg, err := config.LoadConfigFile(cfg.configPath) | ||
if err != nil { | ||
return fmt.Errorf("unable to load config, %w", err) | ||
} | ||
|
||
// Make sure the edit arguments are valid | ||
if len(args) != 1 { | ||
return errInvalidConfigGetArgs | ||
} | ||
|
||
// Find and print the config field, if any | ||
if err := printConfigField(loadedCfg, args[0], io); err != nil { | ||
return fmt.Errorf("unable to update config field, %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// printConfigField prints the value of the field at the given path | ||
func printConfigField(config *config.Config, key string, io commands.IO) error { | ||
// Get the config value using reflect | ||
configValue := reflect.ValueOf(config).Elem() | ||
|
||
// Get the value path, with sections separated out by a period | ||
path := strings.Split(key, ".") | ||
|
||
field, err := getFieldAtPath(configValue, path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
io.Printf("%v", field.Interface()) | ||
|
||
return nil | ||
} |
Oops, something went wrong.