Skip to content

Commit 2736a86

Browse files
committed
feat(cli): support cobra GroupID and set error prefix
Commander layout now reads both cobra's native GroupID and the legacy Annotations["group"] for command grouping. Projects can use either approach while keeping salt's custom help formatting. cli.Init sets SetErrPrefix(name + ":") for consistent error messages across all raystack CLIs (e.g. "Error: frontier: unknown command").
1 parent 71c0949 commit 2736a86

7 files changed

Lines changed: 39 additions & 49 deletions

File tree

cli/cli.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func Init(rootCmd *cobra.Command, opts ...Option) {
4343
opt(cfg)
4444
}
4545

46+
// Set error prefix for consistent error messages.
47+
rootCmd.SetErrPrefix(rootCmd.Name() + ":")
48+
4649
// Inject shared output and prompter into command context.
4750
existing := rootCmd.PersistentPreRun
4851
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {

cli/commander/codex.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ import (
1313
// This command generates a Markdown documentation tree for all commands in the hierarchy.
1414
func (m *Manager) addMarkdownCommand(outputPath string) {
1515
markdownCmd := &cobra.Command{
16-
Use: "markdown",
17-
Short: "Generate Markdown documentation for all commands",
18-
Hidden: true,
19-
Annotations: map[string]string{
20-
"group": "help",
21-
},
16+
Use: "markdown",
17+
Short: "Generate Markdown documentation for all commands",
18+
Hidden: true,
19+
GroupID: "help",
2220
RunE: func(cmd *cobra.Command, args []string) error {
2321
return m.generateMarkdownTree(outputPath, m.RootCmd)
2422
},

cli/commander/layout.go

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
// Section Titles for Help Output
1919
const (
2020
usage = "Usage"
21-
corecmd = "Core commands"
2221
othercmd = "Other commands"
2322
helpcmd = "Help topics"
2423
flags = "Flags"
@@ -86,7 +85,7 @@ func displayHelp(cmd *cobra.Command, args []string) {
8685

8786
// buildHelpEntries constructs a structured help message for a command.
8887
func buildHelpEntries(cmd *cobra.Command) []helpEntry {
89-
var coreCommands, helpCommands, otherCommands []string
88+
var helpCommands, ungroupedCommands []string
9089
groupCommands := map[string][]string{}
9190

9291
for _, c := range cmd.Commands() {
@@ -95,24 +94,15 @@ func buildHelpEntries(cmd *cobra.Command) []helpEntry {
9594
}
9695

9796
entry := fmt.Sprintf("%s%s", rpad(c.Name(), c.NamePadding()+3), c.Short)
98-
if group, ok := c.Annotations["group"]; ok {
99-
switch group {
100-
case "core":
101-
coreCommands = append(coreCommands, entry)
102-
case "help":
103-
helpCommands = append(helpCommands, entry)
104-
default:
105-
groupCommands[group] = append(groupCommands[group], entry)
106-
}
107-
} else {
108-
otherCommands = append(otherCommands, entry)
109-
}
110-
}
11197

112-
// Treat all commands as core if no groups are specified
113-
if len(coreCommands) == 0 && len(groupCommands) == 0 {
114-
coreCommands = otherCommands
115-
otherCommands = []string{}
98+
switch c.GroupID {
99+
case "help":
100+
helpCommands = append(helpCommands, entry)
101+
case "":
102+
ungroupedCommands = append(ungroupedCommands, entry)
103+
default:
104+
groupCommands[c.GroupID] = append(groupCommands[c.GroupID], entry)
105+
}
116106
}
117107

118108
helpEntries := []helpEntry{}
@@ -121,14 +111,16 @@ func buildHelpEntries(cmd *cobra.Command) []helpEntry {
121111
}
122112

123113
helpEntries = append(helpEntries, helpEntry{usage, cmd.UseLine()})
124-
if len(coreCommands) > 0 {
125-
helpEntries = append(helpEntries, helpEntry{corecmd, strings.Join(coreCommands, "\n")})
126-
}
127-
for group, cmds := range groupCommands {
128-
helpEntries = append(helpEntries, helpEntry{fmt.Sprintf("%s commands", toTitle(group)), strings.Join(cmds, "\n")})
129-
}
130-
if len(otherCommands) > 0 {
131-
helpEntries = append(helpEntries, helpEntry{othercmd, strings.Join(otherCommands, "\n")})
114+
if len(ungroupedCommands) > 0 && len(groupCommands) == 0 {
115+
// No groups defined — show all commands under "Commands"
116+
helpEntries = append(helpEntries, helpEntry{"Commands", strings.Join(ungroupedCommands, "\n")})
117+
} else {
118+
for group, cmds := range groupCommands {
119+
helpEntries = append(helpEntries, helpEntry{fmt.Sprintf("%s commands", toTitle(group)), strings.Join(cmds, "\n")})
120+
}
121+
if len(ungroupedCommands) > 0 {
122+
helpEntries = append(helpEntries, helpEntry{othercmd, strings.Join(ungroupedCommands, "\n")})
123+
}
132124
}
133125
if len(helpCommands) > 0 {
134126
helpEntries = append(helpEntries, helpEntry{helpcmd, strings.Join(helpCommands, "\n")})

cli/commander/manager.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ func New(rootCmd *cobra.Command, options ...func(*Manager)) *Manager {
6666
// It enables or disables features like custom help, reference documentation,
6767
// shell completion, help topics, and client hooks based on the Manager's settings.
6868
func (m *Manager) Init() {
69+
// Register the help group used by salt's internal commands
70+
// (reference, completion, topics). Developers register their
71+
// own groups via rootCmd.AddGroup() before calling Init.
72+
m.RootCmd.AddGroup(
73+
&cobra.Group{ID: "help", Title: "Help topics:"},
74+
)
75+
6976
if m.Help {
7077
m.setCustomHelp()
7178
}

cli/commander/reference.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@ import (
1515
func (m *Manager) addReferenceCommand() {
1616
var isPlain bool
1717
refCmd := &cobra.Command{
18-
Use: "reference",
19-
Short: "Comprehensive reference of all commands",
20-
Long: m.generateReferenceMarkdown(),
21-
Run: m.runReferenceCommand(&isPlain),
22-
Annotations: map[string]string{
23-
"group": "help",
24-
},
18+
Use: "reference",
19+
Short: "Comprehensive reference of all commands",
20+
Long: m.generateReferenceMarkdown(),
21+
Run: m.runReferenceCommand(&isPlain),
22+
GroupID: "help",
2523
}
2624
refCmd.SetHelpFunc(m.runReferenceCommand(&isPlain))
2725
refCmd.Flags().BoolVarP(&isPlain, "plain", "p", true, "output in plain markdown (without ANSI color)")

cli/commander/topics.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ func (m *Manager) addHelpTopicCommand(topic HelpTopic) {
2424
Long: topic.Long,
2525
Example: topic.Example,
2626
Hidden: false,
27-
Annotations: map[string]string{
28-
"group": "help",
29-
},
27+
GroupID: "help",
3028
}
3129

3230
helpCmd.SetHelpFunc(helpTopicHelpFunc)

cli/config_cmd.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ func configInitCmd(appName string, defaultCfg interface{}) *cobra.Command {
3535
Use: "init",
3636
Short: "Initialize a new configuration file",
3737
Example: fmt.Sprintf(" $ %s config init", appName),
38-
Annotations: map[string]string{
39-
"group": "core",
40-
},
4138
RunE: func(cmd *cobra.Command, _ []string) error {
4239
loader := config.NewLoader(config.WithAppConfig(appName))
4340
if err := loader.Init(defaultCfg); err != nil {
@@ -54,9 +51,6 @@ func configListCmd(appName string) *cobra.Command {
5451
Use: "list",
5552
Short: "List current configuration",
5653
Example: fmt.Sprintf(" $ %s config list", appName),
57-
Annotations: map[string]string{
58-
"group": "core",
59-
},
6054
RunE: func(cmd *cobra.Command, _ []string) error {
6155
loader := config.NewLoader(config.WithAppConfig(appName))
6256
data, err := loader.View()

0 commit comments

Comments
 (0)