package cli import ( "errors" "fmt" "path/filepath" "strings" "banger/internal/api" "banger/internal/buildinfo" "github.com/spf13/cobra" ) // NewBangerCommand builds the top-level cobra tree with production // defaults wired into the dependency struct. Tests reach into the // package directly — see newRootCommand + defaultDeps. func NewBangerCommand() *cobra.Command { return defaultDeps().newRootCommand() } func (d *deps) newRootCommand() *cobra.Command { root := &cobra.Command{ Use: "banger", Short: "Manage development VMs and images", SilenceUsage: true, SilenceErrors: true, RunE: helpNoArgs, } root.AddCommand( d.newDaemonCommand(), d.newDoctorCommand(), d.newImageCommand(), d.newInternalCommand(), d.newKernelCommand(), newSSHConfigCommand(), newVersionCommand(), d.newPSCommand(), d.newVMCommand(), ) return root } func (d *deps) newDoctorCommand() *cobra.Command { return &cobra.Command{ Use: "doctor", Short: "Check host and runtime readiness", Args: noArgsUsage("usage: banger doctor"), RunE: func(cmd *cobra.Command, args []string) error { report, err := d.doctor(cmd.Context()) if err != nil { return err } if err := printDoctorReport(cmd.OutOrStdout(), report); err != nil { return err } if report.HasFailures() { return errors.New("doctor found failing checks") } return nil }, } } func newVersionCommand() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Show banger build information", Args: noArgsUsage("usage: banger version"), RunE: func(cmd *cobra.Command, args []string) error { _, err := fmt.Fprint(cmd.OutOrStdout(), formatBuildInfoBlock(buildinfo.Current())) return err }, } } func helpNoArgs(cmd *cobra.Command, args []string) error { if len(args) != 0 { return fmt.Errorf("unknown arguments: %s", strings.Join(args, " ")) } return cmd.Help() } func noArgsUsage(usage string) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) != 0 { return errors.New(usage) } return nil } } func exactArgsUsage(n int, usage string) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) != n { return errors.New(usage) } return nil } } func minArgsUsage(n int, usage string) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) < n { return errors.New(usage) } return nil } } func maxArgsUsage(n int, usage string) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) > n { return errors.New(usage) } return nil } } func shellQuote(value string) string { return "'" + strings.ReplaceAll(value, "'", `'"'"'`) + "'" } func absolutizeImageRegisterPaths(params *api.ImageRegisterParams) error { return absolutizePaths( ¶ms.RootfsPath, ¶ms.WorkSeedPath, ¶ms.KernelPath, ¶ms.InitrdPath, ¶ms.ModulesDir, ) } func absolutizePaths(values ...*string) error { var err error for _, value := range values { if *value == "" || filepath.IsAbs(*value) { continue } *value, err = filepath.Abs(*value) if err != nil { return err } } return nil } func formatBuildInfoBlock(info buildinfo.Info) string { return fmt.Sprintf("version: %s\ncommit: %s\nbuilt_at: %s\n", info.Version, info.Commit, info.BuiltAt) }