From d062f50bc796ec8f082afb4105584795b80dda7f Mon Sep 17 00:00:00 2001 From: phamann Date: Fri, 12 Jun 2020 21:46:19 +0100 Subject: [PATCH] Fix bash completion by allowing kingpin to exit early --- README.md | 17 +++++++++++++++++ pkg/app/run.go | 28 +++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d0c57954f..fabfad43f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ## Quick links - [Installation](#Installation) - [Usage](#Usage) +- [Bash/ZSH completion](#bashzsh-shell-completion) - [Development](#Development) - [Issues](#Issues) @@ -102,6 +103,22 @@ Succinct help about any command or subcommand is available via the `-h, --help` flag. Verbose help about any command or subcommand is available via the help argument, e.g. `fastly help service`. +## Bash/ZSH shell completion +The CLI can generate completions for all commands, subcommands and flags. + +By specifying `--completion-bash` as the first argument, the CLI will show possible subcommands. By ending your argv with `--`, hints for flags will be shown. + +### Configuring your shell +To install the completions source them in your `bash_profile` (or equivalent): +``` +eval "$(fastly --completion-script-bash)" +``` + +Or for ZSH in your `zshrc`: +``` +eval "$(fastly --completion-script-zsh)" +``` + ## Development The Fastly CLI requires [Go 1.13 or above](https://golang.org). Clone this repo diff --git a/pkg/app/run.go b/pkg/app/run.go index 297d1c36d..0349ed985 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "os" + "regexp" "time" "github.com/fastly/cli/pkg/api" @@ -53,6 +54,10 @@ import ( "gopkg.in/alecthomas/kingpin.v3-unstable" ) +var ( + completionRegExp = regexp.MustCompile("completion-(?:script-)?(?:bash|zsh)$") +) + // Run constructs the application including all of the subcommands, parses the // args, invokes the client factory with the token to create a Fastly API // client, and executes the chosen command, using the provided io.Reader and @@ -77,13 +82,22 @@ func Run(args []string, env config.Environment, file config.File, configFilePath // bindings, because we need to do things like track where a config // parameter came from. app := kingpin.New("fastly", "A tool to interact with the Fastly API") - app.Terminate(nil) // don't let kingpin call os.Exit app.Writers(out, ioutil.Discard) // don't let kingpin write error output app.UsageContext(&kingpin.UsageContext{ Template: VerboseUsageTemplate, Funcs: UsageTemplateFuncs, }) + // Prevent kingpin from calling os.Exit, this gives us greater control over + // error states and output contorl flow. + app.Terminate(nil) + + // As kingpin generates bash completion as a side-effect of kingpin.Parse we + // allow it to call os.Exit, only if a completetion flag is present. + if isCompletion(args) { + app.Terminate(os.Exit) + } + // WARNING: kingping has no way of decorating flags as being "global" // therefore if you add/remove a global flag you will also need to update // the globalFlag map in pkg/app/usage.go which is used for usage rendering. @@ -678,3 +692,15 @@ func argsIsHelpJSON(args []string) bool { args[1] == "--format" && args[2] == "json") } + +// isCompletion determines whether the supplied command arguments are for +// bash/zsh completion output. +func isCompletion(args []string) bool { + var found bool + for _, arg := range args { + if completionRegExp.MatchString(arg) { + found = true + } + } + return found +}