mirror of https://github.com/docker/cli.git
Output broken CLI plugins in `help` output.
Signed-off-by: Ian Campbell <ijc@docker.com>
This commit is contained in:
parent
e5e578abc7
commit
0ab8ec0e4c
44
cli/cobra.go
44
cli/cobra.go
|
@ -24,11 +24,14 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
|
||||||
|
|
||||||
cobra.AddTemplateFunc("hasSubCommands", hasSubCommands)
|
cobra.AddTemplateFunc("hasSubCommands", hasSubCommands)
|
||||||
cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands)
|
cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands)
|
||||||
|
cobra.AddTemplateFunc("hasInvalidPlugins", hasInvalidPlugins)
|
||||||
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
|
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
|
||||||
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
|
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
|
||||||
|
cobra.AddTemplateFunc("invalidPlugins", invalidPlugins)
|
||||||
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
|
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
|
||||||
cobra.AddTemplateFunc("commandVendor", commandVendor)
|
cobra.AddTemplateFunc("commandVendor", commandVendor)
|
||||||
cobra.AddTemplateFunc("isFirstLevelCommand", isFirstLevelCommand) // is it an immediate sub-command of the root
|
cobra.AddTemplateFunc("isFirstLevelCommand", isFirstLevelCommand) // is it an immediate sub-command of the root
|
||||||
|
cobra.AddTemplateFunc("invalidPluginReason", invalidPluginReason)
|
||||||
|
|
||||||
rootCmd.SetUsageTemplate(usageTemplate)
|
rootCmd.SetUsageTemplate(usageTemplate)
|
||||||
rootCmd.SetHelpTemplate(helpTemplate)
|
rootCmd.SetHelpTemplate(helpTemplate)
|
||||||
|
@ -115,6 +118,10 @@ var helpCommand = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isPlugin(cmd *cobra.Command) bool {
|
||||||
|
return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true"
|
||||||
|
}
|
||||||
|
|
||||||
func hasSubCommands(cmd *cobra.Command) bool {
|
func hasSubCommands(cmd *cobra.Command) bool {
|
||||||
return len(operationSubCommands(cmd)) > 0
|
return len(operationSubCommands(cmd)) > 0
|
||||||
}
|
}
|
||||||
|
@ -123,9 +130,16 @@ func hasManagementSubCommands(cmd *cobra.Command) bool {
|
||||||
return len(managementSubCommands(cmd)) > 0
|
return len(managementSubCommands(cmd)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasInvalidPlugins(cmd *cobra.Command) bool {
|
||||||
|
return len(invalidPlugins(cmd)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func operationSubCommands(cmd *cobra.Command) []*cobra.Command {
|
func operationSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||||
cmds := []*cobra.Command{}
|
cmds := []*cobra.Command{}
|
||||||
for _, sub := range cmd.Commands() {
|
for _, sub := range cmd.Commands() {
|
||||||
|
if isPlugin(sub) && invalidPluginReason(sub) != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if sub.IsAvailableCommand() && !sub.HasSubCommands() {
|
if sub.IsAvailableCommand() && !sub.HasSubCommands() {
|
||||||
cmds = append(cmds, sub)
|
cmds = append(cmds, sub)
|
||||||
}
|
}
|
||||||
|
@ -159,6 +173,9 @@ func commandVendor(cmd *cobra.Command) string {
|
||||||
func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||||
cmds := []*cobra.Command{}
|
cmds := []*cobra.Command{}
|
||||||
for _, sub := range cmd.Commands() {
|
for _, sub := range cmd.Commands() {
|
||||||
|
if isPlugin(sub) && invalidPluginReason(sub) != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if sub.IsAvailableCommand() && sub.HasSubCommands() {
|
if sub.IsAvailableCommand() && sub.HasSubCommands() {
|
||||||
cmds = append(cmds, sub)
|
cmds = append(cmds, sub)
|
||||||
}
|
}
|
||||||
|
@ -166,6 +183,23 @@ func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func invalidPlugins(cmd *cobra.Command) []*cobra.Command {
|
||||||
|
cmds := []*cobra.Command{}
|
||||||
|
for _, sub := range cmd.Commands() {
|
||||||
|
if !isPlugin(sub) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if invalidPluginReason(sub) != "" {
|
||||||
|
cmds = append(cmds, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func invalidPluginReason(cmd *cobra.Command) string {
|
||||||
|
return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid]
|
||||||
|
}
|
||||||
|
|
||||||
var usageTemplate = `Usage:
|
var usageTemplate = `Usage:
|
||||||
|
|
||||||
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
|
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
|
||||||
|
@ -209,6 +243,16 @@ Commands:
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
|
{{- if hasInvalidPlugins . }}
|
||||||
|
|
||||||
|
Invalid Plugins:
|
||||||
|
|
||||||
|
{{- range invalidPlugins . }}
|
||||||
|
{{rpad .Name .NamePadding }} {{invalidPluginReason .}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
{{- if .HasSubCommands }}
|
{{- if .HasSubCommands }}
|
||||||
|
|
||||||
Run '{{.CommandPath}} COMMAND --help' for more information on a command.
|
Run '{{.CommandPath}} COMMAND --help' for more information on a command.
|
||||||
|
|
|
@ -4,8 +4,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
|
is "gotest.tools/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVisitAll(t *testing.T) {
|
func TestVisitAll(t *testing.T) {
|
||||||
|
@ -55,3 +57,22 @@ func TestCommandVendor(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidPlugin(t *testing.T) {
|
||||||
|
root := &cobra.Command{Use: "root"}
|
||||||
|
sub1 := &cobra.Command{Use: "sub1"}
|
||||||
|
sub1sub1 := &cobra.Command{Use: "sub1sub1"}
|
||||||
|
sub1sub2 := &cobra.Command{Use: "sub1sub2"}
|
||||||
|
sub2 := &cobra.Command{Use: "sub2"}
|
||||||
|
|
||||||
|
assert.Assert(t, is.Len(invalidPlugins(root), 0))
|
||||||
|
|
||||||
|
sub1.Annotations = map[string]string{
|
||||||
|
pluginmanager.CommandAnnotationPlugin: "true",
|
||||||
|
pluginmanager.CommandAnnotationPluginInvalid: "foo",
|
||||||
|
}
|
||||||
|
root.AddCommand(sub1, sub2)
|
||||||
|
sub1.AddCommand(sub1sub1, sub1sub2)
|
||||||
|
|
||||||
|
assert.DeepEqual(t, invalidPlugins(root), []*cobra.Command{sub1}, cmpopts.IgnoreUnexported(cobra.Command{}))
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ func TestGlobalHelp(t *testing.T) {
|
||||||
// - Each of the main headings
|
// - Each of the main headings
|
||||||
// - Some builtin commands under the main headings
|
// - Some builtin commands under the main headings
|
||||||
// - The `helloworld` plugin in the appropriate place
|
// - The `helloworld` plugin in the appropriate place
|
||||||
|
// - The `badmeta` plugin under the "Invalid Plugins" heading.
|
||||||
//
|
//
|
||||||
// Regexps are needed because the width depends on `unix.TIOCGWINSZ` or similar.
|
// Regexps are needed because the width depends on `unix.TIOCGWINSZ` or similar.
|
||||||
for _, expected := range []*regexp.Regexp{
|
for _, expected := range []*regexp.Regexp{
|
||||||
|
@ -41,6 +42,8 @@ func TestGlobalHelp(t *testing.T) {
|
||||||
regexp.MustCompile(`^ create\s+Create a new container$`),
|
regexp.MustCompile(`^ create\s+Create a new container$`),
|
||||||
regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`),
|
regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`),
|
||||||
regexp.MustCompile(`^ ps\s+List containers$`),
|
regexp.MustCompile(`^ ps\s+List containers$`),
|
||||||
|
regexp.MustCompile(`^Invalid Plugins:$`),
|
||||||
|
regexp.MustCompile(`^ badmeta\s+invalid metadata: invalid character 'i' looking for beginning of object key string$`),
|
||||||
} {
|
} {
|
||||||
var found bool
|
var found bool
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
|
Loading…
Reference in New Issue