Cli-X is a command line library for Go, inspired by
spf13/cobra
.
- 📦
struct
based API: Similar tocobra
,go-clix/cli
features astruct
based API for easy composition and discovery of available options. - 🚸 Subcommands:
cli.Command
can be nested for agit
like experience. - 📌 Flags: Every command has it's own set of flags. POSIX compliant
using
spf13/pflag
. - 👥 Aliases: Commands can have multiple names, so breaking changes can easily be mitigated.
- 🎯 Go based completion:
<TAB>
completion is supported forbash
,zsh
andfish
. But you can generate suggestions using Go code!
Add the library to your project:
$ go get github.com/go-clix/cli
Then set up your root command:
package main
import (
"fmt"
"github.com/go-clix/cli"
)
func main() {
// create the root command
rootCmd := cli.Command{
Use: "greet",
Short: "print a message",
Run: func(cmd *cli.Command, args []string) error {
fmt.Println("Hello from Cli-X!")
},
}
// run and check for errors
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Every command may have children:
// use a func to return a Command instead of
// a global variable and `init()`
func applyCmd() *cli.Command {
cmd := &cli.Command{
Use: "apply",
Short: "apply the changes"
}
cmd.Run = func(cmd *cli.Command, args []string) error {
fmt.Println("applied", args[0])
}
return cmd
}
func main() {
rootCmd := &cli.Comand{
Use: "kubectl",
Short: "Kubernetes management tool",
}
// add the child command
rootCmd.AddChildren(
applyCmd(),
)
// run and check for errors
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Note: Do not use the
init()
function for setting up your commands. Create constructors as shown above!
A pflag.FlagSet
can be accessed per command using *Command.Flags()
:
func applyCmd() *cli.Command {
cmd := &cli.Command{
Use: "apply",
Short: "apply the changes"
}
force := cmd.Flags().BoolP("force", "f", false, "skip checks")
cmd.Run = func(cmd *cli.Command, args []string) error {
fmt.Println("applied", args[0])
if *force {
fmt.Println("The force was with us.")
}
}
return cmd
}
To make the apply
subcommand also available as make
and do
:
func applyCmd() *cli.Command {
cmd := &cli.Command{
Use: "apply",
Aliases: []string{"make", "do"},
Short: "apply the changes"
}
}
Keep in mind that in --help
outputs the command will still be called apply
.
Cli-X has a very powerful cli completion system, based on
posener/complete
.
It is powerful, because suggestions come directly from the Go application, not
from a bash
script or similar.
Command and Flag names are automatically suggested, custom suggestions can be implemented for positional arguments and flag values:
Custom suggestions for flag values can be set up using Command.Predictors
.
To do so, you need to add a complete.Predictor
for that flag. Cli-X has a
number of predefined ones:
PredictAny()
: Predicts available files and directories, likebash
does when having no better ideaPredictNone()
: Predicts literally nothing. No suggestions will be shown.PredictSet(...string)
: Suggests from a predefined slice of options
If that's not sufficient, use PredictFunc(func(complete.Args) []string)
to
create your own.
import (
"github.com/posener/complete"
"github.com/go-clix/cli"
)
func logsCmd() *cli.Command {
cmd := &cli.Command{
Use: "logs",
Short: "show logs of a pod",
Predictors: map[string]complete.Predictor{
"output": cli.PredictSet("lines", "json", "logfmt"),
},
}
output := cmd.Flags().StringP("output", "o", "lines", "one of [lines, json, logfmt]")
}
For positional arguments, Cli-X uses a slightly different interface
, to allow
validation as well:
interface {
Validate(args []string) error
Predict(Args) []string
}
Predefined options are also available:
ArgsAny()
: accepts any number args, predicts files and directoriesArgsExact(n)
: accepts exactlyn
arguments. Predicts files and directories.ArgsRange(n, m)
: accepts betweenn
andm
arguments (inclusive). Predicts files and directories.ArgsNone()
: accepts no args and predicts nothing.ArgsSet(...string)
: Accepts one argument, which MUST be included in the given set. Predicts the values from the set.
import (
"github.com/posener/complete"
"github.com/go-clix/cli"
)
func applyCmd() *cli.Command {
cmd := &cli.Command{
Use: "logs",
Short: "show logs of a pod",
// we expect one argument which can be anything
Args: cli.ArgsExact(1),
}
}