- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
95 lines
2.4 KiB
Go
95 lines
2.4 KiB
Go
package bonfire
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// NewRoot builds the shared cobra root used by the summer tool and app binaries.
|
|
// It accepts command values rather than a party registry to avoid an import cycle.
|
|
func NewRoot(name string, commands []Command, out io.Writer) (*cobra.Command, error) {
|
|
return NewRootIO(name, commands, os.Stdin, out, out)
|
|
}
|
|
|
|
// NewRootIO is NewRoot with injected stdin/stdout/stderr for tests.
|
|
func NewRootIO(name string, commands []Command, in io.Reader, out, errW io.Writer) (*cobra.Command, error) {
|
|
if out == nil {
|
|
out = os.Stdout
|
|
}
|
|
if errW == nil {
|
|
errW = out
|
|
}
|
|
if in == nil {
|
|
in = os.Stdin
|
|
}
|
|
output := NewOutput(in, out, errW)
|
|
root := &cobra.Command{
|
|
Use: name,
|
|
SilenceUsage: true,
|
|
SilenceErrors: true,
|
|
CompletionOptions: cobra.CompletionOptions{
|
|
DisableDefaultCmd: true,
|
|
},
|
|
}
|
|
root.SetIn(in)
|
|
root.SetOut(out)
|
|
root.SetErr(errW)
|
|
for _, command := range commands {
|
|
cmd, err := wrap(command, output)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
root.AddCommand(cmd)
|
|
}
|
|
return root, nil
|
|
}
|
|
|
|
func wrap(c Command, out Output) (*cobra.Command, error) {
|
|
if !validCommandName(c.Name) {
|
|
return nil, fmt.Errorf("%w: %q", ErrCommandName, c.Name)
|
|
}
|
|
cmd := &cobra.Command{
|
|
Use: commandUsage(c),
|
|
Short: c.Description,
|
|
Args: positionalArgs(c.Args),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if c.Run == nil {
|
|
return nil
|
|
}
|
|
return c.Run(cmd.Context(), cobraInput{cmd: cmd, spec: c, args: args}, out)
|
|
},
|
|
}
|
|
for _, flag := range c.Flags {
|
|
if flag.Repeatable {
|
|
// D-19: an ordered, multi-occurrence string flag (Cobra
|
|
// StringSlice), read back through Input.Flags. Default is a
|
|
// single-element slice only when non-empty, matching the
|
|
// scalar path's zero-value convention.
|
|
var def []string
|
|
if flag.Default != "" {
|
|
def = []string{flag.Default}
|
|
}
|
|
if flag.Shorthand != "" {
|
|
cmd.Flags().StringSliceP(flag.Name, flag.Shorthand, def, flag.Description)
|
|
} else {
|
|
cmd.Flags().StringSlice(flag.Name, def, flag.Description)
|
|
}
|
|
continue
|
|
}
|
|
if flag.Shorthand != "" {
|
|
cmd.Flags().StringP(flag.Name, flag.Shorthand, flag.Default, flag.Description)
|
|
} else {
|
|
cmd.Flags().String(flag.Name, flag.Default, flag.Description)
|
|
}
|
|
if flag.Bare {
|
|
if f := cmd.Flags().Lookup(flag.Name); f != nil {
|
|
f.NoOptDefVal = "true"
|
|
}
|
|
}
|
|
}
|
|
return cmd, nil
|
|
}
|