Skip to content

Commit

Permalink
feat: overhaul gnoland secrets and gnoland config to output JSON (#…
Browse files Browse the repository at this point in the history
…2393)

## Description

This PR overhauls the `gnoland secrets get` and `gnoland config get`
commands to:
- always output JSON by default
- support key paths for specific fields
- have `-raw` support for single value JSON (escapes `"`)

Thank you @moul for the suggestion on this 🙏 


![sample](https://github.com/gnolang/gno/assets/16712663/30c87b02-1c77-4134-a3b0-88b95687e292)

cc @albttx @sw360cab @r3v4s @mazzy89 

<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
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] 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
zivkovicmilos authored Jun 24, 2024
1 parent 072aef3 commit 28c3b1a
Show file tree
Hide file tree
Showing 16 changed files with 1,279 additions and 491 deletions.
8 changes: 5 additions & 3 deletions docs/gno-infrastructure/setting-up-a-local-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ In this tutorial, you will learn how to start a local Gno node (and chain!).
Additionally, you will see the different options you can use to make your Gno instance unique.

## Prerequisites

- **Git**
- **`make` (for running Makefiles)**
- **Go 1.21+**
- **Go Environment Setup**: Ensure you have Go set up as outlined in the [Go official installation documentation](https://go.dev/doc/install) for your environment
- **Go Environment Setup**: Ensure you have Go set up as outlined in
the [Go official installation documentation](https://go.dev/doc/install) for your environment

## Installation

Expand Down Expand Up @@ -153,7 +155,7 @@ A couple of things to note:
- `gnoland config init` initializes a default configuration
- `gnoland secrets init` initializes new node secrets (validator key, node p2p key)

Essentially, `gnoland start --lazy` is simply a combination of `gnoland secrets generate` and `gnoland config generate`,
Essentially, `gnoland start --lazy` is simply a combination of `gnoland secrets init` and `gnoland config init`,
with the default options enabled.

#### Changing the node configuration
Expand Down Expand Up @@ -244,7 +246,7 @@ locally will be the validator node for the new Gno network.
To display the generated node key data, run the following command:

```shell
gnoland secrets get ValidatorPrivateKey
gnoland secrets get validator_key
```

This will display the information we need for updating the `genesis.json`:
Expand Down
57 changes: 57 additions & 0 deletions gno.land/cmd/gnoland/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"path/filepath"
Expand Down Expand Up @@ -58,6 +59,62 @@ func constructConfigPath(nodeDir string) string {
)
}

// printKeyValue searches and prints the given key value in JSON
func printKeyValue[T *secrets | *config.Config](
input T,
raw bool,
io commands.IO,
key ...string,
) error {
// prepareOutput prepares the JSON output, taking into account raw mode
prepareOutput := func(input any) (string, error) {
encoded, err := json.MarshalIndent(input, "", " ")
if err != nil {
return "", fmt.Errorf("unable to marshal JSON, %w", err)
}

output := string(encoded)

if raw {
if err := json.Unmarshal(encoded, &output); err != nil {
return "", fmt.Errorf("unable to unmarshal raw JSON, %w", err)
}
}

return output, nil
}

if len(key) == 0 {
// Print the entire input
output, err := prepareOutput(input)
if err != nil {
return err
}

io.Println(output)

return nil
}

// Get the value using reflect
secretValue := reflect.ValueOf(input).Elem()

// Get the value path, with sections separated out by a period
field, err := getFieldAtPath(secretValue, strings.Split(key[0], "."))
if err != nil {
return err
}

output, err := prepareOutput(field.Interface())
if err != nil {
return err
}

io.Println(output)

return nil
}

// 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
Expand Down
52 changes: 25 additions & 27 deletions gno.land/cmd/gnoland/config_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,29 @@ package main
import (
"context"
"errors"
"flag"
"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")

type configGetCfg struct {
configCfg

raw bool
}

// newConfigGetCmd creates the config get command
func newConfigGetCmd(io commands.IO) *commands.Command {
cfg := &configCfg{}
cfg := &configGetCfg{}

cmd := commands.NewCommand(
commands.Metadata{
Name: "get",
ShortUsage: "config get <key>",
ShortUsage: "config get [flags] [<key>]",
ShortHelp: "shows the Gno node configuration",
LongHelp: "Shows the Gno node configuration at the given path " +
"by fetching the option specified at <key>",
Expand All @@ -34,40 +39,33 @@ func newConfigGetCmd(io commands.IO) *commands.Command {
return cmd
}

func execConfigGet(cfg *configCfg, io commands.IO, args []string) error {
func (c *configGetCfg) RegisterFlags(fs *flag.FlagSet) {
c.configCfg.RegisterFlags(fs)

fs.BoolVar(
&c.raw,
"raw",
false,
"output raw string values, rather than as JSON strings",
)
}

func execConfigGet(cfg *configGetCfg, io commands.IO, args []string) error {
// Load the config
loadedCfg, err := config.LoadConfigFile(cfg.configPath)
if err != nil {
return fmt.Errorf("%s, %w", tryConfigInit, err)
}

// Make sure the edit arguments are valid
if len(args) != 1 {
// Make sure the get 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
if err := printKeyValue(loadedCfg, cfg.raw, io, args...); err != nil {
return fmt.Errorf("unable to get config field, %w", err)
}

io.Printf("%v", field.Interface())

return nil
}
Loading

0 comments on commit 28c3b1a

Please sign in to comment.