feat(client,simapp): inject keyring in autocli opts (#17351)
This commit is contained in:
parent
e60c583d28
commit
bf92632a0e
@ -66,8 +66,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* (types) `module.EndBlockAppModule` has been replaced by Core API `appmodule.HasBeginBlocker`.
|
||||
* (types) `module.BeginBlockAppModule` has been replaced by Core API `appmodule.HasEndBlocker` or `module.HasABCIEndBlock` when needing validator updates.
|
||||
* (types) `module.BeginBlockAppModule` has been replaced by Core API `appmodule.HasBeginBlocker`.
|
||||
* (types) `module.EndBlockAppModule` has been replaced by Core API `appmodule.HasEndBlocker` or `module.HasABCIEndBlock` when needing validator updates.
|
||||
* (types) [#17358](https://github.com/cosmos/cosmos-sdk/pull/17358) Remove deprecated `sdk.Handler`, use `baseapp.MsgServiceHandler` instead.
|
||||
* (x/slashing) [17044](https://github.com/cosmos/cosmos-sdk/pull/17044) Use collections for `AddrPubkeyRelation`:
|
||||
* remove from `types`: `AddrPubkeyRelationKey`
|
||||
@ -174,7 +174,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Improvements
|
||||
|
||||
* (all modules) [#15901](https://github.com/cosmos/cosmos-sdk/issues/15901) All core Cosmos SDK modules query commands have migrated to [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli), ensuring parity between gRPC and CLI queries.
|
||||
* (all modules) [#15901](https://github.com/cosmos/cosmos-sdk/issues/15901) All core Cosmos SDK modules query commands have migrated to [AutoCLI](https://docs.cosmos.network/main/core/autocli), ensuring parity between gRPC and CLI queries.
|
||||
* (types) [#16890](https://github.com/cosmos/cosmos-sdk/pull/16890) Remove `GetTxCmd() *cobra.Command` and `GetQueryCmd() *cobra.Command` from `module.AppModuleBasic` interface.
|
||||
* (cli) [#16856](https://github.com/cosmos/cosmos-sdk/pull/16856) Improve `simd prune` UX by using the app default home directory and set pruning method as first variable argument (defaults to default).
|
||||
* (x/authz) [#16869](https://github.com/cosmos/cosmos-sdk/pull/16869) Improve error message when grant not found.
|
||||
@ -377,7 +377,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### CLI Breaking Changes
|
||||
|
||||
* (all) The migration of modules to [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli) led to no changes in UX but a [small change in CLI outputs](https://github.com/cosmos/cosmos-sdk/issues/16651) where results can be nested.
|
||||
* (all) The migration of modules to [AutoCLI](https://docs.cosmos.network/main/core/autocli) led to no changes in UX but a [small change in CLI outputs](https://github.com/cosmos/cosmos-sdk/issues/16651) where results can be nested.
|
||||
* (all) Query pagination flags have been renamed with the migration to AutoCLI:
|
||||
* `--limit` -> `--page-limit`
|
||||
* `--offset` -> `--page-offset`
|
||||
|
||||
@ -220,7 +220,7 @@ Users manually wiring their chain need to use the new `module.NewBasicManagerFro
|
||||
|
||||
#### AutoCLI
|
||||
|
||||
[`AutoCLI`](https://docs.cosmos.network/main/building-modules/autocli) has been implemented by the SDK for all its module CLI queries. This means chains must add the following in their `root.go` to enable `AutoCLI` in their application:
|
||||
[`AutoCLI`](https://docs.cosmos.network/main/core/autocli) has been implemented by the SDK for all its module CLI queries. This means chains must add the following in their `root.go` to enable `AutoCLI` in their application:
|
||||
|
||||
```go
|
||||
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
|
||||
|
||||
@ -78,7 +78,7 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) {
|
||||
|
||||
keyring, err := client.NewKeyringFromBackend(ctx, conf.KeyringBackend)
|
||||
if err != nil {
|
||||
return ctx, fmt.Errorf("couldn't get key ring: %w", err)
|
||||
return ctx, fmt.Errorf("couldn't get keyring: %w", err)
|
||||
}
|
||||
|
||||
ctx = ctx.WithKeyring(keyring)
|
||||
|
||||
@ -1,7 +1,186 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# AutoCLI
|
||||
|
||||
The `autocli` package is a Go library for generating CLIs (command line interfaces) for Cosmos SDK-based applications.
|
||||
:::note Synopsis
|
||||
This document details how to build CLI and REST interfaces for a module. Examples from various Cosmos SDK modules are included.
|
||||
:::
|
||||
|
||||
Read more about in it the Cosmos SDK documentation:
|
||||
:::note Pre-requisite Readings
|
||||
|
||||
* https://docs.cosmos.network/main/building-modules/autocli
|
||||
* [CLI](https://docs.cosmos.network/main/core/cli)
|
||||
|
||||
:::
|
||||
|
||||
The `autocli` (also known as `client/v2`) package is a [Go library](https://pkg.go.dev/cosmossdk.io/client/v2/autocli) for generating CLI (command line interface) interfaces for Cosmos SDK-based applications. It provides a simple way to add CLI commands to your application by generating them automatically based on your gRPC service definitions. Autocli generates CLI commands and flags directly from your protobuf messages, including options, input parameters, and output parameters. This means that you can easily add a CLI interface to your application without having to manually create and manage commands.
|
||||
|
||||
## Overview
|
||||
|
||||
`autocli` generates CLI commands and flags for each method defined in your gRPC service. By default, it generates commands for each gRPC services. The commands are named based on the name of the service method.
|
||||
|
||||
For example, given the following protobuf definition for a service:
|
||||
|
||||
```protobuf
|
||||
service MyService {
|
||||
rpc MyMethod(MyRequest) returns (MyResponse) {}
|
||||
}
|
||||
```
|
||||
|
||||
For instance, `autocli` would generate a command named `my-method` for the `MyMethod` method. The command will have flags for each field in the `MyRequest` message.
|
||||
|
||||
It is possible to customize the generation of transactions and queries by defining options for each service.
|
||||
|
||||
## Application Wiring
|
||||
|
||||
Here are the steps to use AutoCLI:
|
||||
|
||||
1. Ensure your app's modules implements the `appmodule.AppModule` interface.
|
||||
2. (optional) Configure how behave `autocli` command generation, by implementing the `func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions` method on the module.
|
||||
3. Use the `autocli.AppOptions` struct to specify the modules you defined. If you are using `depinject` / app v2, it can automatically create an instance of `autocli.AppOptions` based on your app's configuration.
|
||||
4. Use the `EnhanceRootCommand()` method provided by `autocli` to add the CLI commands for the specified modules to your root command.
|
||||
|
||||
:::tip
|
||||
AutoCLI is additive only, meaning _enhancing_ the root command will only add subcommands that are not already registered. This means that you can use AutoCLI alongside other custom commands within your app.
|
||||
:::
|
||||
|
||||
Here's an example of how to use `autocli` in your app:
|
||||
|
||||
``` go
|
||||
// Define your app's modules
|
||||
testModules := map[string]appmodule.AppModule{
|
||||
"testModule": &TestModule{},
|
||||
}
|
||||
|
||||
// Define the autocli AppOptions
|
||||
autoCliOpts := autocli.AppOptions{
|
||||
Modules: testModules,
|
||||
}
|
||||
|
||||
// Create the root command
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "app",
|
||||
}
|
||||
|
||||
if err := appOptions.EnhanceRootCommand(rootCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run the root command
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### Keyring
|
||||
|
||||
`autocli` supports a keyring for key name resolving and signing transactions. Providing a keyring is optional, but if you want to use the `autocli` generated commands to sign transactions, you must provide a keyring.
|
||||
|
||||
:::tip
|
||||
This provides a better UX as it allows to resolve key names directly from the keyring in all transactions and commands.
|
||||
|
||||
```sh
|
||||
<appd> q bank balances alice
|
||||
<appd> tx bank send alice bob 1000denom
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
The keyring to be provided to `client/v2` must match the `client/v2` keyring interface. The Cosmos SDK keyring and Hubl keyring both implement this interface.
|
||||
The keyring should be provided in the `appOptions` struct as follows, and can be gotten from the client context:
|
||||
|
||||
:::warning
|
||||
When using AutoCLI the keyring will only be created once and before any command flag parsing.
|
||||
:::
|
||||
|
||||
```go
|
||||
// Get the keyring from the client context
|
||||
keyring := ctx.Keyring
|
||||
// Set the keyring in the appOptions
|
||||
appOptions.Keyring = keyring
|
||||
|
||||
err := autoCliOpts.EnhanceRootCommand(rootCmd)
|
||||
...
|
||||
```
|
||||
|
||||
## Module Wiring & Customization
|
||||
|
||||
The `AutoCLIOptions()` method on your module allows to specify custom commands, sub-commands or flags for each service, as it was a `cobra.Command` instance, within the `RpcCommandOptions` struct. Defining such options will customize the behavior of the `autocli` command generation, which by default generates a command for each method in your gRPC service.
|
||||
|
||||
```go
|
||||
*autocliv1.RpcCommandOptions{
|
||||
RpcMethod: "Params", // The name of the gRPC service
|
||||
Use: "params", // Command usage that is displayed in the help
|
||||
Short: "Query the parameters of the governance process", // Short description of the command
|
||||
Long: "Query the parameters of the governance process. Specify specific param types (voting|tallying|deposit) to filter results.", // Long description of the command
|
||||
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
|
||||
{ProtoField: "params_type", Optional: true}, // Transform a flag into a positional argument
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Specifying Subcommands
|
||||
|
||||
By default, `autocli` generates a command for each method in your gRPC service. However, you can specify subcommands to group related commands together. To specify subcommands, use the `autocliv1.ServiceCommandDescriptor` struct.
|
||||
|
||||
This example shows how to use the `autocliv1.ServiceCommandDescriptor` struct to group related commands together and specify subcommands in your gRPC service by defining an instance of `autocliv1.ModuleOptions` in your `autocli.go`.
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/gov/autocli.go#L94-L97
|
||||
```
|
||||
|
||||
### Positional Arguments
|
||||
|
||||
By default `autocli` generates a flag for each field in your protobuf message. However, you can choose to use positional arguments instead of flags for certain fields.
|
||||
|
||||
To add positional arguments to a command, use the `autocliv1.PositionalArgDescriptor` struct, as seen in the example below. Specify the `ProtoField` parameter, which is the name of the protobuf field that should be used as the positional argument. In addition, if the parameter is a variable-length argument, you can specify the `Varargs` parameter as `true`. This can only be applied to the last positional parameter, and the `ProtoField` must be a repeated field.
|
||||
|
||||
Here's an example of how to define a positional argument for the `Account` method of the `auth` service:
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/auth/autocli.go#L25-L30
|
||||
```
|
||||
|
||||
Then the command can be used as follows, instead of having to specify the `--address` flag:
|
||||
|
||||
```bash
|
||||
<appd> query auth account cosmos1abcd...xyz
|
||||
```
|
||||
|
||||
### Customising Flag Names
|
||||
|
||||
By default, `autocli` generates flag names based on the names of the fields in your protobuf message. However, you can customise the flag names by providing a `FlagOptions`. This parameter allows you to specify custom names for flags based on the names of the message fields.
|
||||
|
||||
For example, if you have a message with the fields `test` and `test1`, you can use the following naming options to customise the flags:
|
||||
|
||||
``` go
|
||||
autocliv1.RpcCommandOptions{
|
||||
FlagOptions: map[string]*autocliv1.FlagOptions{
|
||||
"test": { Name: "custom_name", },
|
||||
"test1": { Name: "other_name", },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`FlagsOptions` is defined like sub commands in the `AutoCLIOptions()` method on your module.
|
||||
|
||||
### Combining AutoCLI with Other Commands Within A Module
|
||||
|
||||
AutoCLI can be used alongside other commands within a module. For example, the `gov` module uses AutoCLI to generate commands for the `query` subcommand, but also defines custom commands for the `proposer` subcommands.
|
||||
|
||||
In order to enable this behavior, set in `AutoCLIOptions()` the `EnhanceCustomCommand` field to `true`, for the command type (queries and/or transactions) you want to enhance.
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/fa4d87ef7e6d87aaccc94c337ffd2fe90fcb7a9d/x/gov/autocli.go#L98
|
||||
```
|
||||
|
||||
If not set to true, `AutoCLI` will not generate commands for the module if there are already commands registered for the module (when `GetTxCmd()` or `GetTxCmd()` are defined).
|
||||
|
||||
## Summary
|
||||
|
||||
`autocli` let you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application.
|
||||
|
||||
To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands.
|
||||
|
||||
For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl).
|
||||
|
||||
@ -50,17 +50,22 @@ func (a addressValue) String() string {
|
||||
return a.value
|
||||
}
|
||||
|
||||
// Set implements the flag.Value interface for addressValue it only supports bech32 addresses.
|
||||
// Set implements the flag.Value interface for addressValue.
|
||||
func (a *addressValue) Set(s string) error {
|
||||
_, err := a.keyring.LookupAddressByKeyName(s)
|
||||
addr, err := a.keyring.LookupAddressByKeyName(s)
|
||||
if err == nil {
|
||||
a.value = s
|
||||
addrStr, err := a.addressCodec.BytesToString(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid account address got from keyring: %w", err)
|
||||
}
|
||||
|
||||
a.value = addrStr
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = a.addressCodec.StringToBytes(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid bech32 account address: %w", err)
|
||||
return fmt.Errorf("invalid account address or key name: %w", err)
|
||||
}
|
||||
|
||||
a.value = s
|
||||
@ -69,7 +74,7 @@ func (a *addressValue) Set(s string) error {
|
||||
}
|
||||
|
||||
func (a addressValue) Type() string {
|
||||
return "bech32 account address"
|
||||
return "account address or key name"
|
||||
}
|
||||
|
||||
type consensusAddressStringType struct{}
|
||||
@ -95,9 +100,14 @@ func (a consensusAddressValue) String() string {
|
||||
}
|
||||
|
||||
func (a *consensusAddressValue) Set(s string) error {
|
||||
_, err := a.keyring.LookupAddressByKeyName(s)
|
||||
addr, err := a.keyring.LookupAddressByKeyName(s)
|
||||
if err == nil {
|
||||
a.value = s
|
||||
addrStr, err := a.addressCodec.BytesToString(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid consensus address got from keyring: %w", err)
|
||||
}
|
||||
|
||||
a.value = addrStr
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -115,7 +125,7 @@ func (a *consensusAddressValue) Set(s string) error {
|
||||
var pk cryptotypes.PubKey
|
||||
err2 := cdc.UnmarshalInterfaceJSON([]byte(s), &pk)
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("input isn't a pubkey %w or is invalid bech32 account address: %w", err, err2)
|
||||
return fmt.Errorf("input isn't a pubkey %w or is an invalid account address: %w", err, err2)
|
||||
}
|
||||
|
||||
a.value = sdk.ConsAddress(pk.Address()).String()
|
||||
|
||||
@ -40,7 +40,7 @@ type Builder struct {
|
||||
ValidatorAddressCodec address.Codec
|
||||
ConsensusAddressCodec address.Codec
|
||||
|
||||
// Keyring
|
||||
// Keyring implementation
|
||||
Keyring keyring.Keyring
|
||||
}
|
||||
|
||||
|
||||
@ -2,5 +2,5 @@ package keyring
|
||||
|
||||
type Keyring interface {
|
||||
// LookupAddressByKeyName returns the address of the key with the given name.
|
||||
LookupAddressByKeyName(name string) (string, error)
|
||||
LookupAddressByKeyName(name string) ([]byte, error)
|
||||
}
|
||||
|
||||
@ -6,6 +6,6 @@ var _ Keyring = NoKeyring{}
|
||||
|
||||
type NoKeyring struct{}
|
||||
|
||||
func (k NoKeyring) LookupAddressByKeyName(name string) (string, error) {
|
||||
return "", errors.New("no keyring configured")
|
||||
func (k NoKeyring) LookupAddressByKeyName(name string) ([]byte, error) {
|
||||
return nil, errors.New("no keyring configured")
|
||||
}
|
||||
|
||||
@ -496,14 +496,14 @@ func TestAddressValidation(t *testing.T) {
|
||||
"1", "abc", "1foo",
|
||||
"--an-address", "regen1y74p8wyy4enfhfn342njve6cjmj5c8dtlqj7ule2",
|
||||
)
|
||||
assert.ErrorContains(t, err, "invalid bech32 account address")
|
||||
assert.ErrorContains(t, err, "invalid account address")
|
||||
|
||||
_, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand,
|
||||
"echo",
|
||||
"1", "abc", "1foo",
|
||||
"--an-address", "cosmps1BAD_ENCODING",
|
||||
)
|
||||
assert.ErrorContains(t, err, "invalid bech32 account address")
|
||||
assert.ErrorContains(t, err, "invalid account address")
|
||||
}
|
||||
|
||||
func TestOutputFormat(t *testing.T) {
|
||||
|
||||
@ -8,9 +8,9 @@ Flags:
|
||||
--a-bool
|
||||
--a-coin cosmos.base.v1beta1.Coin
|
||||
--a-message testpb.AMessage (json)
|
||||
--a-validator-address bech32 account address
|
||||
--a-validator-address account address or key name
|
||||
-a, --account-number uint The account number of the signing account (offline mode only)
|
||||
--an-address bech32 account address
|
||||
--an-address account address or key name
|
||||
--an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified)
|
||||
--aux Generate aux signer data instead of sending a tx
|
||||
--bools bools (default [])
|
||||
|
||||
@ -5,10 +5,10 @@ Usage:
|
||||
Flags:
|
||||
--a-bool
|
||||
--a-coin cosmos.base.v1beta1.Coin
|
||||
--a-consensus-address bech32 account address
|
||||
--a-consensus-address account address or key name
|
||||
--a-message testpb.AMessage (json)
|
||||
--a-validator-address bech32 account address
|
||||
--an-address bech32 account address
|
||||
--a-validator-address account address or key name
|
||||
--an-address account address or key name
|
||||
--an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified)
|
||||
--bools bools (default [])
|
||||
--bz binary
|
||||
|
||||
@ -13,9 +13,9 @@ Flags:
|
||||
--a-bool
|
||||
--a-coin cosmos.base.v1beta1.Coin
|
||||
--a-message testpb.AMessage (json)
|
||||
--a-validator-address bech32 account address
|
||||
--a-validator-address account address or key name
|
||||
-a, --account-number uint The account number of the signing account (offline mode only)
|
||||
--an-address bech32 account address
|
||||
--an-address account address or key name
|
||||
--an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified)
|
||||
--aux Generate aux signer data instead of sending a tx
|
||||
--bools bools (default [])
|
||||
|
||||
6
client/v2/autocli/testdata/help-echo.golden
vendored
6
client/v2/autocli/testdata/help-echo.golden
vendored
@ -12,10 +12,10 @@ echo 1 abc {}
|
||||
Flags:
|
||||
--a-bool
|
||||
--a-coin cosmos.base.v1beta1.Coin some random coin
|
||||
--a-consensus-address bech32 account address
|
||||
--a-consensus-address account address or key name
|
||||
--a-message testpb.AMessage (json)
|
||||
--a-validator-address bech32 account address
|
||||
--an-address bech32 account address
|
||||
--a-validator-address account address or key name
|
||||
--an-address account address or key name
|
||||
--an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified)
|
||||
--bools bools (default [])
|
||||
--bz binary some bytes
|
||||
|
||||
@ -100,6 +100,9 @@ type Keyring interface {
|
||||
Exporter
|
||||
|
||||
Migrator
|
||||
|
||||
// Implements client/v2 keyring interface
|
||||
LookupAddressByKeyName(name string) ([]byte, error)
|
||||
}
|
||||
|
||||
// Signer is implemented by key stores that want to provide signing capabilities.
|
||||
@ -593,6 +596,21 @@ func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) {
|
||||
return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger
|
||||
}
|
||||
|
||||
// LookupAddressByKeyName returns the address of a key stored in the keyring
|
||||
func (ks keystore) LookupAddressByKeyName(name string) ([]byte, error) {
|
||||
record, err := ks.Key(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := record.GetAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// SignWithLedger signs a binary message with the ledger device referenced by an Info object
|
||||
// and returns the signed bytes and the public key. It returns an error if the device could
|
||||
// not be queried or it returned an error.
|
||||
|
||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@ -17,6 +17,7 @@ docs/docs/rfc
|
||||
docs/docs/tooling/01-cosmovisor.md
|
||||
docs/docs/tooling/02-confix.md
|
||||
docs/docs/tooling/03-hubl.md
|
||||
docs/docs/core/17-autocli.md
|
||||
docs/docs/packages/01-depinject.md
|
||||
docs/docs/packages/02-collections.md
|
||||
docs/docs/packages/03-orm.md
|
||||
|
||||
@ -82,7 +82,7 @@ While there are no definitive guidelines for writing modules, here are some impo
|
||||
|
||||
Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components:
|
||||
|
||||
* A [`keeper`](./06-keeper.md), used to access the module's store(s) and update the state.
|
||||
* A [`keeper`](./06-keeper.md), used to access the module's store(s) and update the state.
|
||||
* A [`Msg` service](./02-messages-and-queries.md#messages), used to process messages when they are routed to the module by [`BaseApp`](../core/00-baseapp.md#message-routing) and trigger state-transitions.
|
||||
* A [query service](./04-query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../core/00-baseapp.md#query-routing).
|
||||
* Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module.
|
||||
|
||||
@ -61,7 +61,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/x/bank/module.go#L84-L
|
||||
### Query Commands
|
||||
|
||||
:::warning
|
||||
This section is being rewritten. Refer to [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli) while this section is being updated.
|
||||
This section is being rewritten. Refer to [AutoCLI](https://docs.cosmos.network/main/core/autocli) while this section is being updated.
|
||||
:::
|
||||
|
||||
<!-- UPDATE THIS TO AUTOCLI
|
||||
|
||||
@ -1,157 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
|
||||
# AutoCLI
|
||||
|
||||
:::note Synopsis
|
||||
This document details how to build CLI and REST interfaces for a module. Examples from various Cosmos SDK modules are included.
|
||||
:::
|
||||
|
||||
:::note Pre-requisite Readings
|
||||
|
||||
* [Building Modules Intro](./01-intro.md)
|
||||
|
||||
:::
|
||||
|
||||
The `autocli` package is a [Go library](https://pkg.go.dev/cosmossdk.io/client/v2/autocli) for generating CLI (command line interface) interfaces for Cosmos SDK-based applications. It provides a simple way to add CLI commands to your application by generating them automatically based on your gRPC service definitions. Autocli generates CLI commands and flags directly from your protobuf messages, including options, input parameters, and output parameters. This means that you can easily add a CLI interface to your application without having to manually create and manage commands.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Here are the steps to use AutoCLI:
|
||||
|
||||
1. Ensure your app's modules implements the `appmodule.AppModule` interface.
|
||||
2. (optional) Configure how behave `autocli` command generation, by implementing the `func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions` method on the module. Learn more [here](#advanced-usage).
|
||||
3. Use the `autocli.AppOptions` struct to specify the modules you defined. If you are using `depinject` / app v2, it can automatically create an instance of `autocli.AppOptions` based on your app's configuration.
|
||||
4. Use the `EnhanceRootCommand()` method provided by `autocli` to add the CLI commands for the specified modules to your root command.
|
||||
|
||||
:::tip
|
||||
AutoCLI is additive only, meaning _enhancing_ the root command will only add subcommands that are not already registered. This means that you can use AutoCLI alongside other custom commands within your app.
|
||||
:::
|
||||
|
||||
Here's an example of how to use `autocli`:
|
||||
|
||||
``` go
|
||||
// Define your app's modules
|
||||
testModules := map[string]appmodule.AppModule{
|
||||
"testModule": &TestModule{},
|
||||
}
|
||||
|
||||
// Define the autocli AppOptions
|
||||
autoCliOpts := autocli.AppOptions{
|
||||
Modules: testModules,
|
||||
}
|
||||
|
||||
// Create the root command
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "app",
|
||||
}
|
||||
|
||||
if err := appOptions.EnhanceRootCommand(rootCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run the root command
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
## Commands and Queries
|
||||
|
||||
`autocli` generates CLI commands and flags for each method defined in your gRPC service. By default, it generates commands for each gRPC services. The commands are named based on the name of the service method.
|
||||
|
||||
For example, given the following protobuf definition for a service:
|
||||
|
||||
```protobuf
|
||||
service MyService {
|
||||
rpc MyMethod(MyRequest) returns (MyResponse) {}
|
||||
}
|
||||
```
|
||||
|
||||
For instance, `autocli` would generate a command named `my-method` for the `MyMethod` method. The command will have flags for each field in the `MyRequest` message.
|
||||
|
||||
It is possible to customize the generation of commands and queries by defining options for each service.
|
||||
|
||||
## Customize generated commands
|
||||
|
||||
The `AutoCLIOptions()` method on your module allows to specify custom commands, sub-commands or flags for each service, as it was a `cobra.Command` instance, within the `RpcCommandOptions` struct.
|
||||
|
||||
```go
|
||||
*autocliv1.RpcCommandOptions{
|
||||
RpcMethod: "Params", // The name of the gRPC service
|
||||
Use: "params", // Command usage that is displayed in the help
|
||||
Short: "Query the parameters of the governance process", // Short description of the command
|
||||
Long: "Query the parameters of the governance process. Specify specific param types (voting|tallying|deposit) to filter results.", // Long description of the command
|
||||
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
|
||||
{ProtoField: "params_type", Optional: true}, // Transform a flag into a positional argument
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Specifying Subcommands
|
||||
|
||||
By default, `autocli` generates a command for each method in your gRPC service. However, you can specify subcommands to group related commands together. To specify subcommands, use the `autocliv1.ServiceCommandDescriptor` struct.
|
||||
|
||||
This example shows how to use the `autocliv1.ServiceCommandDescriptor` struct to group related commands together and specify subcommands in your gRPC service by defining an instance of `autocliv1.ModuleOptions` in your `autocli.go`.
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/gov/autocli.go#L94-L97
|
||||
```
|
||||
|
||||
### Positional Arguments
|
||||
|
||||
By default `autocli` generates a flag for each field in your protobuf message. However, you can choose to use positional arguments instead of flags for certain fields.
|
||||
|
||||
To add positional arguments to a command, use the `autocliv1.PositionalArgDescriptor` struct, as seen in the example below. Specify the `ProtoField` parameter, which is the name of the protobuf field that should be used as the positional argument. In addition, if the parameter is a variable-length argument, you can specify the `Varargs` parameter as `true`. This can only be applied to the last positional parameter, and the `ProtoField` must be a repeated field.
|
||||
|
||||
Here's an example of how to define a positional argument for the `Account` method of the `auth` service:
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/auth/autocli.go#L25-L30
|
||||
```
|
||||
|
||||
Then the command can be used as follows, instead of having to specify the `--address` flag:
|
||||
|
||||
```bash
|
||||
<appd> query auth account cosmos1abcd...xyz
|
||||
```
|
||||
|
||||
### Customising Flag Names
|
||||
|
||||
By default, `autocli` generates flag names based on the names of the fields in your protobuf message. However, you can customise the flag names by providing a `FlagOptions`. This parameter allows you to specify custom names for flags based on the names of the message fields.
|
||||
|
||||
For example, if you have a message with the fields `test` and `test1`, you can use the following naming options to customise the flags:
|
||||
|
||||
``` go
|
||||
autocliv1.RpcCommandOptions{
|
||||
FlagOptions: map[string]*autocliv1.FlagOptions{
|
||||
"test": { Name: "custom_name", },
|
||||
"test1": { Name: "other_name", },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`FlagsOptions` is defined like sub commands in the `AutoCLIOptions()` method on your module.
|
||||
|
||||
### Combining AutoCLI with Other Commands Within A Module
|
||||
|
||||
AutoCLI can be used alongside other commands within a module. For example, the `gov` module uses AutoCLI to generate commands for the `query` subcommand, but also defines custom commands for the `proposer` subcommands.
|
||||
|
||||
In order to enable this behavior, set in `AutoCLIOptions()` the `EnhanceCustomCommand` field to `true`, for the command type (queries and/or transactions) you want to enhance.
|
||||
|
||||
|
||||
```go reference
|
||||
https://github.com/cosmos/cosmos-sdk/blob/fa4d87ef7e6d87aaccc94c337ffd2fe90fcb7a9d/x/gov/autocli.go#L98
|
||||
```
|
||||
|
||||
If not set to true, `AutoCLI` will not generate commands for the module if there are already commands registered for the module (when `GetTxCmd()` or `GetTxCmd()` are defined).
|
||||
|
||||
## Conclusion
|
||||
|
||||
`autocli` let you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application.
|
||||
|
||||
To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands.
|
||||
|
||||
For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl).
|
||||
@ -80,7 +80,7 @@ x/{module_name}
|
||||
* `keeper/`: The module's `Keeper` and `MsgServer` implementation.
|
||||
* `module/`: The module's `AppModule` and `AppModuleBasic` implementation.
|
||||
* `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (this file is only required if `BeginBlocker` and/or `EndBlocker` need to be defined).
|
||||
* `autocli.go`: The module [autocli](./10-autocli.md) options.
|
||||
* `autocli.go`: The module [autocli](https://docs.cosmos.network/main/core/autocli) options.
|
||||
* `simulation/`: The module's [simulation](./14-simulator.md) package defines functions used by the blockchain simulator application (`simapp`).
|
||||
* `REAMDE.md`: The module's specification documents outlining important concepts, state storage structure, and message and event type definitions. Learn more how to write module specs in the [spec guidelines](../spec/SPEC_MODULE.md).
|
||||
* The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers.
|
||||
|
||||
@ -72,7 +72,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/simd/cmd/root_v
|
||||
:::tip
|
||||
Use the `EnhanceRootCommand()` from the AutoCLI options to automatically add auto-generated commands from the modules to the root command.
|
||||
Additionnally it adds all manually defined modules commands (`tx` and `query`) as well.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli#getting-started) in its dedicated section.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/core/autocli) in its dedicated section.
|
||||
:::
|
||||
|
||||
`rootCmd` has a function called `initAppConfig()` which is useful for setting the application's custom configs.
|
||||
@ -103,7 +103,7 @@ This `txCommand` function adds all the transaction available to end-users for th
|
||||
|
||||
* **Sign command** from the [`auth`](../modules/auth/README.md) module that signs messages in a transaction. To enable multisig, add the `auth` module's `MultiSign` command. Since every transaction requires some sort of signature in order to be valid, the signing command is necessary for every application.
|
||||
* **Broadcast command** from the Cosmos SDK client tools, to broadcast transactions.
|
||||
* **All [module transaction commands](../building-modules/09-module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddTxCommands()` function, or enhanced by [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli#getting-started).
|
||||
* **All [module transaction commands](../building-modules/09-module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddTxCommands()` function, or enhanced by [AutoCLI](https://docs.cosmos.network/main/core/autocli).
|
||||
|
||||
Here is an example of a `txCommand` aggregating these subcommands from the `simapp` application:
|
||||
|
||||
@ -113,7 +113,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/simd/cmd/root_v
|
||||
|
||||
:::tip
|
||||
When using AutoCLI to generate module transaction commands, `EnhanceRootCommand()` automatically adds the module `tx` command to the root command.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli#getting-started) in its dedicated section.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/core/autocli) in its dedicated section.
|
||||
:::
|
||||
|
||||
### Query Commands
|
||||
@ -130,7 +130,7 @@ This `queryCommand` function adds all the queries available to end-users for the
|
||||
* **Account command** from the `auth` module, which displays the state (e.g. account balance) of an account given an address.
|
||||
* **Validator command** from the Cosmos SDK rpc client tools, which displays the validator set of a given height.
|
||||
* **Block command** from the Cosmos SDK RPC client tools, which displays the block data for a given height.
|
||||
* **All [module query commands](../building-modules/09-module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddQueryCommands()` function, or enhanced by [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli#getting-started).
|
||||
* **All [module query commands](../building-modules/09-module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddQueryCommands()` function, or enhanced by [AutoCLI](https://docs.cosmos.network/main/core/autocli).
|
||||
|
||||
Here is an example of a `queryCommand` aggregating subcommands from the `simapp` application:
|
||||
|
||||
@ -140,7 +140,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/simd/cmd/root_v
|
||||
|
||||
:::tip
|
||||
When using AutoCLI to generate module query commands, `EnhanceRootCommand()` automatically adds the module `query` command to the root command.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli#getting-started) in its dedicated section.
|
||||
Read more about [AutoCLI](https://docs.cosmos.network/main/core/autocli) in its dedicated section.
|
||||
:::
|
||||
|
||||
## Flags
|
||||
|
||||
@ -26,7 +26,7 @@ For more information on SDK tooling, see the [Tooling](https://docs.cosmos.netwo
|
||||
## Automation
|
||||
|
||||
* [Depinject](./01-depinject.md) - Dependency injection framework
|
||||
* [Client/v2](https://pkg.go.dev/cosmossdk.io/client/v2) - Library powering [AutoCLI](https://docs.cosmos.network/main/building-modules/autocli)
|
||||
* [Client/v2](https://pkg.go.dev/cosmossdk.io/client/v2) - Library powering [AutoCLI](https://docs.cosmos.network/main/core/autocli)
|
||||
|
||||
## Utilities
|
||||
|
||||
|
||||
@ -415,6 +415,10 @@ const config = {
|
||||
from: ["/main/tooling/depinject"],
|
||||
to: "/main/packages/depinject",
|
||||
},
|
||||
{
|
||||
from: ["/main/building-modules/autocli"],
|
||||
to: "/main/core/autocli",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@ -7,6 +7,7 @@ rm -rf docs/tooling/03-hubl.md
|
||||
rm -rf docs/packages/01-depinject.md
|
||||
rm -rf docs/packages/02-collections.md
|
||||
rm -rf docs/packages/03-orm.md
|
||||
rm -rf docs/core/17-autocli.md
|
||||
rm -rf docs/run-node/04-rosetta.md
|
||||
rm -rf docs/architecture
|
||||
rm -rf docs/spec
|
||||
|
||||
@ -24,6 +24,7 @@ cp ../tools/hubl/README.md ./docs/tooling/03-hubl.md
|
||||
wget -O docs/run-node/04-rosetta.md https://raw.githubusercontent.com/cosmos/rosetta/main/README.md
|
||||
|
||||
## Add package documentation
|
||||
cp ../client/v2/README.md ./docs/core/17-autocli.md
|
||||
cp ../depinject/README.md ./docs/packages/01-depinject.md
|
||||
cp ../collections/README.md ./docs/packages/02-collections.md
|
||||
cp ../orm/README.md ./docs/packages/03-orm.md
|
||||
|
||||
@ -93,7 +93,12 @@ func NewRootCmd() *cobra.Command {
|
||||
|
||||
initRootCmd(rootCmd, encodingConfig.TxConfig, encodingConfig.InterfaceRegistry, encodingConfig.Codec, tempApp.BasicModuleManager)
|
||||
|
||||
if err := tempApp.AutoCliOpts().EnhanceRootCommand(rootCmd); err != nil {
|
||||
// add keyring to autocli opts
|
||||
autoCliOpts := tempApp.AutoCliOpts()
|
||||
initClientCtx, _ = config.ReadFromClientConfig(initClientCtx)
|
||||
autoCliOpts.Keyring = initClientCtx.Keyring
|
||||
|
||||
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/simapp"
|
||||
@ -31,9 +33,9 @@ func NewRootCmd() *cobra.Command {
|
||||
interfaceRegistry codectypes.InterfaceRegistry
|
||||
appCodec codec.Codec
|
||||
txConfig client.TxConfig
|
||||
legacyAmino *codec.LegacyAmino
|
||||
autoCliOpts autocli.AppOptions
|
||||
moduleBasicManager module.BasicManager
|
||||
initClientCtx client.Context
|
||||
)
|
||||
|
||||
if err := depinject.Inject(
|
||||
@ -42,26 +44,21 @@ func NewRootCmd() *cobra.Command {
|
||||
log.NewNopLogger(),
|
||||
simtestutil.NewAppOptionsWithFlagHome(tempDir()),
|
||||
),
|
||||
depinject.Provide(
|
||||
ProvideClientContext,
|
||||
ProvideKeyring,
|
||||
),
|
||||
),
|
||||
&interfaceRegistry,
|
||||
&appCodec,
|
||||
&txConfig,
|
||||
&legacyAmino,
|
||||
&autoCliOpts,
|
||||
&moduleBasicManager,
|
||||
&initClientCtx,
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
initClientCtx := client.Context{}.
|
||||
WithCodec(appCodec).
|
||||
WithInterfaceRegistry(interfaceRegistry).
|
||||
WithLegacyAmino(legacyAmino).
|
||||
WithInput(os.Stdin).
|
||||
WithAccountRetriever(types.AccountRetriever{}).
|
||||
WithHomeDir(simapp.DefaultNodeHome).
|
||||
WithViper("") // In simapp, we don't use any prefix for env variables.
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "simd",
|
||||
Short: "simulation app",
|
||||
@ -116,3 +113,28 @@ func NewRootCmd() *cobra.Command {
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func ProvideClientContext(appCodec codec.Codec, interfaceRegistry codectypes.InterfaceRegistry, legacyAmino *codec.LegacyAmino) client.Context {
|
||||
initClientCtx := client.Context{}.
|
||||
WithCodec(appCodec).
|
||||
WithInterfaceRegistry(interfaceRegistry).
|
||||
WithLegacyAmino(legacyAmino).
|
||||
WithInput(os.Stdin).
|
||||
WithAccountRetriever(types.AccountRetriever{}).
|
||||
WithHomeDir(simapp.DefaultNodeHome).
|
||||
WithViper("") // In simapp, we don't use any prefix for env variables.
|
||||
|
||||
// Read the config again to overwrite the default values with the values from the config file
|
||||
initClientCtx, _ = config.ReadFromClientConfig(initClientCtx)
|
||||
|
||||
return initClientCtx
|
||||
}
|
||||
|
||||
func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) {
|
||||
kb, err := client.NewKeyringFromBackend(clientCtx, clientCtx.Keyring.Backend())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user