github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/cmd/syft/cli/cli.go (about)

     1  package cli
     2  
     3  import (
     4  	"os"
     5  
     6  	cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd"
     7  	"github.com/spf13/cobra"
     8  
     9  	"github.com/anchore/clio"
    10  	"github.com/anchore/stereoscope"
    11  	"github.com/anchore/syft/cmd/syft/cli/commands"
    12  	handler "github.com/anchore/syft/cmd/syft/cli/ui"
    13  	"github.com/anchore/syft/cmd/syft/internal/ui"
    14  	"github.com/anchore/syft/internal/bus"
    15  	"github.com/anchore/syft/internal/log"
    16  	"github.com/anchore/syft/internal/redact"
    17  )
    18  
    19  // Application constructs the `syft packages` command, aliases the root command to `syft packages`,
    20  // and constructs the `syft power-user` command. It is also responsible for
    21  // organizing flag usage and injecting the application config for each command.
    22  // It also constructs the syft attest command and the syft version command.
    23  // `RunE` is the earliest that the complete application configuration can be loaded.
    24  func Application(id clio.Identification) clio.Application {
    25  	app, _ := create(id)
    26  	return app
    27  }
    28  
    29  // Command returns the root command for the syft CLI application. This is useful for embedding the entire syft CLI
    30  // into an existing application.
    31  func Command(id clio.Identification) *cobra.Command {
    32  	_, cmd := create(id)
    33  	return cmd
    34  }
    35  
    36  func create(id clio.Identification) (clio.Application, *cobra.Command) {
    37  	clioCfg := clio.NewSetupConfig(id).
    38  		WithGlobalConfigFlag().   // add persistent -c <path> for reading an application config from
    39  		WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config
    40  		WithConfigInRootHelp().   // --help on the root command renders the full application config in the help text
    41  		WithUIConstructor(
    42  			// select a UI based on the logging configuration and state of stdin (if stdin is a tty)
    43  			func(cfg clio.Config) ([]clio.UI, error) {
    44  				noUI := ui.None(cfg.Log.Quiet)
    45  				if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet {
    46  					return []clio.UI{noUI}, nil
    47  				}
    48  
    49  				h := handler.New(handler.DefaultHandlerConfig())
    50  
    51  				return []clio.UI{
    52  					ui.New(h, false, cfg.Log.Quiet),
    53  					noUI,
    54  				}, nil
    55  			},
    56  		).
    57  		WithInitializers(
    58  			func(state *clio.State) error {
    59  				// clio is setting up and providing the bus, redact store, and logger to the application. Once loaded,
    60  				// we can hoist them into the internal packages for global use.
    61  				stereoscope.SetBus(state.Bus)
    62  				bus.Set(state.Bus)
    63  				redact.Set(state.RedactStore)
    64  				log.Set(state.Logger)
    65  				stereoscope.SetLogger(state.Logger)
    66  
    67  				return nil
    68  			},
    69  		).
    70  		WithPostRuns(func(state *clio.State, err error) {
    71  			stereoscope.Cleanup()
    72  		})
    73  
    74  	app := clio.New(*clioCfg)
    75  
    76  	// since root is aliased as the packages cmd we need to construct this command first
    77  	// we also need the command to have information about the `root` options because of this alias
    78  	packagesCmd := commands.Packages(app)
    79  
    80  	// rootCmd is currently an alias for the packages command
    81  	rootCmd := commands.Root(app, packagesCmd)
    82  
    83  	// add sub-commands
    84  	rootCmd.AddCommand(
    85  		packagesCmd,
    86  		commands.PowerUser(app),
    87  		commands.Attest(app),
    88  		commands.Convert(app),
    89  		clio.VersionCommand(id),
    90  		cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane
    91  	)
    92  
    93  	return app, rootCmd
    94  }