bbkane / warg

Declarative and Intuitive Command Line Apps with Go
https://pkg.go.dev/go.bbkane.com/warg
MIT License
16 stars 0 forks source link
cli command command-line commandline go golang golang-library subcomm

warg

Build hierarchical CLI applications with warg!

Examples

Full code for this example at ./examples/butler/main.go. Also see the godocs examples and other apps in the examples directory.

    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

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

Alternatives

Concepts

Sections, Commands, Flags, and Values

warg is designed to create hierarchical CLI applications similar to 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 <name> --vault-name <vault-name> --tag <key1=value1> --tag <key2=value2>

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

This structure is both readable and scalable. az makes hundreds of commands browsable with this strategy!

grabbit

grabbit is a much smaller app to download wallpapers from Reddit that IS built with warg. It still benefits from the sections/commands/flags structure. Let's organize some of grabbit's components into a tree diagram:

grabbit                   # app name
├── --color               # section flag
├── --config-path         # section flag
├── --help                # section flag
├── config                # section
│   └── edit              # command
│       └── --editor      # command flag
├── grab                  # command
│   └── --subreddit-name  # command flag
└── version               # command

Similar to az, grabbit organizes its capabilities with sections, commands and flags. Sections are used to group commands. Flags defined in a "parent" section are available to child commands. for example, the config edit command has access to the parent --config-path flag, as does the grab command.

Populating Values

Values can be populated from app defaults, and these can be overridden by environmental variables, and these can be overridden by values read from a config , and, finally, these can be overriden by values passed when invoking the app on the command line via CLI flags.

Specify type-independent options to populate values with FlagOpts and type-specific value options with options specific to each type of value (such as slices or scalars).

See an example.

Specially Handled Flags

--config

Because warg has the capability to read values from a config file into the app, and then override those values from other sources, a--config flag must be parsed before other flags. See the docs for API and an example.

--help and --color

As a special case, the--help/-h flag may be passed without a value. If so, the default help action will be taken. See OverrideHelpFlag and its example for how to change the default help action. By convention, the built-in help commands look for a flag called --color to control whether --help prints to the terminal in color. Use AddColorFlag to easily add this flag to apps and ConditionallyEnableColor to easily look for this flag in custom help.

Unsupported CLI Patterns

One of warg's tradeoffs is that it insists on only using sections, commands, flags, and values. This means it is not possible (by design) to build some styles of CLI apps. warg does not support positional arguments. Instead, use a required flag: git clone <url> would be git clone --url <url>.

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