git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/cobra/user_guide.md (about) 1 # User Guide 2 3 While you are welcome to provide your own organization, typically a Cobra-based 4 application will follow the following organizational structure: 5 6 ``` 7 ▾ appName/ 8 ▾ cmd/ 9 add.go 10 your.go 11 commands.go 12 here.go 13 main.go 14 ``` 15 16 In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra. 17 18 ```go 19 package main 20 21 import ( 22 "{pathToYourApp}/cmd" 23 ) 24 25 func main() { 26 cmd.Execute() 27 } 28 ``` 29 30 ## Using the Cobra Generator 31 32 Cobra-CLI is its own program that will create your application and add any 33 commands you want. It's the easiest way to incorporate Cobra into your application. 34 35 For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) 36 37 ## Using the Cobra Library 38 39 To manually implement Cobra you need to create a bare main.go file and a rootCmd file. 40 You will optionally provide additional commands as you see fit. 41 42 ### Create rootCmd 43 44 Cobra doesn't require any special constructors. Simply create your commands. 45 46 Ideally you place this in app/cmd/root.go: 47 48 ```go 49 var rootCmd = &cobra.Command{ 50 Use: "hugo", 51 Short: "Hugo is a very fast static site generator", 52 Long: `A Fast and Flexible Static Site Generator built with 53 love by spf13 and friends in Go. 54 Complete documentation is available at https://gohugo.io/documentation/`, 55 Run: func(cmd *cobra.Command, args []string) { 56 // Do Stuff Here 57 }, 58 } 59 60 func Execute() { 61 if err := rootCmd.Execute(); err != nil { 62 fmt.Fprintln(os.Stderr, err) 63 os.Exit(1) 64 } 65 } 66 ``` 67 68 You will additionally define flags and handle configuration in your init() function. 69 70 For example cmd/root.go: 71 72 ```go 73 package cmd 74 75 import ( 76 "fmt" 77 "os" 78 79 "github.com/spf13/cobra" 80 "github.com/spf13/viper" 81 ) 82 83 var ( 84 // Used for flags. 85 cfgFile string 86 userLicense string 87 88 rootCmd = &cobra.Command{ 89 Use: "cobra-cli", 90 Short: "A generator for Cobra based Applications", 91 Long: `Cobra is a CLI library for Go that empowers applications. 92 This application is a tool to generate the needed files 93 to quickly create a Cobra application.`, 94 } 95 ) 96 97 // Execute executes the root command. 98 func Execute() error { 99 return rootCmd.Execute() 100 } 101 102 func init() { 103 cobra.OnInitialize(initConfig) 104 105 rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") 106 rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution") 107 rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project") 108 rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration") 109 viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) 110 viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) 111 viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>") 112 viper.SetDefault("license", "apache") 113 114 rootCmd.AddCommand(addCmd) 115 rootCmd.AddCommand(initCmd) 116 } 117 118 func initConfig() { 119 if cfgFile != "" { 120 // Use config file from the flag. 121 viper.SetConfigFile(cfgFile) 122 } else { 123 // Find home directory. 124 home, err := os.UserHomeDir() 125 cobra.CheckErr(err) 126 127 // Search config in home directory with name ".cobra" (without extension). 128 viper.AddConfigPath(home) 129 viper.SetConfigType("yaml") 130 viper.SetConfigName(".cobra") 131 } 132 133 viper.AutomaticEnv() 134 135 if err := viper.ReadInConfig(); err == nil { 136 fmt.Println("Using config file:", viper.ConfigFileUsed()) 137 } 138 } 139 ``` 140 141 ### Create your main.go 142 143 With the root command you need to have your main function execute it. 144 Execute should be run on the root for clarity, though it can be called on any command. 145 146 In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra. 147 148 ```go 149 package main 150 151 import ( 152 "{pathToYourApp}/cmd" 153 ) 154 155 func main() { 156 cmd.Execute() 157 } 158 ``` 159 160 ### Create additional commands 161 162 Additional commands can be defined and typically are each given their own file 163 inside of the cmd/ directory. 164 165 If you wanted to create a version command you would create cmd/version.go and 166 populate it with the following: 167 168 ```go 169 package cmd 170 171 import ( 172 "fmt" 173 174 "github.com/spf13/cobra" 175 ) 176 177 func init() { 178 rootCmd.AddCommand(versionCmd) 179 } 180 181 var versionCmd = &cobra.Command{ 182 Use: "version", 183 Short: "Print the version number of Hugo", 184 Long: `All software has versions. This is Hugo's`, 185 Run: func(cmd *cobra.Command, args []string) { 186 fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") 187 }, 188 } 189 ``` 190 191 ### Returning and handling errors 192 193 If you wish to return an error to the caller of a command, `RunE` can be used. 194 195 ```go 196 package cmd 197 198 import ( 199 "fmt" 200 201 "github.com/spf13/cobra" 202 ) 203 204 func init() { 205 rootCmd.AddCommand(tryCmd) 206 } 207 208 var tryCmd = &cobra.Command{ 209 Use: "try", 210 Short: "Try and possibly fail at something", 211 RunE: func(cmd *cobra.Command, args []string) error { 212 if err := someFunc(); err != nil { 213 return err 214 } 215 return nil 216 }, 217 } 218 ``` 219 220 The error can then be caught at the execute function call. 221 222 ## Working with Flags 223 224 Flags provide modifiers to control how the action command operates. 225 226 ### Assign flags to a command 227 228 Since the flags are defined and used in different locations, we need to 229 define a variable outside with the correct scope to assign the flag to 230 work with. 231 232 ```go 233 var Verbose bool 234 var Source string 235 ``` 236 237 There are two different approaches to assign a flag. 238 239 ### Persistent Flags 240 241 A flag can be 'persistent', meaning that this flag will be available to the 242 command it's assigned to as well as every command under that command. For 243 global flags, assign a flag as a persistent flag on the root. 244 245 ```go 246 rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") 247 ``` 248 249 ### Local Flags 250 251 A flag can also be assigned locally, which will only apply to that specific command. 252 253 ```go 254 localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") 255 ``` 256 257 ### Local Flag on Parent Commands 258 259 By default, Cobra only parses local flags on the target command, and any local flags on 260 parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will 261 parse local flags on each command before executing the target command. 262 263 ```go 264 command := cobra.Command{ 265 Use: "print [OPTIONS] [COMMANDS]", 266 TraverseChildren: true, 267 } 268 ``` 269 270 ### Bind Flags with Config 271 272 You can also bind your flags with [viper](https://github.com/spf13/viper): 273 ```go 274 var author string 275 276 func init() { 277 rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution") 278 viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) 279 } 280 ``` 281 282 In this example, the persistent flag `author` is bound with `viper`. 283 **Note**: the variable `author` will not be set to the value from config, 284 when the `--author` flag is provided by user. 285 286 More in [viper documentation](https://github.com/spf13/viper#working-with-flags). 287 288 ### Required flags 289 290 Flags are optional by default. If instead you wish your command to report an error 291 when a flag has not been set, mark it as required: 292 ```go 293 rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)") 294 rootCmd.MarkFlagRequired("region") 295 ``` 296 297 Or, for persistent flags: 298 ```go 299 rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)") 300 rootCmd.MarkPersistentFlagRequired("region") 301 ``` 302 303 ### Flag Groups 304 305 If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then 306 Cobra can enforce that requirement: 307 ```go 308 rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)") 309 rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)") 310 rootCmd.MarkFlagsRequiredTogether("username", "password") 311 ``` 312 313 You can also prevent different flags from being provided together if they represent mutually 314 exclusive options such as specifying an output format as either `--json` or `--yaml` but never both: 315 ```go 316 rootCmd.Flags().BoolVar(&u, "json", false, "Output in JSON") 317 rootCmd.Flags().BoolVar(&pw, "yaml", false, "Output in YAML") 318 rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") 319 ``` 320 321 In both of these cases: 322 - both local and persistent flags can be used 323 - **NOTE:** the group is only enforced on commands where every flag is defined 324 - a flag may appear in multiple groups 325 - a group may contain any number of flags 326 327 ## Positional and Custom Arguments 328 329 Validation of positional arguments can be specified using the `Args` field of `Command`. 330 The following validators are built in: 331 332 - Number of arguments: 333 - `NoArgs` - report an error if there are any positional args. 334 - `ArbitraryArgs` - accept any number of args. 335 - `MinimumNArgs(int)` - report an error if less than N positional args are provided. 336 - `MaximumNArgs(int)` - report an error if more than N positional args are provided. 337 - `ExactArgs(int)` - report an error if there are not exactly N positional args. 338 - `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`. 339 - Content of the arguments: 340 - `OnlyValidArgs` - report an error if there are any positional args not specified in the `ValidArgs` field of `Command`, which can optionally be set to a list of valid values for positional args. 341 342 If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`. 343 344 Moreover, `MatchAll(pargs ...PositionalArgs)` enables combining existing checks with arbitrary other checks. 345 For instance, if you want to report an error if there are not exactly N positional args OR if there are any positional 346 args that are not in the `ValidArgs` field of `Command`, you can call `MatchAll` on `ExactArgs` and `OnlyValidArgs`, as 347 shown below: 348 349 ```go 350 var cmd = &cobra.Command{ 351 Short: "hello", 352 Args: MatchAll(ExactArgs(2), OnlyValidArgs), 353 Run: func(cmd *cobra.Command, args []string) { 354 fmt.Println("Hello, World!") 355 }, 356 } 357 ``` 358 359 It is possible to set any custom validator that satisfies `func(cmd *cobra.Command, args []string) error`. 360 For example: 361 362 ```go 363 var cmd = &cobra.Command{ 364 Short: "hello", 365 Args: func(cmd *cobra.Command, args []string) error { 366 // Optionally run one of the validators provided by cobra 367 if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { 368 return err 369 } 370 // Run the custom validation logic 371 if myapp.IsValidColor(args[0]) { 372 return nil 373 } 374 return fmt.Errorf("invalid color specified: %s", args[0]) 375 }, 376 Run: func(cmd *cobra.Command, args []string) { 377 fmt.Println("Hello, World!") 378 }, 379 } 380 ``` 381 382 ## Example 383 384 In the example below, we have defined three commands. Two are at the top level 385 and one (cmdTimes) is a child of one of the top commands. In this case the root 386 is not executable, meaning that a subcommand is required. This is accomplished 387 by not providing a 'Run' for the 'rootCmd'. 388 389 We have only defined one flag for a single command. 390 391 More documentation about flags is available at https://github.com/spf13/pflag 392 393 ```go 394 package main 395 396 import ( 397 "fmt" 398 "strings" 399 400 "github.com/spf13/cobra" 401 ) 402 403 func main() { 404 var echoTimes int 405 406 var cmdPrint = &cobra.Command{ 407 Use: "print [string to print]", 408 Short: "Print anything to the screen", 409 Long: `print is for printing anything back to the screen. 410 For many years people have printed back to the screen.`, 411 Args: cobra.MinimumNArgs(1), 412 Run: func(cmd *cobra.Command, args []string) { 413 fmt.Println("Print: " + strings.Join(args, " ")) 414 }, 415 } 416 417 var cmdEcho = &cobra.Command{ 418 Use: "echo [string to echo]", 419 Short: "Echo anything to the screen", 420 Long: `echo is for echoing anything back. 421 Echo works a lot like print, except it has a child command.`, 422 Args: cobra.MinimumNArgs(1), 423 Run: func(cmd *cobra.Command, args []string) { 424 fmt.Println("Echo: " + strings.Join(args, " ")) 425 }, 426 } 427 428 var cmdTimes = &cobra.Command{ 429 Use: "times [string to echo]", 430 Short: "Echo anything to the screen more times", 431 Long: `echo things multiple times back to the user by providing 432 a count and a string.`, 433 Args: cobra.MinimumNArgs(1), 434 Run: func(cmd *cobra.Command, args []string) { 435 for i := 0; i < echoTimes; i++ { 436 fmt.Println("Echo: " + strings.Join(args, " ")) 437 } 438 }, 439 } 440 441 cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input") 442 443 var rootCmd = &cobra.Command{Use: "app"} 444 rootCmd.AddCommand(cmdPrint, cmdEcho) 445 cmdEcho.AddCommand(cmdTimes) 446 rootCmd.Execute() 447 } 448 ``` 449 450 For a more complete example of a larger application, please checkout [Hugo](https://gohugo.io/). 451 452 ## Help Command 453 454 Cobra automatically adds a help command to your application when you have subcommands. 455 This will be called when a user runs 'app help'. Additionally, help will also 456 support all other commands as input. Say, for instance, you have a command called 457 'create' without any additional configuration; Cobra will work when 'app help 458 create' is called. Every command will automatically have the '--help' flag added. 459 460 ### Example 461 462 The following output is automatically generated by Cobra. Nothing beyond the 463 command and flag definitions are needed. 464 465 $ cobra-cli help 466 467 Cobra is a CLI library for Go that empowers applications. 468 This application is a tool to generate the needed files 469 to quickly create a Cobra application. 470 471 Usage: 472 cobra-cli [command] 473 474 Available Commands: 475 add Add a command to a Cobra Application 476 completion Generate the autocompletion script for the specified shell 477 help Help about any command 478 init Initialize a Cobra Application 479 480 Flags: 481 -a, --author string author name for copyright attribution (default "YOUR NAME") 482 --config string config file (default is $HOME/.cobra.yaml) 483 -h, --help help for cobra-cli 484 -l, --license string name of license for the project 485 --viper use Viper for configuration 486 487 Use "cobra-cli [command] --help" for more information about a command. 488 489 490 Help is just a command like any other. There is no special logic or behavior 491 around it. In fact, you can provide your own if you want. 492 493 ### Defining your own help 494 495 You can provide your own Help command or your own template for the default command to use 496 with the following functions: 497 498 ```go 499 cmd.SetHelpCommand(cmd *Command) 500 cmd.SetHelpFunc(f func(*Command, []string)) 501 cmd.SetHelpTemplate(s string) 502 ``` 503 504 The latter two will also apply to any children commands. 505 506 ## Usage Message 507 508 When the user provides an invalid flag or invalid command, Cobra responds by 509 showing the user the 'usage'. 510 511 ### Example 512 You may recognize this from the help above. That's because the default help 513 embeds the usage as part of its output. 514 515 $ cobra-cli --invalid 516 Error: unknown flag: --invalid 517 Usage: 518 cobra-cli [command] 519 520 Available Commands: 521 add Add a command to a Cobra Application 522 completion Generate the autocompletion script for the specified shell 523 help Help about any command 524 init Initialize a Cobra Application 525 526 Flags: 527 -a, --author string author name for copyright attribution (default "YOUR NAME") 528 --config string config file (default is $HOME/.cobra.yaml) 529 -h, --help help for cobra-cli 530 -l, --license string name of license for the project 531 --viper use Viper for configuration 532 533 Use "cobra [command] --help" for more information about a command. 534 535 ### Defining your own usage 536 You can provide your own usage function or template for Cobra to use. 537 Like help, the function and template are overridable through public methods: 538 539 ```go 540 cmd.SetUsageFunc(f func(*Command) error) 541 cmd.SetUsageTemplate(s string) 542 ``` 543 544 ## Version Flag 545 546 Cobra adds a top-level '--version' flag if the Version field is set on the root command. 547 Running an application with the '--version' flag will print the version to stdout using 548 the version template. The template can be customized using the 549 `cmd.SetVersionTemplate(s string)` function. 550 551 ## PreRun and PostRun Hooks 552 553 It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: 554 555 - `PersistentPreRun` 556 - `PreRun` 557 - `Run` 558 - `PostRun` 559 - `PersistentPostRun` 560 561 An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`: 562 563 ```go 564 package main 565 566 import ( 567 "fmt" 568 569 "github.com/spf13/cobra" 570 ) 571 572 func main() { 573 574 var rootCmd = &cobra.Command{ 575 Use: "root [sub]", 576 Short: "My root command", 577 PersistentPreRun: func(cmd *cobra.Command, args []string) { 578 fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) 579 }, 580 PreRun: func(cmd *cobra.Command, args []string) { 581 fmt.Printf("Inside rootCmd PreRun with args: %v\n", args) 582 }, 583 Run: func(cmd *cobra.Command, args []string) { 584 fmt.Printf("Inside rootCmd Run with args: %v\n", args) 585 }, 586 PostRun: func(cmd *cobra.Command, args []string) { 587 fmt.Printf("Inside rootCmd PostRun with args: %v\n", args) 588 }, 589 PersistentPostRun: func(cmd *cobra.Command, args []string) { 590 fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) 591 }, 592 } 593 594 var subCmd = &cobra.Command{ 595 Use: "sub [no options!]", 596 Short: "My subcommand", 597 PreRun: func(cmd *cobra.Command, args []string) { 598 fmt.Printf("Inside subCmd PreRun with args: %v\n", args) 599 }, 600 Run: func(cmd *cobra.Command, args []string) { 601 fmt.Printf("Inside subCmd Run with args: %v\n", args) 602 }, 603 PostRun: func(cmd *cobra.Command, args []string) { 604 fmt.Printf("Inside subCmd PostRun with args: %v\n", args) 605 }, 606 PersistentPostRun: func(cmd *cobra.Command, args []string) { 607 fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) 608 }, 609 } 610 611 rootCmd.AddCommand(subCmd) 612 613 rootCmd.SetArgs([]string{""}) 614 rootCmd.Execute() 615 fmt.Println() 616 rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) 617 rootCmd.Execute() 618 } 619 ``` 620 621 Output: 622 ``` 623 Inside rootCmd PersistentPreRun with args: [] 624 Inside rootCmd PreRun with args: [] 625 Inside rootCmd Run with args: [] 626 Inside rootCmd PostRun with args: [] 627 Inside rootCmd PersistentPostRun with args: [] 628 629 Inside rootCmd PersistentPreRun with args: [arg1 arg2] 630 Inside subCmd PreRun with args: [arg1 arg2] 631 Inside subCmd Run with args: [arg1 arg2] 632 Inside subCmd PostRun with args: [arg1 arg2] 633 Inside subCmd PersistentPostRun with args: [arg1 arg2] 634 ``` 635 636 ## Suggestions when "unknown command" happens 637 638 Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example: 639 640 ``` 641 $ hugo srever 642 Error: unknown command "srever" for "hugo" 643 644 Did you mean this? 645 server 646 647 Run 'hugo --help' for usage. 648 ``` 649 650 Suggestions are automatically generated based on existing subcommands and use an implementation of [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion. 651 652 If you need to disable suggestions or tweak the string distance in your command, use: 653 654 ```go 655 command.DisableSuggestions = true 656 ``` 657 658 or 659 660 ```go 661 command.SuggestionsMinimumDistance = 1 662 ``` 663 664 You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which 665 you don't want aliases. Example: 666 667 ``` 668 $ kubectl remove 669 Error: unknown command "remove" for "kubectl" 670 671 Did you mean this? 672 delete 673 674 Run 'kubectl help' for usage. 675 ``` 676 677 ## Generating documentation for your command 678 679 Cobra can generate documentation based on subcommands, flags, etc. Read more about it in the [docs generation documentation](doc/README.md). 680 681 ## Generating shell completions 682 683 Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md). 684 685 ## Providing Active Help 686 687 Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users. Active Help are messages (hints, warnings, etc) printed as the program is being used. Read more about it in [Active Help](active_help.md).