forked from cometbft/cometbft
-
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(confix): copy confix from cosmos sdk (cometbft#3036)
# Confix `Confix` is a configuration management tool that allows you to manage your configuration via CLI. It is based on the [CometBFT RFC 019](https://github.com/cometbft/cometbft/blob/5013bc3f4a6d64dcc2bf02ccc002ebc9881c62e4/docs/rfc/rfc-019-config-version.md). ## Usage ### Get Get a configuration value, e.g.: ```shell cometbft config get pruning # gets the value pruning cometbft config get chain-id # gets the value chain-id ``` ### Set Set a configuration value, e.g.: ```shell cometbft config set pruning "enabled" # sets the value pruning cometbft config set chain-id "foo-1" # sets the value chain-id ``` ### Migrate Migrate a configuration file to a new version: ```shell cometbft config migrate v0.38 # migrates defaultHome/config/config.toml to the latest v0.38 config ``` ### Diff Get the diff between a given configuration file and the default configuration file, e.g.: ```shell cometbft config diff v0.38 # gets the diff between defaultHome/config/config.toml and the latest v0.38 config ``` ### View View a configuration file, e.g: ```shell cometbft config view # views the current config ``` ## Credits This project is based on the [CometBFT RFC 019](https://github.com/cometbft/cometbft/blob/5013bc3f4a6d64dcc2bf02ccc002ebc9881c62e4/docs/rfc/rfc-019-config-version.md) and their own implementation of [confix](https://github.com/cometbft/cometbft/blob/v0.36.x/scripts/confix/confix.go). Most of the code is copied over from [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/tree/main/tools/confix). --- #### PR checklist - [ ] ~~Tests written/updated~~ - [ ] ~~Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)~~ - [ ] ~~Updated relevant documentation (`docs/` or `spec/`) and code comments~~ - [x] Title follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) spec
- Loading branch information
Showing
23 changed files
with
2,982 additions
and
12 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
- `[cmd/cometbft]` Add `cometbft config` cmd to view, modify and | ||
upgrade configs across different versions | ||
([\#3036](https://github.com/cometbft/cometbft/pull/3036)) |
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,18 @@ | ||
package config | ||
|
||
import ( | ||
"path/filepath" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cometbft/cometbft/cmd/cometbft/commands" | ||
cfg "github.com/cometbft/cometbft/config" | ||
) | ||
|
||
func defaultConfigPath(cmd *cobra.Command) string { | ||
home, err := commands.ConfigHome(cmd) | ||
if err != nil { | ||
return "" | ||
} | ||
return filepath.Join(home, cfg.DefaultConfigDir, cfg.DefaultConfigFileName) | ||
} |
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,24 @@ | ||
package config | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// Command contains all the confix commands | ||
// These command can be used to interactively update a config value. | ||
func Command() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "config", | ||
Short: "Utilities for managing configuration", | ||
} | ||
|
||
cmd.AddCommand( | ||
MigrateCommand(), | ||
DiffCommand(), | ||
GetCommand(), | ||
SetCommand(), | ||
ViewCommand(), | ||
) | ||
|
||
return cmd | ||
} |
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,55 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
"golang.org/x/exp/maps" | ||
|
||
"github.com/cometbft/cometbft/internal/confix" | ||
) | ||
|
||
// DiffCommand creates a new command for comparing configuration files. | ||
func DiffCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "diff [target-version] <config-path>", | ||
Short: "Outputs all config values that are different from the default.", | ||
Long: "This command compares the configuration file with the defaults and outputs any differences.", | ||
Args: cobra.MinimumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var configPath string | ||
if len(args) > 1 { | ||
configPath = args[1] | ||
} else { | ||
configPath = defaultConfigPath(cmd) | ||
} | ||
|
||
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 + ".toml") | ||
if err != nil { | ||
return fmt.Errorf("failed to load internal config: %w", err) | ||
} | ||
|
||
rawFile, err := confix.LoadConfig(configPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to load config: %w", err) | ||
} | ||
|
||
diff := confix.DiffValues(rawFile, targetVersionFile) | ||
if len(diff) == 0 { | ||
fmt.Print("All config values are the same as the defaults.\n") | ||
} | ||
|
||
fmt.Print("The following config values are different from the defaults:\n") | ||
|
||
confix.PrintDiff(cmd.OutOrStdout(), diff) | ||
return nil | ||
}, | ||
} | ||
|
||
return cmd | ||
} |
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,69 @@ | ||
package config | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
"golang.org/x/exp/maps" | ||
|
||
"github.com/cometbft/cometbft/internal/confix" | ||
) | ||
|
||
var ( | ||
FlagStdOut bool | ||
FlagVerbose bool | ||
FlagSkipValidate bool | ||
) | ||
|
||
func MigrateCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "migrate [target-version] <config-path>", | ||
Short: "Migrate configuration file to the specified version", | ||
Long: `Migrate the contents of the configuration to the specified version. | ||
The output is written in-place unless --stdout is provided. | ||
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 configPath string | ||
if len(args) > 1 { | ||
configPath = args[1] | ||
} else { | ||
configPath = defaultConfigPath(cmd) | ||
} | ||
|
||
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)) | ||
} | ||
|
||
rawFile, err := confix.LoadConfig(configPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to load config: %w", err) | ||
} | ||
|
||
ctx := context.Background() | ||
if FlagVerbose { | ||
ctx = confix.WithLogWriter(ctx, cmd.ErrOrStderr()) | ||
} | ||
|
||
outputPath := configPath | ||
if FlagStdOut { | ||
outputPath = "" | ||
} | ||
|
||
if err := confix.Upgrade(ctx, plan(rawFile, targetVersion), configPath, outputPath, FlagSkipValidate); err != nil { | ||
return fmt.Errorf("failed to migrate config: %w", err) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().BoolVar(&FlagStdOut, "stdout", false, "print the updated config to stdout") | ||
cmd.Flags().BoolVar(&FlagVerbose, "verbose", false, "log changes to stderr") | ||
cmd.Flags().BoolVar(&FlagSkipValidate, "skip-validate", false, "skip configuration validation (allows to migrate unknown configurations)") | ||
|
||
return cmd | ||
} |
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,144 @@ | ||
package config | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/creachadair/tomledit" | ||
"github.com/creachadair/tomledit/parser" | ||
"github.com/creachadair/tomledit/transform" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/cometbft/cometbft/internal/confix" | ||
) | ||
|
||
// SetCommand returns a CLI command to interactively update an application config value. | ||
func SetCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "set [config] [key] [value]", | ||
Short: "Set a config value", | ||
Long: "Set a config value. The [config] is an optional absolute path to the config file (default: `~/.cometbft/config/config.toml`)", | ||
Args: cobra.MinimumNArgs(2), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var ( | ||
filename, inputValue string | ||
key []string | ||
) | ||
switch len(args) { | ||
case 2: | ||
{ | ||
filename = defaultConfigPath(cmd) | ||
// parse key e.g mempool.size -> [mempool, size] | ||
key = strings.Split(args[0], ".") | ||
inputValue = args[1] | ||
} | ||
case 3: | ||
{ | ||
filename, inputValue = args[0], args[2] | ||
key = strings.Split(args[1], ".") | ||
} | ||
default: | ||
return errors.New("expected 2 or 3 arguments") | ||
} | ||
|
||
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()) | ||
} | ||
|
||
return confix.Upgrade(ctx, plan, 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 [config] [key]", | ||
Short: "Get a config value", | ||
Long: "Get a config value. The [config] is an optional absolute path to the config file (default: `~/.cometbft/config/config.toml`)", | ||
Args: cobra.MinimumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var ( | ||
filename, key string | ||
keys []string | ||
) | ||
switch len(args) { | ||
case 1: | ||
{ | ||
filename = defaultConfigPath(cmd) | ||
// parse key e.g mempool.size -> [mempool, size] | ||
key = args[0] | ||
keys = strings.Split(key, ".") | ||
} | ||
case 2: | ||
{ | ||
filename = args[0] | ||
key = args[1] | ||
keys = strings.Split(key, ".") | ||
} | ||
default: | ||
return errors.New("expected 1 or 2 arguments") | ||
} | ||
|
||
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) | ||
} | ||
|
||
fmt.Printf("%s\n", results[0].Value.String()) | ||
return nil | ||
}, | ||
} | ||
|
||
return cmd | ||
} |
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,52 @@ | ||
package config | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/pelletier/go-toml/v2" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func ViewCommand() *cobra.Command { | ||
flagOutputFormat := "output-format" | ||
|
||
cmd := &cobra.Command{ | ||
Use: "view [config]", | ||
Short: "View the config file", | ||
Long: "View the config file. The [config] is an optional absolute path to the config file (default: `~/.cometbft/config/config.toml`)", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var filename string | ||
if len(args) > 0 { | ||
filename = args[0] | ||
} else { | ||
filename = defaultConfigPath(cmd) | ||
} | ||
|
||
file, err := os.ReadFile(filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if format, _ := cmd.Flags().GetString(flagOutputFormat); format == "toml" { | ||
cmd.Println(string(file)) | ||
return nil | ||
} | ||
|
||
var v any | ||
if err := toml.Unmarshal(file, &v); err != nil { | ||
return fmt.Errorf("failed to decode config file: %w", err) | ||
} | ||
|
||
e := json.NewEncoder(cmd.OutOrStdout()) | ||
e.SetIndent("", " ") | ||
return e.Encode(v) | ||
}, | ||
} | ||
|
||
// output flag | ||
cmd.Flags().String(flagOutputFormat, "toml", "Output format (json|toml)") | ||
|
||
return cmd | ||
} |
Oops, something went wrong.