From e88a084840cc0c016f64f23f876fb9a0059fc6f5 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 16 Jan 2023 14:27:41 +0100 Subject: [PATCH] feat: add diff and home command in confix (#14568) --- simapp/go.mod | 2 ++ tools/confix/CHANGELOG.md | 1 + tools/confix/README.md | 56 +++++++++++++++++++++++++++++++++++++ tools/confix/cmd/config.go | 1 + tools/confix/cmd/diff.go | 47 ++++++++++++++++++++++++++++--- tools/confix/cmd/home.go | 22 +++++++++++++++ tools/confix/cmd/migrate.go | 3 +- tools/confix/cmd/mutate.go | 2 +- tools/confix/diff.go | 51 +++++++++++++++++++++++++++------ tools/confix/file.go | 34 ++++++++++++++++++++++ tools/confix/migrations.go | 14 ++-------- tools/confix/upgrade.go | 11 -------- tools/cosmovisor/README.md | 6 ++-- 13 files changed, 209 insertions(+), 41 deletions(-) create mode 100644 tools/confix/cmd/home.go create mode 100644 tools/confix/file.go diff --git a/simapp/go.mod b/simapp/go.mod index 9955331616..addfd0e82c 100644 --- a/simapp/go.mod +++ b/simapp/go.mod @@ -185,6 +185,8 @@ require ( ) replace ( + // TODO delete after release of confix + cosmossdk.io/tools/confix => ../tools/confix github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 // Simapp always use the latest version of the cosmos-sdk github.com/cosmos/cosmos-sdk => ../. diff --git a/tools/confix/CHANGELOG.md b/tools/confix/CHANGELOG.md index 1373611f25..902c1f7f60 100644 --- a/tools/confix/CHANGELOG.md +++ b/tools/confix/CHANGELOG.md @@ -31,4 +31,5 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +* [#14568](https://github.com/cosmos/cosmos-sdk/pull/14568) Add `diff` and `home` commands. * [#14342](https://github.com/cosmos/cosmos-sdk/pull/14342) Add `confix` tool to manage configuration files. diff --git a/tools/confix/README.md b/tools/confix/README.md index 3ffec10fbc..9d277d11b9 100644 --- a/tools/confix/README.md +++ b/tools/confix/README.md @@ -10,6 +10,50 @@ It is based on the [Tendermint RFC 019](https://github.com/tendermint/tendermint ## Installation +### Add Config Command + +To add the confix tool, it's required to add the `ConfigCommand` to your application's root command file (e.g. `simd/cmd/root.go`). + +Import the `confixCmd` package: + +```go +import "cosmossdk.io/tools/confix/cmd" +``` + +Find the following line: + +```go +initRootCmd(rootCmd, encodingConfig) +``` + +After that line, add the following: + +```go +rootCmd.AddCommand( + confixcmd.ConfigCommand(), +) +``` + +The `ConfixCommand` function builds the `config` root command and is defined in the `confixCmd` package (`cosmossdk.io/tools/confix/cmd`). +An implementation example can be found in `simapp`. + +The command will be available as `simd config`. + +### Using Confix Standalone + +To use Confix standalone, without having to add it in your application, install it with the following command: + +```bash +go install cosmossdk.io/tools/confix/cmd/confix@latest +``` + +:::warning +Currently, due to the replace directive in the Confix go.mod, it is not possible to use `go install`. +Building from source or importing in an application is required until that replace directive is removed. +::: + +Alternatively, for building from source, simply run `make confix`. The binary will be located in `tools/confix`. + ## Usage Use standalone: @@ -64,6 +108,18 @@ simd config migrate v0.47 # migrates defaultHome/config/app.toml to the latest v confix migrate v0.47 ~/.simapp/config/app.toml # migrate ~/.simapp/config/app.toml to the latest v0.47 config ``` +### Diff + +Get the diff between a given configuration file and the default configuration file, e.g.: + +```shell +simd config diff v0.47 # gets the diff between defaultHome/config/app.toml and the latest v0.47 config +``` + +```shell +confix diff v0.47 ~/.simapp/config/app.toml # gets the diff between ~/.simapp/config/app.toml and the latest v0.47 config +``` + ### Maintainer At each SDK modification of the default configuration, add the default SDK config under `data/v0.XX-app.toml`. diff --git a/tools/confix/cmd/config.go b/tools/confix/cmd/config.go index c47e247736..8ee2e371a9 100644 --- a/tools/confix/cmd/config.go +++ b/tools/confix/cmd/config.go @@ -17,6 +17,7 @@ func ConfigCommand() *cobra.Command { DiffCommand(), GetCommand(), SetCommand(), + HomeCommand(), ) return cmd diff --git a/tools/confix/cmd/diff.go b/tools/confix/cmd/diff.go index a4d491f173..28cd337a2f 100644 --- a/tools/confix/cmd/diff.go +++ b/tools/confix/cmd/diff.go @@ -1,16 +1,55 @@ package cmd import ( + "fmt" + + "cosmossdk.io/tools/confix" + "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" + "golang.org/x/exp/maps" ) func DiffCommand() *cobra.Command { return &cobra.Command{ - Use: "diff [config]", - Short: "Display the diff between the current config and the SDK default config", - Args: cobra.ExactArgs(2), + Use: "diff [target-version] ", + Short: "Outputs all config values that are different from the app.toml defaults.", + Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - // TODO to implement in the next PR + var filename string + clientCtx := client.GetClientContextFromCmd(cmd) + if len(args) > 1 { + filename = args[1] + } else if clientCtx.HomeDir != "" { + filename = fmt.Sprintf("%s/config/app.toml", clientCtx.HomeDir) + } else { + return fmt.Errorf("must provide a path to the app.toml file") + } + + targetVersion := args[0] + if _, ok := confix.Migrations[targetVersion]; !ok { + return fmt.Errorf("unknown version %q, supported versions are: %q", targetVersion, maps.Keys(confix.Migrations)) + } + + targetVersionFile, err := confix.LoadLocalConfig(targetVersion) + if err != nil { + panic(fmt.Errorf("failed to load internal config: %w", err)) + } + + rawFile, err := confix.LoadConfig(filename) + if err != nil { + return fmt.Errorf("failed to load config: %v", err) + } + + diff := confix.DiffValues(rawFile, targetVersionFile) + if len(diff) == 0 { + return clientCtx.PrintString("All config values are the same as the defaults.\n") + } + + if err := clientCtx.PrintString("The following config values are different from the defaults:\n"); err != nil { + return err + } + + confix.PrintDiff(cmd.OutOrStdout(), diff) return nil }, } diff --git a/tools/confix/cmd/home.go b/tools/confix/cmd/home.go new file mode 100644 index 0000000000..21ecdb3884 --- /dev/null +++ b/tools/confix/cmd/home.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +func HomeCommand() *cobra.Command { + return &cobra.Command{ + Use: "home", + Short: "Outputs the string being used as the home path. No home directory is set when using the tool standalone.", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + clientCtx := client.GetClientContextFromCmd(cmd) + if clientCtx.HomeDir == "" { + cmd.Println("No home directory set.") + } else { + cmd.Println(clientCtx.HomeDir) + } + }, + } +} diff --git a/tools/confix/cmd/migrate.go b/tools/confix/cmd/migrate.go index 9ae373a0e4..f31425bfd3 100644 --- a/tools/confix/cmd/migrate.go +++ b/tools/confix/cmd/migrate.go @@ -26,8 +26,6 @@ In case of any error in updating the file, no output is written.`, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var filename string - targetVersion := args[0] - clientCtx := client.GetClientContextFromCmd(cmd) if len(args) > 1 { filename = args[1] @@ -37,6 +35,7 @@ In case of any error in updating the file, no output is written.`, return fmt.Errorf("must provide a path to the app.toml file") } + targetVersion := args[0] plan, ok := confix.Migrations[targetVersion] if !ok { return fmt.Errorf("unknown version %q, supported versions are: %q", targetVersion, maps.Keys(confix.Migrations)) diff --git a/tools/confix/cmd/mutate.go b/tools/confix/cmd/mutate.go index 6b6ae60c91..96938c9877 100644 --- a/tools/confix/cmd/mutate.go +++ b/tools/confix/cmd/mutate.go @@ -20,7 +20,7 @@ func SetCommand() *cobra.Command { cmd := &cobra.Command{ Use: "set [config] [key] [value]", 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 too standalone, otherwise it must be the name of the config file without the .toml extension.", + Long: "Set an application config value. The [config] argument must be the path of the file when using the 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] diff --git a/tools/confix/diff.go b/tools/confix/diff.go index 6e422cb1c2..e5d1fede4e 100644 --- a/tools/confix/diff.go +++ b/tools/confix/diff.go @@ -31,7 +31,8 @@ type Diff struct { // DiffKeys diffs the keyspaces of the TOML documents in files lhs and rhs. // Comments, order, and values are ignored for comparison purposes. func DiffKeys(lhs, rhs *tomledit.Document) []Diff { - diff := diffSections(lhs.Global, rhs.Global) + // diff sections + diff := diffDocs(allKVs(lhs.Global), allKVs(rhs.Global), false) lsec, rsec := lhs.Sections, rhs.Sections transform.SortSectionsByName(lsec) @@ -52,7 +53,7 @@ func DiffKeys(lhs, rhs *tomledit.Document) []Diff { } j++ } else { - diff = append(diff, diffSections(lsec[i], rsec[j])...) + diff = append(diff, diffDocs(allKVs(lsec[i]), allKVs(rsec[j]), false)...) i++ j++ } @@ -73,11 +74,42 @@ func DiffKeys(lhs, rhs *tomledit.Document) []Diff { return diff } +// DiffKeys diffs the keyspaces with different values of the TOML documents in files lhs and rhs. +func DiffValues(lhs, rhs *tomledit.Document) []Diff { + diff := diffDocs(allKVs(lhs.Global), allKVs(rhs.Global), true) + + lsec, rsec := lhs.Sections, rhs.Sections + transform.SortSectionsByName(lsec) + transform.SortSectionsByName(rsec) + + i, j := 0, 0 + for i < len(lsec) && j < len(rsec) { + if lsec[i].Name.Before(rsec[j].Name) { + // skip keys present in lhs but not in rhs + i++ + } else if rsec[j].Name.Before(lsec[i].Name) { + // skip keys present in rhs but not in lhs + j++ + } else { + for _, d := range diffDocs(allKVs(lsec[i]), allKVs(rsec[j]), true) { + if !d.Deleted { + diff = append(diff, d) + } + } + i++ + j++ + } + } + + return diff +} + func allKVs(s *tomledit.Section) []KV { keys := []KV{} s.Scan(func(key parser.Key, entry *tomledit.Entry) bool { keys = append(keys, KV{ - Key: key.String(), + Key: key.String(), + // we get the value of the current configuration (i.e the one we want to compare/migrate) Value: entry.Value.String(), Block: entry.Block, }) @@ -87,11 +119,9 @@ func allKVs(s *tomledit.Section) []KV { return keys } -func diffSections(lhs, rhs *tomledit.Section) []Diff { - return diffKeys(allKVs(lhs), allKVs(rhs)) -} - -func diffKeys(lhs, rhs []KV) []Diff { +// diffDocs get the diff between all keys in lhs and rhs. +// when a key is in both lhs and rhs, it is ignored, unless value is true in which case the value is as well compared. +func diffDocs(lhs, rhs []KV, value bool) []Diff { diff := []Diff{} sort.Slice(lhs, func(i, j int) bool { @@ -110,6 +140,11 @@ func diffKeys(lhs, rhs []KV) []Diff { diff = append(diff, Diff{Type: Mapping, KV: rhs[j]}) j++ } else { + // key exists in both lhs and rhs + // if value is true, compare the values + if value && lhs[i].Value != rhs[j].Value { + diff = append(diff, Diff{Type: Mapping, KV: lhs[i]}) + } i++ j++ } diff --git a/tools/confix/file.go b/tools/confix/file.go new file mode 100644 index 0000000000..4b46957110 --- /dev/null +++ b/tools/confix/file.go @@ -0,0 +1,34 @@ +package confix + +import ( + "embed" + "fmt" + "os" + + "github.com/creachadair/tomledit" +) + +//go:embed data +var data embed.FS + +// LoadConfig loads and parses the TOML document from confix data +func LoadLocalConfig(name string) (*tomledit.Document, error) { + f, err := data.Open(fmt.Sprintf("data/%s-app.toml", name)) + if err != nil { + panic(fmt.Errorf("failed to read file: %w. This file should have been included in confix", err)) + } + defer f.Close() + + return tomledit.Parse(f) +} + +// LoadConfig loads and parses the TOML document from path. +func LoadConfig(path string) (*tomledit.Document, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open %q: %v", path, err) + } + defer f.Close() + + return tomledit.Parse(f) +} diff --git a/tools/confix/migrations.go b/tools/confix/migrations.go index 73694299c2..3cde95cde8 100644 --- a/tools/confix/migrations.go +++ b/tools/confix/migrations.go @@ -2,7 +2,6 @@ package confix import ( "context" - "embed" "fmt" "strings" @@ -28,27 +27,18 @@ var ( // "v0.47.x": PlanBuilder, // add specific migration in case of configuration changes in minor versions // "v0.48": PlanBuilder, } - - //go:embed data - data embed.FS ) // PlanBuilder is a function that returns a transformation plan for a given diff between two files. func PlanBuilder(from *tomledit.Document, to string) transform.Plan { plan := transform.Plan{} + deletedSections := map[string]bool{} - file, err := data.Open(fmt.Sprintf("data/%s-app.toml", to)) - if err != nil { - panic(fmt.Errorf("failed to read file: %w. This file should have been included in confix", err)) - } - - target, err := tomledit.Parse(file) + target, err := LoadLocalConfig(to) if err != nil { panic(fmt.Errorf("failed to parse file: %w. This file should have been valid", err)) } - deletedSections := map[string]bool{} - diffs := DiffKeys(from, target) for _, diff := range diffs { diff := diff diff --git a/tools/confix/upgrade.go b/tools/confix/upgrade.go index 1194227b63..5afd7c0eeb 100644 --- a/tools/confix/upgrade.go +++ b/tools/confix/upgrade.go @@ -65,17 +65,6 @@ func Upgrade(ctx context.Context, plan transform.Plan, configPath, outputPath st return err } -// LoadConfig loads and parses the TOML document from path. -func LoadConfig(path string) (*tomledit.Document, error) { - f, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("failed to open %q: %v", path, err) - } - defer f.Close() - - return tomledit.Parse(f) -} - // CheckValid checks whether the specified config appears to be a valid Cosmos SDK config file. // It tries to unmarshal the config into both the server and client config structs. func CheckValid(fileName string, data []byte) error { diff --git a/tools/cosmovisor/README.md b/tools/cosmovisor/README.md index 5544454a12..45f48e28ba 100644 --- a/tools/cosmovisor/README.md +++ b/tools/cosmovisor/README.md @@ -266,9 +266,9 @@ Clean `~/.simapp` (never do this in a production environment): Set up app config: ```shell -./build/simd config chain-id test -./build/simd config keyring-backend test -./build/simd config broadcast-mode sync +./build/simd config set client chain-id test +./build/simd config set client keyring-backend test +./build/simd config set client broadcast-mode sync ``` Initialize the node and overwrite any previous genesis file (never do this in a production environment):