-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(autocli): fix simapp enhancing #15906
Changes from all commits
03a1c95
2d680de
c02a8bd
39eccbb
b336aa8
0e377d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<!-- | ||
Guiding Principles: | ||
|
||
Changelogs are for humans, not machines. | ||
There should be an entry for every single version. | ||
The same types of changes should be grouped. | ||
Versions and sections should be linkable. | ||
The latest version comes first. | ||
The release date of each version is displayed. | ||
Mention whether you follow Semantic Versioning. | ||
|
||
Usage: | ||
|
||
Change log entries are to be added to the Unreleased section under the | ||
appropriate stanza (see below). Each entry should ideally include a tag and | ||
the Github issue reference in the following format: | ||
|
||
* (<tag>) \#<issue-number> message | ||
|
||
The issue numbers will later be link-ified during the release process so you do | ||
not have to worry about including a link manually, but you can if you wish. | ||
|
||
Types of changes (Stanzas): | ||
|
||
"Features" for new features. | ||
"Improvements" for changes in existing functionality. | ||
"Deprecated" for soon-to-be removed features. | ||
"Bug Fixes" for any bug fixes. | ||
"Client Breaking" for breaking Protobuf, gRPC and REST routes used by end-users. | ||
"CLI Breaking" for breaking CLI commands. | ||
"API Breaking" for breaking exported APIs used by developers building on SDK. | ||
Ref: https://keepachangelog.com/en/1.0.0/ | ||
--> | ||
|
||
# Changelog | ||
|
||
## [Unreleased] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# AutoCLI | ||
|
||
The `autocli` package is a Go library for generating CLIs (command line interfaces) for Cosmos SDK-based applications. | ||
|
||
Read more about in it the Cosmos SDK documentation: | ||
|
||
* https://docs.cosmos.network/main/building-modules/autocli |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import ( | |
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/exp/maps" | ||
"google.golang.org/protobuf/reflect/protoreflect" | ||
"sigs.k8s.io/yaml" | ||
|
||
|
@@ -67,22 +68,27 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip | |
// options or the provided custom commands for each module. If the provided query command already contains a command | ||
// for a module, that command is not over-written by this method. This allows a graceful addition of autocli to | ||
// automatically fill in missing commands. | ||
func (b *Builder) enhanceCommandCommon(cmd *cobra.Command, moduleOptions map[string]*autocliv1.ModuleOptions, customCmds map[string]*cobra.Command, buildModuleCommand func(*cobra.Command, *autocliv1.ModuleOptions, string) error) error { | ||
allModuleNames := map[string]bool{} | ||
for moduleName := range moduleOptions { | ||
allModuleNames[moduleName] = true | ||
} | ||
for moduleName := range customCmds { | ||
allModuleNames[moduleName] = true | ||
func (b *Builder) enhanceCommandCommon( | ||
cmd *cobra.Command, | ||
appOptions AppOptions, | ||
customCmds map[string]*cobra.Command, | ||
buildModuleCommand enhanceCommandFunc, | ||
) error { | ||
moduleOptions := appOptions.ModuleOptions | ||
if len(moduleOptions) == 0 { | ||
moduleOptions = map[string]*autocliv1.ModuleOptions{} | ||
for name, module := range appOptions.Modules { | ||
if module, ok := module.(HasAutoCLIConfig); ok { | ||
moduleOptions[name] = module.AutoCLIOptions() | ||
} | ||
} | ||
} | ||
|
||
for moduleName := range allModuleNames { | ||
modules := append(maps.Keys(appOptions.Modules), maps.Keys(moduleOptions)...) | ||
for _, moduleName := range modules { | ||
// if we have an existing command skip adding one here | ||
if cmd.HasSubCommands() { | ||
if _, _, err := cmd.Find([]string{moduleName}); err == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was always returning true for simapp. We already had a helper that was working. |
||
// command already exists, skip | ||
continue | ||
} | ||
if findSubCommand(cmd, moduleName) != nil { | ||
continue | ||
} | ||
|
||
// if we have a custom command use that instead of generating one | ||
|
@@ -98,28 +104,59 @@ func (b *Builder) enhanceCommandCommon(cmd *cobra.Command, moduleOptions map[str | |
continue | ||
} | ||
|
||
err := buildModuleCommand(cmd, modOpts, moduleName) | ||
if err != nil { | ||
if err := buildModuleCommand(b, moduleName, cmd, modOpts); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type enhanceCommandFunc func(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error | ||
|
||
// enhanceQuery enhances the provided query command with the autocli commands for a module. | ||
func enhanceQuery(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error { | ||
queryCmdDesc := modOpts.Query | ||
if queryCmdDesc != nil { | ||
subCmd := topLevelCmd(moduleName, fmt.Sprintf("Querying commands for the %s module", moduleName)) | ||
if err := builder.AddQueryServiceCommands(subCmd, queryCmdDesc); err != nil { | ||
return err | ||
} | ||
|
||
cmd.AddCommand(subCmd) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// enhanceMsg enhances the provided msg command with the autocli commands for a module. | ||
func enhanceMsg(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error { | ||
txCmdDesc := modOpts.Tx | ||
if txCmdDesc != nil { | ||
subCmd := topLevelCmd(moduleName, fmt.Sprintf("Transactions commands for the %s module", moduleName)) | ||
if err := builder.AddMsgServiceCommands(subCmd, txCmdDesc); err != nil { | ||
return err | ||
} | ||
|
||
cmd.AddCommand(subCmd) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// outOrStdoutFormat formats the output based on the output flag and writes it to the command's output stream. | ||
func (b *Builder) outOrStdoutFormat(cmd *cobra.Command, out []byte) error { | ||
var err error | ||
outputType := cmd.Flag(flags.FlagOutput) | ||
// if the output type is text, convert the json to yaml | ||
// if output type is json or nil, default to json | ||
if outputType != nil && outputType.Value.String() == "text" { | ||
if outputType != nil && outputType.Value.String() == flags.OutputFormatText { | ||
out, err = yaml.JSONToYAML(out) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(out)) | ||
|
||
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(out)) | ||
return err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,16 +2,19 @@ package flag | |
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" | ||
"github.com/cosmos/cosmos-sdk/types" | ||
"cosmossdk.io/core/address" | ||
"google.golang.org/protobuf/reflect/protoreflect" | ||
|
||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address" | ||
) | ||
|
||
type addressStringType struct{} | ||
|
||
func (a addressStringType) NewValue(ctx context.Context, b *Builder) Value { | ||
if b.AddressPrefix == "" { | ||
if b.AddressCodec == nil { | ||
conn, err := b.GetClientConn() | ||
if err != nil { | ||
panic(err) | ||
|
@@ -24,18 +27,20 @@ func (a addressStringType) NewValue(ctx context.Context, b *Builder) Value { | |
if resp == nil || resp.Config == nil { | ||
panic("bech32 account address prefix is not set") | ||
} | ||
b.AddressPrefix = resp.Config.Bech32AccountAddressPrefix | ||
|
||
b.AddressCodec = addresscodec.NewBech32Codec(resp.Config.Bech32AccountAddressPrefix) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't call grpc every time to get the bech32 prefix. This should be done by hubl during unit and update and then cached There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, do you want me to do that here or in a follow-up? This PR does not touch hubl's code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a follow up |
||
} | ||
return &addressValue{addressPrefix: b.AddressPrefix} | ||
|
||
return &addressValue{addressCodec: b.AddressCodec} | ||
} | ||
|
||
func (a addressStringType) DefaultValue() string { | ||
return "" | ||
} | ||
|
||
type addressValue struct { | ||
value string | ||
addressPrefix string | ||
value string | ||
addressCodec address.Codec | ||
} | ||
|
||
func (a addressValue) Get(protoreflect.Value) (protoreflect.Value, error) { | ||
|
@@ -48,10 +53,11 @@ func (a addressValue) String() string { | |
|
||
// Set implements the flag.Value interface for addressValue it only supports bech32 addresses. | ||
func (a *addressValue) Set(s string) error { | ||
_, err := types.GetFromBech32(s, a.addressPrefix) | ||
_, err := a.addressCodec.StringToBytes(s) | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("invalid bech32 account address: %w", err) | ||
} | ||
|
||
a.value = s | ||
|
||
return nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So here it was passing the custom query commands, which is incorrect.