feat: add diff and home command in confix (#14568)
This commit is contained in:
parent
f2b6013cee
commit
e88a084840
@ -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 => ../.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -17,6 +17,7 @@ func ConfigCommand() *cobra.Command {
|
||||
DiffCommand(),
|
||||
GetCommand(),
|
||||
SetCommand(),
|
||||
HomeCommand(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
@ -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] <app-toml-path>",
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
22
tools/confix/cmd/home.go
Normal file
22
tools/confix/cmd/home.go
Normal file
@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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++
|
||||
}
|
||||
|
||||
34
tools/confix/file.go
Normal file
34
tools/confix/file.go
Normal file
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user