allow setting group-scoped flags

This commit is contained in:
Roberto Hidalgo 2023-04-03 18:13:42 -06:00
parent fef0a55288
commit 7b17646738
3 changed files with 75 additions and 20 deletions

View File

@ -54,7 +54,7 @@ func newCobraRoot(root *command.Command) *cobra.Command {
}
}
func ToCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command {
func ToCobra(cmd *command.Command, globalOptions command.Options, parent *cobra.Command) *cobra.Command {
localName := cmd.Name()
useSpec := []string{localName, "[options]"}
for _, arg := range cmd.Arguments {
@ -97,8 +97,20 @@ func ToCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
log.Errorf("Failed setting up autocompletion for option <%s> of command <%s>", name, cmd.FullName())
}
}
parent.AddCommand(cc)
cc.SetHelpFunc(cmd.HelpRenderer(globalOptions))
cmdGlobalOptions := globalOptions
if parent != cc.Root() {
cmdGlobalOptions = subOptions(globalOptions)
log.Tracef("Adding subflags from %s to child %s", parent.Name(), cmd.FullName())
if p := FromCobra(parent); p != nil {
for key, opt := range p.Options {
cmdGlobalOptions[key] = opt
}
}
}
cc.SetHelpFunc(cmd.HelpRenderer(cmdGlobalOptions))
cmd.SetCobra(cc)
return cc
}

View File

@ -62,6 +62,14 @@ func CommandList() []*command.Command {
return registry.byPath
}
func subOptions(m command.Options) command.Options {
m2 := make(map[string]*command.Option, len(m))
for id, opt := range m {
m2[id] = opt
}
return m2
}
func Execute(version string) error {
log.Debug("starting execution")
cmdRoot := command.Root
@ -97,10 +105,15 @@ func Execute(version string) error {
container := ccRoot
for idx, cp := range cmd.Path {
if idx == len(cmd.Path)-1 {
leaf := ToCobra(cmd, globalOptions)
container.AddCommand(leaf)
log.Tracef("cobra: %s => %s", leaf.Name(), container.CommandPath())
break
if cmd.Action != nil {
// nil actions come when the current command consists only
// of metadata for a "group parent" command
// and we don't wanna cobraize it like a regular, actionable one
leaf := ToCobra(cmd, globalOptions, container)
log.Tracef("cobra: %s => %s", leaf.Name(), container.CommandPath())
break
}
log.Tracef("Found command with no action: %s, assuming group parent action", cmd.Path)
}
query := []string{cp}
@ -112,6 +125,11 @@ func Execute(version string) error {
for _, sub := range container.Commands() {
if sub.Name() == cp {
if parent := FromCobra(container); parent != nil {
log.Tracef("pflags to %s from %s", sub.Name(), parent.FullName())
fs := parent.FlagSet()
sub.PersistentFlags().AddFlagSet(fs)
}
container = sub
found = true
}
@ -119,16 +137,35 @@ func Execute(version string) error {
if !found {
groupName := strings.Join(query, " ")
groupPath := append(cmdRoot.Path, append(cmd.Path[0:idx], query...)...) // nolint:gocritic
cleanPath := append(cmd.Path[0:idx], query...)
groupPath := append(cmdRoot.Path, cleanPath...) // nolint:gocritic
cmdGlobalOptions := globalOptions
groupParent := Get(groupName)
if groupParent == nil {
log.Tracef("creating group parent for %s", groupPath)
groupParent = &command.Command{
Path: cleanPath,
Summary: fmt.Sprintf("%s subcommands", groupName),
Description: fmt.Sprintf("Runs subcommands within %s", groupName),
Arguments: command.Arguments{},
Options: command.Options{},
}
Register(groupParent)
} else {
log.Tracef("using pre-existing group parent for %s (%s)", groupPath, groupParent.Path)
}
cc := &cobra.Command{
Use: cp,
Short: fmt.Sprintf("%s subcommands", groupName),
Short: groupParent.Summary,
DisableAutoGenTag: true,
SuggestionsMinimumDistance: 2,
SilenceUsage: true,
SilenceErrors: true,
Annotations: map[string]string{
ContextKeyRuntimeIndex: strings.Join(groupPath, " "),
ContextKeyRuntimeIndex: strings.Join(cleanPath, " "),
},
ValidArgs: []string{},
Args: func(cmd *cobra.Command, args []string) error {
@ -161,15 +198,23 @@ func Execute(version string) error {
},
}
groupParent := &command.Command{
Path: groupPath,
Summary: fmt.Sprintf("%s subcommands", groupName),
Description: fmt.Sprintf("Runs subcommands within %s", groupName),
Arguments: command.Arguments{},
Options: command.Options{},
if len(groupParent.Options) > 0 {
fs := cmd.FlagSet()
cc.PersistentFlags().AddFlagSet(fs)
log.Debugf("adding sub-global option set to %s", groupName)
}
Register(groupParent)
cc.SetHelpFunc(groupParent.HelpRenderer(globalOptions))
if container != cc.Root() {
cmdGlobalOptions = subOptions(globalOptions)
if p := FromCobra(container); p != nil {
for key, opt := range p.Options {
cmdGlobalOptions[key] = opt
}
}
}
cc.PersistentFlags().AddFlagSet(container.PersistentFlags())
cc.SetHelpFunc(groupParent.HelpRenderer(cmdGlobalOptions))
cc.SetHelpCommand(commands.Help)
container.AddCommand(cc)
container = cc

View File

@ -73,7 +73,6 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
fs := pflag.NewFlagSet(strings.Join(cmd.Path, " "), pflag.ContinueOnError)
fs.SortFlags = false
fs.Usage = func() {}
for name, opt := range cmd.Options {
switch opt.Type {
case ValueTypeBoolean:
@ -81,7 +80,6 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
if opt.Default != nil {
def = opt.Default.(bool)
}
fs.BoolP(name, opt.ShortName, def, opt.Description)
case ValueTypeInt:
def := -1
@ -97,7 +95,6 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
def = casted
}
}
fs.IntP(name, opt.ShortName, def, opt.Description)
case ValueTypeDefault, ValueTypeString:
opt.Type = ValueTypeString
@ -112,6 +109,7 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
continue
}
}
cmd.runtimeFlags = fs
}
return cmd.runtimeFlags