Skip to content

Latest commit

 

History

History
214 lines (167 loc) · 5.19 KB

README.md

File metadata and controls

214 lines (167 loc) · 5.19 KB

go-clix

Cli-X is a command line library for Go, inspired by spf13/cobra.

  • 📦 struct based API: Similar to cobra, go-clix/cli features a struct based API for easy composition and discovery of available options.
  • 🚸 Subcommands: cli.Command can be nested for a git 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 for bash, zsh and fish. But you can generate suggestions using Go code!

Getting started

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)
    }
}

Subcommands

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!

Flags

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
}

Aliases

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.

Completion

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:

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, like bash does when having no better idea
  • PredictNone(): 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]")
}

Positional Arguments

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 directories
  • ArgsExact(n): accepts exactly n arguments. 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),
    }
}