Cobra

repository·main·Indexed 13 days ago

https://github.com/spf13/cobra

A library for creating modern CLI applications in Go, featuring support for subcommands, POSIX-compliant flags, and automatic help generation. It includes the cobra-cli generator for scaffolding and provides advanced features such as shell autocomplete (bash, zsh, fish, powershell), man page generation, and positional argument validation via PositionalArgs.

Tokens
26.7K
Snippets
99
Records
130
Agent score
97%

What's inside Cobra

  1. Overview of Cobra features

    main

    Cobra provides a complete suite of features for building modern CLI tools:

    • Subcommands: Easy support for nested subcommands (e.g., app server).
    • POSIX-compliant Flags: Supports both short and long versions of flags.
    • Flag Scoping: Supports global, local, and cascading (persistent) flags.
    • User Experience:
      • Intelligent suggestions for typos.
      • Automatic help generation and flag recognition (-h, --help).
      • Grouped help for subcommands.
    • Automation:
      • Automatically generated shell autocomplete (bash, zsh, fish, powershell).
      • Automatically generated man pages.
    • Extensibility:
      • Command aliases for backward compatibility.
      • Seamless integration with viper for 12-factor applications.
  2. What is Active Help and how does it work?

    main

    Active Help is a framework in Cobra that allows a program to display helpful messages (hints, warnings, etc.) during shell completion. It is designed to guide users when the standard completion system doesn't provide suggestions (e.g., when a user is missing a required argument).

    Key Characteristics:

    • Trigger: Messages are printed when the user triggers shell completion (e.g., pressing [TAB]).
    • Implementation: It is implemented by enhancing custom dynamic completions.
    • Supported Shells:
  3. How PreRun and PostRun hooks work

    main

    Cobra allows you to execute functions before or after the main Run function of a command. These hooks follow a specific execution order and have different inheritance behaviors:

    Execution Order:

    1. PersistentPreRun
    2. PreRun
    3. Run
    4. PostRun
    5. PersistentPostRun

    Key Behaviors:

    • Inheritance: PersistentPreRun and PersistentPostRun are inherited by child commands if the child does not declare its own. PreRun and PostRun are local to the command and are not inherited.
    • Requirement: *PreRun and *PostRun functions are only executed if the command has a Run function declared.
    • Traversal: By default, only the first persistent hook found in the command chain is executed. If you want to execute all parents' persistent hooks, set the global variable EnableTraverseRunHooks = true.
    package main
    
    import (
      "fmt"
      "github.com/spf13/cobra"
    )
    
    func main() {
      var rootCmd = &cobra.Command{
        Use:   "root [sub]",
        PersistentPreRun: func(cmd *cobra.Command, args []string) {
          fmt.Printf("Inside rootCmd PersistentPreRun\n")
        },
        Run: func(cmd *cobra.Command, args []string) {
          fmt.Printf("Inside rootCmd Run\n")
        },
      }
    
      var subCmd = &cobra.Command{
        Use: "sub",
        Run: func(cmd *cobra.Command, args []string) {
          fmt.Printf("Inside subCmd Run\n")
        },
      }
    
      rootCmd.AddCommand(subCmd)
      rootCmd.Execute()
    }
  4. Understand Cobra core concepts: Commands, Args, and Flags

    main

    Cobra applications are built on a hierarchy of three main components:

    1. Commands: Represent actions (e.g., server, clone). Commands are the central point of the application and can have child commands.
    2. Args (Arguments): Represent the "things" the command acts upon (e.g., a URL).
    3. Flags: Modifiers for actions (e.g., --port, --bare). Flags are provided by the pflag library and can be local to a command or persistent (cascading to child commands).

    A common pattern for CLI interaction is: APPNAME COMMAND ARG --FLAG (e.g., git clone URL --bare)

    # Example pattern
    hugo server --port=1313
    
    git clone URL --bare
  5. Organize subcommands using AddCommand

    main

    Commands can be nested to create a hierarchy (e.g., app sub1 sub2). Use AddCommand to build this tree. To avoid cyclic references in large applications, use the init() function of each command file to add itself to its immediate parent.

    Example Hierarchy:

    • root.go's init() adds sub1.go to rootCmd.
    • sub1.go's init() adds sub2.go to sub1Cmd.
    • sub2.go's init() adds leafA.go and leafB.go to sub2Cmd.
  6. Use legacy dynamic completions for Bash

    main

    Cobra supports a legacy dynamic completion solution specifically for the Bash shell. This is used to inject custom Bash functions into the completion script to provide dynamic choices.

    Important Considerations:

    • This solution is only compatible with Bash; it will not work for other shells.
    • It can coexist with the newer ValidArgsFunction and RegisterFlagCompletionFunc() as long as they are not used for the same command.
    • Warning: Cobra's default completion command uses Bash completion V2. If you rely on this legacy solution, do not use the default completion command; continue using your own custom completion script.

    To implement this, you define a string containing Bash functions and assign it to the BashCompletionFunction field of your cobra.Command (typically on the root command).

    cmds := &cobra.Command{
    	Use:   "kubectl",
    	Short: "kubectl controls the Kubernetes cluster manager",
    	Run:   runHelp,
    	BashCompletionFunction: bash_completion_func, // string containing Bash function definitions
    }
  7. Migrate deprecated Zsh completion APIs to ValidArgsFunction

    main

    Cobra 1.1 deprecated several Zsh-specific methods in favor of a unified ValidArgsFunction approach. If you are using older versions, you should migrate to avoid silent ignores.

    Deprecated Positional Argument Methods

    • cmd.MarkZshCompPositionalArgumentFile(pos, []string{}): Deprecated. File completion is now enabled by default for all arguments. To disable it for a specific argument, use ValidArgsFunction with ShellCompDirectiveNoFileComp.
    • cmd.MarkZshCompPositionalArgumentFile(pos, glob[]): Deprecated. To filter by file extension instead of full globbing, use ValidArgsFunction with ShellCompDirectiveFilterFileExt.
    • cmd.MarkZshCompPositionalArgumentWords(pos, words[]): Deprecated. Use ValidArgsFunction to provide specific completion choices for a positional argument.

    Summary of Behavioral Changes

    FeatureOld BehaviorNew Behavior
    Noun completionNo file completion by defaultFile completion enabled by default; use ShellCompDirectiveNoFileComp to disable
    Flag completionFlags completed without - prefixFlags only completed after the first - is typed
    File filteringZsh-specific globbingUse ShellCompDirectiveFilterFileExt for extension filtering
    Flag-value completionNo file completion by defaultFile completion enabled by default; use RegisterFlagCompletionFunc() with ShellCompDirectiveNoFileComp to disable
  8. Manage ShellCompDirective and file completion

    main

    Cobra uses ShellCompDirectiveDefault by default, which invokes the shell's filename completion. If a command or flag does not operate on filenames, you should disable file completion to avoid incorrect suggestions.

    Ways to manage directives:

    • Disable file completion for a specific flag: Use cobra.NoFileCompletions in a completion function.
    • Recursively disable file completion for a command and all subcommands: Use cmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp).
    • Re-enable file completion for specific leaf commands/flags: Use cobra.FixedCompletions(nil, ShellCompDirectiveDefault) within a completion function.
    // Disable file completion for a specific flag
    cmd.RegisterFlagCompletionFunc("flag-name", cobra.NoFileCompletions)
    
    // Recursively set default for a command tree
    cmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp)
    
    // Re-enable file completion for a specific flag in a NoFileComp tree
    cmd.RegisterFlagCompletionFunc("flag-name", cobra.FixedCompletions(nil, ShellCompDirectiveDefault))
  9. Standard Cobra Application Structure

    main

    A typical Cobra application follows a specific directory structure to keep commands organized. The main.go file is kept minimal, serving only to initialize the Cobra command tree, while individual commands are defined in a cmd/ directory.

    ▾ appName/
      ▾ cmd/
          add.go
          your.go
          commands.go
          here.go
      main.go
  10. Customize Help output and grouping

    main

    Cobra automatically generates help commands (help and --help). You can customize this behavior:

    Grouping Commands: Use AddGroup() on a parent command to define groups. Then, assign a subcommand to a group using its GroupID field. Groups appear in the help output in the order they were defined.

    Customizing Help:

    • SetHelpCommand(cmd *Command): Provide your own help command.
    • SetHelpFunc(f func(*Command, []string)): Provide a custom help function.
    • SetHelpTemplate(s string): Provide a custom text/template for help output.

    For generated commands, use SetHelpCommandGroupId() and SetCompletionCommandGroupId() on the root command.

  11. Initialize Cobra in main.go

    main

    The main.go file should be very bare. Its primary purpose is to call the Execute() function of your root command (typically located in your cmd package) to start the application.

    package main
    
    import "{pathToYourApp}/cmd"
    
    func main() {
      cmd.Execute()
    }
  12. Create a custom completion command

    main

    If you need to provide your own completion command (e.g., for backwards compatibility or custom behavior), you can implement it manually. If using cobra-cli, run cobra-cli add completion and then implement the Run function using Cobra's generation methods:

    • cmd.Root().GenBashCompletion(os.Stdout)
    • cmd.Root().GenZshCompletion(os.Stdout)
    • cmd.Root().GenFishCompletion(os.Stdout, true)
    • cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)

    Warning: Ensure your command does not print anything to stdout other than the completion script itself (e.g., from config file loading), as this will break the shell completion logic.

    var completionCmd = &cobra.Command{
    	Use:   "completion [bash|zsh|fish|powershell]",
    	Short: "Generate completion script",
    	ValidArgs:             []string{"bash", "zsh", "fish", "powershell"},
    	Args:                  cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
    	Run: func(cmd *cobra.Command, args []string) {
    		switch args[0] {
    		case "bash":
    			cmd.Root().GenBashCompletion(os.Stdout)
    		case "zsh":
    			cmd.Root().GenZshCompletion(os.Stdout)
    		case "fish":
    			cmd.Root().GenFishCompletion(os.Stdout, true)
    		case "powershell":
    			cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
    		}
    	},
    }