Documentation ¶
Overview ¶
Package cobrax helpers for the commonly used https://github.com/spf13/cobra library. It specifically aims to make commands composable and reusable, as well as to make testing easier.
Example (ExecDependency) ¶
ExampleExecDependency shows how a cobra command can be executed like os/exec.Command, but without the OS overhead.
package main import ( "context" "fmt" "time" "github.com/spf13/cobra" "github.com/zepatrik/cobrax" ) // NewKetoCmd returns the whole tree of Keto commands, including the server and client commands. // This would be imported from [github.com/ory/keto/cmd.NewRootCmd]. func NewKetoCmd() *cobra.Command { return &cobra.Command{ Use: "keto", Short: "Global and consistent permission and authorization server by Ory", } } // ExampleExecDependency shows how a cobra command can be executed like [os/exec.Command], but without the OS overhead. func main() { serverCtx, serverCancel := context.WithCancel(context.Background()) defer serverCancel() eg := cobrax.ExecBackgroundCtx(serverCtx, NewKetoCmd(), nil, nil, nil, "serve", "--config", "keto.yml") defer func() { fmt.Printf("Keto server exited with error: %v", eg.Wait()) }() ctx, cancel := context.WithCancel(context.Background()) defer cancel() cmd := cobrax.CommandExecutor{ New: NewKetoCmd, Ctx: ctx, PersistentArgs: []string{"--read-remote", "localhost:4466"}, } // Blocks until the command exits. statusCtx, statusCancel := context.WithTimeout(ctx, 10*time.Second) defer statusCancel() _, _, _ = cmd.ExecCtx(statusCtx, nil, "status", "--block") stdOut, _, err := cmd.Exec(nil, "check", "relation-tuple", "--namespace", "default", "--object", "article:1", "--relation", "view", "--subject", "user:1") if err != nil { fmt.Printf("Keto client gave unexpected error: %v", err) return } fmt.Printf("Keto client gave output: %s", stdOut) stdOut, _, err = cmd.Exec(nil, "check", "relation-tuple", "--namespace", "default", "--object", "article:1", "--relation", "view", "--subject", "user:2") // ...and so on }
Output:
Example (ExecuteRoot) ¶
ExampleExecuteRoot shows how to use the helpers for the spf13/cobra.Command.RunE and main function.
package main import ( "bufio" "fmt" "runtime/debug" "github.com/spf13/cobra" "github.com/zepatrik/cobrax" ) // NewRootCmd creates a new root greetme command. This function takes care of all the flag initialization and registering all subcommands. func NewRootCmd() *cobra.Command { var errorExitCode int cmd := &cobra.Command{ Use: "greetme", Short: "This is a friendly program to greet you.", RunE: func(cmd *cobra.Command, args []string) error { fmt.Fprintln(cmd.ErrOrStderr(), "Hello, what is your name?") name, err := bufio.NewReader(cmd.InOrStdin()).ReadString('\n') if err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "Error reading name: %v", err) return cobrax.WithExitCode(cobrax.FailSilently(cmd), errorExitCode) } fmt.Fprintf(cmd.ErrOrStderr(), "Hello %s\n", name) return nil }, } cmd.Flags().IntVar(&errorExitCode, "exit-code", 1, "Exit code to return on error") cmd.AddCommand(NewVersionCmd("")) return cmd } // NewVersionCmd returns a new version subcommand. This could be in a totally different package, and reused by multiple projects. // The version can be passed as a string. If the version is empty, the version embedded by [runtime/debug.ReadBuildInfo] is used. func NewVersionCmd(version string) *cobra.Command { cmd := &cobra.Command{ Use: "version", Short: "Prints the version of this program.", RunE: func(cmd *cobra.Command, args []string) error { if version == "" { bi, ok := debug.ReadBuildInfo() if !ok { fmt.Fprintln(cmd.ErrOrStderr(), "No version information available.") return cobrax.FailSilently(cmd) } version = bi.Main.Version } fmt.Fprintf(cmd.OutOrStdout(), "Version: %s", version) return nil }, } cmd.AddCommand(newVersionSubCmd()) return cmd } // NewVersionSubCmd returns a new version subcommand. This would be in the same package as the version command. // It could depend on the version command as a parent, e.g. to access persistent flags, and would therefore not be exported. func newVersionSubCmd() *cobra.Command { return &cobra.Command{ Use: "deeply-nested", Short: "This commmand is an example for a deeply nested subcommand.", RunE: func(cmd *cobra.Command, args []string) error { // Do stuff return nil }, } } // ExampleExecuteRoot shows how to use the helpers for the [spf13/cobra.Command.RunE] and main function. func main() { cobrax.ExecuteRootCommand(NewRootCmd()) }
Output:
Index ¶
- Variables
- func AddUsageTemplateFunc(name string, f interface{})
- func AssertUsageTemplates(t TestingT, cmd *cobra.Command)
- func DisableUsageTemplating(cmds ...*cobra.Command)
- func EnableUsageTemplating(cmds ...*cobra.Command)
- func Exec(cmd *cobra.Command, stdIn io.Reader, args ...string) (stdOut string, stdErr string, err error)
- func ExecBackgroundCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, ...) *errgroup.Group
- func ExecCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, args ...string) (stdOut string, stdErr string, err error)
- func ExecExpectedErr(t TestingT, cmd *cobra.Command, args ...string) (stdErr string)
- func ExecExpectedErrCtx(ctx context.Context, t TestingT, cmd *cobra.Command, args ...string) (stdErr string)
- func ExecNoErr(t TestingT, cmd *cobra.Command, args ...string) (stdOut string)
- func ExecNoErrCtx(ctx context.Context, t TestingT, cmd *cobra.Command, args ...string) string
- func ExecuteRootCommand(cmd *cobra.Command)
- func ExecuteRootCommandContext(ctx context.Context, cmd *cobra.Command)
- func FailSilently(cmd *cobra.Command) error
- func WithExitCode(err error, exitCode int) error
- type CommandExecutor
- func (c *CommandExecutor) Exec(stdin io.Reader, args ...string) (stdOut string, stdErr string, err error)
- func (c *CommandExecutor) ExecBackground(stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group
- func (c *CommandExecutor) ExecBackgroundCtx(ctx context.Context, stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group
- func (c *CommandExecutor) ExecCtx(ctx context.Context, stdin io.Reader, args ...string) (stdOut string, stdErr string, err error)
- func (c *CommandExecutor) ExecExpectedErr(t TestingT, args ...string) string
- func (c *CommandExecutor) ExecNoErr(t TestingT, args ...string) string
- type ExitCodeCarrier
- type TestingT
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoPrintButFail = errors.New("this error should never be printed")
ErrNoPrintButFail is returned to detect a failure state that was already reported to the user in some way
Functions ¶
func AddUsageTemplateFunc ¶
func AddUsageTemplateFunc(name string, f interface{})
AddUsageTemplateFunc adds a template function to the usage template.
func AssertUsageTemplates ¶
AssertUsageTemplates asserts that the usage string of the commands are properly templated.
func DisableUsageTemplating ¶
DisableUsageTemplating resets the commands usage template to the default. This can be used to undo the effects of EnableUsageTemplating, specifically for a subcommand.
func EnableUsageTemplating ¶
EnableUsageTemplating enables gotemplates for usage strings, i.e. cmd.Short, cmd.Long, and cmd.Example. The data for the template is the command itself. Especially useful are `.Root.Name` and `.CommandPath`. This will be inherited by all subcommands, so enabling it on the root command is sufficient.
func Exec ¶
func Exec(cmd *cobra.Command, stdIn io.Reader, args ...string) (stdOut string, stdErr string, err error)
Exec runs the provided cobra command with the given reader as STD_IN and the given args. This function can also be used outside of tests.
func ExecBackgroundCtx ¶
func ExecBackgroundCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group
ExecBackgroundCtx runs the cobra command in the background. This function can also be used outside of tests. Pass nil for stdIn, stdOur, or stdErr to use os.Std*.
func ExecCtx ¶
func ExecCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, args ...string) (stdOut string, stdErr string, err error)
ExecCtx is the same as Exec but with a user-supplied context. This function can also be used outside of tests.
func ExecExpectedErr ¶
ExecExpectedErr is a test helper that assumes a failing run from Exec returning ErrNoPrintButFail.
func ExecExpectedErrCtx ¶
func ExecExpectedErrCtx(ctx context.Context, t TestingT, cmd *cobra.Command, args ...string) (stdErr string)
ExecExpectedErrCtx is the same as ExecExpectedErr but with a user-supplied context.
func ExecNoErrCtx ¶
ExecNoErrCtx is the same as ExecNoErr but with a user-supplied context.
func ExecuteRootCommand ¶
ExecuteRootCommand executes the given command (usually the root command) and exits the process with a non-zero exit code if an error occurs. The error is only printed if it is not ErrNoPrintButFail, because in that case we assume the error was already printed. To customize the status code you can add an error to the error chain that implements the ExitCodeCarrier interface.
func ExecuteRootCommandContext ¶
ExecuteRootCommandContext is the same as ExecuteRootCommand but with a user-supplied context.
func FailSilently ¶
FailSilently is supposed to be used within a commands RunE function. It silences cobras default error handling and returns the ErrNoPrintButFail error.
func WithExitCode ¶
Types ¶
type CommandExecutor ¶
type CommandExecutor struct { New func() *cobra.Command Ctx context.Context PersistentArgs []string }
CommandExecutor is a struct that can be used to execute a cobra command multiple times.
func (*CommandExecutor) Exec ¶
func (c *CommandExecutor) Exec(stdin io.Reader, args ...string) (stdOut string, stdErr string, err error)
Exec runs the cobra command with the given reader as STD_IN and the given args appended to the persistent args.
func (*CommandExecutor) ExecBackground ¶
func (c *CommandExecutor) ExecBackground(stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group
ExecBackground runs the cobra command in the background with the given reader as STD_IN and the given args appended to the persistent args.
func (*CommandExecutor) ExecBackgroundCtx ¶
func (c *CommandExecutor) ExecBackgroundCtx(ctx context.Context, stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group
ExecBackgroundCtx is the same as [ExecBackground] but with a user-supplied context.
func (*CommandExecutor) ExecCtx ¶
func (c *CommandExecutor) ExecCtx(ctx context.Context, stdin io.Reader, args ...string) (stdOut string, stdErr string, err error)
ExecCtx is the same as Exec but with a user-supplied context.
func (*CommandExecutor) ExecExpectedErr ¶
func (c *CommandExecutor) ExecExpectedErr(t TestingT, args ...string) string
ExecExpectedErr is a test helper that assumes a failing run returning ErrNoPrintButFail. The args are appended to the persistent args.
type ExitCodeCarrier ¶
type TestingT ¶
type TestingT interface { Errorf(format string, args ...interface{}) FailNow() Helper() }
TestingT is an interface excerpt of testing.TB.