-
Notifications
You must be signed in to change notification settings - Fork 116
Grouped flags #2766
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
Grouped flags #2766
Changes from 12 commits
3c670c7
d96b225
0bd96a2
1ee9561
7b73aec
b898179
816380e
d24de4c
3f8fc81
1de5edc
6d4d9ec
6aea5a8
656d0ba
416889c
557d057
443e6b0
c3244a7
1c73f71
c187095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,13 +11,13 @@ import ( | |
| "path/filepath" | ||
|
|
||
| "github.com/ava-labs/avalanche-cli/pkg/dependencies" | ||
| "github.com/spf13/pflag" | ||
|
|
||
| "github.com/ava-labs/avalanche-cli/cmd/flags" | ||
| "github.com/ava-labs/avalanche-cli/cmd/interchaincmd/messengercmd" | ||
| "github.com/ava-labs/avalanche-cli/cmd/interchaincmd/relayercmd" | ||
| "github.com/ava-labs/avalanche-cli/cmd/networkcmd" | ||
| "github.com/ava-labs/avalanche-cli/pkg/blockchain" | ||
| "github.com/ava-labs/avalanche-cli/pkg/cobrautils" | ||
| "github.com/ava-labs/avalanche-cli/pkg/constants" | ||
| "github.com/ava-labs/avalanche-cli/pkg/contract" | ||
| "github.com/ava-labs/avalanche-cli/pkg/interchain/relayer" | ||
|
|
@@ -126,9 +126,16 @@ redeploy the chain with fresh state. You can deploy the same Blockchain to multi | |
| so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet.`, | ||
| RunE: deployBlockchain, | ||
| PersistentPostRun: handlePostRun, | ||
| Args: cobrautils.ExactArgs(1), | ||
| PreRunE: func(cmd *cobra.Command, args []string) error { | ||
| requiredArgCount := 1 | ||
| if len(args) != requiredArgCount { | ||
| _ = cmd.Help() // show full help with flag grouping | ||
| return utils.ErrWrongArgCount(requiredArgCount, len(args)) | ||
|
||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, networkoptions.DefaultSupportedNetworkOptions) | ||
| networkGroup := networkoptions.GetNetworkFlagsGroup(cmd, &globalNetworkFlags, true, networkoptions.DefaultSupportedNetworkOptions) | ||
| flags.AddSignatureAggregatorFlagsToCmd(cmd, &deployFlags.SigAggFlags) | ||
| cmd.Flags().StringVar( | ||
| &userProvidedAvagoVersion, | ||
|
|
@@ -142,49 +149,13 @@ so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet. | |
| cmd.Flags().StringSliceVar(&controlKeys, "control-keys", nil, "addresses that may make blockchain changes") | ||
| cmd.Flags().StringSliceVar(&subnetAuthKeys, "auth-keys", nil, "control keys that will be used to authenticate chain creation") | ||
| cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the blockchain creation tx") | ||
| cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet deploy only]") | ||
| cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [local/devnet deploy only]") | ||
| cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") | ||
| cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") | ||
| cmd.Flags().StringVarP(&subnetIDStr, "subnet-id", "u", "", "do not create a subnet, deploy the blockchain into the given subnet id") | ||
| cmd.Flags().Uint32Var(&mainnetChainID, "mainnet-chain-id", 0, "use different ChainID for mainnet deployment") | ||
| cmd.Flags().StringVar(&avagoBinaryPath, "avalanchego-path", "", "use this avalanchego binary path") | ||
| cmd.Flags().BoolVar(&subnetOnly, "subnet-only", false, "only create a subnet") | ||
| cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic ICM deploy on local networks [to be deprecated]") | ||
| cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic ICM deploy") | ||
| cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-icm-deploy", false, "skip automatic ICM deploy") | ||
| cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "noicm", false, "skip automatic ICM deploy") | ||
| cmd.Flags().BoolVar(&icmSpec.SkipRelayerDeploy, skipRelayerFlagName, false, "skip relayer deploy") | ||
| cmd.Flags().StringVar( | ||
| &icmSpec.ICMVersion, | ||
| "teleporter-version", | ||
| constants.LatestReleaseVersionTag, | ||
| "ICM version to deploy", | ||
| ) | ||
| cmd.Flags().StringVar( | ||
| &icmSpec.ICMVersion, | ||
| "icm-version", | ||
| constants.LatestReleaseVersionTag, | ||
| "ICM version to deploy", | ||
| ) | ||
| cmd.Flags().StringVar( | ||
| &icmSpec.RelayerVersion, | ||
| "relayer-version", | ||
| constants.DefaultRelayerVersion, | ||
| "relayer version to deploy", | ||
| ) | ||
| cmd.Flags().StringVar(&icmSpec.RelayerBinPath, "relayer-path", "", "relayer binary to use") | ||
| cmd.Flags().StringVar(&icmSpec.RelayerLogLevel, "relayer-log-level", "info", "log level to be used for relayer logs") | ||
| cmd.Flags().Float64Var(&relayerAmount, "relayer-amount", 0, "automatically fund relayer fee payments with the given amount") | ||
| cmd.Flags().StringVar(&relayerKeyName, "relayer-key", "", "key to be used by default both for rewards and to pay fees") | ||
| cmd.Flags().StringVar(&icmKeyName, "icm-key", constants.ICMKeyName, "key to be used to pay for ICM deploys") | ||
| cmd.Flags().StringVar(&cchainIcmKeyName, "cchain-icm-key", "", "key to be used to pay for ICM deploys on C-Chain") | ||
| cmd.Flags().BoolVar(&relayCChain, "relay-cchain", true, "relay C-Chain as source and destination") | ||
| cmd.Flags().StringVar(&cChainFundingKey, "cchain-funding-key", "", "key to be used to fund relayer account on cchain") | ||
| cmd.Flags().BoolVar(&relayerAllowPrivateIPs, "relayer-allow-private-ips", true, "allow relayer to connec to private ips") | ||
| cmd.Flags().StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an ICM Messenger contract address file") | ||
| cmd.Flags().StringVar(&icmSpec.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an ICM Messenger deployer address file") | ||
| cmd.Flags().StringVar(&icmSpec.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an ICM Messenger deployer tx file") | ||
| cmd.Flags().StringVar(&icmSpec.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an ICM Registry bytecode file") | ||
| cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") | ||
| cmd.Flags().BoolVar(&generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") | ||
| cmd.Flags().StringSliceVar(&bootstrapEndpoints, "bootstrap-endpoints", nil, "take validator node info from the given endpoints") | ||
|
|
@@ -202,15 +173,51 @@ so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet. | |
| cmd.Flags().UintSliceVar(&stakingPorts, "staking-port", []uint{}, "staking port for node(s)") | ||
| cmd.Flags().StringVar(&changeOwnerAddress, "change-owner-address", "", "address that will receive change if node is no longer L1 validator") | ||
|
|
||
| cmd.Flags().Uint64Var(&poSMinimumStakeAmount, "pos-minimum-stake-amount", 1, "minimum stake amount") | ||
| cmd.Flags().Uint64Var(&poSMaximumStakeAmount, "pos-maximum-stake-amount", 1000, "maximum stake amount") | ||
| cmd.Flags().Uint64Var(&poSMinimumStakeDuration, "pos-minimum-stake-duration", constants.PoSL1MinimumStakeDurationSeconds, "minimum stake duration (in seconds)") | ||
| cmd.Flags().Uint16Var(&poSMinimumDelegationFee, "pos-minimum-delegation-fee", 1, "minimum delegation fee") | ||
| cmd.Flags().Uint8Var(&poSMaximumStakeMultiplier, "pos-maximum-stake-multiplier", 1, "maximum stake multiplier") | ||
| cmd.Flags().Uint64Var(&poSWeightToValueFactor, "pos-weight-to-value-factor", 1, "weight to value factor") | ||
|
|
||
| cmd.Flags().BoolVar(&partialSync, "partial-sync", true, "set primary network partial sync for new validators") | ||
| cmd.Flags().Uint32Var(&numNodes, "num-nodes", constants.LocalNetworkNumNodes, "number of nodes to be created on local network deploy") | ||
|
|
||
| icmGroup := flags.RegisterFlagGroup(cmd, "ICM Flags", "show-icm-flags", false, func(set *pflag.FlagSet) { | ||
| set.BoolVar(&icmSpec.SkipICMDeploy, "skip-icm-deploy", false, "Skip automatic ICM deploy") | ||
| set.BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic ICM deploy on local networks [to be deprecated]") | ||
| set.BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic ICM deploy") | ||
| set.BoolVar(&icmSpec.SkipICMDeploy, "noicm", false, "skip automatic ICM deploy") | ||
|
|
||
| set.BoolVar(&icmSpec.SkipRelayerDeploy, skipRelayerFlagName, false, "skip relayer deploy") | ||
|
|
||
| set.StringVar(&icmSpec.ICMVersion, "teleporter-version", constants.LatestReleaseVersionTag, "ICM version to deploy") | ||
| set.StringVar(&icmSpec.ICMVersion, "icm-version", constants.LatestReleaseVersionTag, "ICM version to deploy") | ||
|
|
||
| set.StringVar(&icmSpec.RelayerVersion, "relayer-version", constants.DefaultRelayerVersion, "relayer version to deploy") | ||
| set.StringVar(&icmSpec.RelayerBinPath, "relayer-path", "", "relayer binary to use") | ||
| set.StringVar(&icmSpec.RelayerLogLevel, "relayer-log-level", "info", "log level to be used for relayer logs") | ||
|
|
||
| set.Float64Var(&relayerAmount, "relayer-amount", 0, "automatically fund relayer fee payments with the given amount") | ||
| set.StringVar(&relayerKeyName, "relayer-key", "", "key to be used by default both for rewards and to pay fees") | ||
|
|
||
| set.StringVar(&icmKeyName, "icm-key", constants.ICMKeyName, "key to be used to pay for ICM deploys") | ||
| set.StringVar(&cchainIcmKeyName, "cchain-icm-key", "", "key to be used to pay for ICM deploys on C-Chain") | ||
|
|
||
| set.BoolVar(&relayCChain, "relay-cchain", true, "relay C-Chain as source and destination") | ||
| set.StringVar(&cChainFundingKey, "cchain-funding-key", "", "key to be used to fund relayer account on cchain") | ||
|
|
||
| set.BoolVar(&relayerAllowPrivateIPs, "relayer-allow-private-ips", true, "allow relayer to connec to private ips") | ||
|
|
||
| set.StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an ICM Messenger contract address file") | ||
| set.StringVar(&icmSpec.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an ICM Messenger deployer address file") | ||
| set.StringVar(&icmSpec.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an ICM Messenger deployer tx file") | ||
| set.StringVar(&icmSpec.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an ICM Registry bytecode file") | ||
| }) | ||
|
|
||
| posGroup := flags.RegisterFlagGroup(cmd, "Proof Of Stake Flags", "show-pos-flags", false, func(set *pflag.FlagSet) { | ||
| set.Uint64Var(&poSMinimumStakeAmount, "pos-minimum-stake-amount", 1, "minimum stake amount") | ||
| set.Uint64Var(&poSMaximumStakeAmount, "pos-maximum-stake-amount", 1000, "maximum stake amount") | ||
| set.Uint64Var(&poSMinimumStakeDuration, "pos-minimum-stake-duration", constants.PoSL1MinimumStakeDurationSeconds, "minimum stake duration (in seconds)") | ||
| set.Uint16Var(&poSMinimumDelegationFee, "pos-minimum-delegation-fee", 1, "minimum delegation fee") | ||
| set.Uint8Var(&poSMaximumStakeMultiplier, "pos-maximum-stake-multiplier", 1, "maximum stake multiplier") | ||
| set.Uint64Var(&poSWeightToValueFactor, "pos-weight-to-value-factor", 1, "weight to value factor") | ||
| }) | ||
|
|
||
| cmd.SetHelpFunc(flags.WithGroupedHelp([]flags.GroupedFlags{networkGroup, icmGroup, posGroup})) | ||
| return cmd | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
| package flags | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "strings" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| "github.com/spf13/pflag" | ||
| ) | ||
|
|
||
| type GroupedFlags struct { | ||
| Name string | ||
| ShowFlag string | ||
| FlagSet *pflag.FlagSet | ||
| IsAlwaysVisible bool | ||
| } | ||
|
|
||
| // WithGroupedHelp returns a cobra Run function that shows help organized by groups. | ||
| // It first shows normal command usage, then prints grouped flags, hiding some unless explicitly requested. | ||
| func WithGroupedHelp(groups []GroupedFlags) func(cmd *cobra.Command, args []string) { | ||
| return func(cmd *cobra.Command, _ []string) { | ||
| shownGroups := determineShownGroups(groups) | ||
|
|
||
| printUsage(cmd) | ||
|
|
||
| for _, group := range groups { | ||
| printGroup(cmd, group, shownGroups[group.Name]) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // determineShownGroups decides which flag groups should be visible based on user input and group settings. | ||
| func determineShownGroups(groups []GroupedFlags) map[string]bool { | ||
| shown := make(map[string]bool) | ||
| for _, group := range groups { | ||
| if group.IsAlwaysVisible || flagExists(group.ShowFlag, os.Args) { | ||
| shown[group.Name] = true | ||
| } | ||
| } | ||
| return shown | ||
| } | ||
|
|
||
| // printUsage prints the general command usage/help text. | ||
| func printUsage(cmd *cobra.Command) { | ||
| if err := cmd.Root().UsageFunc()(cmd); err != nil { | ||
| fmt.Fprintf(cmd.ErrOrStderr(), "error showing command usage: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| // printGroup prints a specific group of flags, properly formatted and optionally hidden if not shown. | ||
| func printGroup(cmd *cobra.Command, group GroupedFlags, isShown bool) { | ||
| fmt.Fprintf(cmd.OutOrStdout(), "\n%s:\n", group.Name) | ||
|
|
||
| if !isShown { | ||
| fmt.Fprintf(cmd.OutOrStdout(), " (hidden) Use %s to show these options\n", group.ShowFlag) | ||
| return | ||
| } | ||
|
|
||
| flags, maxLen := collectFlags(group.FlagSet) | ||
|
|
||
| for _, flag := range flags { | ||
| padding := strings.Repeat(" ", maxLen-len(flag.nameAndType)+2) | ||
| fmt.Fprintf(cmd.OutOrStdout(), " %s%s%s\n", flag.nameAndType, padding, flag.usage) | ||
| } | ||
| } | ||
|
|
||
| // flagInfo holds the formatted flag name/type and its usage string. | ||
| type flagInfo struct { | ||
| nameAndType string | ||
| usage string | ||
| } | ||
|
|
||
| // collectFlags gathers all flags in a flag set, calculating the maximum width needed for alignment. | ||
| func collectFlags(flagSet *pflag.FlagSet) ([]flagInfo, int) { | ||
| var flags []flagInfo | ||
| maxLen := 0 | ||
|
|
||
| flagSet.VisitAll(func(flag *pflag.Flag) { | ||
| nameAndType := "--" + flag.Name | ||
| if flag.Value.Type() != "bool" { | ||
| nameAndType += " " + flag.Value.Type() | ||
| } | ||
| if len(nameAndType) > maxLen { | ||
| maxLen = len(nameAndType) | ||
| } | ||
| flags = append(flags, flagInfo{nameAndType, flag.Usage}) | ||
| }) | ||
|
|
||
| return flags, maxLen | ||
| } | ||
|
|
||
| func flagExists(target string, args []string) bool { | ||
| for _, arg := range args { | ||
| if arg == target { | ||
|
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. how do we handle option casing (upper case\lower case) in CLI in general?
Contributor
Author
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. good question, we always put lowercase for flags in CLI. Not sure if cobra (the CLI SDK that we use) automatically does this for us. 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. found this https://github.com/spf13/pflag/blob/master/flag.go#L224 so i assume if we don't do anything special, the |
||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| func RegisterFlagGroup(cmd *cobra.Command, groupName string, showFlag string, isAlwaysVisible bool, defineFlags func(set *pflag.FlagSet)) GroupedFlags { | ||
| // Define the showFlag for controlling visibility | ||
| show := false | ||
| cmd.Flags().BoolVar(&show, showFlag, false, fmt.Sprintf("Show %s", groupName)) | ||
|
|
||
| // Always hide the showFlag itself | ||
| cmd.Flags().Lookup(showFlag).Hidden = true | ||
|
|
||
| // Create a new FlagSet for this group | ||
| flagSet := pflag.NewFlagSet(groupName, pflag.ContinueOnError) | ||
|
|
||
| // Caller defines the flags | ||
| defineFlags(flagSet) | ||
|
|
||
| // Add the flagSet to the cmd's global flag list | ||
| cmd.Flags().AddFlagSet(flagSet) | ||
|
|
||
| // Always hide the grouped flags globally | ||
| flagSet.VisitAll(func(f *pflag.Flag) { | ||
| cmd.Flags().Lookup(f.Name).Hidden = true | ||
| }) | ||
|
|
||
| // Return GroupedFlags | ||
| return GroupedFlags{ | ||
| Name: groupName, | ||
| ShowFlag: "--" + showFlag, | ||
| FlagSet: flagSet, | ||
| IsAlwaysVisible: isAlwaysVisible, | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ import ( | |
| "regexp" | ||
| "strings" | ||
|
|
||
| "github.com/spf13/pflag" | ||
|
|
||
| "github.com/ava-labs/avalanche-cli/cmd/flags" | ||
| "github.com/ava-labs/avalanche-cli/pkg/application" | ||
| "github.com/ava-labs/avalanche-cli/pkg/constants" | ||
|
|
@@ -127,6 +129,35 @@ func AddNetworkFlagsToCmd(cmd *cobra.Command, networkFlags *NetworkFlags, addEnd | |
| } | ||
| } | ||
|
|
||
| func GetNetworkFlagsGroup(cmd *cobra.Command, networkFlags *NetworkFlags, addEndpoint bool, supportedNetworkOptions []NetworkOption) flags.GroupedFlags { | ||
|
Contributor
Author
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 should replace the existing AddNetworkFlagsToCmd command that is used by other commands right now |
||
| return flags.RegisterFlagGroup(cmd, "Network Flags (Select One)", "show-network-flags", true, func(set *pflag.FlagSet) { | ||
|
Contributor
Author
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. network group is always visible |
||
| addCluster := false | ||
| for _, networkOption := range supportedNetworkOptions { | ||
| switch networkOption { | ||
| case Local: | ||
| set.BoolVarP(&networkFlags.UseLocal, "local", "l", false, "operate on a local network") | ||
| case Devnet: | ||
| set.BoolVar(&networkFlags.UseDevnet, "devnet", false, "operate on a devnet network") | ||
| addEndpoint = true | ||
| addCluster = true | ||
| case Fuji: | ||
| set.BoolVarP(&networkFlags.UseFuji, "testnet", "t", false, "operate on testnet (alias to `fuji`)") | ||
| set.BoolVarP(&networkFlags.UseFuji, "fuji", "f", false, "operate on fuji (alias to `testnet`)") | ||
| case Mainnet: | ||
| set.BoolVarP(&networkFlags.UseMainnet, "mainnet", "m", false, "operate on mainnet") | ||
| case Cluster: | ||
| addCluster = true | ||
| } | ||
| } | ||
| if addCluster { | ||
| set.StringVar(&networkFlags.ClusterName, "cluster", "", "operate on the given cluster") | ||
| } | ||
| if addEndpoint { | ||
| set.StringVar(&networkFlags.Endpoint, "endpoint", "", "use the given endpoint for network operations") | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func GetSupportedNetworkOptionsForSubnet( | ||
| app *application.Avalanche, | ||
| subnetName string, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.