Skip to content

Commit

Permalink
Update help text template to fix target usage (vmware-tanzu#94)
Browse files Browse the repository at this point in the history
* Update help text template to fix target usage

* Add two spaces for tanzu command

* Convert the help template to go code

Signed-off-by: Marc Khouzam <[email protected]>

* testing the lint and tests

* refactor the print help

* refactor the indentation

* Update check to HasPrefix from HasSuffix

* Update tests to include global flags and fetch --help command

* Update additional help topics section with tanzu and target prefix

* Revert additional help topics targeted prefix

* Deprecate CmdTemplate and introduce private cmdTemplate variable and update all usages

* Fix sub command unit tests by executing plugin instead of command

* Fix spacing in examples section and update tests

---------

Signed-off-by: Marc Khouzam <[email protected]>
Co-authored-by: Marc Khouzam <[email protected]>
  • Loading branch information
mpanchajanya and marckhouzam authored Sep 15, 2023
1 parent 15f5bef commit 3923e33
Show file tree
Hide file tree
Showing 3 changed files with 546 additions and 7 deletions.
2 changes: 1 addition & 1 deletion plugin/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func newRootCmd(descriptor *PluginDescriptor) *cobra.Command {
},
}
cobra.AddTemplateFuncs(TemplateFuncs)
cmd.SetUsageTemplate(CmdTemplate)
cmd.SetUsageTemplate(cmdTemplate)

cmd.AddCommand(
newDescribeCmd(descriptor.Description),
Expand Down
139 changes: 138 additions & 1 deletion plugin/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@ package plugin

import (
"os"
"strings"
"text/template"

"github.com/spf13/cobra"

"github.com/vmware-tanzu/tanzu-plugin-runtime/component"
"github.com/vmware-tanzu/tanzu-plugin-runtime/config/types"
)

// UsageFunc is the usage func for a plugin.
var UsageFunc = func(c *cobra.Command) error {
t, err := template.New("usage").Funcs(TemplateFuncs).Parse(CmdTemplate)
t, err := template.New("usage").Funcs(TemplateFuncs).Parse(cmdTemplate)
if err != nil {
return err
}
return t.Execute(os.Stdout, c)
}

// CmdTemplate is the template for plugin commands.
// Deprecated: This variable is deprecated.
const CmdTemplate = `{{ bold "Usage:" }}
{{if .Runnable}}{{ $target := index .Annotations "target" }}{{ if or (eq $target "kubernetes") (eq $target "k8s") }}tanzu {{.UseLine}}{{ end }}{{ if and (ne $target "global") (ne $target "") }}tanzu {{ $target }} {{ else }} {{ end }}{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}{{ $target := index .Annotations "target" }}{{ if or (eq $target "kubernetes") (eq $target "k8s") }}tanzu {{.CommandPath}} [command]{{end}}{{ if and (ne $target "global") (ne $target "") }}tanzu {{ $target }} {{ else }} {{ end }}{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Expand All @@ -46,8 +49,142 @@ const CmdTemplate = `{{ bold "Usage:" }}
{{ $target := index .Annotations "target" }}{{ if or (eq $target "kubernetes") (eq $target "k8s") }}Use "{{if beginsWith .CommandPath "tanzu "}}{{.CommandPath}}{{else}}tanzu {{.CommandPath}}{{end}} [command] --help" for more information about a command.{{end}}Use "{{if beginsWith .CommandPath "tanzu "}}{{.CommandPath}}{{else}}tanzu{{ $target := index .Annotations "target" }}{{ if and (ne $target "global") (ne $target "") }} {{ $target }} {{ else }} {{ end }}{{.CommandPath}}{{end}} [command] --help" for more information about a command.{{end}}
`

// cmdTemplate is the template for plugin commands.
const cmdTemplate = `{{ printHelp . }}`

// Constants for help text labels
const (
usageStr = "Usage:"
aliasesStr = "Aliases:"
examplesStr = "Examples:"
availableCommandsStr = "Available Commands:"
flagsStr = "Flags:"
globalFlagsStr = "Global Flags:"
additionalHelpTopicsStr = "Additional help topics:"
indentStr = " "
)

// Helper to format the usage help section.
func formatUsageHelpSection(cmd *cobra.Command, target types.Target) string {
var output strings.Builder

output.WriteString(component.Bold(usageStr) + "\n")
base := indentStr + "tanzu "

if cmd.Runnable() {
// For kubernetes, k8s, global, or no target display tanzu command path without target
if target == types.TargetK8s || target == types.TargetGlobal || target == types.TargetUnknown {
output.WriteString(base + cmd.UseLine() + "\n")
}

// For non global, or no target ;display tanzu command path with target
if target != types.TargetGlobal && target != types.TargetUnknown {
output.WriteString(base + string(target) + " " + cmd.UseLine() + "\n")
}
}

if cmd.HasAvailableSubCommands() {
if cmd.Runnable() {
// If the command is both Runnable and has sub-commands, let's insert an empty
// line between the usage for the Runnable and the one for the sub-commands
output.WriteString("\n")
}
// For kubernetes, k8s, global, or no target display tanzu command path without target
if target == types.TargetK8s || target == types.TargetGlobal || target == types.TargetUnknown {
output.WriteString(base + cmd.CommandPath() + " [command]\n")
}

// For non global, or no target display tanzu command path with target
if target != types.TargetGlobal && target != types.TargetUnknown {
output.WriteString(base + string(target) + " " + cmd.CommandPath() + " [command]\n")
}
}
return output.String()
}

// Helper to format the help footer.
func formatHelpFooter(cmd *cobra.Command, target types.Target) string {
var footer strings.Builder
if !cmd.HasAvailableSubCommands() {
return ""
}

footer.WriteString("\n")

// For kubernetes, k8s, global, or no target display tanzu command path without target
if target == types.TargetK8s || target == types.TargetGlobal || target == types.TargetUnknown {
footer.WriteString(`Use "`)
if !strings.HasPrefix(cmd.CommandPath(), "tanzu ") {
footer.WriteString("tanzu ")
}
footer.WriteString(cmd.CommandPath() + ` [command] --help" for more information about a command.` + "\n")
}

// For non global, or no target display tanzu command path with target
if target != types.TargetGlobal && target != types.TargetUnknown {
footer.WriteString(`Use "`)
if !strings.HasPrefix(cmd.CommandPath(), "tanzu ") {
footer.WriteString("tanzu ")
}
footer.WriteString(string(target) + " " + cmd.CommandPath() + ` [command] --help" for more information about a command.` + "\n")
}

return footer.String()
}

func printHelp(cmd *cobra.Command) string {
var output strings.Builder
target := types.StringToTarget(cmd.Annotations["target"])

output.WriteString(formatUsageHelpSection(cmd, target))

if len(cmd.Aliases) > 0 {
output.WriteString("\n" + component.Bold(aliasesStr) + "\n")
output.WriteString(indentStr + cmd.NameAndAliases() + "\n")
}

if cmd.HasExample() {
output.WriteString("\n" + component.Bold(examplesStr) + "\n")
output.WriteString(indentStr + cmd.Example + "\n")
}

if cmd.HasAvailableSubCommands() {
output.WriteString("\n" + component.Bold(availableCommandsStr) + "\n")
for _, c := range cmd.Commands() {
if c.IsAvailableCommand() {
output.WriteString(indentStr + component.Rpad(c.Name(), c.NamePadding()) + " " + c.Short + "\n")
}
}
}

if cmd.HasAvailableLocalFlags() {
output.WriteString("\n" + component.Bold(flagsStr) + "\n")
output.WriteString(strings.TrimRight(cmd.LocalFlags().FlagUsages(), " "))
}

if cmd.HasAvailableInheritedFlags() {
output.WriteString("\n" + component.Bold(globalFlagsStr) + "\n")
output.WriteString(strings.TrimRight(cmd.InheritedFlags().FlagUsages(), " "))
}

if cmd.HasHelpSubCommands() {
output.WriteString("\n" + component.Bold(additionalHelpTopicsStr) + "\n")
for _, c := range cmd.Commands() {
if c.IsAdditionalHelpTopicCommand() {
output.WriteString(indentStr + component.Rpad(c.CommandPath(), c.CommandPathPadding()) + " " + c.Short + "\n")
}
}
}
output.WriteString(formatHelpFooter(cmd, target))

return output.String()
}

// TemplateFuncs are the template usage funcs.
var TemplateFuncs = template.FuncMap{
"printHelp": printHelp,
// The below are not needed but are kept for backwards-compatibility
// in case it is being used through the API
"rpad": component.Rpad,
"bold": component.Bold,
"underline": component.Underline,
Expand Down
Loading

0 comments on commit 3923e33

Please sign in to comment.