I’ve been using Cobra for a really long time. Whenever I start a project that needs to handle more than one command add Cobra. For a small toolbox for datasphere I wanted to try something different: urfave/cli.
Compared to Cobra, urfave/cli is much more lightweight by not integrating any kind of configuration management, fewer hooks, and also handling flags differently, but for that small project I don’t need that anyway 😅
Right when you start with this library you run into the first API difference: While in Cobra you always work with cobra.Command
objects, in urfave/cli you start off with a cli.App
and only work with cli.Command
instances if you’re dealing with sub-commands:
package main
import (
"fmt"
"github.com/urfave/cli"
)
func main() {
app := cli.App{
Action: function(ctx *cli.Context) error {
fmt.Println("hello world")
},
}
app.Run(os.Args)
}
Flags
If you need any flags, you add them to an array that is attached to the App instance:
cli.App{
...
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
},
},
...
}
Flag-names longer than one character are rendered with two dashes in the usage message, those with just one character receive only a single dash:
--verbose, -v Verbose logging (default: false)
As you saw in the example above, the action of an app receives only a single argument: a cli.Context
instance. This is also how you gain access to the parsed flags using functions like GetBool(name)
.
Don’t let that name confuse you, though: cli.Context
doesn’t implement the context.Context
interface but you can still work with those thanks to cli.Context.Context
😬
Sub-commands
Sub-commands work pretty much like Apps but can be nested:
app := cli.App{}
app.Commands = []*cli.Command{
&cli.Command{
Name: "subcommand",
Action: func(ctx *cli.Context) error {
return nil
},
Subcommands: []*cli.Command{
&cli.Command{
Name: "subsubcommand",
Action: func(ctx *cli.Context) error {
return nil
},
},
},
},
}
One thing to keep in mind with sub-commands, though, is that you have to pass the flags at the same level as the one where it has been declared. If a flag is defined for the application, you cannot specify it after the sub-command.
That’s a bit counter-intuitive coming from Cobra, but it’s no show-stopper for me. And that’s as far as I’ve come so far. It seems to get the job done while not getting in my way (similar to Cobra) 👍
Do you want to give me feedback about this article in private? Please send it to comments@zerokspot.com.
Alternatively, this website also supports Webmentions. If you write a post on a blog that supports this technique, I should get notified about your link 🙂