Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/bbkane/warg

Declarative and Intuitive Command Line Apps with Go
https://github.com/bbkane/warg

cli command command-line commandline go golang golang-library subcomm

Last synced: about 2 months ago
JSON representation

Declarative and Intuitive Command Line Apps with Go

Awesome Lists containing this project

README

        

# warg

Build hierarchical CLI applications with warg!

- warg uses [funcopt](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) style declarative APIs to keep CLIs readable (nested commands can be indented!) and terse. warg does not require code generation.
- warg is extremely interested in getting information into your app. Ensure a flag can be set from an environmental variable, configuration file, or default value by adding a single line to the flag declaration (configuration files also take some app-level config).
- warg is customizable. Add new types of flag values, config file formats, or --help outputs using the public API.
- Warg is easy to integrate into or remove from an existing codebase. This follows mostly from warg being terse and declarative. If you decide to remove warg, simply remove the app declaration and turn the passed flags into other types of function arguments for your command handlers. Done!

## Examples

Full code for this example at [./examples/butler/main.go](./examples/butler/main.go). Also see the [godocs examples](https://pkg.go.dev/go.bbkane.com/warg#pkg-examples) and other apps in [the examples directory](./examples/).

```go
app := warg.New(
"butler",
section.New(
section.HelpShort("A virtual assistant"),
section.Command(
"present",
"Formally present a guest (guests are never introduced, always presented).",
present,
command.Flag(
"--name",
"Guest to address.",
scalar.String(),
flag.Alias("-n"),
flag.EnvVars("BUTLER_PRESENT_NAME", "USER"),
flag.Required(),
),
),
section.Command("version", "Print version", command.PrintVersion),
),
)
```

## Run Butler

Color can be toggled on/off/auto with the `--color` flag:


Sublime's custom image

The default help for a command dynamically includes each flag's **current** value and how it was was set (passed flag, config, envvar, app default).


Sublime's custom image

Of course, running it with the flag also works


Sublime's custom image

## Apps Using Warg

- [fling](https://github.com/bbkane/fling/) - GNU Stow replacement to manage my dotfiles
- [grabbit](https://github.com/bbkane/grabbit) - Grab images from Reddit
- [starghaze](https://github.com/bbkane/starghaze/) - Save GitHub Starred repos to GSheets, Zinc

# Should You Use warg?

I'm using warg for my personal projects, but the API is not finalized and there
are some known issues (see below). I will eventually improve warg, but I'm currently ( 2021-11-19 )
taking a break from developing on warg to develop some CLIs with warg.

## Known Issues / Design Tradeoffs

- lists containing aggregate values ( values in list objects from configs ) should be checked to have the same size and source but that must currently be done by the application ( see [grabbit](https://github.com/bbkane/grabbit/blob/d1f30b87c4e5c8112f08e9889fa541dbeab66842/main.go#L311) )
- By design, warg does not support positional arguments. Instead, use required flags. See "Unsupported CLI Patterns" below.
- By design, warg requires at least one subcommand. This makes adding additional subcommands easy. See "Unsupported CLI Patterns" below.

## Alternatives

- [cobra](https://github.com/spf13/cobra) is by far the most popular CLI framework for Go. It relies on codegen.
- [cli](https://github.com/urfave/cli) is also very popular.
- I've used the now unmaintained [kingpin](https://github.com/alecthomas/kingpin) fairly successfully.

# Concepts

## Sections, Commands, Flags, and Values

warg is designed to create hierarchical CLI applications similar to [azure-cli](https://github.com/Azure/azure-cli) (just to be clear, azure-cli is not built with warg, but it was my inspiration for warg). These apps use sections to group subcommands, and pass information via flags, not positional arguments. A few examples:

### azure-cli

```
az keyvault certificate create --name --vault-name --tag --tag
```

If we try to dissect the parts of this command, we see that it:

- Starts with the app name (`az`).
- Narrows down intent with a **section** (`keyvault`). Sections are usually nouns and function similarly to a directory hierarchy on a computer - used to group related sections and commands so they're easy to find and use together.
- Narrows down intent further with another **section** (`certificate`).
- Ends with a **command** (`create`). Commands are usually verbs and specify a single action to take within that section.
- Passes information to the command with **flags** (`--name`, `--vault-name`).
- Each flag is passed exactly one **value** (``, ``, and `` would be `git clone --url `.

All warg apps must have at least one nested command. It is not possible to design a warg app such that calling ` --flag ` does useful work. Instead, ` --flag ` must be used.