From 4acc9f0d2c4d5f4eba2f54006ed675a492d6cd24 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 29 Oct 2021 12:10:39 -0400 Subject: [PATCH] main: Error out if an unrecognized subcommand is provided Surprisingly, the spf13/cobra CLI parsing logic, when presented with an unknown subcommand outputs usage to stdout and *exits successfully*. This is bad for both users and scripts. Cargo cult some code I found in podman to handle this. Motivated by https://github.com/containers/containers-image-proxy-rs/pull/1 Signed-off-by: Colin Walters --- cmd/skopeo/main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmd/skopeo/main.go b/cmd/skopeo/main.go index d9a4b2ea..a6714ce2 100644 --- a/cmd/skopeo/main.go +++ b/cmd/skopeo/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "strings" "time" commonFlag "github.com/containers/common/pkg/flag" @@ -34,6 +35,21 @@ type globalOptions struct { tmpDir string // Path to use for big temporary files } +// requireSubcommand returns an error if no sub command is provided +// This was copied from podman: `github.com/containers/podman/cmd/podman/validate/args.go +// Some small style changes to match skopeo were applied, but try to apply any +// bugfixes there first. +func requireSubcommand(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + suggestions := cmd.SuggestionsFor(args[0]) + if len(suggestions) == 0 { + return fmt.Errorf("Unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0]) + } + return fmt.Errorf("Unrecognized command `%[1]s %[2]s`\n\nDid you mean this?\n\t%[3]s\n\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0], strings.Join(suggestions, "\n\t")) + } + return fmt.Errorf("Missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information", cmd.CommandPath()) +} + // createApp returns a cobra.Command, and the underlying globalOptions object, to be run or tested. func createApp() (*cobra.Command, *globalOptions) { opts := globalOptions{} @@ -41,6 +57,7 @@ func createApp() (*cobra.Command, *globalOptions) { rootCommand := &cobra.Command{ Use: "skopeo", Long: "Various operations with container images and container image registries", + RunE: requireSubcommand, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return opts.before(cmd) },