Home

Awesome

mcli

GoDoc Go Report Card Coverage Issues GitHub release MIT License

mcli is a minimal but powerful cli library for Go. m stands for minimal and magic.

It is extremely easy to use, it makes you love writing cli programs in Go.

Disclaimer: the original idea is inspired by shafreeck/cortana, which is licensed under the Apache License 2.0.

Features

Usage

Use in main function:

func main() {
    var args struct {
        Name string `cli:"-n, --name, Who do you want to say to" default:"tom"`

        // This argument is required.
        Text string `cli:"#R, text, The 'message' you want to send"`

        // This argument reads environment variable and requires the variable must exist,
        // it doesn't accept input from command line.
        APIAccessKey string `cli:"#ER, The access key to your service provider" env:"MY_API_ACCESS_KEY"`
    }
    mcli.Parse(&args)
    fmt.Printf("Say to %s: %s\n", args.Name, args.Text)
}
$ go run say.go -h
Usage:
  say [flags] <text>

Flags:
  -n, --name <string>    Who do you want to say to
                         [default: "tom"]

Arguments:
  text <message> [REQUIRED]    The message you want to send

Environment Variables:
  - MY_API_ACCESS_KEY <string> [REQUIRED]
    The access key to your service provider

$ MY_API_ACCESS_KEY=xxxx go run say.go hello
Say to tom: hello

Use sub-commands:

func main() {
    mcli.Add("cmd1", runCmd1, "An awesome command cmd1")

    mcli.AddGroup("cmd2", "This is a command group called cmd2")
    mcli.Add("cmd2 sub1", runCmd2Sub1, "Do something with cmd2 sub1")
    mcli.Add("cmd2 sub2", runCmd2Sub2, "Brief description about cmd2 sub2")

    // A sub-command can also be added without registering the group.
    mcli.Add("group3 sub1 subsub1", runGroup3Sub1Subsub1, "Blah blah Blah")

    // This is a hidden command, it won't be showed in help,
    // except that when flag "--mcli-show-hidden" is given.
    mcli.AddHidden("secret-cmd", secretCmd, "An secret command won't be showed in help")

    // Enable shell auto-completion, see `program completion -h` for help.
    mcli.AddCompletion()

    mcli.Run()
}

func runCmd1() {
    var args struct {
        Branch    string `cli:"-b, --branch, Select another branch by passing in the branch name"`
        Commit    bool   `cli:"-c, --commit, Open the last commit"`
        NoBrowser bool   `cli:"-n, --no-browser, Print destination URL instead of opening the browser"`
        Projects  bool   `cli:"-p, --projects, Open repository projects"`
        Repo      string `cli:"-R, --repo, Select another repository using the '[HOST/]OWNER/REPO' format"`
        Settings  bool   `cli:"-s, --settings, Open repository settings"`
        Wiki      bool   `cli:"-w, --wiki, Open repository wiki"`

        Location  string `cli:"location, A browser location can be specified using arguments in the following format:\n- by number for issue or pull request, e.g. \"123\"; or\n- by path for opening folders and files, e.g. \"cmd/gh/main.go\""`
    }
    mcli.Parse(&args)

    // Do something
}

type Cmd2CommonArgs struct {
    Repo string `cli:"-R, --repo, Select another repository using the '[HOST/]OWNER/REPO' format"`
}

func runCmd2Sub1() {
    // Note that the flag/argument description can be seperated either
    // by a comma or spaces, and can be mixed.
    var args struct {
        Body     string `cli:"-b, --body        Supply a body. Will prompt for one otherwise."`
        BodyFile string `cli:"-F, --body-file   Read body text from 'file' (use \"-\" to read from standard input)"`
        Editor   bool   `cli:"-e, --editor,     Add body using editor"`
        Web      bool   `cli:"-w, --web,        Add body in browser"`

        // Can embed other structs.
        Cmd2CommonArgs
    }
    mcli.Parse(&args)

    // Do something
}

Also, there are some sophisticated examples:

API

Use the default App:

Create a new App instance:

Custom options

App:

CmdOpt:

ParseOpt:

Tag syntax

Struct tag is a powerful feature in Go, mcli uses struct tag to define flags and arguments.

The syntax is

/* cli tag, only Name is required.
 * Short name and long name are both optional, but at least one must be given.
 * See below for details about modifiers.
 * e.g.
 * - `cli:"-c, Open the last commit"`
 * - `cli:"#R, -b, --branch, Select another branch by passing in the branch name"`
 * - `cli:"--an-obvious-flag-dont-need-description"`
 * - `cli:"#ER, AWS Secret Access Key" env:"AWS_SECRET_ACCESS_KEY"`
 */
CliTag       <-  ( Modifiers ',' Space? )? Name ( ( ',' | Space ) Description )?
Modifiers    <-  '#' [DHRE]+
Name         <-  ( ShortName LongName? ) | LongName
Description  <-  ( ![\r\n] . )*

/* env tag, optional.
 * Multiple environment names can be specified, the first non-empty value takes effect.
 * e.g.
 * - `env:"SOME_ENV"`
 * - `env:"ANOTHER_ENV_1, ANOTHER_ENV_2"`
 */
EnvTag  <-  ( EnvName ',' Space? )* EnvName

/* default value tag, optional.
 * e.g.
 * - `default:"1.5s"` // duration
 * - `default:"true"` // bool
 */
DefaultValueTag  <-  ( ![\r\n] . )*

Modifiers

Modifier represents an option to a flag, it sets the flag to be deprecated, hidden, or required. In a cli tag, modifiers appears as the first segment, starting with a # character.

Fow now the following modifiers are available:

Hidden flags won't be showed in help, except that when a special flag "--mcli-show-hidden" is provided.

Modifier H shall not be used for an argument, else it panics. An argument must be showed in help to tell user how to use the program correctly.

Modifier E is useful when you want to read an environment variable, but don't want user to provide from command line (e.g. password or other secrets). Using together with R also ensures that the env variable must exist.

Some modifiers cannot be used together, else it panics, e.g.

Compatibility with package flag

Parse returns a *flag.FlagSet if success, all defined flags are available with the flag set, including both short and long names.

Note that the package flag requires command line flags must present before arguments, this package does not have this requirement. Positional arguments can present either before flags or after flags, even both before and after flags, in which case, the args will be reordered and all arguments can be accessed by calling flagSet.Args() and flagSet.Arg(i).

If there is slice or map arguments, it will match all following arguments.

Shell completion

mcli supports auto shell completion for bash, zsh, fish, and powershell. Use AddCompletion to enable the feature, run program help completion [bash|zsh|fish|powershell] for usage guide.

Also check AddCompletion, EnableFlagCompletion, and Options.EnableFlagCompletionForAllCommands for detail docs about command flag completion.

User can use WithArgCompFuncs to specify functions to suggest flag values and positional arguments programmatically, already provided flags and arguments can be accessed in the functions.

Changelog

See CHANGELOG for detailed change history.