Documentation
¶
Overview ¶
Package clikit is the invocation and help contract shared by the ~/dev Go command-line tools. A tool declares its commands as native Kong structs (github.com/alecthomas/kong) and describes itself once, as an App; main and the tests run that same value.
One declaration drives both parsing and help: a command is a struct whose fields are flags and positional arguments, each with a help tag, and whose Run method does the work. Long prose belongs in a Help() string method on the same type, and titled blocks such as examples in a HelpSections method. Nothing about a command's syntax is written twice.
type CLI struct {
Dir string `help:"Directory that holds the store." env:"TOOL_DIR" default:"~/.tool" type:"path"`
Add AddCmd `cmd:"" help:"Register a repository."`
}
type AddCmd struct {
Path string `arg:"" type:"existingdir" help:"Checkout to register."`
}
func (c *AddCmd) Run(ctx *kong.Context, s *Store) error { ... }
var app = clikit.App{
Name: "tool",
Description: "tool keeps a registry of repositories.",
New: func() any { return &CLI{} },
Options: []kong.Option{
kong.BindToProvider(func(cli *CLI) (*Store, error) { return openStore(cli.Dir) }),
},
ExitCodes: map[int]string{3: "a repository is missing"},
}
func main() { os.Exit(app.Run(os.Args[1:])) }
Each invocation parses into a fresh grammar from New, so Options must not capture one. A Run method or provider that needs the parsed root or its flags takes the root type as a parameter (cli *CLI above); Kong binds it.
Run guarantees:
- Help always works. --help (or "help [command…]", unless the tool declares a help command of its own) prints the page to stdout and exits 0 before any command runs, any hook after BeforeReset fires, or any environment variable is read. Load configuration and credentials in Run methods or through kong.BindToProvider (or kong.BindSingletonProvider, for a value several providers share), called only when a Run method asks for the value, never in grammar hooks or mappers. No option can take this away.
- Help is rendered from the model. The synopsis, arguments, flags, typed placeholders, defaults, enums, environment variables, flag-group constraints, passthrough and exit codes come from the same declaration that parses argv, and Kong's error lines name a flag as help does. A default:"withargs" command's arguments and flags parse at its parent's level, so the parent's page lists them with their constraints; its Help() and sections stay on its own page: a tool whose default command is the whole tool returns the same text and sections from the root's methods. Generated rows wrap at 100 columns; Help() text and sections print verbatim, a command's own sections before the Shared ones it inherits.
- A declaration whose help would be missing or untrue fails before parsing, and the first test (or run) of the tool names each problem: anything visible without help, and each form help cannot state, such as a Run method beside commands, which Kong runs after theirs too, or a flag group Kong checks differently from its tags.
- Outcomes are classified by origin. Invalid argv exits with App.UsageCode after an error line, the synopsis of the command it concerns and a pointer to its --help, all on stderr. Around a default:"withargs" command, argv is held to its parent's page: a flag of the default typed before another command is refused, since the default would not run, and a word that names no command is reported against the parent, as if it had no default, unless the default takes the word as its argument. A bad value from an environment variable, and anything else that fails, exits with App.FailureCode. A command chooses its own status with Exit (it already reported) or Fail (Run prints the error), each code declared in App.ExitCodes, or reports a usage error it found itself with Usage. App.Outcome maps the tool's own errors onto those once, for every command; what a command returned through them skips it. UsageCode defaults to 2, which a migrating tool often already uses for something else: set it to the tool's existing code.
Commands write through the bound *kong.Context (ctx.Stdout, ctx.Stderr), which are App.Stdout and App.Stderr themselves, and read input from a bound io.Reader parameter (App.Stdin); a provider may take the *kong.Context too. The clikittest package runs an App with captured output and pins every help page. A test swaps a dependency (a clock, tmux, a terminal check) by appending an option that binds the same type to a copy of App.Options: Kong keeps one binding per type, and the later one wins.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Exit ¶
Exit makes a command end with status code without printing anything more: the command has already reported what happened. Declare the code in App.ExitCodes so help lists it. Exit(0) is nil.
func Fail ¶ added in v0.2.0
Fail makes a command end with status code after Run prints err the way it prints any failure, "<tool>: error: <err>": for a failure the tool gives a code of its own, such as an ambiguity its callers tell apart. Declare the code in App.ExitCodes so help lists it. Fail(code, nil) is nil, and Fail(0, err) is err.
Types ¶
type App ¶
type App struct {
// Name is the program name in help and error lines. Empty means the base
// name of os.Args[0].
Name string
// Description says what the tool is for; required.
Description string
// New returns a fresh grammar, a pointer to the root command struct. Every
// invocation gets its own, since parsing writes into it.
New func() any
// Outcome maps an error a command returned onto Usage, Exit or Fail, so a
// tool states that once rather than in every Run method. An error it
// passes on is a failure as usual, and nil is a success. It can end in a
// catch-all: what a command chose with Exit, Fail or Usage skips it, as
// does a parse error.
Outcome func(error) error
// Options are Kong options: kong.Bind, kong.BindToProvider, kong.Vars,
// mappers and so on. They are shared by every invocation, so they must not
// capture a grammar instance; a provider or Run method that needs the
// parsed root takes it as a parameter (func(cli *CLI) (*Store, error)).
Options []kong.Option
// ExitCodes adds the statuses the tool's commands return through Exit,
// with their meaning. 0, UsageCode and FailureCode are always listed; an
// entry here rewords one of them.
ExitCodes map[int]string
// UsageCode is the exit status for invalid argv. Zero means 2.
UsageCode int
// FailureCode is the exit status for every other failure. Zero means 1.
FailureCode int
// Stdin is bound for Run methods as an io.Reader. Nil means os.Stdin.
Stdin io.Reader
// Stdout and Stderr become ctx.Stdout and ctx.Stderr of the bound
// *kong.Context, unwrapped. Nil means os.Stdout and os.Stderr.
Stdout, Stderr io.Writer
}
App is one tool's whole command line: main runs it and its tests run the same value, so nothing about the tool is assembled twice.
type HelpSections ¶
type HelpSections interface {
HelpSections() []Section
}
HelpSections is implemented by a command struct (or the root struct) to add sections such as Examples or Output to its help page.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package clikittest runs a clikit App in tests exactly as main does, and pins every help page against golden files.
|
Package clikittest runs a clikit App in tests exactly as main does, and pins every help page against golden files. |