From 46636ccb3c7343067bcaf8d2ba742a70f0a6a551 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 22 Mar 2021 15:25:04 -0400 Subject: [PATCH] simplify command alias logic + remove deprecation warning for root command Signed-off-by: Alex Goodman --- README.md | 3 +++ cmd/cmd.go | 38 ++++++---------------------- cmd/packages.go | 2 +- test/cli/root_cmd_test.go | 53 ++++++++++++++++++++++++++++----------- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 62123c14b..47ba28c61 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ If you encounter an issue, please [let us know using the issue tracker](https:// To generate an SBOM for a Docker or OCI image: ``` syft packages + +# note: this is the same as not providing the "packages" subcommand +syft ``` The above output includes only software that is visible in the container (i.e., the squashed representation of the image). diff --git a/cmd/cmd.go b/cmd/cmd.go index 9e0bda837..ac95e2d0a 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "os" - "strings" "github.com/spf13/cobra" @@ -33,10 +32,6 @@ func init() { ) } -// provided to disambiguate the root vs packages command, whichever is indicated by the cli args will be set here. -// TODO: when the root alias command is removed, this variable can be removed -var activeCmd *cobra.Command - func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, color.Red.Sprint(err.Error())) @@ -48,30 +43,9 @@ func Execute() { // be done without determining what the primary command that the config options should be bound to since there are // shared concerns (the root-packages alias). func initCmdAliasBindings() { - // TODO: when the root alias command is removed, this function (hack) can be removed - - // map of all commands except for root - commands := make(map[string]*cobra.Command) - for _, c := range rootCmd.Commands() { - name := strings.Split(c.Use, " ")[0] - commands[name] = c - } - - activeCmd = rootCmd - for i, a := range os.Args { - if i == 0 { - // don't consider the bin - continue - } - // check to see if this argument may be a command - if c, exists := commands[a]; exists { - activeCmd = c - } - } - - if activeCmd == rootCmd { - // note: cobra supports command deprecation, however the command name is empty and does not report to stderr - fmt.Fprintln(os.Stderr, color.New(color.Bold, color.Red).Sprintf("The root command is deprecated, please use the 'packages' subcommand")) + activeCmd, _, err := rootCmd.Find(os.Args[1:]) + if err != nil { + panic(err) } if activeCmd == packagesCmd || activeCmd == rootCmd { @@ -81,11 +55,13 @@ func initCmdAliasBindings() { // reading the application configuration, which implies that it must be an initializer (or rewrite the command // initialization structure against typical patterns used with cobra, which is somewhat extreme for a // temporary alias) - if err := bindConfigOptions(activeCmd.Flags()); err != nil { + if err = bindPackagesConfigOptions(activeCmd.Flags()); err != nil { panic(err) } } else { - if err := bindConfigOptions(packagesCmd.Flags()); err != nil { + // even though the root command or packages command is NOT being run, we still need default bindings + // such that application config parsing passes. + if err = bindPackagesConfigOptions(packagesCmd.Flags()); err != nil { panic(err) } } diff --git a/cmd/packages.go b/cmd/packages.go index d0d205919..e19bee44e 100644 --- a/cmd/packages.go +++ b/cmd/packages.go @@ -138,7 +138,7 @@ func setPackageFlags(flags *pflag.FlagSet) { ) } -func bindConfigOptions(flags *pflag.FlagSet) error { +func bindPackagesConfigOptions(flags *pflag.FlagSet) error { ///////// Formatting & Input options ////////////////////////////////////////////// if err := viper.BindPFlag("package.cataloger.scope", flags.Lookup("scope")); err != nil { diff --git a/test/cli/root_cmd_test.go b/test/cli/root_cmd_test.go index 9c58ec299..ec46ed132 100644 --- a/test/cli/root_cmd_test.go +++ b/test/cli/root_cmd_test.go @@ -9,25 +9,50 @@ import ( func TestRootCmdAliasesToPackagesSubcommand(t *testing.T) { request := "docker-archive:" + getFixtureImage(t, "image-pkg-coverage") - deprecationWarning := "The root command is deprecated" - _, aliasStdout, aliasStderr := runSyftCommand(t, nil, request) - - if !strings.Contains(aliasStderr, deprecationWarning) { - t.Errorf("missing root-packages alias deprecation warning") + tests := []struct { + name string + env map[string]string + assertions []traitAssertion + }{ + { + name: "go-case", + assertions: []traitAssertion{ + assertTableReport, + assertSuccessfulReturnCode, + }, + }, + { + name: "respond-to-output-binding", + env: map[string]string{ + "SYFT_OUTPUT": "text", + }, + assertions: []traitAssertion{ + assertInOutput("[Image]"), + assertSuccessfulReturnCode, + }, + }, } - _, pkgsStdout, pkgsStderr := runSyftCommand(t, nil, "packages", request) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + aliasCmd, aliasStdout, aliasStderr := runSyftCommand(t, test.env, request) + for _, traitFn := range test.assertions { + traitFn(t, aliasStdout, aliasStderr, aliasCmd.ProcessState.ExitCode()) + } - if strings.Contains(pkgsStderr, deprecationWarning) { - t.Errorf("packages command should not have deprecation warning") - } + pkgCmd, pkgsStdout, pkgsStderr := runSyftCommand(t, test.env, "packages", request) + for _, traitFn := range test.assertions { + traitFn(t, pkgsStdout, pkgsStderr, pkgCmd.ProcessState.ExitCode()) + } - if aliasStdout != pkgsStdout { - t.Errorf("packages and root command should have same report output but do not!") - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(aliasStdout, pkgsStdout, true) - t.Error(dmp.DiffPrettyText(diffs)) + if aliasStdout != pkgsStdout { + t.Errorf("packages and root command should have same report output but do not!") + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(aliasStdout, pkgsStdout, true) + t.Error(dmp.DiffPrettyText(diffs)) + } + }) } }