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.Shorthand != "" { cmd.Flags().StringP(flag.Name, flag.Shorthand, flag.Default, flag.Description) continue } cmd.Flags().String(flag.Name, flag.Default, flag.Description) } return cmd, nil }