1 // Copyright © 2013 Steve Francia <spf@spf13.com>. |
1 // Copyright 2013-2022 The Cobra Authors |
2 // |
2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); |
3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. |
4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at |
5 // You may obtain a copy of the License at |
6 // http://www.apache.org/licenses/LICENSE-2.0 |
6 // |
|
7 // http://www.apache.org/licenses/LICENSE-2.0 |
7 // |
8 // |
8 // Unless required by applicable law or agreed to in writing, software |
9 // Unless required by applicable law or agreed to in writing, software |
9 // distributed under the License is distributed on an "AS IS" BASIS, |
10 // distributed under the License is distributed on an "AS IS" BASIS, |
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
11 // See the License for the specific language governing permissions and |
12 // See the License for the specific language governing permissions and |
27 "strings" |
28 "strings" |
28 |
29 |
29 flag "github.com/spf13/pflag" |
30 flag "github.com/spf13/pflag" |
30 ) |
31 ) |
31 |
32 |
|
33 const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" |
|
34 |
32 // FParseErrWhitelist configures Flag parse errors to be ignored |
35 // FParseErrWhitelist configures Flag parse errors to be ignored |
33 type FParseErrWhitelist flag.ParseErrorsWhitelist |
36 type FParseErrWhitelist flag.ParseErrorsWhitelist |
|
37 |
|
38 // Structure to manage groups for commands |
|
39 type Group struct { |
|
40 ID string |
|
41 Title string |
|
42 } |
34 |
43 |
35 // Command is just that, a command for your application. |
44 // Command is just that, a command for your application. |
36 // E.g. 'go run ...' - 'run' is the command. Cobra requires |
45 // E.g. 'go run ...' - 'run' is the command. Cobra requires |
37 // you to define the usage and description as part of your command |
46 // you to define the usage and description as part of your command |
38 // definition to ensure usability. |
47 // definition to ensure usability. |
55 // similar to aliases but only suggests. |
64 // similar to aliases but only suggests. |
56 SuggestFor []string |
65 SuggestFor []string |
57 |
66 |
58 // Short is the short description shown in the 'help' output. |
67 // Short is the short description shown in the 'help' output. |
59 Short string |
68 Short string |
|
69 |
|
70 // The group id under which this subcommand is grouped in the 'help' output of its parent. |
|
71 GroupID string |
60 |
72 |
61 // Long is the long message shown in the 'help <this-command>' output. |
73 // Long is the long message shown in the 'help <this-command>' output. |
62 Long string |
74 Long string |
63 |
75 |
64 // Example is examples of how to use the command. |
76 // Example is examples of how to use the command. |
123 // PersistentPostRun: children of this command will inherit and execute after PostRun. |
135 // PersistentPostRun: children of this command will inherit and execute after PostRun. |
124 PersistentPostRun func(cmd *Command, args []string) |
136 PersistentPostRun func(cmd *Command, args []string) |
125 // PersistentPostRunE: PersistentPostRun but returns an error. |
137 // PersistentPostRunE: PersistentPostRun but returns an error. |
126 PersistentPostRunE func(cmd *Command, args []string) error |
138 PersistentPostRunE func(cmd *Command, args []string) error |
127 |
139 |
|
140 // groups for subcommands |
|
141 commandgroups []*Group |
|
142 |
128 // args is actual args parsed from flags. |
143 // args is actual args parsed from flags. |
129 args []string |
144 args []string |
130 // flagErrorBuf contains all error messages from pflag. |
145 // flagErrorBuf contains all error messages from pflag. |
131 flagErrorBuf *bytes.Buffer |
146 flagErrorBuf *bytes.Buffer |
132 // flags is full set of flags. |
147 // flags is full set of flags. |
155 // helpFunc is help func defined by user. |
170 // helpFunc is help func defined by user. |
156 helpFunc func(*Command, []string) |
171 helpFunc func(*Command, []string) |
157 // helpCommand is command with usage 'help'. If it's not defined by user, |
172 // helpCommand is command with usage 'help'. If it's not defined by user, |
158 // cobra uses default help command. |
173 // cobra uses default help command. |
159 helpCommand *Command |
174 helpCommand *Command |
|
175 // helpCommandGroupID is the group id for the helpCommand |
|
176 helpCommandGroupID string |
|
177 |
|
178 // completionCommandGroupID is the group id for the completion command |
|
179 completionCommandGroupID string |
|
180 |
160 // versionTemplate is the version template defined by user. |
181 // versionTemplate is the version template defined by user. |
161 versionTemplate string |
182 versionTemplate string |
162 |
183 |
163 // inReader is a reader defined by the user that replaces stdin |
184 // inReader is a reader defined by the user that replaces stdin |
164 inReader io.Reader |
185 inReader io.Reader |
234 // returned by Context after one of these functions has been called. |
255 // returned by Context after one of these functions has been called. |
235 func (c *Command) Context() context.Context { |
256 func (c *Command) Context() context.Context { |
236 return c.ctx |
257 return c.ctx |
237 } |
258 } |
238 |
259 |
239 // SetContext sets context for the command. It is set to context.Background by default and will be overwritten by |
260 // SetContext sets context for the command. This context will be overwritten by |
240 // Command.ExecuteContext or Command.ExecuteContextC |
261 // Command.ExecuteContext or Command.ExecuteContextC. |
241 func (c *Command) SetContext(ctx context.Context) { |
262 func (c *Command) SetContext(ctx context.Context) { |
242 c.ctx = ctx |
263 c.ctx = ctx |
243 } |
264 } |
244 |
265 |
245 // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden |
266 // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden |
296 } |
317 } |
297 |
318 |
298 // SetHelpCommand sets help command. |
319 // SetHelpCommand sets help command. |
299 func (c *Command) SetHelpCommand(cmd *Command) { |
320 func (c *Command) SetHelpCommand(cmd *Command) { |
300 c.helpCommand = cmd |
321 c.helpCommand = cmd |
|
322 } |
|
323 |
|
324 // SetHelpCommandGroup sets the group id of the help command. |
|
325 func (c *Command) SetHelpCommandGroupID(groupID string) { |
|
326 if c.helpCommand != nil { |
|
327 c.helpCommand.GroupID = groupID |
|
328 } |
|
329 // helpCommandGroupID is used if no helpCommand is defined by the user |
|
330 c.helpCommandGroupID = groupID |
|
331 } |
|
332 |
|
333 // SetCompletionCommandGroup sets the group id of the completion command. |
|
334 func (c *Command) SetCompletionCommandGroupID(groupID string) { |
|
335 // completionCommandGroupID is used if no completion command is defined by the user |
|
336 c.Root().completionCommandGroupID = groupID |
301 } |
337 } |
302 |
338 |
303 // SetHelpTemplate sets help template to be used. Application can use it to set custom template. |
339 // SetHelpTemplate sets help template to be used. Application can use it to set custom template. |
304 func (c *Command) SetHelpTemplate(s string) { |
340 func (c *Command) SetHelpTemplate(s string) { |
305 c.helpTemplate = s |
341 c.helpTemplate = s |
506 |
542 |
507 Aliases: |
543 Aliases: |
508 {{.NameAndAliases}}{{end}}{{if .HasExample}} |
544 {{.NameAndAliases}}{{end}}{{if .HasExample}} |
509 |
545 |
510 Examples: |
546 Examples: |
511 {{.Example}}{{end}}{{if .HasAvailableSubCommands}} |
547 {{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} |
512 |
548 |
513 Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} |
549 Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} |
514 {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} |
550 {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} |
|
551 |
|
552 {{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} |
|
553 {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} |
|
554 |
|
555 Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} |
|
556 {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} |
515 |
557 |
516 Flags: |
558 Flags: |
517 {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} |
559 {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} |
518 |
560 |
519 Global Flags: |
561 Global Flags: |
674 } |
716 } |
675 |
717 |
676 func (c *Command) findNext(next string) *Command { |
718 func (c *Command) findNext(next string) *Command { |
677 matches := make([]*Command, 0) |
719 matches := make([]*Command, 0) |
678 for _, cmd := range c.commands { |
720 for _, cmd := range c.commands { |
679 if cmd.Name() == next || cmd.HasAlias(next) { |
721 if commandNameMatches(cmd.Name(), next) || cmd.HasAlias(next) { |
680 cmd.commandCalledAs.name = next |
722 cmd.commandCalledAs.name = next |
681 return cmd |
723 return cmd |
682 } |
724 } |
683 if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) { |
725 if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) { |
684 matches = append(matches, cmd) |
726 matches = append(matches, cmd) |
859 } |
903 } |
860 } else if c.PreRun != nil { |
904 } else if c.PreRun != nil { |
861 c.PreRun(c, argWoFlags) |
905 c.PreRun(c, argWoFlags) |
862 } |
906 } |
863 |
907 |
864 if err := c.validateRequiredFlags(); err != nil { |
908 if err := c.ValidateRequiredFlags(); err != nil { |
865 return err |
909 return err |
866 } |
910 } |
867 if err := c.validateFlagGroups(); err != nil { |
911 if err := c.ValidateFlagGroups(); err != nil { |
868 return err |
912 return err |
869 } |
913 } |
870 |
914 |
871 if c.RunE != nil { |
915 if c.RunE != nil { |
872 if err := c.RunE(c, argWoFlags); err != nil { |
916 if err := c.RunE(c, argWoFlags); err != nil { |
901 for _, x := range initializers { |
945 for _, x := range initializers { |
902 x() |
946 x() |
903 } |
947 } |
904 } |
948 } |
905 |
949 |
|
950 func (c *Command) postRun() { |
|
951 for _, x := range finalizers { |
|
952 x() |
|
953 } |
|
954 } |
|
955 |
906 // ExecuteContext is the same as Execute(), but sets the ctx on the command. |
956 // ExecuteContext is the same as Execute(), but sets the ctx on the command. |
907 // Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs |
957 // Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs |
908 // functions. |
958 // functions. |
909 func (c *Command) ExecuteContext(ctx context.Context) error { |
959 func (c *Command) ExecuteContext(ctx context.Context) error { |
910 c.ctx = ctx |
960 c.ctx = ctx |
944 } |
994 } |
945 |
995 |
946 // initialize help at the last point to allow for user overriding |
996 // initialize help at the last point to allow for user overriding |
947 c.InitDefaultHelpCmd() |
997 c.InitDefaultHelpCmd() |
948 // initialize completion at the last point to allow for user overriding |
998 // initialize completion at the last point to allow for user overriding |
949 c.initDefaultCompletionCmd() |
999 c.InitDefaultCompletionCmd() |
|
1000 |
|
1001 // Now that all commands have been created, let's make sure all groups |
|
1002 // are properly created also |
|
1003 c.checkCommandGroups() |
950 |
1004 |
951 args := c.args |
1005 args := c.args |
952 |
1006 |
953 // Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155 |
1007 // Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155 |
954 if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" { |
1008 if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" { |
1016 return ArbitraryArgs(c, args) |
1070 return ArbitraryArgs(c, args) |
1017 } |
1071 } |
1018 return c.Args(c, args) |
1072 return c.Args(c, args) |
1019 } |
1073 } |
1020 |
1074 |
1021 func (c *Command) validateRequiredFlags() error { |
1075 // ValidateRequiredFlags validates all required flags are present and returns an error otherwise |
|
1076 func (c *Command) ValidateRequiredFlags() error { |
1022 if c.DisableFlagParsing { |
1077 if c.DisableFlagParsing { |
1023 return nil |
1078 return nil |
1024 } |
1079 } |
1025 |
1080 |
1026 flags := c.Flags() |
1081 flags := c.Flags() |
1037 |
1092 |
1038 if len(missingFlagNames) > 0 { |
1093 if len(missingFlagNames) > 0 { |
1039 return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`)) |
1094 return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`)) |
1040 } |
1095 } |
1041 return nil |
1096 return nil |
|
1097 } |
|
1098 |
|
1099 // checkCommandGroups checks if a command has been added to a group that does not exists. |
|
1100 // If so, we panic because it indicates a coding error that should be corrected. |
|
1101 func (c *Command) checkCommandGroups() { |
|
1102 for _, sub := range c.commands { |
|
1103 // if Group is not defined let the developer know right away |
|
1104 if sub.GroupID != "" && !c.ContainsGroup(sub.GroupID) { |
|
1105 panic(fmt.Sprintf("group id '%s' is not defined for subcommand '%s'", sub.GroupID, sub.CommandPath())) |
|
1106 } |
|
1107 |
|
1108 sub.checkCommandGroups() |
|
1109 } |
1042 } |
1110 } |
1043 |
1111 |
1044 // InitDefaultHelpFlag adds default help flag to c. |
1112 // InitDefaultHelpFlag adds default help flag to c. |
1045 // It is called automatically by executing the c or by calling help and usage. |
1113 // It is called automatically by executing the c or by calling help and usage. |
1046 // If c already has help flag, it will do nothing. |
1114 // If c already has help flag, it will do nothing. |
1052 usage += "this command" |
1120 usage += "this command" |
1053 } else { |
1121 } else { |
1054 usage += c.Name() |
1122 usage += c.Name() |
1055 } |
1123 } |
1056 c.Flags().BoolP("help", "h", false, usage) |
1124 c.Flags().BoolP("help", "h", false, usage) |
|
1125 _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) |
1057 } |
1126 } |
1058 } |
1127 } |
1059 |
1128 |
1060 // InitDefaultVersionFlag adds default version flag to c. |
1129 // InitDefaultVersionFlag adds default version flag to c. |
1061 // It is called automatically by executing the c. |
1130 // It is called automatically by executing the c. |
1077 if c.Flags().ShorthandLookup("v") == nil { |
1146 if c.Flags().ShorthandLookup("v") == nil { |
1078 c.Flags().BoolP("version", "v", false, usage) |
1147 c.Flags().BoolP("version", "v", false, usage) |
1079 } else { |
1148 } else { |
1080 c.Flags().Bool("version", false, usage) |
1149 c.Flags().Bool("version", false, usage) |
1081 } |
1150 } |
|
1151 _ = c.Flags().SetAnnotation("version", FlagSetByCobraAnnotation, []string{"true"}) |
1082 } |
1152 } |
1083 } |
1153 } |
1084 |
1154 |
1085 // InitDefaultHelpCmd adds default help command to c. |
1155 // InitDefaultHelpCmd adds default help command to c. |
1086 // It is called automatically by executing the c or by calling help and usage. |
1156 // It is called automatically by executing the c or by calling help and usage. |
1119 cmd, _, e := c.Root().Find(args) |
1189 cmd, _, e := c.Root().Find(args) |
1120 if cmd == nil || e != nil { |
1190 if cmd == nil || e != nil { |
1121 c.Printf("Unknown help topic %#q\n", args) |
1191 c.Printf("Unknown help topic %#q\n", args) |
1122 CheckErr(c.Root().Usage()) |
1192 CheckErr(c.Root().Usage()) |
1123 } else { |
1193 } else { |
1124 cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown |
1194 cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown |
|
1195 cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown |
1125 CheckErr(cmd.Help()) |
1196 CheckErr(cmd.Help()) |
1126 } |
1197 } |
1127 }, |
1198 }, |
|
1199 GroupID: c.helpCommandGroupID, |
1128 } |
1200 } |
1129 } |
1201 } |
1130 c.RemoveCommand(c.helpCommand) |
1202 c.RemoveCommand(c.helpCommand) |
1131 c.AddCommand(c.helpCommand) |
1203 c.AddCommand(c.helpCommand) |
1132 } |
1204 } |
1181 x.SetGlobalNormalizationFunc(c.globNormFunc) |
1253 x.SetGlobalNormalizationFunc(c.globNormFunc) |
1182 } |
1254 } |
1183 c.commands = append(c.commands, x) |
1255 c.commands = append(c.commands, x) |
1184 c.commandsAreSorted = false |
1256 c.commandsAreSorted = false |
1185 } |
1257 } |
|
1258 } |
|
1259 |
|
1260 // Groups returns a slice of child command groups. |
|
1261 func (c *Command) Groups() []*Group { |
|
1262 return c.commandgroups |
|
1263 } |
|
1264 |
|
1265 // AllChildCommandsHaveGroup returns if all subcommands are assigned to a group |
|
1266 func (c *Command) AllChildCommandsHaveGroup() bool { |
|
1267 for _, sub := range c.commands { |
|
1268 if (sub.IsAvailableCommand() || sub == c.helpCommand) && sub.GroupID == "" { |
|
1269 return false |
|
1270 } |
|
1271 } |
|
1272 return true |
|
1273 } |
|
1274 |
|
1275 // ContainGroups return if groupID exists in the list of command groups. |
|
1276 func (c *Command) ContainsGroup(groupID string) bool { |
|
1277 for _, x := range c.commandgroups { |
|
1278 if x.ID == groupID { |
|
1279 return true |
|
1280 } |
|
1281 } |
|
1282 return false |
|
1283 } |
|
1284 |
|
1285 // AddGroup adds one or more command groups to this parent command. |
|
1286 func (c *Command) AddGroup(groups ...*Group) { |
|
1287 c.commandgroups = append(c.commandgroups, groups...) |
1186 } |
1288 } |
1187 |
1289 |
1188 // RemoveCommand removes one or more commands from a parent command. |
1290 // RemoveCommand removes one or more commands from a parent command. |
1189 func (c *Command) RemoveCommand(cmds ...*Command) { |
1291 func (c *Command) RemoveCommand(cmds ...*Command) { |
1190 commands := []*Command{} |
1292 commands := []*Command{} |
1503 if c.globNormFunc != nil { |
1605 if c.globNormFunc != nil { |
1504 c.lflags.SetNormalizeFunc(c.globNormFunc) |
1606 c.lflags.SetNormalizeFunc(c.globNormFunc) |
1505 } |
1607 } |
1506 |
1608 |
1507 addToLocal := func(f *flag.Flag) { |
1609 addToLocal := func(f *flag.Flag) { |
1508 if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil { |
1610 // Add the flag if it is not a parent PFlag, or it shadows a parent PFlag |
|
1611 if c.lflags.Lookup(f.Name) == nil && f != c.parentsPflags.Lookup(f.Name) { |
1509 c.lflags.AddFlag(f) |
1612 c.lflags.AddFlag(f) |
1510 } |
1613 } |
1511 } |
1614 } |
1512 c.Flags().VisitAll(addToLocal) |
1615 c.Flags().VisitAll(addToLocal) |
1513 c.PersistentFlags().VisitAll(addToLocal) |
1616 c.PersistentFlags().VisitAll(addToLocal) |
1692 |
1795 |
1693 c.VisitParents(func(parent *Command) { |
1796 c.VisitParents(func(parent *Command) { |
1694 c.parentsPflags.AddFlagSet(parent.PersistentFlags()) |
1797 c.parentsPflags.AddFlagSet(parent.PersistentFlags()) |
1695 }) |
1798 }) |
1696 } |
1799 } |
|
1800 |
|
1801 // commandNameMatches checks if two command names are equal |
|
1802 // taking into account case sensitivity according to |
|
1803 // EnableCaseInsensitive global configuration. |
|
1804 func commandNameMatches(s string, t string) bool { |
|
1805 if EnableCaseInsensitive { |
|
1806 return strings.EqualFold(s, t) |
|
1807 } |
|
1808 |
|
1809 return s == t |
|
1810 } |