Kong, The Beautiful CLI Framework
There is a unique elegance to a well designed command-line interface (CLI), and the joy of using one is surpassed only by the satisfaction of creating it. Today I'm going to talk about my favourite Go CLI library, Kong, by Alec Thomas.
I’ll preface this by saying that while Kong is now my favourite CLI framework, it wasn’t always my first choice. Early on, I built many of my CLI tools using Cobra, inspired by Ricardo Gerardi’s excellent book, Powerful Command-Line Applications in Go, which I still highly recommend. For smaller-scale CLIs, I usually turn to urfave/cli or Peter Bourgon’s Flags First package, both of which are excellent libraries as well.
Why Kong is King
Declarative approach
Kong’s approach, leveraging Go structs and tags, eases the transition from CLI concept to code. For the purposes of these examples I use the xair-cli package. The CLI struct defines the root of the CLI and embeds a Config struct:
type CLI struct {
Config `embed:"" prefix:"" help:"The configuration for the CLI."`
}
That Config struct then uses tags to define:
- default values
- environment variables
- long and short versions of the flag
- help output
This is clean, declarative and easy to read.
Subcommands as structs
Many CLIs require complex subcommand structures and how a library allows you to layout your structure can make a big difference to readability.
By defining subcommands as struct fields tagged with cmd defining complex structures becomes possible:
type CLI struct {
Main MainCmdGroup `cmd:""`
Strip StripCmdGroup `cmd:""`
Bus BusCmdGroup `cmd:""`
Headamp HeadampCmdGroup `cmd:""`
Snapshot SnapshotCmdGroup `cmd:""`
Dca DCACmdGroup `cmd:""`
}
Where each one of these can be it's own command or subcommand group:
type MainCmdGroup struct {
Mute MainMuteCmd `cmd:""`
Fader MainFaderCmd `cmd:""`
Fadein MainFadeinCmd `cmd:""`
Fadeout MainFadeoutCmd `cmd:""`
Eq MainEqCmdGroup `cmd:""`
Comp MainCompCmdGroup `cmd:""`
}
This approach makes it straightforward to design CLI structures of arbitrary depth.
Supports all the goodies
Kong offers many features allowing developers to create powerful and intuitive interfaces to fit a variety of domains. These include:
- Custom validators
- Branching off positional arguments
- Argument definitions with slices, maps and pointers
- Help customisation at a granular level
For example, the xair-cli package models an 18-channel rack mixer consisting of 18 input strips, 6 auxiliary buses, a Main L/R bus and a wide variety of control types such as EQ, effects, and gain sliders. When we look at the OSC spec we see addresses such as /ch/01/mix/01/level and /bus/1/eq/1/f.
The pattern here is:
- select the channel type and index into it
- select the parameter type and index into it
- select the item you wish to control.
So ideally we want to represent these commands like so:
xair-cli strip <index> send <send-index> [<level>]
xair-cli bus <index> eq <band> freq [<freq>]
Since Kong allows us to branch off positional arguments we can achieve this by embedding structs tagged with arg into command structs:
type BusCmdGroup struct {
Index struct {
Index int `arg:""`
} `arg:"" help:"Control a specific bus by index."`
}
If we repeat this pattern:
type BusEqCmdGroup struct {
Band struct {
Band *int `arg:""`
} `arg:"" help:"Control a specific EQ band of the bus."`
}
The final result is an interface that closely matches the format typically expected in audio CLIs.
Highly extensible
Being a well designed library it supports all kinds of plugins, here are two of my favourites:
- mango-kong for generating man pages.
- kong-completion for generating shell completion scripts.
Both integrate seamlessly with the library allowing a developer to extend their CLI by defining fields on the CLI struct:
type CLI struct {
Man mangokong.ManFlag `help:"Print man page."`
Completion kongcompletion.Completion `help:"Generate completions."`
}
Quick, simple and effective!
Conclusion
Kong is a beautiful, powerful and highly flexible library perfect for writing all kinds of command-line interfaces. It's my personal favourite but I encourage you to explore the different libraries available, they all offer their own unique take.
Further notes:
- Historically, Kong has lacked robust shell completion, but the recently released kong-completion plugin is compatible with the latest version. While this plugin works for bash, zsh, and fish, it does not currently extend to PowerShell.
Subscribe to this blog's RSS feed