From 863c9ae33be41436753bce94524d17294575fd7d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 19 May 2018 01:19:16 +0200 Subject: [PATCH 1/9] Bump spf13/cobra to v0.0.3, pflag to v1.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a tagged release of Cobra. All relevant PR's were merged, so the fork is no longer needed. Relevant changes: - spf13/cobra#552 Add a field to disable [flags] in UseLine() - spf13/cobra#567 Add `CalledAs` method to cobra.Command - spf13/cobra#580 Update error message for missing required flags - spf13/cobra#584 Add support for --version flag - spf13/cobra#614 If user has a project in symlink, just use its destination folder and work there - spf13/cobra#649 terminates the flags when -- is found in commandline - spf13/cobra#662 Add support for ignoring parse errors - spf13/cobra#686 doc: hide hidden parent flags Also various improvements were added for generating Bash completion scripts (currently not used by us) Fixes usage output for dockerd; Before this update: dockerd --help Usage: dockerd COMMAND A self-sufficient runtime for containers. After this update: dockerd --help Usage: dockerd [OPTIONS] [flags] A self-sufficient runtime for containers. Bump spf13/pflag to v1.0.1 Relevant changes: - spf13/pflag#106 allow lookup by shorthand - spf13/pflag#113 Add SortFlags option - spf13/pflag#138 Generate flag error output for errors returned from the parseFunc - spf13/pflag#141 Fixing Count flag usage string - spf13/pflag#143 add int16 flag - spf13/pflag#122 DurationSlice: implementation and tests - spf13/pflag#115 Implement BytesHex type of argument - spf13/pflag#150 Add uintSlice and boolSlice to name prettifier - spf13/pflag#155 Add multiline wrapping support - spf13/pflag#158 doc: clarify difference between string slice vs. array - spf13/pflag#160 add ability to ignore unknown flags - spf13/pflag#163 Allow Users To Show Deprecated Flags Hide [flags] in usage output Hides the [flags] in the usage output of commands (present in newer versions of Cobra), using the `.DisableFlagsInUseLine` option. Before this change: dockerd --help Usage: dockerd [OPTIONS] [flags] A self-sufficient runtime for containers. After this change: dockerd --help Usage: dockerd [OPTIONS] A self-sufficient runtime for containers. Signed-off-by: Sebastiaan van Stijn Â# modified: vendor/github.com/spf13/pflag/string_array.go § Signed-off-by: Sebastiaan van Stijn Upstream-commit: ed75c7727bf3e454a5faa6baf686de12152d911c Component: engine --- components/engine/cmd/dockerd/docker.go | 1 + components/engine/vendor.conf | 4 +- .../vendor/github.com/spf13/cobra/README.md | 888 +++++++--------- .../vendor/github.com/spf13/cobra/args.go | 25 +- .../spf13/cobra/bash_completions.go | 452 ++++---- .../vendor/github.com/spf13/cobra/cobra.go | 75 +- .../vendor/github.com/spf13/cobra/command.go | 986 ++++++++++-------- .../github.com/spf13/cobra/command_win.go | 8 +- .../github.com/spf13/cobra/zsh_completions.go | 126 +++ .../vendor/github.com/spf13/pflag/README.md | 25 +- .../vendor/github.com/spf13/pflag/bytes.go | 105 ++ .../vendor/github.com/spf13/pflag/count.go | 16 +- .../github.com/spf13/pflag/duration_slice.go | 128 +++ .../vendor/github.com/spf13/pflag/flag.go | 372 +++++-- .../github.com/spf13/pflag/golangflag.go | 4 + .../vendor/github.com/spf13/pflag/int16.go | 88 ++ .../github.com/spf13/pflag/string_array.go | 8 +- .../github.com/spf13/pflag/string_slice.go | 20 + 18 files changed, 1957 insertions(+), 1374 deletions(-) create mode 100644 components/engine/vendor/github.com/spf13/cobra/zsh_completions.go create mode 100644 components/engine/vendor/github.com/spf13/pflag/bytes.go create mode 100644 components/engine/vendor/github.com/spf13/pflag/duration_slice.go create mode 100644 components/engine/vendor/github.com/spf13/pflag/int16.go diff --git a/components/engine/cmd/dockerd/docker.go b/components/engine/cmd/dockerd/docker.go index e90a12e366..850a8a4d6a 100644 --- a/components/engine/cmd/dockerd/docker.go +++ b/components/engine/cmd/dockerd/docker.go @@ -31,6 +31,7 @@ func newDaemonCommand() *cobra.Command { opts.flags = cmd.Flags() return runDaemon(opts) }, + DisableFlagsInUseLine: true, } cli.SetupRootCommand(cmd) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index a347459285..5ddb6e11aa 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -141,8 +141,8 @@ github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 # cli -github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git -github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +github.com/spf13/cobra v0.0.3 +github.com/spf13/pflag v1.0.1 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty diff --git a/components/engine/vendor/github.com/spf13/cobra/README.md b/components/engine/vendor/github.com/spf13/cobra/README.md index 6814a6d483..851fcc087c 100644 --- a/components/engine/vendor/github.com/spf13/cobra/README.md +++ b/components/engine/vendor/github.com/spf13/cobra/README.md @@ -8,6 +8,7 @@ Many of the most widely used Go projects are built using Cobra including: * [Hugo](http://gohugo.io) * [rkt](https://github.com/coreos/rkt) * [etcd](https://github.com/coreos/etcd) +* [Moby (former Docker)](https://github.com/moby/moby) * [Docker (distribution)](https://github.com/docker/distribution) * [OpenShift](https://www.openshift.com/) * [Delve](https://github.com/derekparker/delve) @@ -15,16 +16,37 @@ Many of the most widely used Go projects are built using Cobra including: * [CockroachDB](http://www.cockroachlabs.com/) * [Bleve](http://www.blevesearch.com/) * [ProjectAtomic (enterprise)](http://www.projectatomic.io/) -* [Parse (CLI)](https://parse.com/) * [GiantSwarm's swarm](https://github.com/giantswarm/cli) * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) - +* [rclone](http://rclone.org/) +* [nehm](https://github.com/bogem/nehm) +* [Pouch](https://github.com/alibaba/pouch) [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra) -[![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra) +[![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra) -![cobra](https://cloud.githubusercontent.com/assets/173412/10911369/84832a8e-8212-11e5-9f82-cc96660a4794.gif) +# Table of Contents + +- [Overview](#overview) +- [Concepts](#concepts) + * [Commands](#commands) + * [Flags](#flags) +- [Installing](#installing) +- [Getting Started](#getting-started) + * [Using the Cobra Generator](#using-the-cobra-generator) + * [Using the Cobra Library](#using-the-cobra-library) + * [Working with Flags](#working-with-flags) + * [Positional and Custom Arguments](#positional-and-custom-arguments) + * [Example](#example) + * [Help Command](#help-command) + * [Usage Message](#usage-message) + * [PreRun and PostRun Hooks](#prerun-and-postrun-hooks) + * [Suggestions when "unknown command" happens](#suggestions-when-unknown-command-happens) + * [Generating documentation for your command](#generating-documentation-for-your-command) + * [Generating bash completions](#generating-bash-completions) +- [Contributing](#contributing) +- [License](#license) # Overview @@ -39,27 +61,16 @@ Cobra provides: * Fully POSIX-compliant flags (including short & long versions) * Nested subcommands * Global, local and cascading flags -* Easy generation of applications & commands with `cobra create appname` & `cobra add cmdname` +* Easy generation of applications & commands with `cobra init appname` & `cobra add cmdname` * Intelligent suggestions (`app srver`... did you mean `app server`?) * Automatic help generation for commands and flags -* Automatic detailed help for `app help [command]` * Automatic help flag recognition of `-h`, `--help`, etc. * Automatically generated bash autocomplete for your application * Automatically generated man pages for your application * Command aliases so you can change things without breaking them -* The flexibilty to define your own help, usage, etc. +* The flexibility to define your own help, usage, etc. * Optional tight integration with [viper](http://github.com/spf13/viper) for 12-factor apps -Cobra has an exceptionally clean interface and simple design without needless -constructors or initialization methods. - -Applications built with Cobra commands are designed to be as user-friendly as -possible. Flags can be placed before or after the command (as long as a -confusing space isn’t provided). Both short and long flags can be used. A -command need not even be fully typed. Help is automatically generated and -available for the application or for a specific command using either the help -command or the `--help` flag. - # Concepts Cobra is built on a structure of commands, arguments & flags. @@ -78,11 +89,11 @@ A few good real world examples may better illustrate this point. In the following example, 'server' is a command, and 'port' is a flag: - > hugo server --port=1313 + hugo server --port=1313 In this command we are telling Git to clone the url bare. - > git clone URL --bare + git clone URL --bare ## Commands @@ -92,20 +103,11 @@ have children commands and optionally run an action. In the example above, 'server' is the command. -A Command has the following structure: - -```go -type Command struct { - Use string // The one-line usage message. - Short string // The short description shown in the 'help' output. - Long string // The long message shown in the 'help ' output. - Run func(cmd *Command, args []string) // Run runs the command. -} -``` +[More about cobra.Command](https://godoc.org/github.com/spf13/cobra#Command) ## Flags -A Flag is a way to modify the behavior of a command. Cobra supports +A flag is a way to modify the behavior of a command. Cobra supports fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/). A Cobra command can define flags that persist through to children commands and flags that are only available to that command. @@ -113,23 +115,15 @@ and flags that are only available to that command. In the example above, 'port' is the flag. Flag functionality is provided by the [pflag -library](https://github.com/ogier/pflag), a fork of the flag standard library +library](https://github.com/spf13/pflag), a fork of the flag standard library which maintains the same interface while adding POSIX compliance. -## Usage - -Cobra works by creating a set of commands and then organizing them into a tree. -The tree defines the structure of the application. - -Once each command is defined with its corresponding flags, then the -tree is assigned to the commander which is finally executed. - # Installing Using Cobra is easy. First, use `go get` to install the latest version -of the library. This command will install the `cobra` generator executible -along with the library: +of the library. This command will install the `cobra` generator executable +along with the library and its dependencies: - > go get -v github.com/spf13/cobra/cobra + go get -u github.com/spf13/cobra/cobra Next, include Cobra in your application: @@ -139,8 +133,8 @@ import "github.com/spf13/cobra" # Getting Started -While you are welcome to provide your own organization, typically a Cobra based -application will follow the following organizational structure. +While you are welcome to provide your own organization, typically a Cobra-based +application will follow the following organizational structure: ``` ▾ appName/ @@ -152,18 +146,20 @@ application will follow the following organizational structure. main.go ``` -In a Cobra app, typically the main.go file is very bare. It serves, one purpose, to initialize Cobra. +In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra. ```go package main -import "{pathToYourApp}/cmd" +import ( + "fmt" + "os" + + "{pathToYourApp}/cmd" +) func main() { - if err := cmd.RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } + cmd.Execute() } ``` @@ -172,128 +168,89 @@ func main() { Cobra provides its own program that will create your application and add any commands you want. It's the easiest way to incorporate Cobra into your application. -### cobra init +[Here](https://github.com/spf13/cobra/blob/master/cobra/README.md) you can find more information about it. -The `cobra init [yourApp]` command will create your initial application code -for you. It is a very powerful application that will populate your program with -the right structure so you can immediately enjoy all the benefits of Cobra. It -will also automatically apply the license you specify to your application. +## Using the Cobra Library -Cobra init is pretty smart. You can provide it a full path, or simply a path -similar to what is expected in the import. - -``` -cobra init github.com/spf13/newAppName -``` - -### cobra add - -Once an application is initialized Cobra can create additional commands for you. -Let's say you created an app and you wanted the following commands for it: - -* app serve -* app config -* app config create - -In your project directory (where your main.go file is) you would run the following: - -``` -cobra add serve -cobra add config -cobra add create -p 'configCmd' -``` - -Once you have run these three commands you would have an app structure that would look like: - -``` - ▾ app/ - ▾ cmd/ - serve.go - config.go - create.go - main.go -``` - -at this point you can run `go run main.go` and it would run your app. `go run -main.go serve`, `go run main.go config`, `go run main.go config create` along -with `go run main.go help serve`, etc would all work. - -Obviously you haven't added your own code to these yet, the commands are ready -for you to give them their tasks. Have fun. - -### Configuring the cobra generator - -The cobra generator will be easier to use if you provide a simple configuration -file which will help you eliminate providing a bunch of repeated information in -flags over and over. - -An example ~/.cobra.yaml file: - -```yaml -author: Steve Francia -license: MIT -``` - -You can specify no license by setting `license` to `none` or you can specify -a custom license: - -```yaml -license: - header: This file is part of {{ .appName }}. - text: | - {{ .copyright }} - - This is my license. There are many like it, but this one is mine. - My license is my best friend. It is my life. I must master it as I must - master my life. -``` - -## Manually implementing Cobra - -To manually implement cobra you need to create a bare main.go file and a RootCmd file. +To manually implement Cobra you need to create a bare main.go file and a rootCmd file. You will optionally provide additional commands as you see fit. -### Create the root command - -The root command represents your binary itself. - - -#### Manually create rootCmd +### Create rootCmd Cobra doesn't require any special constructors. Simply create your commands. Ideally you place this in app/cmd/root.go: ```go -var RootCmd = &cobra.Command{ - Use: "hugo", - Short: "Hugo is a very fast static site generator", - Long: `A Fast and Flexible Static Site Generator built with +var rootCmd = &cobra.Command{ + Use: "hugo", + Short: "Hugo is a very fast static site generator", + Long: `A Fast and Flexible Static Site Generator built with love by spf13 and friends in Go. Complete documentation is available at http://hugo.spf13.com`, - Run: func(cmd *cobra.Command, args []string) { - // Do Stuff Here - }, + Run: func(cmd *cobra.Command, args []string) { + // Do Stuff Here + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } } ``` You will additionally define flags and handle configuration in your init() function. -for example cmd/root.go: +For example cmd/root.go: ```go +import ( + "fmt" + "os" + + homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + func init() { - cobra.OnInitialize(initConfig) - RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") - RootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/") - RootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution") - RootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)") - RootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration") - viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author")) - viper.BindPFlag("projectbase", RootCmd.PersistentFlags().Lookup("projectbase")) - viper.BindPFlag("useViper", RootCmd.PersistentFlags().Lookup("viper")) - viper.SetDefault("author", "NAME HERE ") - viper.SetDefault("license", "apache") + cobra.OnInitialize(initConfig) + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") + rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/") + rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution") + rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)") + rootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration") + viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) + viper.BindPFlag("projectbase", rootCmd.PersistentFlags().Lookup("projectbase")) + viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) + viper.SetDefault("author", "NAME HERE ") + viper.SetDefault("license", "apache") +} + +func initConfig() { + // Don't forget to read config either from cfgFile or from home directory! + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Search config in home directory with name ".cobra" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(".cobra") + } + + if err := viper.ReadInConfig(); err != nil { + fmt.Println("Can't read config:", err) + os.Exit(1) + } } ``` @@ -307,17 +264,18 @@ In a Cobra app, typically the main.go file is very bare. It serves, one purpose, ```go package main -import "{pathToYourApp}/cmd" +import ( + "fmt" + "os" + + "{pathToYourApp}/cmd" +) func main() { - if err := cmd.RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } + cmd.Execute() } ``` - ### Create additional commands Additional commands can be defined and typically are each given their own file @@ -330,47 +288,25 @@ populate it with the following: package cmd import ( - "github.com/spf13/cobra" + "fmt" + + "github.com/spf13/cobra" ) func init() { - RootCmd.AddCommand(versionCmd) + rootCmd.AddCommand(versionCmd) } var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print the version number of Hugo", - Long: `All software has versions. This is Hugo's`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") - }, + Use: "version", + Short: "Print the version number of Hugo", + Long: `All software has versions. This is Hugo's`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") + }, } ``` -### Attach command to its parent - - -If you notice in the above example we attach the command to its parent. In -this case the parent is the rootCmd. In this example we are attaching it to the -root, but commands can be attached at any level. - -```go -RootCmd.AddCommand(versionCmd) -``` - -### Remove a command from its parent - -Removing a command is not a common action in simple programs, but it allows 3rd -parties to customize an existing command tree. - -In this example, we remove the existing `VersionCmd` command of an existing -root command, and we replace it with our own version: - -```go -mainlib.RootCmd.RemoveCommand(mainlib.VersionCmd) -mainlib.RootCmd.AddCommand(versionCmd) -``` - ## Working with Flags Flags provide modifiers to control how the action command operates. @@ -395,7 +331,7 @@ command it's assigned to as well as every command under that command. For global flags, assign a flag as a persistent flag on the root. ```go -RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") +rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") ``` ### Local Flags @@ -403,43 +339,81 @@ RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose out A flag can also be assigned locally which will only apply to that specific command. ```go -RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") +rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") ``` -### Positional Arguments +### Local Flag on Parent Commands -Validation of positional arguments can be specified using the `Args` field, which accepts -one of the following values: +By default Cobra only parses local flags on the target command, any local flags on +parent commands are ignored. By enabling `Command.TraverseChildren` Cobra will +parse local flags on each command before executing the target command. + +```go +command := cobra.Command{ + Use: "print [OPTIONS] [COMMANDS]", + TraverseChildren: true, +} +``` + +### Bind Flags with Config + +You can also bind your flags with [viper](https://github.com/spf13/viper): +```go +var author string + +func init() { + rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution") + viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) +} +``` + +In this example the persistent flag `author` is bound with `viper`. +**Note**, that the variable `author` will not be set to the value from config, +when the `--author` flag is not provided by user. + +More in [viper documentation](https://github.com/spf13/viper#working-with-flags). + +### Required flags + +Flags are optional by default. If instead you wish your command to report an error +when a flag has not been set, mark it as required: +```go +rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)") +rootCmd.MarkFlagRequired("region") +``` + +## Positional and Custom Arguments + +Validation of positional arguments can be specified using the `Args` field +of `Command`. + +The following validators are built in: - `NoArgs` - the command will report an error if there are any positional args. - `ArbitraryArgs` - the command will accept any args. -- `OnlyValidArgs` - the command will report an error if there are any positiona - args that are not in the `ValidArgs` list. -- `MinimumNArgs(int)` - the command will report an error if there are not at - least N positional args. -- `MaximumNArgs(int)` - the command will report an error if there are more than - N positional args. -- `ExactArgs(int)` - the command will report an error if there are not - exactly N positional args. -- `RangeArgs(min, max)` - the command will report an error if the number of args - is not between the minimum and maximum number of expected args. - -By default, `Args` uses the following legacy behaviour: -- root commands with no subcommands can take arbitrary arguments -- root commands with subcommands will do subcommand validity checking -- subcommands will always accept arbitrary arguments and do no subsubcommand validity checking +- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`. +- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args. +- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args. +- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args. +- `RangeArgs(min, max)` - the command will report an error if the number of args is not between the minimum and maximum number of expected args. +An example of setting the custom validator: ```go -var HugoCmd = &cobra.Command{ - Use: "hugo", - Short: "Hugo is a very fast static site generator", - ValidArgs: []string{"one", "two"} - Args: cobra.OnlyValidArgs - Run: func(cmd *cobra.Command, args []string) { - // args will only have the values one, two - // or the cmd.Execute() will fail. - }, +var cmd = &cobra.Command{ + Short: "hello", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("requires at least one arg") + } + if myapp.IsValidColor(args[0]) { + return nil + } + return fmt.Errorf("invalid color specified: %s", args[0]) + }, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Hello, World!") + }, } ``` @@ -458,62 +432,62 @@ More documentation about flags is available at https://github.com/spf13/pflag package main import ( - "fmt" - "strings" + "fmt" + "strings" - "github.com/spf13/cobra" + "github.com/spf13/cobra" ) func main() { + var echoTimes int - var echoTimes int + var cmdPrint = &cobra.Command{ + Use: "print [string to print]", + Short: "Print anything to the screen", + Long: `print is for printing anything back to the screen. +For many years people have printed back to the screen.`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Print: " + strings.Join(args, " ")) + }, + } - var cmdPrint = &cobra.Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `print is for printing anything back to the screen. - For many years people have printed back to the screen. - `, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Print: " + strings.Join(args, " ")) - }, - } + var cmdEcho = &cobra.Command{ + Use: "echo [string to echo]", + Short: "Echo anything to the screen", + Long: `echo is for echoing anything back. +Echo works a lot like print, except it has a child command.`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Print: " + strings.Join(args, " ")) + }, + } - var cmdEcho = &cobra.Command{ - Use: "echo [string to echo]", - Short: "Echo anything to the screen", - Long: `echo is for echoing anything back. - Echo works a lot like print, except it has a child command. - `, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Print: " + strings.Join(args, " ")) - }, - } + var cmdTimes = &cobra.Command{ + Use: "times [# times] [string to echo]", + Short: "Echo anything to the screen more times", + Long: `echo things multiple times back to the user by providing +a count and a string.`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + for i := 0; i < echoTimes; i++ { + fmt.Println("Echo: " + strings.Join(args, " ")) + } + }, + } - var cmdTimes = &cobra.Command{ - Use: "times [# times] [string to echo]", - Short: "Echo anything to the screen more times", - Long: `echo things multiple times back to the user by providing - a count and a string.`, - Run: func(cmd *cobra.Command, args []string) { - for i := 0; i < echoTimes; i++ { - fmt.Println("Echo: " + strings.Join(args, " ")) - } - }, - } + cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input") - cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input") - - var rootCmd = &cobra.Command{Use: "app"} - rootCmd.AddCommand(cmdPrint, cmdEcho) - cmdEcho.AddCommand(cmdTimes) - rootCmd.Execute() + var rootCmd = &cobra.Command{Use: "app"} + rootCmd.AddCommand(cmdPrint, cmdEcho) + cmdEcho.AddCommand(cmdTimes) + rootCmd.Execute() } ``` For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/). -## The Help Command +## Help Command Cobra automatically adds a help command to your application when you have subcommands. This will be called when a user runs 'app help'. Additionally, help will also @@ -526,60 +500,28 @@ create' is called. Every command will automatically have the '--help' flag adde The following output is automatically generated by Cobra. Nothing beyond the command and flag definitions are needed. - > hugo help + $ cobra help - hugo is the main command, used to build your Hugo site. - - Hugo is a Fast and Flexible Static Site Generator - built with love by spf13 and friends in Go. - - Complete documentation is available at http://gohugo.io/. + Cobra is a CLI library for Go that empowers applications. + This application is a tool to generate the needed files + to quickly create a Cobra application. Usage: - hugo [flags] - hugo [command] + cobra [command] Available Commands: - server Hugo runs its own webserver to render the files - version Print the version number of Hugo - config Print the site configuration - check Check content in the source directory - benchmark Benchmark hugo by building a site a number of times. - convert Convert your content to different formats - new Create new content for your site - list Listing out various types of content - undraft Undraft changes the content's draft status from 'True' to 'False' - genautocomplete Generate shell autocompletion script for Hugo - gendoc Generate Markdown documentation for the Hugo CLI. - genman Generate man page for Hugo - import Import your site from others. + add Add a command to a Cobra Application + help Help about any command + init Initialize a Cobra Application Flags: - -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ - -D, --buildDrafts[=false]: include content marked as draft - -F, --buildFuture[=false]: include content with publishdate in the future - --cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ - --canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL - --config="": config file (default is path/config.yaml|json|toml) - -d, --destination="": filesystem path to write files to - --disableRSS[=false]: Do not build RSS files - --disableSitemap[=false]: Do not build Sitemap file - --editor="": edit new content with this editor, if provided - --ignoreCache[=false]: Ignores the cache directory for reading but still writes to it - --log[=false]: Enable Logging - --logFile="": Log File path (if set, logging enabled automatically) - --noTimes[=false]: Don't sync modification time of files - --pluralizeListTitles[=true]: Pluralize titles in lists using inflect - --preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu") - -s, --source="": filesystem path to read files relative from - --stepAnalysis[=false]: display memory and timing of different steps of the program - -t, --theme="": theme to use (located in /themes/THEMENAME/) - --uglyURLs[=false]: if true, use /filename.html instead of /filename/ - -v, --verbose[=false]: verbose output - --verboseLog[=false]: verbose logging - -w, --watch[=false]: watch filesystem for changes and recreate as needed + -a, --author string author name for copyright attribution (default "YOUR NAME") + --config string config file (default is $HOME/.cobra.yaml) + -h, --help help for cobra + -l, --license string name of license for the project + --viper use Viper for configuration (default true) - Use "hugo [command] --help" for more information about a command. + Use "cobra [command] --help" for more information about a command. Help is just a command like any other. There is no special logic or behavior @@ -587,38 +529,18 @@ around it. In fact, you can provide your own if you want. ### Defining your own help -You can provide your own Help command or your own template for the default command to use. - -The default help command is +You can provide your own Help command or your own template for the default command to use +with following functions: ```go -func (c *Command) initHelp() { - if c.helpCommand == nil { - c.helpCommand = &Command{ - Use: "help [command]", - Short: "Help about any command", - Long: `Help provides help for any command in the application. - Simply type ` + c.Name() + ` help [path to command] for full details.`, - Run: c.HelpFunc(), - } - } - c.AddCommand(c.helpCommand) -} -``` - -You can provide your own command, function or template through the following methods: - -```go -command.SetHelpCommand(cmd *Command) - -command.SetHelpFunc(f func(*Command, []string)) - -command.SetHelpTemplate(s string) +cmd.SetHelpCommand(cmd *Command) +cmd.SetHelpFunc(f func(*Command, []string)) +cmd.SetHelpTemplate(s string) ``` The latter two will also apply to any children commands. -## Usage +## Usage Message When the user provides an invalid flag or invalid command, Cobra responds by showing the user the 'usage'. @@ -627,73 +549,44 @@ showing the user the 'usage'. You may recognize this from the help above. That's because the default help embeds the usage as part of its output. + $ cobra --invalid + Error: unknown flag: --invalid Usage: - hugo [flags] - hugo [command] + cobra [command] Available Commands: - server Hugo runs its own webserver to render the files - version Print the version number of Hugo - config Print the site configuration - check Check content in the source directory - benchmark Benchmark hugo by building a site a number of times. - convert Convert your content to different formats - new Create new content for your site - list Listing out various types of content - undraft Undraft changes the content's draft status from 'True' to 'False' - genautocomplete Generate shell autocompletion script for Hugo - gendoc Generate Markdown documentation for the Hugo CLI. - genman Generate man page for Hugo - import Import your site from others. + add Add a command to a Cobra Application + help Help about any command + init Initialize a Cobra Application Flags: - -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ - -D, --buildDrafts[=false]: include content marked as draft - -F, --buildFuture[=false]: include content with publishdate in the future - --cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ - --canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL - --config="": config file (default is path/config.yaml|json|toml) - -d, --destination="": filesystem path to write files to - --disableRSS[=false]: Do not build RSS files - --disableSitemap[=false]: Do not build Sitemap file - --editor="": edit new content with this editor, if provided - --ignoreCache[=false]: Ignores the cache directory for reading but still writes to it - --log[=false]: Enable Logging - --logFile="": Log File path (if set, logging enabled automatically) - --noTimes[=false]: Don't sync modification time of files - --pluralizeListTitles[=true]: Pluralize titles in lists using inflect - --preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu") - -s, --source="": filesystem path to read files relative from - --stepAnalysis[=false]: display memory and timing of different steps of the program - -t, --theme="": theme to use (located in /themes/THEMENAME/) - --uglyURLs[=false]: if true, use /filename.html instead of /filename/ - -v, --verbose[=false]: verbose output - --verboseLog[=false]: verbose logging - -w, --watch[=false]: watch filesystem for changes and recreate as needed + -a, --author string author name for copyright attribution (default "YOUR NAME") + --config string config file (default is $HOME/.cobra.yaml) + -h, --help help for cobra + -l, --license string name of license for the project + --viper use Viper for configuration (default true) + + Use "cobra [command] --help" for more information about a command. ### Defining your own usage You can provide your own usage function or template for Cobra to use. - -The default usage function is: - -```go -return func(c *Command) error { - err := tmpl(c.Out(), c.UsageTemplate(), c) - return err -} -``` - Like help, the function and template are overridable through public methods: ```go -command.SetUsageFunc(f func(*Command) error) - -command.SetUsageTemplate(s string) +cmd.SetUsageFunc(f func(*Command) error) +cmd.SetUsageTemplate(s string) ``` -## PreRun or PostRun Hooks +## Version Flag -It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order: +Cobra adds a top-level '--version' flag if the Version field is set on the root command. +Running an application with the '--version' flag will print the version to stdout using +the version template. The template can be customized using the +`cmd.SetVersionTemplate(s string)` function. + +## PreRun and PostRun Hooks + +It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: - `PersistentPreRun` - `PreRun` @@ -707,105 +600,73 @@ An example of two commands which use all of these features is below. When the s package main import ( - "fmt" + "fmt" - "github.com/spf13/cobra" + "github.com/spf13/cobra" ) func main() { - var rootCmd = &cobra.Command{ - Use: "root [sub]", - Short: "My root command", - PersistentPreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) - }, - PreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PreRun with args: %v\n", args) - }, - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd Run with args: %v\n", args) - }, - PostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PostRun with args: %v\n", args) - }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) - }, - } + var rootCmd = &cobra.Command{ + Use: "root [sub]", + Short: "My root command", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) + }, + PreRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PreRun with args: %v\n", args) + }, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd Run with args: %v\n", args) + }, + PostRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PostRun with args: %v\n", args) + }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) + }, + } - var subCmd = &cobra.Command{ - Use: "sub [no options!]", - Short: "My subcommand", - PreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PreRun with args: %v\n", args) - }, - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd Run with args: %v\n", args) - }, - PostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PostRun with args: %v\n", args) - }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) - }, - } + var subCmd = &cobra.Command{ + Use: "sub [no options!]", + Short: "My subcommand", + PreRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd PreRun with args: %v\n", args) + }, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd Run with args: %v\n", args) + }, + PostRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd PostRun with args: %v\n", args) + }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) + }, + } - rootCmd.AddCommand(subCmd) + rootCmd.AddCommand(subCmd) - rootCmd.SetArgs([]string{""}) - _ = rootCmd.Execute() - fmt.Print("\n") - rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) - _ = rootCmd.Execute() + rootCmd.SetArgs([]string{""}) + rootCmd.Execute() + fmt.Println() + rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) + rootCmd.Execute() } ``` +Output: +``` +Inside rootCmd PersistentPreRun with args: [] +Inside rootCmd PreRun with args: [] +Inside rootCmd Run with args: [] +Inside rootCmd PostRun with args: [] +Inside rootCmd PersistentPostRun with args: [] -## Alternative Error Handling - -Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, -providing a way to handle the errors in one location. The current list of functions that return an error is: - -* PersistentPreRunE -* PreRunE -* RunE -* PostRunE -* PersistentPostRunE - -If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage` -and `SilenceErrors` to `false` on the command. A child command respects these flags if they are set on the parent -command. - -**Example Usage using RunE:** - -```go -package main - -import ( - "errors" - "log" - - "github.com/spf13/cobra" -) - -func main() { - var rootCmd = &cobra.Command{ - Use: "hugo", - Short: "Hugo is a very fast static site generator", - Long: `A Fast and Flexible Static Site Generator built with - love by spf13 and friends in Go. - Complete documentation is available at http://hugo.spf13.com`, - RunE: func(cmd *cobra.Command, args []string) error { - // Do Stuff Here - return errors.New("some random error") - }, - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} +Inside rootCmd PersistentPreRun with args: [arg1 arg2] +Inside subCmd PreRun with args: [arg1 arg2] +Inside subCmd Run with args: [arg1 arg2] +Inside subCmd PostRun with args: [arg1 arg2] +Inside subCmd PersistentPostRun with args: [arg1 arg2] ``` ## Suggestions when "unknown command" happens @@ -848,81 +709,28 @@ Did you mean this? Run 'kubectl help' for usage. ``` -## Generating Markdown-formatted documentation for your command +## Generating documentation for your command -Cobra can generate a Markdown-formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](doc/md_docs.md). +Cobra can generate documentation based on subcommands, flags, etc. in the following formats: -## Generating man pages for your command +- [Markdown](doc/md_docs.md) +- [ReStructured Text](doc/rest_docs.md) +- [Man Page](doc/man_docs.md) -Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](doc/man_docs.md). - -## Generating bash completions for your command +## Generating bash completions Cobra can generate a bash-completion file. If you add more information to your command, these completions can be amazingly powerful and flexible. Read more about it in [Bash Completions](bash_completions.md). -## Debugging - -Cobra provides a ‘DebugFlags’ method on a command which, when called, will print -out everything Cobra knows about the flags for each command. - -### Example - -```go -command.DebugFlags() -``` - -## Release Notes -* **0.9.0** June 17, 2014 - * flags can appears anywhere in the args (provided they are unambiguous) - * --help prints usage screen for app or command - * Prefix matching for commands - * Cleaner looking help and usage output - * Extensive test suite -* **0.8.0** Nov 5, 2013 - * Reworked interface to remove commander completely - * Command now primary structure - * No initialization needed - * Usage & Help templates & functions definable at any level - * Updated Readme -* **0.7.0** Sept 24, 2013 - * Needs more eyes - * Test suite - * Support for automatic error messages - * Support for help command - * Support for printing to any io.Writer instead of os.Stderr - * Support for persistent flags which cascade down tree - * Ready for integration into Hugo -* **0.1.0** Sept 3, 2013 - * Implement first draft - -## Extensions - -Libraries for extending Cobra: - -* [cmdns](https://github.com/gosuri/cmdns): Enables name spacing a command's immediate children. It provides an alternative way to structure subcommands, similar to `heroku apps:create` and `ovrclk clusters:launch`. - -## ToDo -* Launch proper documentation site - -## Contributing +# Contributing 1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +2. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`) +3. Create your feature branch (`git checkout -b my-new-feature`) +4. Make changes and add them (`git add .`) +5. Commit your changes (`git commit -m 'Add some feature'`) +6. Push to the branch (`git push origin my-new-feature`) +7. Create new pull request -## Contributors - -Names in no particular order: - -* [spf13](https://github.com/spf13), -[eparis](https://github.com/eparis), -[bep](https://github.com/bep), and many more! - -## License +# License Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) - - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/spf13/cobra/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/components/engine/vendor/github.com/spf13/cobra/args.go b/components/engine/vendor/github.com/spf13/cobra/args.go index 639fae7cd0..a5d8a9273e 100644 --- a/components/engine/vendor/github.com/spf13/cobra/args.go +++ b/components/engine/vendor/github.com/spf13/cobra/args.go @@ -16,14 +16,14 @@ func legacyArgs(cmd *Command, args []string) error { return nil } - // root command with subcommands, do subcommand checking + // root command with subcommands, do subcommand checking. if !cmd.HasParent() && len(args) > 0 { return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) } return nil } -// NoArgs returns an error if any args are included +// NoArgs returns an error if any args are included. func NoArgs(cmd *Command, args []string) error { if len(args) > 0 { return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath()) @@ -31,7 +31,7 @@ func NoArgs(cmd *Command, args []string) error { return nil } -// OnlyValidArgs returns an error if any args are not in the list of ValidArgs +// OnlyValidArgs returns an error if any args are not in the list of ValidArgs. func OnlyValidArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { for _, v := range args { @@ -43,21 +43,12 @@ func OnlyValidArgs(cmd *Command, args []string) error { return nil } -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -// ArbitraryArgs never returns an error +// ArbitraryArgs never returns an error. func ArbitraryArgs(cmd *Command, args []string) error { return nil } -// MinimumNArgs returns an error if there is not at least N args +// MinimumNArgs returns an error if there is not at least N args. func MinimumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < n { @@ -67,7 +58,7 @@ func MinimumNArgs(n int) PositionalArgs { } } -// MaximumNArgs returns an error if there are more than N args +// MaximumNArgs returns an error if there are more than N args. func MaximumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) > n { @@ -77,7 +68,7 @@ func MaximumNArgs(n int) PositionalArgs { } } -// ExactArgs returns an error if there are not exactly n args +// ExactArgs returns an error if there are not exactly n args. func ExactArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) != n { @@ -87,7 +78,7 @@ func ExactArgs(n int) PositionalArgs { } } -// RangeArgs returns an error if the number of args is not within the expected range +// RangeArgs returns an error if the number of args is not within the expected range. func RangeArgs(min int, max int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < min || len(args) > max { diff --git a/components/engine/vendor/github.com/spf13/cobra/bash_completions.go b/components/engine/vendor/github.com/spf13/cobra/bash_completions.go index 236dee67f2..8fa8f486fa 100644 --- a/components/engine/vendor/github.com/spf13/cobra/bash_completions.go +++ b/components/engine/vendor/github.com/spf13/cobra/bash_completions.go @@ -1,6 +1,7 @@ package cobra import ( + "bytes" "fmt" "io" "os" @@ -10,20 +11,18 @@ import ( "github.com/spf13/pflag" ) +// Annotations for Bash completion. const ( - BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions" + BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) -func preamble(out io.Writer, name string) error { - _, err := fmt.Fprintf(out, "# bash completion for %-36s -*- shell-script -*-\n", name) - if err != nil { - return err - } - _, err = fmt.Fprint(out, ` -__debug() +func writePreamble(buf *bytes.Buffer, name string) { + buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) + buf.WriteString(fmt.Sprintf(` +__%[1]s_debug() { if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" @@ -32,13 +31,13 @@ __debug() # Homebrew on Macs have version 1.3 of bash-completion which doesn't include # _init_completion. This is a very minimal version of that function. -__my_init_completion() +__%[1]s_init_completion() { COMPREPLY=() _get_comp_words_by_ref "$@" cur prev words cword } -__index_of_word() +__%[1]s_index_of_word() { local w word=$1 shift @@ -50,7 +49,7 @@ __index_of_word() index=-1 } -__contains_word() +__%[1]s_contains_word() { local w word=$1; shift for w in "$@"; do @@ -59,9 +58,9 @@ __contains_word() return 1 } -__handle_reply() +__%[1]s_handle_reply() { - __debug "${FUNCNAME[0]}" + __%[1]s_debug "${FUNCNAME[0]}" case $cur in -*) if [[ $(type -t compopt) = "builtin" ]]; then @@ -86,14 +85,14 @@ __handle_reply() local index flag flag="${cur%%=*}" - __index_of_word "${flag}" "${flags_with_completion[@]}" + __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}" + COMPREPLY=() if [[ ${index} -ge 0 ]]; then - COMPREPLY=() PREFIX="" cur="${cur#*=}" ${flags_completion[${index}]} if [ -n "${ZSH_VERSION}" ]; then - # zfs completion needs --flag= prefix + # zsh completion needs --flag= prefix eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" fi fi @@ -104,7 +103,7 @@ __handle_reply() # check if we are handling a flag with special work handling local index - __index_of_word "${prev}" "${flags_with_completion[@]}" + __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}" if [[ ${index} -ge 0 ]]; then ${flags_completion[${index}]} return @@ -133,25 +132,34 @@ __handle_reply() declare -F __custom_func >/dev/null && __custom_func fi - __ltrim_colon_completions "$cur" + # available in bash-completion >= 2, not always present on macOS + if declare -F __ltrim_colon_completions >/dev/null; then + __ltrim_colon_completions "$cur" + fi + + # If there is only 1 completion and it is a flag with an = it will be completed + # but we don't want a space after the = + if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then + compopt -o nospace + fi } # The arguments should be in the form "ext1|ext2|extn" -__handle_filename_extension_flag() +__%[1]s_handle_filename_extension_flag() { local ext="$1" _filedir "@(${ext})" } -__handle_subdirs_in_dir_flag() +__%[1]s_handle_subdirs_in_dir_flag() { local dir="$1" pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 } -__handle_flag() +__%[1]s_handle_flag() { - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" # if a command required a flag, and we found it, unset must_have_one_flag() local flagname=${words[c]} @@ -162,27 +170,30 @@ __handle_flag() flagname=${flagname%%=*} # strip everything after the = flagname="${flagname}=" # but put the = back fi - __debug "${FUNCNAME[0]}: looking for ${flagname}" - if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then + __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}" + if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then must_have_one_flag=() fi # if you set a flag which only applies to this command, don't show subcommands - if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then + if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then commands=() fi # keep flag value with flagname as flaghash - if [ -n "${flagvalue}" ] ; then - flaghash[${flagname}]=${flagvalue} - elif [ -n "${words[ $((c+1)) ]}" ] ; then - flaghash[${flagname}]=${words[ $((c+1)) ]} - else - flaghash[${flagname}]="true" # pad "true" for bool flag + # flaghash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi fi # skip the argument to a two word flag - if __contains_word "${words[c]}" "${two_word_flags[@]}"; then + if __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then c=$((c+1)) # if we are looking for a flags value, don't show commands if [[ $c -eq $cword ]]; then @@ -194,13 +205,13 @@ __handle_flag() } -__handle_noun() +__%[1]s_handle_noun() { - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then + if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then must_have_one_noun=() - elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then + elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then must_have_one_noun=() fi @@ -208,61 +219,66 @@ __handle_noun() c=$((c+1)) } -__handle_command() +__%[1]s_handle_command() { - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" local next_command if [[ -n ${last_command} ]]; then next_command="_${last_command}_${words[c]//:/__}" else if [[ $c -eq 0 ]]; then - next_command="_$(basename "${words[c]//:/__}")" + next_command="_%[1]s_root_command" else next_command="_${words[c]//:/__}" fi fi c=$((c+1)) - __debug "${FUNCNAME[0]}: looking for ${next_command}" - declare -F $next_command >/dev/null && $next_command + __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}" + declare -F "$next_command" >/dev/null && $next_command } -__handle_word() +__%[1]s_handle_word() { if [[ $c -ge $cword ]]; then - __handle_reply + __%[1]s_handle_reply return fi - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if [[ "${words[c]}" == -* ]]; then - __handle_flag - elif __contains_word "${words[c]}" "${commands[@]}"; then - __handle_command - elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then - __handle_command + __%[1]s_handle_flag + elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then + __%[1]s_handle_command + elif [[ $c -eq 0 ]]; then + __%[1]s_handle_command + elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then + # aliashash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + words[c]=${aliashash[${words[c]}]} + __%[1]s_handle_command + else + __%[1]s_handle_noun + fi else - __handle_noun + __%[1]s_handle_noun fi - __handle_word + __%[1]s_handle_word } -`) - return err +`, name)) } -func postscript(w io.Writer, name string) error { +func writePostscript(buf *bytes.Buffer, name string) { name = strings.Replace(name, ":", "__", -1) - _, err := fmt.Fprintf(w, "__start_%s()\n", name) - if err != nil { - return err - } - _, err = fmt.Fprintf(w, `{ + buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) + buf.WriteString(fmt.Sprintf(`{ local cur prev words cword declare -A flaghash 2>/dev/null || : + declare -A aliashash 2>/dev/null || : if declare -F _init_completion >/dev/null 2>&1; then _init_completion -s || return else - __my_init_completion -n "=" || return + __%[1]s_init_completion -n "=" || return fi local c=0 @@ -271,350 +287,288 @@ func postscript(w io.Writer, name string) error { local local_nonpersistent_flags=() local flags_with_completion=() local flags_completion=() - local commands=("%s") + local commands=("%[1]s") local must_have_one_flag=() local must_have_one_noun=() local last_command local nouns=() - __handle_word + __%[1]s_handle_word } -`, name) - if err != nil { - return err - } - _, err = fmt.Fprintf(w, `if [[ $(type -t compopt) = "builtin" ]]; then +`, name)) + buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then complete -o default -F __start_%s %s else complete -o default -o nospace -F __start_%s %s fi -`, name, name, name, name) - if err != nil { - return err - } - _, err = fmt.Fprintf(w, "# ex: ts=4 sw=4 et filetype=sh\n") - return err +`, name, name, name, name)) + buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") } -func writeCommands(cmd *Command, w io.Writer) error { - if _, err := fmt.Fprintf(w, " commands=()\n"); err != nil { - return err - } +func writeCommands(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" commands=()\n") for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c == cmd.helpCommand { continue } - if _, err := fmt.Fprintf(w, " commands+=(%q)\n", c.Name()); err != nil { - return err - } + buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) + writeCmdAliases(buf, c) } - _, err := fmt.Fprintf(w, "\n") - return err + buf.WriteString("\n") } -func writeFlagHandler(name string, annotations map[string][]string, w io.Writer) error { +func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { for key, value := range annotations { switch key { case BashCompFilenameExt: - _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) - if err != nil { - return err - } + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + var ext string if len(value) > 0 { - ext := "__handle_filename_extension_flag " + strings.Join(value, "|") - _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) + ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|") } else { - ext := "_filedir" - _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) - } - if err != nil { - return err + ext = "_filedir" } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) case BashCompCustom: - _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) - if err != nil { - return err - } + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) if len(value) > 0 { handlers := strings.Join(value, "; ") - _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", handlers) + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) } else { - _, err = fmt.Fprintf(w, " flags_completion+=(:)\n") - } - if err != nil { - return err + buf.WriteString(" flags_completion+=(:)\n") } case BashCompSubdirsInDir: - _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + var ext string if len(value) == 1 { - ext := "__handle_subdirs_in_dir_flag " + value[0] - _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) + ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0] } else { - ext := "_filedir -d" - _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) - } - if err != nil { - return err + ext = "_filedir -d" } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) } } - return nil } -func writeShortFlag(flag *pflag.Flag, w io.Writer) error { - b := (len(flag.NoOptDefVal) > 0) +func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { name := flag.Shorthand format := " " - if !b { + if len(flag.NoOptDefVal) == 0 { format += "two_word_" } format += "flags+=(\"-%s\")\n" - if _, err := fmt.Fprintf(w, format, name); err != nil { - return err - } - return writeFlagHandler("-"+name, flag.Annotations, w) + buf.WriteString(fmt.Sprintf(format, name)) + writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) } -func writeFlag(flag *pflag.Flag, w io.Writer) error { - b := (len(flag.NoOptDefVal) > 0) +func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { name := flag.Name format := " flags+=(\"--%s" - if !b { + if len(flag.NoOptDefVal) == 0 { format += "=" } format += "\")\n" - if _, err := fmt.Fprintf(w, format, name); err != nil { - return err - } - return writeFlagHandler("--"+name, flag.Annotations, w) + buf.WriteString(fmt.Sprintf(format, name)) + writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) } -func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error { - b := (len(flag.NoOptDefVal) > 0) +func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) { name := flag.Name format := " local_nonpersistent_flags+=(\"--%s" - if !b { + if len(flag.NoOptDefVal) == 0 { format += "=" } format += "\")\n" - if _, err := fmt.Fprintf(w, format, name); err != nil { - return err - } - return nil + buf.WriteString(fmt.Sprintf(format, name)) } -func writeFlags(cmd *Command, w io.Writer) error { - _, err := fmt.Fprintf(w, ` flags=() +func writeFlags(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(` flags=() two_word_flags=() local_nonpersistent_flags=() flags_with_completion=() flags_completion=() `) - if err != nil { - return err - } localNonPersistentFlags := cmd.LocalNonPersistentFlags() - var visitErr error cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { - if err := writeFlag(flag, w); err != nil { - visitErr = err + if nonCompletableFlag(flag) { return } + writeFlag(buf, flag, cmd) if len(flag.Shorthand) > 0 { - if err := writeShortFlag(flag, w); err != nil { - visitErr = err - return - } + writeShortFlag(buf, flag, cmd) } if localNonPersistentFlags.Lookup(flag.Name) != nil { - if err := writeLocalNonPersistentFlag(flag, w); err != nil { - visitErr = err - return - } + writeLocalNonPersistentFlag(buf, flag) } }) - if visitErr != nil { - return visitErr - } cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { - if err := writeFlag(flag, w); err != nil { - visitErr = err + if nonCompletableFlag(flag) { return } + writeFlag(buf, flag, cmd) if len(flag.Shorthand) > 0 { - if err := writeShortFlag(flag, w); err != nil { - visitErr = err - return - } + writeShortFlag(buf, flag, cmd) } }) - if visitErr != nil { - return visitErr - } - _, err = fmt.Fprintf(w, "\n") - return err + buf.WriteString("\n") } -func writeRequiredFlag(cmd *Command, w io.Writer) error { - if _, err := fmt.Fprintf(w, " must_have_one_flag=()\n"); err != nil { - return err - } +func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_flag=()\n") flags := cmd.NonInheritedFlags() - var visitErr error flags.VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } for key := range flag.Annotations { switch key { case BashCompOneRequiredFlag: format := " must_have_one_flag+=(\"--%s" - b := (flag.Value.Type() == "bool") - if !b { + if flag.Value.Type() != "bool" { format += "=" } format += "\")\n" - if _, err := fmt.Fprintf(w, format, flag.Name); err != nil { - visitErr = err - return - } + buf.WriteString(fmt.Sprintf(format, flag.Name)) if len(flag.Shorthand) > 0 { - if _, err := fmt.Fprintf(w, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand); err != nil { - visitErr = err - return - } + buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) } } } }) - return visitErr } -func writeRequiredNouns(cmd *Command, w io.Writer) error { - if _, err := fmt.Fprintf(w, " must_have_one_noun=()\n"); err != nil { - return err - } +func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_noun=()\n") sort.Sort(sort.StringSlice(cmd.ValidArgs)) for _, value := range cmd.ValidArgs { - if _, err := fmt.Fprintf(w, " must_have_one_noun+=(%q)\n", value); err != nil { - return err - } + buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) } - return nil } -func writeArgAliases(cmd *Command, w io.Writer) error { - if _, err := fmt.Fprintf(w, " noun_aliases=()\n"); err != nil { - return err +func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { + if len(cmd.Aliases) == 0 { + return } + + sort.Sort(sort.StringSlice(cmd.Aliases)) + + buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) + for _, value := range cmd.Aliases { + buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) + buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) + } + buf.WriteString(` fi`) + buf.WriteString("\n") +} +func writeArgAliases(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" noun_aliases=()\n") sort.Sort(sort.StringSlice(cmd.ArgAliases)) for _, value := range cmd.ArgAliases { - if _, err := fmt.Fprintf(w, " noun_aliases+=(%q)\n", value); err != nil { - return err - } + buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) } - return nil } -func gen(cmd *Command, w io.Writer) error { +func gen(buf *bytes.Buffer, cmd *Command) { for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c == cmd.helpCommand { continue } - if err := gen(c, w); err != nil { - return err - } + gen(buf, c) } commandName := cmd.CommandPath() commandName = strings.Replace(commandName, " ", "_", -1) commandName = strings.Replace(commandName, ":", "__", -1) - if _, err := fmt.Fprintf(w, "_%s()\n{\n", commandName); err != nil { - return err + + if cmd.Root() == cmd { + buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) + } else { + buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) } - if _, err := fmt.Fprintf(w, " last_command=%q\n", commandName); err != nil { - return err - } - if err := writeCommands(cmd, w); err != nil { - return err - } - if err := writeFlags(cmd, w); err != nil { - return err - } - if err := writeRequiredFlag(cmd, w); err != nil { - return err - } - if err := writeRequiredNouns(cmd, w); err != nil { - return err - } - if err := writeArgAliases(cmd, w); err != nil { - return err - } - if _, err := fmt.Fprintf(w, "}\n\n"); err != nil { - return err - } - return nil + + buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) + buf.WriteString("\n") + buf.WriteString(" command_aliases=()\n") + buf.WriteString("\n") + + writeCommands(buf, cmd) + writeFlags(buf, cmd) + writeRequiredFlag(buf, cmd) + writeRequiredNouns(buf, cmd) + writeArgAliases(buf, cmd) + buf.WriteString("}\n\n") } -func (cmd *Command) GenBashCompletion(w io.Writer) error { - if err := preamble(w, cmd.Name()); err != nil { - return err +// GenBashCompletion generates bash completion file and writes to the passed writer. +func (c *Command) GenBashCompletion(w io.Writer) error { + buf := new(bytes.Buffer) + writePreamble(buf, c.Name()) + if len(c.BashCompletionFunction) > 0 { + buf.WriteString(c.BashCompletionFunction + "\n") } - if len(cmd.BashCompletionFunction) > 0 { - if _, err := fmt.Fprintf(w, "%s\n", cmd.BashCompletionFunction); err != nil { - return err - } - } - if err := gen(cmd, w); err != nil { - return err - } - return postscript(w, cmd.Name()) + gen(buf, c) + writePostscript(buf, c.Name()) + + _, err := buf.WriteTo(w) + return err } -func (cmd *Command) GenBashCompletionFile(filename string) error { +func nonCompletableFlag(flag *pflag.Flag) bool { + return flag.Hidden || len(flag.Deprecated) > 0 +} + +// GenBashCompletionFile generates bash completion file. +func (c *Command) GenBashCompletionFile(filename string) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() - return cmd.GenBashCompletion(outFile) + return c.GenBashCompletion(outFile) } -// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists. -func (cmd *Command) MarkFlagRequired(name string) error { - return MarkFlagRequired(cmd.Flags(), name) +// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, +// and causes your command to report an error if invoked without the flag. +func (c *Command) MarkFlagRequired(name string) error { + return MarkFlagRequired(c.Flags(), name) } -// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists. -func (cmd *Command) MarkPersistentFlagRequired(name string) error { - return MarkFlagRequired(cmd.PersistentFlags(), name) +// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag if it exists, +// and causes your command to report an error if invoked without the flag. +func (c *Command) MarkPersistentFlagRequired(name string) error { + return MarkFlagRequired(c.PersistentFlags(), name) } -// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag in the flag set, if it exists. +// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, +// and causes your command to report an error if invoked without the flag. func MarkFlagRequired(flags *pflag.FlagSet, name string) error { return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"}) } // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists. // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. -func (cmd *Command) MarkFlagFilename(name string, extensions ...string) error { - return MarkFlagFilename(cmd.Flags(), name, extensions...) +func (c *Command) MarkFlagFilename(name string, extensions ...string) error { + return MarkFlagFilename(c.Flags(), name, extensions...) } // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. // Generated bash autocompletion will call the bash function f for the flag. -func (cmd *Command) MarkFlagCustom(name string, f string) error { - return MarkFlagCustom(cmd.Flags(), name, f) +func (c *Command) MarkFlagCustom(name string, f string) error { + return MarkFlagCustom(c.Flags(), name, f) } // MarkPersistentFlagFilename adds the BashCompFilenameExt annotation to the named persistent flag, if it exists. // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. -func (cmd *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { - return MarkFlagFilename(cmd.PersistentFlags(), name, extensions...) +func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { + return MarkFlagFilename(c.PersistentFlags(), name, extensions...) } // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists. diff --git a/components/engine/vendor/github.com/spf13/cobra/cobra.go b/components/engine/vendor/github.com/spf13/cobra/cobra.go index 93a2c0f3a7..7010fd15b7 100644 --- a/components/engine/vendor/github.com/spf13/cobra/cobra.go +++ b/components/engine/vendor/github.com/spf13/cobra/cobra.go @@ -27,48 +27,60 @@ import ( ) var templateFuncs = template.FuncMap{ - "trim": strings.TrimSpace, - "trimRightSpace": trimRightSpace, - "appendIfNotPresent": appendIfNotPresent, - "rpad": rpad, - "gt": Gt, - "eq": Eq, + "trim": strings.TrimSpace, + "trimRightSpace": trimRightSpace, + "trimTrailingWhitespaces": trimRightSpace, + "appendIfNotPresent": appendIfNotPresent, + "rpad": rpad, + "gt": Gt, + "eq": Eq, } var initializers []func() -// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools. -// Set this to true to enable it +// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing +// to automatically enable in CLI tools. +// Set this to true to enable it. var EnablePrefixMatching = false -//EnableCommandSorting controls sorting of the slice of commands, which is turned on by default. -//To disable sorting, set it to false. +// EnableCommandSorting controls sorting of the slice of commands, which is turned on by default. +// To disable sorting, set it to false. var EnableCommandSorting = true -//AddTemplateFunc adds a template function that's available to Usage and Help -//template generation. +// MousetrapHelpText enables an information splash screen on Windows +// if the CLI is started from explorer.exe. +// To disable the mousetrap, just set this variable to blank string (""). +// Works only on Microsoft Windows. +var MousetrapHelpText string = `This is a command line tool. + +You need to open cmd.exe and run it from there. +` + +// AddTemplateFunc adds a template function that's available to Usage and Help +// template generation. func AddTemplateFunc(name string, tmplFunc interface{}) { templateFuncs[name] = tmplFunc } -//AddTemplateFuncs adds multiple template functions availalble to Usage and -//Help template generation. +// AddTemplateFuncs adds multiple template functions that are available to Usage and +// Help template generation. func AddTemplateFuncs(tmplFuncs template.FuncMap) { for k, v := range tmplFuncs { templateFuncs[k] = v } } -//OnInitialize takes a series of func() arguments and appends them to a slice of func(). +// OnInitialize sets the passed functions to be run when each command's +// Execute method is called. func OnInitialize(y ...func()) { - for _, x := range y { - initializers = append(initializers, x) - } + initializers = append(initializers, y...) } -//Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans, -//Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as -//ints and then compared. +// FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. + +// Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans, +// Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as +// ints and then compared. func Gt(a interface{}, b interface{}) bool { var left, right int64 av := reflect.ValueOf(a) @@ -96,7 +108,9 @@ func Gt(a interface{}, b interface{}) bool { return left > right } -//Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic. +// FIXME Eq is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. + +// Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic. func Eq(a interface{}, b interface{}) bool { av := reflect.ValueOf(a) bv := reflect.ValueOf(b) @@ -116,7 +130,9 @@ func trimRightSpace(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) } -// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s +// FIXME appendIfNotPresent is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. + +// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s. func appendIfNotPresent(s, stringToAppend string) string { if strings.Contains(s, stringToAppend) { return s @@ -124,7 +140,7 @@ func appendIfNotPresent(s, stringToAppend string) string { return s + " " + stringToAppend } -//rpad adds padding to the right of a string +// rpad adds padding to the right of a string. func rpad(s string, padding int) string { template := fmt.Sprintf("%%-%ds", padding) return fmt.Sprintf(template, s) @@ -138,7 +154,7 @@ func tmpl(w io.Writer, text string, data interface{}) error { return t.Execute(w, data) } -// ld compares two strings and returns the levenshtein distance between them +// ld compares two strings and returns the levenshtein distance between them. func ld(s, t string, ignoreCase bool) int { if ignoreCase { s = strings.ToLower(s) @@ -173,3 +189,12 @@ func ld(s, t string, ignoreCase bool) int { } return d[len(s)][len(t)] } + +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} diff --git a/components/engine/vendor/github.com/spf13/cobra/command.go b/components/engine/vendor/github.com/spf13/cobra/command.go index 2887208620..34d1bf3671 100644 --- a/components/engine/vendor/github.com/spf13/cobra/command.go +++ b/components/engine/vendor/github.com/spf13/cobra/command.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces. -//In addition to providing an interface, Cobra simultaneously provides a controller to organize your application code. +// Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces. +// In addition to providing an interface, Cobra simultaneously provides a controller to organize your application code. package cobra import ( @@ -27,177 +27,224 @@ import ( flag "github.com/spf13/pflag" ) +// FParseErrWhitelist configures Flag parse errors to be ignored +type FParseErrWhitelist flag.ParseErrorsWhitelist + // Command is just that, a command for your application. -// eg. 'go run' ... 'run' is the command. Cobra requires +// E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command // definition to ensure usability. type Command struct { - // Name is the command name, usually the executable's name. - name string - // The one-line usage message. + // Use is the one-line usage message. Use string - // An array of aliases that can be used instead of the first word in Use. + + // Aliases is an array of aliases that can be used instead of the first word in Use. Aliases []string - // An array of command names for which this command will be suggested - similar to aliases but only suggests. + + // SuggestFor is an array of command names for which this command will be suggested - + // similar to aliases but only suggests. SuggestFor []string - // The short description shown in the 'help' output. + + // Short is the short description shown in the 'help' output. Short string - // The long message shown in the 'help ' output. + + // Long is the long message shown in the 'help ' output. Long string - // Examples of how to use the command + + // Example is examples of how to use the command. Example string - // List of all valid non-flag arguments that are accepted in bash completions + + // ValidArgs is list of all valid non-flag arguments that are accepted in bash completions ValidArgs []string - // List of aliases for ValidArgs. These are not suggested to the user in the bash - // completion, but accepted if entered manually. - ArgAliases []string + // Expected arguments Args PositionalArgs - // Custom functions used by the bash autocompletion generator + + // ArgAliases is List of aliases for ValidArgs. + // These are not suggested to the user in the bash completion, + // but accepted if entered manually. + ArgAliases []string + + // BashCompletionFunction is custom functions used by the bash autocompletion generator. BashCompletionFunction string - // Is this command deprecated and should print this string when used? + + // Deprecated defines, if this command is deprecated and should print this string when used. Deprecated string - // Is this command hidden and should NOT show up in the list of available commands? + + // Hidden defines, if this command is hidden and should NOT show up in the list of available commands. Hidden bool - // Tags are key/value pairs that can be used by applications to identify or - // group commands - Tags map[string]string - // Full set of flags - flags *flag.FlagSet - // Set of flags childrens of this command will inherit - pflags *flag.FlagSet - // Flags that are declared specifically by this command (not inherited). - lflags *flag.FlagSet - // SilenceErrors is an option to quiet errors down stream - SilenceErrors bool - // Silence Usage is an option to silence usage when an error occurs. - SilenceUsage bool + + // Annotations are key/value pairs that can be used by applications to identify or + // group commands. + Annotations map[string]string + + // Version defines the version for this command. If this value is non-empty and the command does not + // define a "version" flag, a "version" boolean flag will be added to the command and, if specified, + // will print content of the "Version" variable. + Version string + // The *Run functions are executed in the following order: // * PersistentPreRun() // * PreRun() // * Run() // * PostRun() // * PersistentPostRun() - // All functions get the same args, the arguments after the command name - // PersistentPreRun: children of this command will inherit and execute + // All functions get the same args, the arguments after the command name. + // + // PersistentPreRun: children of this command will inherit and execute. PersistentPreRun func(cmd *Command, args []string) - // PersistentPreRunE: PersistentPreRun but returns an error + // PersistentPreRunE: PersistentPreRun but returns an error. PersistentPreRunE func(cmd *Command, args []string) error // PreRun: children of this command will not inherit. PreRun func(cmd *Command, args []string) - // PreRunE: PreRun but returns an error + // PreRunE: PreRun but returns an error. PreRunE func(cmd *Command, args []string) error - // Run: Typically the actual work function. Most commands will only implement this + // Run: Typically the actual work function. Most commands will only implement this. Run func(cmd *Command, args []string) - // RunE: Run but returns an error + // RunE: Run but returns an error. RunE func(cmd *Command, args []string) error // PostRun: run after the Run command. PostRun func(cmd *Command, args []string) - // PostRunE: PostRun but returns an error + // PostRunE: PostRun but returns an error. PostRunE func(cmd *Command, args []string) error - // PersistentPostRun: children of this command will inherit and execute after PostRun + // PersistentPostRun: children of this command will inherit and execute after PostRun. PersistentPostRun func(cmd *Command, args []string) - // PersistentPostRunE: PersistentPostRun but returns an error + // PersistentPostRunE: PersistentPostRun but returns an error. PersistentPostRunE func(cmd *Command, args []string) error - // DisableAutoGenTag remove + + // SilenceErrors is an option to quiet errors down stream. + SilenceErrors bool + + // SilenceUsage is an option to silence usage when an error occurs. + SilenceUsage bool + + // DisableFlagParsing disables the flag parsing. + // If this is true all flags will be passed to the command as arguments. + DisableFlagParsing bool + + // DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...") + // will be printed by generating docs for this command. DisableAutoGenTag bool - // Commands is the list of commands supported by this program. + + // DisableFlagsInUseLine will disable the addition of [flags] to the usage + // line of a command when printing help or generating docs + DisableFlagsInUseLine bool + + // DisableSuggestions disables the suggestions based on Levenshtein distance + // that go along with 'unknown command' messages. + DisableSuggestions bool + // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions. + // Must be > 0. + SuggestionsMinimumDistance int + + // TraverseChildren parses flags on all parents before executing child command. + TraverseChildren bool + + //FParseErrWhitelist flag parse errors to be ignored + FParseErrWhitelist FParseErrWhitelist + + // commands is the list of commands supported by this program. commands []*Command - // Parent Command for this command + // parent is a parent command for this command. parent *Command - // max lengths of commands' string lengths for use in padding + // Max lengths of commands' string lengths for use in padding. commandsMaxUseLen int commandsMaxCommandPathLen int commandsMaxNameLen int - // is commands slice are sorted or not + // commandsAreSorted defines, if command slice are sorted or not. commandsAreSorted bool + // commandCalledAs is the name or alias value used to call this command. + commandCalledAs struct { + name string + called bool + } + // args is actual args parsed from flags. + args []string + // flagErrorBuf contains all error messages from pflag. flagErrorBuf *bytes.Buffer - - args []string // actual args parsed from flags - output *io.Writer // nil means stderr; use Out() method instead - usageFunc func(*Command) error // Usage can be defined by application - usageTemplate string // Can be defined by Application - flagErrorFunc func(*Command, error) error - helpTemplate string // Can be defined by Application - helpFunc func(*Command, []string) // Help can be defined by application - helpCommand *Command // The help command - // The global normalization function that we can use on every pFlag set and children commands + // flags is full set of flags. + flags *flag.FlagSet + // pflags contains persistent flags. + pflags *flag.FlagSet + // lflags contains local flags. + lflags *flag.FlagSet + // iflags contains inherited flags. + iflags *flag.FlagSet + // parentsPflags is all persistent flags of cmd's parents. + parentsPflags *flag.FlagSet + // globNormFunc is the global normalization function + // that we can use on every pflag set and children commands globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName - // Disable the suggestions based on Levenshtein distance that go along with 'unknown command' messages - DisableSuggestions bool - // If displaying suggestions, allows to set the minimum levenshtein distance to display, must be > 0 - SuggestionsMinimumDistance int - - // Disable the flag parsing. If this is true all flags will be passed to the command as arguments. - DisableFlagParsing bool - - // TraverseChildren parses flags on all parents before executing child command - TraverseChildren bool + // output is an output writer defined by user. + output io.Writer + // usageFunc is usage func defined by user. + usageFunc func(*Command) error + // usageTemplate is usage template defined by user. + usageTemplate string + // flagErrorFunc is func defined by user and it's called when the parsing of + // flags returns an error. + flagErrorFunc func(*Command, error) error + // helpTemplate is help template defined by user. + helpTemplate string + // helpFunc is help func defined by user. + helpFunc func(*Command, []string) + // helpCommand is command with usage 'help'. If it's not defined by user, + // cobra uses default help command. + helpCommand *Command + // versionTemplate is the version template defined by user. + versionTemplate string } -// os.Args[1:] by default, if desired, can be overridden +// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden // particularly useful when testing. func (c *Command) SetArgs(a []string) { c.args = a } -func (c *Command) getOut(def io.Writer) io.Writer { - if c.output != nil { - return *c.output - } - - if c.HasParent() { - return c.parent.Out() - } - return def -} - -func (c *Command) Out() io.Writer { - return c.getOut(os.Stderr) -} - -func (c *Command) getOutOrStdout() io.Writer { - return c.getOut(os.Stdout) -} - // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (c *Command) SetOutput(output io.Writer) { - c.output = &output + c.output = output } -// Usage can be defined by application +// SetUsageFunc sets usage function. Usage can be defined by application. func (c *Command) SetUsageFunc(f func(*Command) error) { c.usageFunc = f } -// Can be defined by Application +// SetUsageTemplate sets usage template. Can be defined by Application. func (c *Command) SetUsageTemplate(s string) { c.usageTemplate = s } // SetFlagErrorFunc sets a function to generate an error when flag parsing -// fails +// fails. func (c *Command) SetFlagErrorFunc(f func(*Command, error) error) { c.flagErrorFunc = f } -// Can be defined by Application +// SetHelpFunc sets help function. Can be defined by Application. func (c *Command) SetHelpFunc(f func(*Command, []string)) { c.helpFunc = f } +// SetHelpCommand sets help command. func (c *Command) SetHelpCommand(cmd *Command) { c.helpCommand = cmd } -// Can be defined by Application +// SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { c.helpTemplate = s } +// SetVersionTemplate sets version template to be used. Application can use it to set custom template. +func (c *Command) SetVersionTemplate(s string) { + c.versionTemplate = s +} + // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { @@ -210,41 +257,88 @@ func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string } } +// OutOrStdout returns output to stdout. +func (c *Command) OutOrStdout() io.Writer { + return c.getOut(os.Stdout) +} + +// OutOrStderr returns output to stderr +func (c *Command) OutOrStderr() io.Writer { + return c.getOut(os.Stderr) +} + +func (c *Command) getOut(def io.Writer) io.Writer { + if c.output != nil { + return c.output + } + if c.HasParent() { + return c.parent.getOut(def) + } + return def +} + +// UsageFunc returns either the function set by SetUsageFunc for this command +// or a parent, or it returns a default usage function. func (c *Command) UsageFunc() (f func(*Command) error) { if c.usageFunc != nil { return c.usageFunc } - if c.HasParent() { - return c.parent.UsageFunc() + return c.Parent().UsageFunc() } return func(c *Command) error { - err := tmpl(c.Out(), c.UsageTemplate(), c) + c.mergePersistentFlags() + err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c) if err != nil { - fmt.Print(err) + c.Println(err) } return err } } +// Usage puts out the usage for the command. +// Used when a user provides invalid input. +// Can be defined by user by overriding UsageFunc. +func (c *Command) Usage() error { + return c.UsageFunc()(c) +} + // HelpFunc returns either the function set by SetHelpFunc for this command -// or a parent, or it returns a function which calls c.Help() +// or a parent, or it returns a function with default help behavior. func (c *Command) HelpFunc() func(*Command, []string) { - cmd := c - for cmd != nil { - if cmd.helpFunc != nil { - return cmd.helpFunc - } - cmd = cmd.parent + if c.helpFunc != nil { + return c.helpFunc } - return func(*Command, []string) { - err := c.Help() + if c.HasParent() { + return c.Parent().HelpFunc() + } + return func(c *Command, a []string) { + c.mergePersistentFlags() + err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) if err != nil { c.Println(err) } } } +// Help puts out the help for the command. +// Used when a user calls help [command]. +// Can be defined by user by overriding HelpFunc. +func (c *Command) Help() error { + c.HelpFunc()(c, []string{}) + return nil +} + +// UsageString return usage string. +func (c *Command) UsageString() string { + tmpOutput := c.output + bb := new(bytes.Buffer) + c.SetOutput(bb) + c.Usage() + c.output = tmpOutput + return bb.String() +} + // FlagErrorFunc returns either the function set by SetFlagErrorFunc for this // command or a parent, or it returns a function which returns the original // error. @@ -263,6 +357,7 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) { var minUsagePadding = 25 +// UsagePadding return padding for the usage. func (c *Command) UsagePadding() int { if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen { return minUsagePadding @@ -272,7 +367,7 @@ func (c *Command) UsagePadding() int { var minCommandPathPadding = 11 -// +// CommandPathPadding return padding for the command path. func (c *Command) CommandPathPadding() int { if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen { return minCommandPathPadding @@ -282,6 +377,7 @@ func (c *Command) CommandPathPadding() int { var minNamePadding = 11 +// NamePadding returns padding for the name. func (c *Command) NamePadding() int { if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen { return minNamePadding @@ -289,6 +385,7 @@ func (c *Command) NamePadding() int { return c.parent.commandsMaxNameLen } +// UsageTemplate returns usage template for the command. func (c *Command) UsageTemplate() string { if c.usageTemplate != "" { return c.usageTemplate @@ -298,32 +395,32 @@ func (c *Command) UsageTemplate() string { return c.parent.UsageTemplate() } return `Usage:{{if .Runnable}} - {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: - {{.NameAndAliases}} -{{end}}{{if .HasExample}} + {{.NameAndAliases}}{{end}}{{if .HasExample}} Examples: -{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}} +{{.Example}}{{end}}{{if .HasAvailableSubCommands}} -Available Commands:{{range .Commands}}{{if .IsAvailableCommand}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}} +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} Flags: -{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}} +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} Global Flags: -{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}} +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} -Additional help topics:{{range .Commands}}{{if .IsHelpCommand}} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }} +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} ` } +// HelpTemplate return help template for the command. func (c *Command) HelpTemplate() string { if c.helpTemplate != "" { return c.helpTemplate @@ -332,72 +429,76 @@ func (c *Command) HelpTemplate() string { if c.HasParent() { return c.parent.HelpTemplate() } - return `{{with or .Long .Short }}{{. | trim}} + return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} {{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` } -// Really only used when casting a command to a commander -func (c *Command) resetChildrensParents() { - for _, x := range c.commands { - x.parent = c +// VersionTemplate return version template for the command. +func (c *Command) VersionTemplate() string { + if c.versionTemplate != "" { + return c.versionTemplate } + + if c.HasParent() { + return c.parent.VersionTemplate() + } + return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} +` } -// Test if the named flag is a boolean flag. -func isBooleanFlag(name string, f *flag.FlagSet) bool { - flag := f.Lookup(name) +func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { + flag := fs.Lookup(name) if flag == nil { return false } - return flag.Value.Type() == "bool" + return flag.NoOptDefVal != "" } -// Test if the named flag is a boolean flag. -func isBooleanShortFlag(name string, f *flag.FlagSet) bool { - result := false - f.VisitAll(func(f *flag.Flag) { - if f.Shorthand == name && f.Value.Type() == "bool" { - result = true - } - }) - return result +func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool { + if len(name) == 0 { + return false + } + + flag := fs.ShorthandLookup(name[:1]) + if flag == nil { + return false + } + return flag.NoOptDefVal != "" } func stripFlags(args []string, c *Command) []string { - if len(args) < 1 { + if len(args) == 0 { return args } c.mergePersistentFlags() commands := []string{} + flags := c.Flags() - inQuote := false - inFlag := false - for _, y := range args { - if !inQuote { - switch { - case strings.HasPrefix(y, "\""): - inQuote = true - case strings.Contains(y, "=\""): - inQuote = true - case strings.HasPrefix(y, "--") && !strings.Contains(y, "="): - // TODO: this isn't quite right, we should really check ahead for 'true' or 'false' - inFlag = !isBooleanFlag(y[2:], c.Flags()) - case strings.HasPrefix(y, "-") && !strings.Contains(y, "=") && len(y) == 2 && !isBooleanShortFlag(y[1:], c.Flags()): - inFlag = true - case inFlag: - inFlag = false - case y == "": - // strip empty commands, as the go tests expect this to be ok.... - case !strings.HasPrefix(y, "-"): - commands = append(commands, y) - inFlag = false +Loop: + for len(args) > 0 { + s := args[0] + args = args[1:] + switch { + case s == "--": + // "--" terminates the flags + break Loop + case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags): + // If '--flag arg' then + // delete arg from args. + fallthrough // (do the same as below) + case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags): + // If '-f arg' then + // delete 'arg' from args or break the loop if len(args) <= 1. + if len(args) <= 1 { + break Loop + } else { + args = args[1:] + continue } - } - - if strings.HasSuffix(y, "\"") && !strings.HasSuffix(y, "\\\"") { - inQuote = false + case s != "" && !strings.HasPrefix(s, "-"): + commands = append(commands, s) } } @@ -449,13 +550,31 @@ func (c *Command) Find(args []string) (*Command, []string, error) { return commandFound, a, nil } +func (c *Command) findSuggestions(arg string) string { + if c.DisableSuggestions { + return "" + } + if c.SuggestionsMinimumDistance <= 0 { + c.SuggestionsMinimumDistance = 2 + } + suggestionsString := "" + if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 { + suggestionsString += "\n\nDid you mean this?\n" + for _, s := range suggestions { + suggestionsString += fmt.Sprintf("\t%v\n", s) + } + } + return suggestionsString +} + func (c *Command) findNext(next string) *Command { matches := make([]*Command, 0) for _, cmd := range c.commands { if cmd.Name() == next || cmd.HasAlias(next) { + cmd.commandCalledAs.name = next return cmd } - if EnablePrefixMatching && cmd.HasNameOrAliasPrefix(next) { + if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) { matches = append(matches, cmd) } } @@ -463,6 +582,7 @@ func (c *Command) findNext(next string) *Command { if len(matches) == 1 { return matches[0] } + return nil } @@ -477,11 +597,11 @@ func (c *Command) Traverse(args []string) (*Command, []string, error) { // A long flag with a space separated value case strings.HasPrefix(arg, "--") && !strings.Contains(arg, "="): // TODO: this isn't quite right, we should really check ahead for 'true' or 'false' - inFlag = !isBooleanFlag(arg[2:], c.Flags()) + inFlag = !hasNoOptDefVal(arg[2:], c.Flags()) flags = append(flags, arg) continue // A short flag with a space separated value - case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !isBooleanShortFlag(arg[1:], c.Flags()): + case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !shortHasNoOptDefVal(arg[1:], c.Flags()): inFlag = true flags = append(flags, arg) continue @@ -509,23 +629,7 @@ func (c *Command) Traverse(args []string) (*Command, []string, error) { return c, args, nil } -func (c *Command) findSuggestions(arg string) string { - if c.DisableSuggestions { - return "" - } - if c.SuggestionsMinimumDistance <= 0 { - c.SuggestionsMinimumDistance = 2 - } - suggestionsString := "" - if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 { - suggestionsString += "\n\nDid you mean this?\n" - for _, s := range suggestions { - suggestionsString += fmt.Sprintf("\t%v\n", s) - } - } - return suggestionsString -} - +// SuggestionsFor provides suggestions for the typedName. func (c *Command) SuggestionsFor(typedName string) []string { suggestions := []string{} for _, cmd := range c.commands { @@ -546,38 +650,24 @@ func (c *Command) SuggestionsFor(typedName string) []string { return suggestions } +// VisitParents visits all parents of the command and invokes fn on each parent. func (c *Command) VisitParents(fn func(*Command)) { - var traverse func(*Command) *Command - - traverse = func(x *Command) *Command { - if x != c { - fn(x) - } - if x.HasParent() { - return traverse(x.parent) - } - return x + if c.HasParent() { + fn(c.Parent()) + c.Parent().VisitParents(fn) } - traverse(c) } +// Root finds root command. func (c *Command) Root() *Command { - var findRoot func(*Command) *Command - - findRoot = func(x *Command) *Command { - if x.HasParent() { - return findRoot(x.parent) - } - return x + if c.HasParent() { + return c.Parent().Root() } - - return findRoot(c) + return c } -// ArgsLenAtDash will return the length of f.Args at the moment when a -- was -// found during arg parsing. This allows your program to know which args were -// before the -- and which came after. (Description from -// https://godoc.org/github.com/spf13/pflag#FlagSet.ArgsLenAtDash). +// ArgsLenAtDash will return the length of c.Flags().Args at the moment +// when a -- was found during args parsing. func (c *Command) ArgsLenAtDash() int { return c.Flags().ArgsLenAtDash() } @@ -591,24 +681,47 @@ func (c *Command) execute(a []string) (err error) { c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated) } - // initialize help flag as the last point possible to allow for user + // initialize help and version flag at the last point possible to allow for user // overriding - c.initHelpFlag() + c.InitDefaultHelpFlag() + c.InitDefaultVersionFlag() err = c.ParseFlags(a) if err != nil { return c.FlagErrorFunc()(c, err) } - // If help is called, regardless of other flags, return we want help + + // If help is called, regardless of other flags, return we want help. // Also say we need help if the command isn't runnable. helpVal, err := c.Flags().GetBool("help") if err != nil { // should be impossible to get here as we always declare a help - // flag in initHelpFlag() + // flag in InitDefaultHelpFlag() c.Println("\"help\" flag declared as non-bool. Please correct your code") return err } - if helpVal || !c.Runnable() { + + if helpVal { + return flag.ErrHelp + } + + // for back-compat, only add version flag behavior if version is defined + if c.Version != "" { + versionVal, err := c.Flags().GetBool("version") + if err != nil { + c.Println("\"version\" flag declared as non-bool. Please correct your code") + return err + } + if versionVal { + err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) + if err != nil { + c.Println(err) + } + return err + } + } + + if !c.Runnable() { return flag.ErrHelp } @@ -642,6 +755,9 @@ func (c *Command) execute(a []string) (err error) { c.PreRun(c, argWoFlags) } + if err := c.validateRequiredFlags(); err != nil { + return err + } if c.RunE != nil { if err := c.RunE(c, argWoFlags); err != nil { return err @@ -677,18 +793,7 @@ func (c *Command) preRun() { } } -func (c *Command) errorMsgFromParse() string { - s := c.flagErrorBuf.String() - - x := strings.Split(s, "\n") - - if len(x) > 0 { - return x[0] - } - return "" -} - -// Call execute to use the args (os.Args[1:] by default) +// Execute uses the args (os.Args[1:] by default) // and run through the command tree finding appropriate matches // for commands and then corresponding flags. func (c *Command) Execute() error { @@ -696,8 +801,8 @@ func (c *Command) Execute() error { return err } +// ExecuteC executes the command. func (c *Command) ExecuteC() (cmd *Command, err error) { - // Regardless of what command execute is called on, run on Root only if c.HasParent() { return c.Root().ExecuteC() @@ -710,7 +815,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // initialize help as the last point possible to allow for user // overriding - c.initHelpCmd() + c.InitDefaultHelpCmd() var args []string @@ -739,6 +844,11 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { return c, err } + cmd.commandCalledAs.called = true + if cmd.commandCalledAs.name == "" { + cmd.commandCalledAs.name = cmd.Name() + } + err = cmd.execute(flags) if err != nil { // Always show help if requested, even if SilenceErrors is in @@ -759,9 +869,8 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { if !cmd.SilenceUsage && !c.SilenceUsage { c.Println(cmd.UsageString()) } - return cmd, err } - return cmd, nil + return cmd, err } func (c *Command) ValidateArgs(args []string) error { @@ -771,49 +880,102 @@ func (c *Command) ValidateArgs(args []string) error { return c.Args(c, args) } -func (c *Command) initHelpFlag() { +func (c *Command) validateRequiredFlags() error { + flags := c.Flags() + missingFlagNames := []string{} + flags.VisitAll(func(pflag *flag.Flag) { + requiredAnnotation, found := pflag.Annotations[BashCompOneRequiredFlag] + if !found { + return + } + if (requiredAnnotation[0] == "true") && !pflag.Changed { + missingFlagNames = append(missingFlagNames, pflag.Name) + } + }) + + if len(missingFlagNames) > 0 { + return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`)) + } + return nil +} + +// InitDefaultHelpFlag adds default help flag to c. +// It is called automatically by executing the c or by calling help and usage. +// If c already has help flag, it will do nothing. +func (c *Command) InitDefaultHelpFlag() { c.mergePersistentFlags() if c.Flags().Lookup("help") == nil { - c.Flags().BoolP("help", "h", false, "help for "+c.Name()) + usage := "help for " + if c.Name() == "" { + usage += "this command" + } else { + usage += c.Name() + } + c.Flags().BoolP("help", "h", false, usage) } } -func (c *Command) initHelpCmd() { - if c.helpCommand == nil { - if !c.HasSubCommands() { - return - } +// InitDefaultVersionFlag adds default version flag to c. +// It is called automatically by executing the c. +// If c already has a version flag, it will do nothing. +// If c.Version is empty, it will do nothing. +func (c *Command) InitDefaultVersionFlag() { + if c.Version == "" { + return + } + c.mergePersistentFlags() + if c.Flags().Lookup("version") == nil { + usage := "version for " + if c.Name() == "" { + usage += "this command" + } else { + usage += c.Name() + } + c.Flags().Bool("version", false, usage) + } +} + +// InitDefaultHelpCmd adds default help command to c. +// It is called automatically by executing the c or by calling help and usage. +// If c already has help command or c has no subcommands, it will do nothing. +func (c *Command) InitDefaultHelpCmd() { + if !c.HasSubCommands() { + return + } + + if c.helpCommand == nil { c.helpCommand = &Command{ Use: "help [command]", Short: "Help about any command", Long: `Help provides help for any command in the application. - Simply type ` + c.Name() + ` help [path to command] for full details.`, - PersistentPreRun: func(cmd *Command, args []string) {}, - PersistentPostRun: func(cmd *Command, args []string) {}, +Simply type ` + c.Name() + ` help [path to command] for full details.`, Run: func(c *Command, args []string) { cmd, _, e := c.Root().Find(args) if cmd == nil || e != nil { - c.Printf("Unknown help topic %#q.", args) + c.Printf("Unknown help topic %#q\n", args) c.Root().Usage() } else { - helpFunc := cmd.HelpFunc() - helpFunc(cmd, args) + cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown + cmd.Help() } }, } } + c.RemoveCommand(c.helpCommand) c.AddCommand(c.helpCommand) } -// Used for testing +// ResetCommands delete parent, subcommand and help command from c. func (c *Command) ResetCommands() { + c.parent = nil c.commands = nil c.helpCommand = nil + c.parentsPflags = nil } -// Sorts commands by their names +// Sorts commands by their names. type commandSorterByName []*Command func (c commandSorterByName) Len() int { return len(c) } @@ -893,72 +1055,48 @@ main: } } -// Print is a convenience method to Print to the defined output +// Print is a convenience method to Print to the defined output, fallback to Stderr if not set. func (c *Command) Print(i ...interface{}) { - fmt.Fprint(c.Out(), i...) + fmt.Fprint(c.OutOrStderr(), i...) } -// Println is a convenience method to Println to the defined output +// Println is a convenience method to Println to the defined output, fallback to Stderr if not set. func (c *Command) Println(i ...interface{}) { - str := fmt.Sprintln(i...) - c.Print(str) + c.Print(fmt.Sprintln(i...)) } -// Printf is a convenience method to Printf to the defined output +// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set. func (c *Command) Printf(format string, i ...interface{}) { - str := fmt.Sprintf(format, i...) - c.Print(str) -} - -// Output the usage for the command -// Used when a user provides invalid input -// Can be defined by user by overriding UsageFunc -func (c *Command) Usage() error { - c.mergePersistentFlags() - err := c.UsageFunc()(c) - return err -} - -// Output the help for the command -// Used when a user calls help [command] -// by the default HelpFunc in the commander -func (c *Command) Help() error { - c.mergePersistentFlags() - err := tmpl(c.getOutOrStdout(), c.HelpTemplate(), c) - return err -} - -func (c *Command) UsageString() string { - tmpOutput := c.output - bb := new(bytes.Buffer) - c.SetOutput(bb) - c.Usage() - c.output = tmpOutput - return bb.String() + c.Print(fmt.Sprintf(format, i...)) } // CommandPath returns the full path to this command. func (c *Command) CommandPath() string { - str := c.Name() - x := c - for x.HasParent() { - str = x.parent.Name() + " " + str - x = x.parent - } - return str -} - -//The full usage for a given command (including parents) -func (c *Command) UseLine() string { - str := "" if c.HasParent() { - str = c.parent.CommandPath() + " " + return c.Parent().CommandPath() + " " + c.Name() } - return str + c.Use + return c.Name() } -// For use in determining which flags have been assigned to which commands -// and which persist +// UseLine puts out the full usage for a given command (including parents). +func (c *Command) UseLine() string { + var useline string + if c.HasParent() { + useline = c.parent.CommandPath() + " " + c.Use + } else { + useline = c.Use + } + if c.DisableFlagsInUseLine { + return useline + } + if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") { + useline += " [flags]" + } + return useline +} + +// DebugFlags used to determine which flags have been assigned to which commands +// and which persist. func (c *Command) DebugFlags() { c.Println("DebugFlags called on", c.Name()) var debugflags func(*Command) @@ -969,12 +1107,8 @@ func (c *Command) DebugFlags() { } if x.HasFlags() { x.flags.VisitAll(func(f *flag.Flag) { - if x.HasPersistentFlags() { - if x.persistentFlag(f.Name) == nil { - c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]") - } else { - c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [LP]") - } + if x.HasPersistentFlags() && x.persistentFlag(f.Name) != nil { + c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [LP]") } else { c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]") } @@ -1004,9 +1138,6 @@ func (c *Command) DebugFlags() { // Name returns the command's name: the first word in the use line. func (c *Command) Name() string { - if c.name != "" { - return c.name - } name := c.Use i := strings.Index(name, " ") if i >= 0 { @@ -1025,40 +1156,53 @@ func (c *Command) HasAlias(s string) bool { return false } -// HasNameOrAliasPrefix returns true if the Name or any of aliases start +// CalledAs returns the command name or alias that was used to invoke +// this command or an empty string if the command has not been called. +func (c *Command) CalledAs() string { + if c.commandCalledAs.called { + return c.commandCalledAs.name + } + return "" +} + +// hasNameOrAliasPrefix returns true if the Name or any of aliases start // with prefix -func (c *Command) HasNameOrAliasPrefix(prefix string) bool { +func (c *Command) hasNameOrAliasPrefix(prefix string) bool { if strings.HasPrefix(c.Name(), prefix) { + c.commandCalledAs.name = c.Name() return true } for _, alias := range c.Aliases { if strings.HasPrefix(alias, prefix) { + c.commandCalledAs.name = alias return true } } return false } +// NameAndAliases returns a list of the command name and all aliases func (c *Command) NameAndAliases() string { return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") } +// HasExample determines if the command has example. func (c *Command) HasExample() bool { return len(c.Example) > 0 } -// Runnable determines if the command is itself runnable +// Runnable determines if the command is itself runnable. func (c *Command) Runnable() bool { return c.Run != nil || c.RunE != nil } -// HasSubCommands determines if the command has children commands +// HasSubCommands determines if the command has children commands. func (c *Command) HasSubCommands() bool { return len(c.commands) > 0 } // IsAvailableCommand determines if a command is available as a non-help command -// (this includes all non deprecated/hidden commands) +// (this includes all non deprecated/hidden commands). func (c *Command) IsAvailableCommand() bool { if len(c.Deprecated) != 0 || c.Hidden { return false @@ -1075,11 +1219,12 @@ func (c *Command) IsAvailableCommand() bool { return false } -// IsHelpCommand determines if a command is a 'help' command; a help command is -// determined by the fact that it is NOT runnable/hidden/deprecated, and has no -// sub commands that are runnable/hidden/deprecated -func (c *Command) IsHelpCommand() bool { - +// IsAdditionalHelpTopicCommand determines if a command is an additional +// help topic command; additional help topic command is determined by the +// fact that it is NOT runnable/hidden/deprecated, and has no sub commands that +// are runnable/hidden/deprecated. +// Concrete example: https://github.com/spf13/cobra/issues/393#issuecomment-282741924. +func (c *Command) IsAdditionalHelpTopicCommand() bool { // if a command is runnable, deprecated, or hidden it is not a 'help' command if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden { return false @@ -1087,7 +1232,7 @@ func (c *Command) IsHelpCommand() bool { // if any non-help sub commands are found, the command is not a 'help' command for _, sub := range c.commands { - if !sub.IsHelpCommand() { + if !sub.IsAdditionalHelpTopicCommand() { return false } } @@ -1096,14 +1241,13 @@ func (c *Command) IsHelpCommand() bool { return true } -// HasHelpSubCommands determines if a command has any avilable 'help' sub commands +// HasHelpSubCommands determines if a command has any available 'help' sub commands // that need to be shown in the usage/help default template under 'additional help -// topics' +// topics'. func (c *Command) HasHelpSubCommands() bool { - // return true on the first found available 'help' sub command for _, sub := range c.commands { - if sub.IsHelpCommand() { + if sub.IsAdditionalHelpTopicCommand() { return true } } @@ -1113,9 +1257,8 @@ func (c *Command) HasHelpSubCommands() bool { } // HasAvailableSubCommands determines if a command has available sub commands that -// need to be shown in the usage/help default template under 'available commands' +// need to be shown in the usage/help default template under 'available commands'. func (c *Command) HasAvailableSubCommands() bool { - // return true on the first found available (non deprecated/help/hidden) // sub command for _, sub := range c.commands { @@ -1124,22 +1267,23 @@ func (c *Command) HasAvailableSubCommands() bool { } } - // the command either has no sub comamnds, or no available (non deprecated/help/hidden) + // the command either has no sub commands, or no available (non deprecated/help/hidden) // sub commands return false } -// Determine if the command is a child command +// HasParent determines if the command is a child command. func (c *Command) HasParent() bool { return c.parent != nil } -// GlobalNormalizationFunc returns the global normalization function or nil if doesn't exists +// GlobalNormalizationFunc returns the global normalization function or nil if it doesn't exist. func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) flag.NormalizedName { return c.globNormFunc } -// Get the complete FlagSet that applies to this command (local and persistent declared here and by all parents) +// Flags returns the complete FlagSet that applies +// to this command (local and persistent declared here and by all parents). func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) @@ -1148,10 +1292,11 @@ func (c *Command) Flags() *flag.FlagSet { } c.flags.SetOutput(c.flagErrorBuf) } + return c.flags } -// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands +// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands. func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { persistentFlags := c.PersistentFlags() @@ -1164,59 +1309,63 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { return out } -// Get the local FlagSet specifically set in the current command +// LocalFlags returns the local FlagSet specifically set in the current command. func (c *Command) LocalFlags() *flag.FlagSet { c.mergePersistentFlags() - local := flag.NewFlagSet(c.Name(), flag.ContinueOnError) - c.lflags.VisitAll(func(f *flag.Flag) { - local.AddFlag(f) - }) - if !c.HasParent() { - flag.CommandLine.VisitAll(func(f *flag.Flag) { - if local.Lookup(f.Name) == nil { - local.AddFlag(f) - } - }) + if c.lflags == nil { + c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + if c.flagErrorBuf == nil { + c.flagErrorBuf = new(bytes.Buffer) + } + c.lflags.SetOutput(c.flagErrorBuf) } - return local + c.lflags.SortFlags = c.Flags().SortFlags + if c.globNormFunc != nil { + c.lflags.SetNormalizeFunc(c.globNormFunc) + } + + addToLocal := func(f *flag.Flag) { + if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil { + c.lflags.AddFlag(f) + } + } + c.Flags().VisitAll(addToLocal) + c.PersistentFlags().VisitAll(addToLocal) + return c.lflags } -// All Flags which were inherited from parents commands +// InheritedFlags returns all flags which were inherited from parents commands. func (c *Command) InheritedFlags() *flag.FlagSet { c.mergePersistentFlags() - inherited := flag.NewFlagSet(c.Name(), flag.ContinueOnError) + if c.iflags == nil { + c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + if c.flagErrorBuf == nil { + c.flagErrorBuf = new(bytes.Buffer) + } + c.iflags.SetOutput(c.flagErrorBuf) + } + local := c.LocalFlags() - - var rmerge func(x *Command) - - rmerge = func(x *Command) { - if x.HasPersistentFlags() { - x.PersistentFlags().VisitAll(func(f *flag.Flag) { - if inherited.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil { - inherited.AddFlag(f) - } - }) - } - if x.HasParent() { - rmerge(x.parent) - } + if c.globNormFunc != nil { + c.iflags.SetNormalizeFunc(c.globNormFunc) } - if c.HasParent() { - rmerge(c.parent) - } - - return inherited + c.parentsPflags.VisitAll(func(f *flag.Flag) { + if c.iflags.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil { + c.iflags.AddFlag(f) + } + }) + return c.iflags } -// All Flags which were not inherited from parent commands +// NonInheritedFlags returns all flags which were not inherited from parent commands. func (c *Command) NonInheritedFlags() *flag.FlagSet { return c.LocalFlags() } -// Get the Persistent FlagSet specifically set in the current command +// PersistentFlags returns the persistent FlagSet specifically set in the current command. func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) @@ -1228,7 +1377,7 @@ func (c *Command) PersistentFlags() *flag.FlagSet { return c.pflags } -// For use in testing +// ResetFlags deletes all flags from command. func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() @@ -1236,52 +1385,56 @@ func (c *Command) ResetFlags() { c.flags.SetOutput(c.flagErrorBuf) c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) + + c.lflags = nil + c.iflags = nil + c.parentsPflags = nil } -// Does the command contain any flags (local plus persistent from the entire structure) +// HasFlags checks if the command contains any flags (local plus persistent from the entire structure). func (c *Command) HasFlags() bool { return c.Flags().HasFlags() } -// Does the command contain persistent flags +// HasPersistentFlags checks if the command contains persistent flags. func (c *Command) HasPersistentFlags() bool { return c.PersistentFlags().HasFlags() } -// Does the command has flags specifically declared locally +// HasLocalFlags checks if the command has flags specifically declared locally. func (c *Command) HasLocalFlags() bool { return c.LocalFlags().HasFlags() } -// Does the command have flags inherited from its parent command +// HasInheritedFlags checks if the command has flags inherited from its parent command. func (c *Command) HasInheritedFlags() bool { return c.InheritedFlags().HasFlags() } -// Does the command contain any flags (local plus persistent from the entire -// structure) which are not hidden or deprecated +// HasAvailableFlags checks if the command contains any flags (local plus persistent from the entire +// structure) which are not hidden or deprecated. func (c *Command) HasAvailableFlags() bool { return c.Flags().HasAvailableFlags() } -// Does the command contain persistent flags which are not hidden or deprecated +// HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated. func (c *Command) HasAvailablePersistentFlags() bool { return c.PersistentFlags().HasAvailableFlags() } -// Does the command has flags specifically declared locally which are not hidden -// or deprecated +// HasAvailableLocalFlags checks if the command has flags specifically declared locally which are not hidden +// or deprecated. func (c *Command) HasAvailableLocalFlags() bool { return c.LocalFlags().HasAvailableFlags() } -// Does the command have flags inherited from its parent command which are -// not hidden or deprecated +// HasAvailableInheritedFlags checks if the command has flags inherited from its parent command which are +// not hidden or deprecated. func (c *Command) HasAvailableInheritedFlags() bool { return c.InheritedFlags().HasAvailableFlags() } -// Flag climbs up the command tree looking for matching flag +// Flag climbs up the command tree looking for matching flag. func (c *Command) Flag(name string) (flag *flag.Flag) { flag = c.Flags().Lookup(name) @@ -1292,68 +1445,73 @@ func (c *Command) Flag(name string) (flag *flag.Flag) { return } -// recursively find matching persistent flag +// Recursively find matching persistent flag. func (c *Command) persistentFlag(name string) (flag *flag.Flag) { if c.HasPersistentFlags() { flag = c.PersistentFlags().Lookup(name) } - if flag == nil && c.HasParent() { - flag = c.parent.persistentFlag(name) + if flag == nil { + c.updateParentsPflags() + flag = c.parentsPflags.Lookup(name) } return } -// ParseFlags parses persistent flag tree & local flags -func (c *Command) ParseFlags(args []string) (err error) { +// ParseFlags parses persistent flag tree and local flags. +func (c *Command) ParseFlags(args []string) error { if c.DisableFlagParsing { return nil } + + if c.flagErrorBuf == nil { + c.flagErrorBuf = new(bytes.Buffer) + } + beforeErrorBufLen := c.flagErrorBuf.Len() c.mergePersistentFlags() - err = c.Flags().Parse(args) - return + + //do it here after merging all flags and just before parse + c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist) + + err := c.Flags().Parse(args) + // Print warnings if they occurred (e.g. deprecated flag messages). + if c.flagErrorBuf.Len()-beforeErrorBufLen > 0 && err == nil { + c.Print(c.flagErrorBuf.String()) + } + + return err } -// Parent returns a commands parent command +// Parent returns a commands parent command. func (c *Command) Parent() *Command { return c.parent } +// mergePersistentFlags merges c.PersistentFlags() to c.Flags() +// and adds missing persistent flags of all parents. func (c *Command) mergePersistentFlags() { - var rmerge func(x *Command) - - // Save the set of local flags - if c.lflags == nil { - c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) - if c.flagErrorBuf == nil { - c.flagErrorBuf = new(bytes.Buffer) - } - c.lflags.SetOutput(c.flagErrorBuf) - addtolocal := func(f *flag.Flag) { - c.lflags.AddFlag(f) - } - c.Flags().VisitAll(addtolocal) - c.PersistentFlags().VisitAll(addtolocal) - } - rmerge = func(x *Command) { - if !x.HasParent() { - flag.CommandLine.VisitAll(func(f *flag.Flag) { - if x.PersistentFlags().Lookup(f.Name) == nil { - x.PersistentFlags().AddFlag(f) - } - }) - } - if x.HasPersistentFlags() { - x.PersistentFlags().VisitAll(func(f *flag.Flag) { - if c.Flags().Lookup(f.Name) == nil { - c.Flags().AddFlag(f) - } - }) - } - if x.HasParent() { - rmerge(x.parent) - } - } - - rmerge(c) + c.updateParentsPflags() + c.Flags().AddFlagSet(c.PersistentFlags()) + c.Flags().AddFlagSet(c.parentsPflags) +} + +// updateParentsPflags updates c.parentsPflags by adding +// new persistent flags of all parents. +// If c.parentsPflags == nil, it makes new. +func (c *Command) updateParentsPflags() { + if c.parentsPflags == nil { + c.parentsPflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.parentsPflags.SetOutput(c.flagErrorBuf) + c.parentsPflags.SortFlags = false + } + + if c.globNormFunc != nil { + c.parentsPflags.SetNormalizeFunc(c.globNormFunc) + } + + c.Root().PersistentFlags().AddFlagSet(flag.CommandLine) + + c.VisitParents(func(parent *Command) { + c.parentsPflags.AddFlagSet(parent.PersistentFlags()) + }) } diff --git a/components/engine/vendor/github.com/spf13/cobra/command_win.go b/components/engine/vendor/github.com/spf13/cobra/command_win.go index 4b0eaa1b6b..edec728e4f 100644 --- a/components/engine/vendor/github.com/spf13/cobra/command_win.go +++ b/components/engine/vendor/github.com/spf13/cobra/command_win.go @@ -11,14 +11,8 @@ import ( var preExecHookFn = preExecHook -// enables an information splash screen on Windows if the CLI is started from explorer.exe. -var MousetrapHelpText string = `This is a command line tool - -You need to open cmd.exe and run it from there. -` - func preExecHook(c *Command) { - if mousetrap.StartedByExplorer() { + if MousetrapHelpText != "" && mousetrap.StartedByExplorer() { c.Print(MousetrapHelpText) time.Sleep(5 * time.Second) os.Exit(1) diff --git a/components/engine/vendor/github.com/spf13/cobra/zsh_completions.go b/components/engine/vendor/github.com/spf13/cobra/zsh_completions.go new file mode 100644 index 0000000000..889c22e273 --- /dev/null +++ b/components/engine/vendor/github.com/spf13/cobra/zsh_completions.go @@ -0,0 +1,126 @@ +package cobra + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" +) + +// GenZshCompletionFile generates zsh completion file. +func (c *Command) GenZshCompletionFile(filename string) error { + outFile, err := os.Create(filename) + if err != nil { + return err + } + defer outFile.Close() + + return c.GenZshCompletion(outFile) +} + +// GenZshCompletion generates a zsh completion file and writes to the passed writer. +func (c *Command) GenZshCompletion(w io.Writer) error { + buf := new(bytes.Buffer) + + writeHeader(buf, c) + maxDepth := maxDepth(c) + writeLevelMapping(buf, maxDepth) + writeLevelCases(buf, maxDepth, c) + + _, err := buf.WriteTo(w) + return err +} + +func writeHeader(w io.Writer, cmd *Command) { + fmt.Fprintf(w, "#compdef %s\n\n", cmd.Name()) +} + +func maxDepth(c *Command) int { + if len(c.Commands()) == 0 { + return 0 + } + maxDepthSub := 0 + for _, s := range c.Commands() { + subDepth := maxDepth(s) + if subDepth > maxDepthSub { + maxDepthSub = subDepth + } + } + return 1 + maxDepthSub +} + +func writeLevelMapping(w io.Writer, numLevels int) { + fmt.Fprintln(w, `_arguments \`) + for i := 1; i <= numLevels; i++ { + fmt.Fprintf(w, ` '%d: :->level%d' \`, i, i) + fmt.Fprintln(w) + } + fmt.Fprintf(w, ` '%d: :%s'`, numLevels+1, "_files") + fmt.Fprintln(w) +} + +func writeLevelCases(w io.Writer, maxDepth int, root *Command) { + fmt.Fprintln(w, "case $state in") + defer fmt.Fprintln(w, "esac") + + for i := 1; i <= maxDepth; i++ { + fmt.Fprintf(w, " level%d)\n", i) + writeLevel(w, root, i) + fmt.Fprintln(w, " ;;") + } + fmt.Fprintln(w, " *)") + fmt.Fprintln(w, " _arguments '*: :_files'") + fmt.Fprintln(w, " ;;") +} + +func writeLevel(w io.Writer, root *Command, i int) { + fmt.Fprintf(w, " case $words[%d] in\n", i) + defer fmt.Fprintln(w, " esac") + + commands := filterByLevel(root, i) + byParent := groupByParent(commands) + + for p, c := range byParent { + names := names(c) + fmt.Fprintf(w, " %s)\n", p) + fmt.Fprintf(w, " _arguments '%d: :(%s)'\n", i, strings.Join(names, " ")) + fmt.Fprintln(w, " ;;") + } + fmt.Fprintln(w, " *)") + fmt.Fprintln(w, " _arguments '*: :_files'") + fmt.Fprintln(w, " ;;") + +} + +func filterByLevel(c *Command, l int) []*Command { + cs := make([]*Command, 0) + if l == 0 { + cs = append(cs, c) + return cs + } + for _, s := range c.Commands() { + cs = append(cs, filterByLevel(s, l-1)...) + } + return cs +} + +func groupByParent(commands []*Command) map[string][]*Command { + m := make(map[string][]*Command) + for _, c := range commands { + parent := c.Parent() + if parent == nil { + continue + } + m[parent.Name()] = append(m[parent.Name()], c) + } + return m +} + +func names(commands []*Command) []string { + ns := make([]string, len(commands)) + for i, c := range commands { + ns[i] = c.Name() + } + return ns +} diff --git a/components/engine/vendor/github.com/spf13/pflag/README.md b/components/engine/vendor/github.com/spf13/pflag/README.md index eefb46dec8..b052414d12 100644 --- a/components/engine/vendor/github.com/spf13/pflag/README.md +++ b/components/engine/vendor/github.com/spf13/pflag/README.md @@ -246,6 +246,25 @@ It is possible to mark a flag as hidden, meaning it will still function as norma flags.MarkHidden("secretFlag") ``` +## Disable sorting of flags +`pflag` allows you to disable sorting of flags for help and usage message. + +**Example**: +```go +flags.BoolP("verbose", "v", false, "verbose output") +flags.String("coolflag", "yeaah", "it's really cool flag") +flags.Int("usefulflag", 777, "sometimes it's very useful") +flags.SortFlags = false +flags.PrintDefaults() +``` +**Output**: +``` + -v, --verbose verbose output + --coolflag string it's really cool flag (default "yeaah") + --usefulflag int sometimes it's very useful (default 777) +``` + + ## Supporting Go flags when using pflag In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary to support flags defined by third-party dependencies (e.g. `golang/glog`). @@ -270,8 +289,8 @@ func main() { You can see the full reference documentation of the pflag package [at godoc.org][3], or through go's standard documentation system by running `godoc -http=:6060` and browsing to -[http://localhost:6060/pkg/github.com/ogier/pflag][2] after +[http://localhost:6060/pkg/github.com/spf13/pflag][2] after installation. -[2]: http://localhost:6060/pkg/github.com/ogier/pflag -[3]: http://godoc.org/github.com/ogier/pflag +[2]: http://localhost:6060/pkg/github.com/spf13/pflag +[3]: http://godoc.org/github.com/spf13/pflag diff --git a/components/engine/vendor/github.com/spf13/pflag/bytes.go b/components/engine/vendor/github.com/spf13/pflag/bytes.go new file mode 100644 index 0000000000..12c58db9fe --- /dev/null +++ b/components/engine/vendor/github.com/spf13/pflag/bytes.go @@ -0,0 +1,105 @@ +package pflag + +import ( + "encoding/hex" + "fmt" + "strings" +) + +// BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded +type bytesHexValue []byte + +func (bytesHex bytesHexValue) String() string { + return fmt.Sprintf("%X", []byte(bytesHex)) +} + +func (bytesHex *bytesHexValue) Set(value string) error { + bin, err := hex.DecodeString(strings.TrimSpace(value)) + + if err != nil { + return err + } + + *bytesHex = bin + + return nil +} + +func (*bytesHexValue) Type() string { + return "bytesHex" +} + +func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue { + *p = val + return (*bytesHexValue)(p) +} + +func bytesHexConv(sval string) (interface{}, error) { + + bin, err := hex.DecodeString(sval) + + if err == nil { + return bin, nil + } + + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) +} + +// GetBytesHex return the []byte value of a flag with the given name +func (f *FlagSet) GetBytesHex(name string) ([]byte, error) { + val, err := f.getFlagType(name, "bytesHex", bytesHexConv) + + if err != nil { + return []byte{}, err + } + + return val.([]byte), nil +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func (f *FlagSet) BytesHexVar(p *[]byte, name string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func BytesHexVar(p *[]byte, name string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func (f *FlagSet) BytesHex(name string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, "", value, usage) + return p +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, shorthand, value, usage) + return p +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func BytesHex(name string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, "", value, usage) +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, shorthand, value, usage) +} diff --git a/components/engine/vendor/github.com/spf13/pflag/count.go b/components/engine/vendor/github.com/spf13/pflag/count.go index d22be41f29..aa126e44d1 100644 --- a/components/engine/vendor/github.com/spf13/pflag/count.go +++ b/components/engine/vendor/github.com/spf13/pflag/count.go @@ -11,13 +11,13 @@ func newCountValue(val int, p *int) *countValue { } func (i *countValue) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - // -1 means that no specific value was passed, so increment - if v == -1 { + // "+1" means that no specific value was passed, so increment + if s == "+1" { *i = countValue(*i + 1) - } else { - *i = countValue(v) + return nil } + v, err := strconv.ParseInt(s, 0, 0) + *i = countValue(v) return err } @@ -54,7 +54,7 @@ func (f *FlagSet) CountVar(p *int, name string, usage string) { // CountVarP is like CountVar only take a shorthand for the flag name. func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) - flag.NoOptDefVal = "-1" + flag.NoOptDefVal = "+1" } // CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set @@ -83,7 +83,9 @@ func (f *FlagSet) CountP(name, shorthand string, usage string) *int { return p } -// Count like Count only the flag is placed on the CommandLine isntead of a given flag set +// Count defines a count flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +// A count flag will add 1 to its value evey time it is found on the command line func Count(name string, usage string) *int { return CommandLine.CountP(name, "", usage) } diff --git a/components/engine/vendor/github.com/spf13/pflag/duration_slice.go b/components/engine/vendor/github.com/spf13/pflag/duration_slice.go new file mode 100644 index 0000000000..52c6b6dc10 --- /dev/null +++ b/components/engine/vendor/github.com/spf13/pflag/duration_slice.go @@ -0,0 +1,128 @@ +package pflag + +import ( + "fmt" + "strings" + "time" +) + +// -- durationSlice Value +type durationSliceValue struct { + value *[]time.Duration + changed bool +} + +func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue { + dsv := new(durationSliceValue) + dsv.value = p + *dsv.value = val + return dsv +} + +func (s *durationSliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *durationSliceValue) Type() string { + return "durationSlice" +} + +func (s *durationSliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%s", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func durationSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []time.Duration{}, nil + } + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetDurationSlice returns the []time.Duration value of a flag with the given name +func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error) { + val, err := f.getFlagType(name, "durationSlice", durationSliceConv) + if err != nil { + return []time.Duration{}, err + } + return val.([]time.Duration), nil +} + +// DurationSliceVar defines a durationSlice flag with specified name, default value, and usage string. +// The argument p points to a []time.Duration variable in which to store the value of the flag. +func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSliceVar defines a duration[] flag with specified name, default value, and usage string. +// The argument p points to a duration[] variable in which to store the value of the flag. +func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, "", value, usage) + return &p +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, "", value, usage) +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, shorthand, value, usage) +} diff --git a/components/engine/vendor/github.com/spf13/pflag/flag.go b/components/engine/vendor/github.com/spf13/pflag/flag.go index 746af6327e..5eadc84e3c 100644 --- a/components/engine/vendor/github.com/spf13/pflag/flag.go +++ b/components/engine/vendor/github.com/spf13/pflag/flag.go @@ -16,9 +16,9 @@ pflag is a drop-in replacement of Go's native flag package. If you import pflag under the name "flag" then all code should continue to function with no changes. - import flag "github.com/ogier/pflag" + import flag "github.com/spf13/pflag" - There is one exception to this: if you directly instantiate the Flag struct +There is one exception to this: if you directly instantiate the Flag struct there is one more field "Shorthand" that you will need to set. Most code never instantiates this struct directly, and instead uses functions such as String(), BoolVar(), and Var(), and is therefore @@ -101,6 +101,7 @@ package pflag import ( "bytes" "errors" + goflag "flag" "fmt" "io" "os" @@ -123,6 +124,12 @@ const ( PanicOnError ) +// ParseErrorsWhitelist defines the parsing errors that can be ignored +type ParseErrorsWhitelist struct { + // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags + UnknownFlags bool +} + // NormalizedName is a flag name that has been normalized according to rules // for the FlagSet (e.g. making '-' and '_' equivalent). type NormalizedName string @@ -134,18 +141,30 @@ type FlagSet struct { // a custom error handler. Usage func() + // SortFlags is used to indicate, if user wants to have sorted flags in + // help/usage messages. + SortFlags bool + + // ParseErrorsWhitelist is used to configure a whitelist of errors + ParseErrorsWhitelist ParseErrorsWhitelist + name string parsed bool actual map[NormalizedName]*Flag + orderedActual []*Flag + sortedActual []*Flag formal map[NormalizedName]*Flag + orderedFormal []*Flag + sortedFormal []*Flag shorthands map[byte]*Flag args []string // arguments after flags argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- - exitOnError bool // does the program exit if there's an error? errorHandling ErrorHandling output io.Writer // nil means stderr; use out() accessor interspersed bool // allow interspersed option/non-option args normalizeNameFunc func(f *FlagSet, name string) NormalizedName + + addedGoFlagSets []*goflag.FlagSet } // A Flag represents the state of a flag. @@ -156,7 +175,7 @@ type Flag struct { Value Value // value as set DefValue string // default value (as text); for usage message Changed bool // If the user set the value (or if left to default) - NoOptDefVal string //default value (as text); if the flag is on the command line without any options + NoOptDefVal string // default value (as text); if the flag is on the command line without any options Deprecated string // If this flag is deprecated, this string is the new or now thing to use Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use @@ -194,11 +213,19 @@ func sortFlags(flags map[NormalizedName]*Flag) []*Flag { // "--getUrl" which may also be translated to "geturl" and everything will work. func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { f.normalizeNameFunc = n - for k, v := range f.formal { - delete(f.formal, k) - nname := f.normalizeFlagName(string(k)) - f.formal[nname] = v - v.Name = string(nname) + f.sortedFormal = f.sortedFormal[:0] + for fname, flag := range f.formal { + nname := f.normalizeFlagName(flag.Name) + if fname == nname { + continue + } + flag.Name = string(nname) + delete(f.formal, fname) + f.formal[nname] = flag + if _, set := f.actual[fname]; set { + delete(f.actual, fname) + f.actual[nname] = flag + } } } @@ -229,46 +256,78 @@ func (f *FlagSet) SetOutput(output io.Writer) { f.output = output } -// VisitAll visits the flags in lexicographical order, calling fn for each. +// VisitAll visits the flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. // It visits all flags, even those not set. func (f *FlagSet) VisitAll(fn func(*Flag)) { - for _, flag := range sortFlags(f.formal) { + if len(f.formal) == 0 { + return + } + + var flags []*Flag + if f.SortFlags { + if len(f.formal) != len(f.sortedFormal) { + f.sortedFormal = sortFlags(f.formal) + } + flags = f.sortedFormal + } else { + flags = f.orderedFormal + } + + for _, flag := range flags { fn(flag) } } -// HasFlags returns a bool to indicate if the FlagSet has any flags definied. +// HasFlags returns a bool to indicate if the FlagSet has any flags defined. func (f *FlagSet) HasFlags() bool { return len(f.formal) > 0 } // HasAvailableFlags returns a bool to indicate if the FlagSet has any flags -// definied that are not hidden or deprecated. +// that are not hidden. func (f *FlagSet) HasAvailableFlags() bool { for _, flag := range f.formal { - if !flag.Hidden && len(flag.Deprecated) == 0 { + if !flag.Hidden { return true } } return false } -// VisitAll visits the command-line flags in lexicographical order, calling -// fn for each. It visits all flags, even those not set. +// VisitAll visits the command-line flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits all flags, even those not set. func VisitAll(fn func(*Flag)) { CommandLine.VisitAll(fn) } -// Visit visits the flags in lexicographical order, calling fn for each. +// Visit visits the flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. // It visits only those flags that have been set. func (f *FlagSet) Visit(fn func(*Flag)) { - for _, flag := range sortFlags(f.actual) { + if len(f.actual) == 0 { + return + } + + var flags []*Flag + if f.SortFlags { + if len(f.actual) != len(f.sortedActual) { + f.sortedActual = sortFlags(f.actual) + } + flags = f.sortedActual + } else { + flags = f.orderedActual + } + + for _, flag := range flags { fn(flag) } } -// Visit visits the command-line flags in lexicographical order, calling fn -// for each. It visits only those flags that have been set. +// Visit visits the command-line flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits only those flags that have been set. func Visit(fn func(*Flag)) { CommandLine.Visit(fn) } @@ -278,6 +337,22 @@ func (f *FlagSet) Lookup(name string) *Flag { return f.lookup(f.normalizeFlagName(name)) } +// ShorthandLookup returns the Flag structure of the short handed flag, +// returning nil if none exists. +// It panics, if len(name) > 1. +func (f *FlagSet) ShorthandLookup(name string) *Flag { + if name == "" { + return nil + } + if len(name) > 1 { + msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) + fmt.Fprintf(f.out(), msg) + panic(msg) + } + c := name[0] + return f.shorthands[c] +} + // lookup returns the Flag structure of the named flag, returning nil if none exists. func (f *FlagSet) lookup(name NormalizedName) *Flag { return f.formal[name] @@ -319,10 +394,11 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { if flag == nil { return fmt.Errorf("flag %q does not exist", name) } - if len(usageMessage) == 0 { + if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) } flag.Deprecated = usageMessage + flag.Hidden = true return nil } @@ -334,7 +410,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro if flag == nil { return fmt.Errorf("flag %q does not exist", name) } - if len(usageMessage) == 0 { + if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) } flag.ShorthandDeprecated = usageMessage @@ -358,6 +434,12 @@ func Lookup(name string) *Flag { return CommandLine.Lookup(name) } +// ShorthandLookup returns the Flag structure of the short handed flag, +// returning nil if none exists. +func ShorthandLookup(name string) *Flag { + return CommandLine.ShorthandLookup(name) +} + // Set sets the value of the named flag. func (f *FlagSet) Set(name, value string) error { normalName := f.normalizeFlagName(name) @@ -365,17 +447,30 @@ func (f *FlagSet) Set(name, value string) error { if !ok { return fmt.Errorf("no such flag -%v", name) } + err := flag.Value.Set(value) if err != nil { - return err + var flagName string + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) + } else { + flagName = fmt.Sprintf("--%s", flag.Name) + } + return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) } - if f.actual == nil { - f.actual = make(map[NormalizedName]*Flag) + + if !flag.Changed { + if f.actual == nil { + f.actual = make(map[NormalizedName]*Flag) + } + f.actual[normalName] = flag + f.orderedActual = append(f.orderedActual, flag) + + flag.Changed = true } - f.actual[normalName] = flag - flag.Changed = true - if len(flag.Deprecated) > 0 { - fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + + if flag.Deprecated != "" { + fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) } return nil } @@ -482,6 +577,14 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { name = "int" case "uint64": name = "uint" + case "stringSlice": + name = "strings" + case "intSlice": + name = "ints" + case "uintSlice": + name = "uints" + case "boolSlice": + name = "bools" } return @@ -496,11 +599,14 @@ func wrapN(i, slop int, s string) (string, string) { return s, "" } - w := strings.LastIndexAny(s[:i], " \t") + w := strings.LastIndexAny(s[:i], " \t\n") if w <= 0 { return s, "" } - + nlPos := strings.LastIndex(s[:i], "\n") + if nlPos > 0 && nlPos < w { + return s[:nlPos], s[nlPos+1:] + } return s[:w], s[w+1:] } @@ -509,7 +615,7 @@ func wrapN(i, slop int, s string) (string, string) { // caller). Pass `w` == 0 to do no wrapping func wrap(i, w int, s string) string { if w == 0 { - return s + return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) } // space between indent i and end of line width w into which @@ -527,7 +633,7 @@ func wrap(i, w int, s string) string { } // If still not enough space then don't even try to wrap. if wrap < 24 { - return s + return strings.Replace(s, "\n", r, -1) } // Try to avoid short orphan words on the final line, by @@ -539,14 +645,14 @@ func wrap(i, w int, s string) string { // Handle first line, which is indented by the caller (or the // special case above) l, s = wrapN(wrap, slop, s) - r = r + l + r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) // Now wrap the rest for s != "" { var t string t, s = wrapN(wrap, slop, s) - r = r + "\n" + strings.Repeat(" ", i) + t + r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) } return r @@ -557,28 +663,28 @@ func wrap(i, w int, s string) string { // for all flags in the FlagSet. Wrapped to `cols` columns (0 for no // wrapping) func (f *FlagSet) FlagUsagesWrapped(cols int) string { - x := new(bytes.Buffer) + buf := new(bytes.Buffer) lines := make([]string, 0, len(f.formal)) maxlen := 0 f.VisitAll(func(flag *Flag) { - if len(flag.Deprecated) > 0 || flag.Hidden { + if flag.Hidden { return } line := "" - if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 { + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) } else { line = fmt.Sprintf(" --%s", flag.Name) } varname, usage := UnquoteUsage(flag) - if len(varname) > 0 { + if varname != "" { line += " " + varname } - if len(flag.NoOptDefVal) > 0 { + if flag.NoOptDefVal != "" { switch flag.Value.Type() { case "string": line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) @@ -586,6 +692,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { if flag.NoOptDefVal != "true" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } default: line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } @@ -601,11 +711,14 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { line += usage if !flag.defaultIsZeroValue() { if flag.Value.Type() == "string" { - line += fmt.Sprintf(" (default \"%s\")", flag.DefValue) + line += fmt.Sprintf(" (default %q)", flag.DefValue) } else { line += fmt.Sprintf(" (default %s)", flag.DefValue) } } + if len(flag.Deprecated) != 0 { + line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) + } lines = append(lines, line) }) @@ -614,10 +727,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { sidx := strings.Index(line, "\x00") spacing := strings.Repeat(" ", maxlen-sidx) // maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx - fmt.Fprintln(x, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) + fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) } - return x.String() + return buf.String() } // FlagUsages returns a string containing the usage information for all flags in @@ -714,11 +827,10 @@ func (f *FlagSet) VarP(value Value, name, shorthand, usage string) { // AddFlag will add the flag to the FlagSet func (f *FlagSet) AddFlag(flag *Flag) { - // Call normalizeFlagName function only once normalizedFlagName := f.normalizeFlagName(flag.Name) - _, alreadythere := f.formal[normalizedFlagName] - if alreadythere { + _, alreadyThere := f.formal[normalizedFlagName] + if alreadyThere { msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) fmt.Fprintln(f.out(), msg) panic(msg) // Happens only if flags are declared with identical names @@ -729,28 +841,31 @@ func (f *FlagSet) AddFlag(flag *Flag) { flag.Name = string(normalizedFlagName) f.formal[normalizedFlagName] = flag + f.orderedFormal = append(f.orderedFormal, flag) - if len(flag.Shorthand) == 0 { + if flag.Shorthand == "" { return } if len(flag.Shorthand) > 1 { - fmt.Fprintf(f.out(), "%s shorthand more than ASCII character: %s\n", f.name, flag.Shorthand) - panic("shorthand is more than one character") + msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) + fmt.Fprintf(f.out(), msg) + panic(msg) } if f.shorthands == nil { f.shorthands = make(map[byte]*Flag) } c := flag.Shorthand[0] - old, alreadythere := f.shorthands[c] - if alreadythere { - fmt.Fprintf(f.out(), "%s shorthand reused: %q for %s already used for %s\n", f.name, c, flag.Name, old.Name) - panic("shorthand redefinition") + used, alreadyThere := f.shorthands[c] + if alreadyThere { + msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) + fmt.Fprintf(f.out(), msg) + panic(msg) } f.shorthands[c] = flag } // AddFlagSet adds one FlagSet to another. If a flag is already present in f -// the flag from newSet will be ignored +// the flag from newSet will be ignored. func (f *FlagSet) AddFlagSet(newSet *FlagSet) { if newSet == nil { return @@ -781,8 +896,10 @@ func VarP(value Value, name, shorthand, usage string) { // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) - fmt.Fprintln(f.out(), err) - f.usage() + if f.errorHandling != ContinueOnError { + fmt.Fprintln(f.out(), err) + f.usage() + } return err } @@ -798,32 +915,23 @@ func (f *FlagSet) usage() { } } -func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error { - if err := flag.Value.Set(value); err != nil { - return f.failf("invalid argument %q for %s: %v", value, origArg, err) +//--unknown (args will be empty) +//--unknown --next-flag ... (args will be --next-flag ...) +//--unknown arg ... (args will be arg ...) +func stripUnknownFlagValue(args []string) []string { + if len(args) == 0 { + //--unknown + return args } - // mark as visited for Visit() - if f.actual == nil { - f.actual = make(map[NormalizedName]*Flag) - } - f.actual[f.normalizeFlagName(flag.Name)] = flag - flag.Changed = true - if len(flag.Deprecated) > 0 { - fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) - } - if len(flag.ShorthandDeprecated) > 0 && containsShorthand(origArg, flag.Shorthand) { - fmt.Fprintf(os.Stderr, "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) - } - return nil -} -func containsShorthand(arg, shorthand string) bool { - // filter out flags -- - if strings.HasPrefix(arg, "-") { - return false + first := args[0] + if first[0] == '-' { + //--unknown --next-flag ... + return args } - arg = strings.SplitN(arg, "=", 2)[0] - return strings.Contains(arg, shorthand) + + //--unknown arg ... (args will be arg ...) + return args[1:] } func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) { @@ -833,22 +941,35 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin err = f.failf("bad flag syntax: %s", s) return } + split := strings.SplitN(name, "=", 2) name = split[0] - flag, alreadythere := f.formal[f.normalizeFlagName(name)] - if !alreadythere { - if name == "help" { // special case for nice help message. + flag, exists := f.formal[f.normalizeFlagName(name)] + + if !exists { + switch { + case name == "help": f.usage() return a, ErrHelp + case f.ParseErrorsWhitelist.UnknownFlags: + // --unknown=unknownval arg ... + // we do not want to lose arg in this case + if len(split) >= 2 { + return a, nil + } + + return stripUnknownFlagValue(a), nil + default: + err = f.failf("unknown flag: --%s", name) + return } - err = f.failf("unknown flag: --%s", name) - return } + var value string if len(split) == 2 { // '--flag=arg' value = split[1] - } else if len(flag.NoOptDefVal) > 0 { + } else if flag.NoOptDefVal != "" { // '--flag' (arg was optional) value = flag.NoOptDefVal } else if len(a) > 0 { @@ -860,7 +981,11 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin err = f.failf("flag needs an argument: %s", s) return } - err = fn(flag, value, s) + + err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } @@ -868,38 +993,64 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse if strings.HasPrefix(shorthands, "test.") { return } + outArgs = args outShorts = shorthands[1:] c := shorthands[0] - flag, alreadythere := f.shorthands[c] - if !alreadythere { - if c == 'h' { // special case for nice help message. + flag, exists := f.shorthands[c] + if !exists { + switch { + case c == 'h': f.usage() err = ErrHelp return + case f.ParseErrorsWhitelist.UnknownFlags: + // '-f=arg arg ...' + // we do not want to lose arg in this case + if len(shorthands) > 2 && shorthands[1] == '=' { + outShorts = "" + return + } + + outArgs = stripUnknownFlagValue(outArgs) + return + default: + err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) + return } - //TODO continue on error - err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) - return } + var value string if len(shorthands) > 2 && shorthands[1] == '=' { + // '-f=arg' value = shorthands[2:] outShorts = "" - } else if len(flag.NoOptDefVal) > 0 { + } else if flag.NoOptDefVal != "" { + // '-f' (arg was optional) value = flag.NoOptDefVal } else if len(shorthands) > 1 { + // '-farg' value = shorthands[1:] outShorts = "" } else if len(args) > 0 { + // '-f arg' value = args[0] outArgs = args[1:] } else { + // '-f' (arg was required) err = f.failf("flag needs an argument: %q in -%s", c, shorthands) return } - err = fn(flag, value, shorthands) + + if flag.ShorthandDeprecated != "" { + fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) + } + + err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } @@ -907,6 +1058,7 @@ func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []stri a = args shorthands := s[1:] + // "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv"). for len(shorthands) > 0 { shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn) if err != nil { @@ -953,19 +1105,30 @@ func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) { // are defined and before flags are accessed by the program. // The return value will be ErrHelp if -help was set but not defined. func (f *FlagSet) Parse(arguments []string) error { + if f.addedGoFlagSets != nil { + for _, goFlagSet := range f.addedGoFlagSets { + goFlagSet.Parse(nil) + } + } f.parsed = true - f.args = make([]string, 0, len(arguments)) - assign := func(flag *Flag, value, origArg string) error { - return f.setFlag(flag, value, origArg) + if len(arguments) < 0 { + return nil } - err := f.parseArgs(arguments, assign) + f.args = make([]string, 0, len(arguments)) + + set := func(flag *Flag, value string) error { + return f.Set(flag.Name, value) + } + + err := f.parseArgs(arguments, set) if err != nil { switch f.errorHandling { case ContinueOnError: return err case ExitOnError: + fmt.Println(err) os.Exit(2) case PanicOnError: panic(err) @@ -974,7 +1137,7 @@ func (f *FlagSet) Parse(arguments []string) error { return nil } -type parseFunc func(flag *Flag, value, origArg string) error +type parseFunc func(flag *Flag, value string) error // ParseAll parses flag definitions from the argument list, which should not // include the command name. The arguments for fn are flag and value. Must be @@ -985,11 +1148,7 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) f.parsed = true f.args = make([]string, 0, len(arguments)) - assign := func(flag *Flag, value, origArg string) error { - return fn(flag, value) - } - - err := f.parseArgs(arguments, assign) + err := f.parseArgs(arguments, fn) if err != nil { switch f.errorHandling { case ContinueOnError: @@ -1036,14 +1195,15 @@ func Parsed() bool { // CommandLine is the default set of command-line flags, parsed from os.Args. var CommandLine = NewFlagSet(os.Args[0], ExitOnError) -// NewFlagSet returns a new, empty flag set with the specified name and -// error handling property. +// NewFlagSet returns a new, empty flag set with the specified name, +// error handling property and SortFlags set to true. func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, errorHandling: errorHandling, argsLenAtDash: -1, interspersed: true, + SortFlags: true, } return f } diff --git a/components/engine/vendor/github.com/spf13/pflag/golangflag.go b/components/engine/vendor/github.com/spf13/pflag/golangflag.go index c4f47ebe59..d3dd72b7fe 100644 --- a/components/engine/vendor/github.com/spf13/pflag/golangflag.go +++ b/components/engine/vendor/github.com/spf13/pflag/golangflag.go @@ -98,4 +98,8 @@ func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) { newSet.VisitAll(func(goflag *goflag.Flag) { f.AddGoFlag(goflag) }) + if f.addedGoFlagSets == nil { + f.addedGoFlagSets = make([]*goflag.FlagSet, 0) + } + f.addedGoFlagSets = append(f.addedGoFlagSets, newSet) } diff --git a/components/engine/vendor/github.com/spf13/pflag/int16.go b/components/engine/vendor/github.com/spf13/pflag/int16.go new file mode 100644 index 0000000000..f1a01d05e6 --- /dev/null +++ b/components/engine/vendor/github.com/spf13/pflag/int16.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- int16 Value +type int16Value int16 + +func newInt16Value(val int16, p *int16) *int16Value { + *p = val + return (*int16Value)(p) +} + +func (i *int16Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 16) + *i = int16Value(v) + return err +} + +func (i *int16Value) Type() string { + return "int16" +} + +func (i *int16Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int16Conv(sval string) (interface{}, error) { + v, err := strconv.ParseInt(sval, 0, 16) + if err != nil { + return 0, err + } + return int16(v), nil +} + +// GetInt16 returns the int16 value of a flag with the given name +func (f *FlagSet) GetInt16(name string) (int16, error) { + val, err := f.getFlagType(name, "int16", int16Conv) + if err != nil { + return 0, err + } + return val.(int16), nil +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func (f *FlagSet) Int16Var(p *int16, name string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func Int16Var(p *int16, name string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func (f *FlagSet) Int16(name string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, "", value, usage) + return p +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16P(name, shorthand string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, shorthand, value, usage) + return p +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func Int16(name string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, "", value, usage) +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func Int16P(name, shorthand string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, shorthand, value, usage) +} diff --git a/components/engine/vendor/github.com/spf13/pflag/string_array.go b/components/engine/vendor/github.com/spf13/pflag/string_array.go index 276b7ed49e..fa7bc60187 100644 --- a/components/engine/vendor/github.com/spf13/pflag/string_array.go +++ b/components/engine/vendor/github.com/spf13/pflag/string_array.go @@ -52,7 +52,7 @@ func (f *FlagSet) GetStringArray(name string) ([]string, error) { // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the values of the multiple flags. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringArrayValue(value, p), name, "", usage) } @@ -64,7 +64,7 @@ func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []s // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArrayVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringArrayValue(value, p), name, "", usage) } @@ -76,7 +76,7 @@ func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string { p := []string{} f.StringArrayVarP(&p, name, "", value, usage) @@ -92,7 +92,7 @@ func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage str // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArray(name string, value []string, usage string) *[]string { return CommandLine.StringArrayP(name, "", value, usage) } diff --git a/components/engine/vendor/github.com/spf13/pflag/string_slice.go b/components/engine/vendor/github.com/spf13/pflag/string_slice.go index 05eee75438..0cd3ccc083 100644 --- a/components/engine/vendor/github.com/spf13/pflag/string_slice.go +++ b/components/engine/vendor/github.com/spf13/pflag/string_slice.go @@ -82,6 +82,11 @@ func (f *FlagSet) GetStringSlice(name string) ([]string, error) { // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -93,6 +98,11 @@ func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []s // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func StringSliceVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -104,6 +114,11 @@ func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { p := []string{} f.StringSliceVarP(&p, name, "", value, usage) @@ -119,6 +134,11 @@ func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage str // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func StringSlice(name string, value []string, usage string) *[]string { return CommandLine.StringSliceP(name, "", value, usage) } From f19625f9c495622a4a86b59a9e496e81d1acf65f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 19 May 2018 03:10:23 +0200 Subject: [PATCH 2/9] Use HasAvailableFlags instead of HasFlags for Options in help > HasAvailableFlags checks if the command contains any flags (local > plus persistent from the entire structure) which are not hidden or > deprecated. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 9411856536ef49883fb3550fd1ee804ab99c4072 Component: engine --- components/engine/cli/cobra.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/cli/cobra.go b/components/engine/cli/cobra.go index da52430360..f110ceae0a 100644 --- a/components/engine/cli/cobra.go +++ b/components/engine/cli/cobra.go @@ -116,7 +116,7 @@ Examples: {{ .Example }} {{- end}} -{{- if .HasFlags}} +{{- if .HasAvailableFlags}} Options: {{ wrappedFlagUsages . | trimRightSpace}} From 8497f34c119c4e643af5a95fe78a3857b7a4ee80 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 19 May 2018 03:15:08 +0200 Subject: [PATCH 3/9] Use Cobra built-in --version feature Signed-off-by: Sebastiaan van Stijn Upstream-commit: 0c3192da8c0686b1fe6aba393c1d3279e41c48a0 Component: engine --- components/engine/cli/cobra.go | 1 + components/engine/cmd/dockerd/docker.go | 11 ++--------- components/engine/cmd/dockerd/options.go | 1 - 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/components/engine/cli/cobra.go b/components/engine/cli/cobra.go index f110ceae0a..30854a0092 100644 --- a/components/engine/cli/cobra.go +++ b/components/engine/cli/cobra.go @@ -22,6 +22,7 @@ func SetupRootCommand(rootCmd *cobra.Command) { rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetFlagErrorFunc(FlagErrorFunc) rootCmd.SetHelpCommand(helpCommand) + rootCmd.SetVersionTemplate("Docker version {{.Version}}\n") rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") diff --git a/components/engine/cmd/dockerd/docker.go b/components/engine/cmd/dockerd/docker.go index 850a8a4d6a..463482e938 100644 --- a/components/engine/cmd/dockerd/docker.go +++ b/components/engine/cmd/dockerd/docker.go @@ -24,19 +24,16 @@ func newDaemonCommand() *cobra.Command { SilenceErrors: true, Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - if opts.version { - showVersion() - return nil - } opts.flags = cmd.Flags() return runDaemon(opts) }, DisableFlagsInUseLine: true, + Version: fmt.Sprintf("%s, build %s", dockerversion.Version, dockerversion.GitCommit), } cli.SetupRootCommand(cmd) flags := cmd.Flags() - flags.BoolVarP(&opts.version, "version", "v", false, "Print version information and quit") + flags.BoolP("version", "v", false, "Print version information and quit") flags.StringVar(&opts.configFile, "config-file", defaultDaemonConfigFile, "Daemon configuration file") opts.InstallFlags(flags) installConfigFlags(opts.daemonConfig, flags) @@ -45,10 +42,6 @@ func newDaemonCommand() *cobra.Command { return cmd } -func showVersion() { - fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit) -} - func main() { if reexec.Init() { return diff --git a/components/engine/cmd/dockerd/options.go b/components/engine/cmd/dockerd/options.go index 4fd597d610..a6276add59 100644 --- a/components/engine/cmd/dockerd/options.go +++ b/components/engine/cmd/dockerd/options.go @@ -30,7 +30,6 @@ var ( ) type daemonOptions struct { - version bool configFile string daemonConfig *config.Config flags *pflag.FlagSet From c53f559578e83425b08fba88532c59a7e9a49647 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 19 May 2018 03:20:27 +0200 Subject: [PATCH 4/9] Remove unused help command dockerd has no subcommands, so the help command is not used. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 298a26e0b2d086a32f14ee675bf24322cc92ddd4 Component: engine --- components/engine/cli/cobra.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/components/engine/cli/cobra.go b/components/engine/cli/cobra.go index 30854a0092..7c4196e141 100644 --- a/components/engine/cli/cobra.go +++ b/components/engine/cli/cobra.go @@ -2,10 +2,8 @@ package cli // import "github.com/docker/docker/cli" import ( "fmt" - "strings" "github.com/docker/docker/pkg/term" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -21,7 +19,6 @@ func SetupRootCommand(rootCmd *cobra.Command) { rootCmd.SetUsageTemplate(usageTemplate) rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetFlagErrorFunc(FlagErrorFunc) - rootCmd.SetHelpCommand(helpCommand) rootCmd.SetVersionTemplate("Docker version {{.Version}}\n") rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") @@ -45,23 +42,6 @@ func FlagErrorFunc(cmd *cobra.Command, err error) error { } } -var helpCommand = &cobra.Command{ - Use: "help [command]", - Short: "Help about the command", - PersistentPreRun: func(cmd *cobra.Command, args []string) {}, - PersistentPostRun: func(cmd *cobra.Command, args []string) {}, - RunE: func(c *cobra.Command, args []string) error { - cmd, args, e := c.Root().Find(args) - if cmd == nil || e != nil || len(args) > 0 { - return errors.Errorf("unknown help topic: %v", strings.Join(args, " ")) - } - - helpFunc := cmd.HelpFunc() - helpFunc(cmd, args) - return nil - }, -} - func hasSubCommands(cmd *cobra.Command) bool { return len(operationSubCommands(cmd)) > 0 } From 4f52a192acca84be7440dbc801e2792a10665207 Mon Sep 17 00:00:00 2001 From: Grant Millar Date: Fri, 27 Apr 2018 14:28:33 +0100 Subject: [PATCH 5/9] Fix swagger file type for ExecIds Signed-off-by: Grant Millar Signed-off-by: Sebastiaan van Stijn Upstream-commit: 58a35eaede4b121cd57cdb41e67d3654a90a144a Component: engine --- components/engine/api/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index 762d19b208..459ce9b186 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -4663,7 +4663,7 @@ paths: AppArmorProfile: type: "string" ExecIDs: - type: "string" + type: "array" HostConfig: $ref: "#/definitions/HostConfig" GraphDriver: From c2266b164c89710861afbb154204910bc0e350b6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 3 Nov 2017 17:39:53 +0100 Subject: [PATCH 6/9] Improve GetTimestamp parsing `GetTimestamp()` "assumed" values it could not parse to be a valid unix timestamp, and would use invalid values ("hello world") as-is (even testing that it did so). This patch validates unix timestamp to be a valid numeric value, and makes other values invalid. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 48cfe3f0872647bf0f52e6ba65022e1859e808e8 Component: engine --- components/engine/api/types/time/timestamp.go | 11 +++- .../engine/api/types/time/timestamp_test.go | 4 +- components/engine/client/container_logs.go | 5 +- .../engine/client/container_logs_test.go | 59 +++++++++++-------- components/engine/client/service_logs.go | 3 +- components/engine/client/service_logs_test.go | 44 +++++++------- 6 files changed, 73 insertions(+), 53 deletions(-) diff --git a/components/engine/api/types/time/timestamp.go b/components/engine/api/types/time/timestamp.go index 8d573accb1..ea3495efeb 100644 --- a/components/engine/api/types/time/timestamp.go +++ b/components/engine/api/types/time/timestamp.go @@ -82,11 +82,14 @@ func GetTimestamp(value string, reference time.Time) (string, error) { } if err != nil { - // if there is a `-` then it's an RFC3339 like timestamp otherwise assume unixtimestamp + // if there is a `-` then it's an RFC3339 like timestamp if strings.Contains(value, "-") { return "", err // was probably an RFC3339 like timestamp but the parser failed with an error } - return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server) + if _, _, err := parseTimestamp(value); err != nil { + return "", fmt.Errorf("failed to parse value as time or duration: %q", value) + } + return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server) } return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil @@ -104,6 +107,10 @@ func ParseTimestamps(value string, def int64) (int64, int64, error) { if value == "" { return def, 0, nil } + return parseTimestamp(value) +} + +func parseTimestamp(value string) (int64, int64, error) { sa := strings.SplitN(value, ".", 2) s, err := strconv.ParseInt(sa[0], 10, 64) if err != nil { diff --git a/components/engine/api/types/time/timestamp_test.go b/components/engine/api/types/time/timestamp_test.go index 1a8b20b60f..2535d895c6 100644 --- a/components/engine/api/types/time/timestamp_test.go +++ b/components/engine/api/types/time/timestamp_test.go @@ -49,8 +49,8 @@ func TestGetTimestamp(t *testing.T) { {"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false}, {"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false}, - // String fallback - {"invalid", "invalid", false}, + {"invalid", "", true}, + {"", "", true}, } for _, c := range cases { diff --git a/components/engine/client/container_logs.go b/components/engine/client/container_logs.go index 7c7fc9ea54..5b6541f035 100644 --- a/components/engine/client/container_logs.go +++ b/components/engine/client/container_logs.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api/types" timetypes "github.com/docker/docker/api/types/time" + "github.com/pkg/errors" ) // ContainerLogs returns the logs generated by a container in an io.ReadCloser. @@ -45,7 +46,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options if options.Since != "" { ts, err := timetypes.GetTimestamp(options.Since, time.Now()) if err != nil { - return nil, err + return nil, errors.Wrap(err, `invalid value for "since"`) } query.Set("since", ts) } @@ -53,7 +54,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options if options.Until != "" { ts, err := timetypes.GetTimestamp(options.Until, time.Now()) if err != nil { - return nil, err + return nil, errors.Wrap(err, `invalid value for "until"`) } query.Set("until", ts) } diff --git a/components/engine/client/container_logs_test.go b/components/engine/client/container_logs_test.go index b1f5c8ddba..f4ba0e76b4 100644 --- a/components/engine/client/container_logs_test.go +++ b/components/engine/client/container_logs_test.go @@ -2,6 +2,7 @@ package client // import "github.com/docker/docker/client" import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -12,10 +13,9 @@ import ( "testing" "time" - "context" - "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestContainerLogsNotFoundError(t *testing.T) { @@ -33,17 +33,15 @@ func TestContainerLogsError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) - } + assert.Check(t, is.Error(err, "Error response from daemon: Server error")) _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) - testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) + assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`)) _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Until: "2006-01-02TZ", }) - testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) + assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`)) } func TestContainerLogs(t *testing.T) { @@ -51,6 +49,7 @@ func TestContainerLogs(t *testing.T) { cases := []struct { options types.ContainerLogsOptions expectedQueryParams map[string]string + expectedError string }{ { expectedQueryParams: map[string]string{ @@ -84,32 +83,44 @@ func TestContainerLogs(t *testing.T) { }, { options: types.ContainerLogsOptions{ - // An complete invalid date, timestamp or go duration will be - // passed as is - Since: "invalid but valid", + // timestamp will be passed as is + Since: "1136073600.000000001", }, expectedQueryParams: map[string]string{ "tail": "", - "since": "invalid but valid", + "since": "1136073600.000000001", }, }, { options: types.ContainerLogsOptions{ - // An complete invalid date, timestamp or go duration will be - // passed as is - Until: "invalid but valid", + // timestamp will be passed as is + Until: "1136073600.000000001", }, expectedQueryParams: map[string]string{ "tail": "", - "until": "invalid but valid", + "until": "1136073600.000000001", }, }, + { + options: types.ContainerLogsOptions{ + // An complete invalid date will not be passed + Since: "invalid value", + }, + expectedError: `invalid value for "since": failed to parse value as time or duration: "invalid value"`, + }, + { + options: types.ContainerLogsOptions{ + // An complete invalid date will not be passed + Until: "invalid value", + }, + expectedError: `invalid value for "until": failed to parse value as time or duration: "invalid value"`, + }, } for _, logCase := range cases { client := &Client{ client: newMockClient(func(r *http.Request) (*http.Response, error) { if !strings.HasPrefix(r.URL.Path, expectedURL) { - return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, r.URL) + return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, r.URL) } // Check query parameters query := r.URL.Query() @@ -126,17 +137,15 @@ func TestContainerLogs(t *testing.T) { }), } body, err := client.ContainerLogs(context.Background(), "container_id", logCase.options) - if err != nil { - t.Fatal(err) + if logCase.expectedError != "" { + assert.Check(t, is.Error(err, logCase.expectedError)) + continue } + assert.NilError(t, err) defer body.Close() content, err := ioutil.ReadAll(body) - if err != nil { - t.Fatal(err) - } - if string(content) != "response" { - t.Fatalf("expected response to contain 'response', got %s", string(content)) - } + assert.NilError(t, err) + assert.Check(t, is.Contains(string(content), "response")) } } diff --git a/components/engine/client/service_logs.go b/components/engine/client/service_logs.go index af96b1e62c..906fd4059e 100644 --- a/components/engine/client/service_logs.go +++ b/components/engine/client/service_logs.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api/types" timetypes "github.com/docker/docker/api/types/time" + "github.com/pkg/errors" ) // ServiceLogs returns the logs generated by a service in an io.ReadCloser. @@ -25,7 +26,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ty if options.Since != "" { ts, err := timetypes.GetTimestamp(options.Since, time.Now()) if err != nil { - return nil, err + return nil, errors.Wrap(err, `invalid value for "since"`) } query.Set("since", ts) } diff --git a/components/engine/client/service_logs_test.go b/components/engine/client/service_logs_test.go index 764b7dad74..c3d624e212 100644 --- a/components/engine/client/service_logs_test.go +++ b/components/engine/client/service_logs_test.go @@ -2,6 +2,7 @@ package client // import "github.com/docker/docker/client" import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -12,9 +13,9 @@ import ( "testing" "time" - "context" - "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestServiceLogsError(t *testing.T) { @@ -22,15 +23,11 @@ func TestServiceLogsError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ServiceLogs(context.Background(), "service_id", types.ContainerLogsOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) - } + assert.Check(t, is.Error(err, "Error response from daemon: Server error")) _, err = client.ServiceLogs(context.Background(), "service_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) - if err == nil || !strings.Contains(err.Error(), `parsing time "2006-01-02TZ"`) { - t.Fatalf("expected a 'parsing time' error, got %v", err) - } + assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`)) } func TestServiceLogs(t *testing.T) { @@ -38,6 +35,7 @@ func TestServiceLogs(t *testing.T) { cases := []struct { options types.ContainerLogsOptions expectedQueryParams map[string]string + expectedError string }{ { expectedQueryParams: map[string]string{ @@ -71,21 +69,27 @@ func TestServiceLogs(t *testing.T) { }, { options: types.ContainerLogsOptions{ - // An complete invalid date, timestamp or go duration will be - // passed as is - Since: "invalid but valid", + // timestamp will be passed as is + Since: "1136073600.000000001", }, expectedQueryParams: map[string]string{ "tail": "", - "since": "invalid but valid", + "since": "1136073600.000000001", }, }, + { + options: types.ContainerLogsOptions{ + // An complete invalid date will not be passed + Since: "invalid value", + }, + expectedError: `invalid value for "since": failed to parse value as time or duration: "invalid value"`, + }, } for _, logCase := range cases { client := &Client{ client: newMockClient(func(r *http.Request) (*http.Response, error) { if !strings.HasPrefix(r.URL.Path, expectedURL) { - return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, r.URL) + return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, r.URL) } // Check query parameters query := r.URL.Query() @@ -102,17 +106,15 @@ func TestServiceLogs(t *testing.T) { }), } body, err := client.ServiceLogs(context.Background(), "service_id", logCase.options) - if err != nil { - t.Fatal(err) + if logCase.expectedError != "" { + assert.Check(t, is.Error(err, logCase.expectedError)) + continue } + assert.NilError(t, err) defer body.Close() content, err := ioutil.ReadAll(body) - if err != nil { - t.Fatal(err) - } - if string(content) != "response" { - t.Fatalf("expected response to contain 'response', got %s", string(content)) - } + assert.NilError(t, err) + assert.Check(t, is.Contains(string(content), "response")) } } From fac8e1d52b90219c1790ae698bb37191572f8c39 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 21 May 2018 00:06:50 +0200 Subject: [PATCH 7/9] Replace deprecated testutil.ErrorContains() Signed-off-by: Sebastiaan van Stijn Upstream-commit: 55bebbaecf5e40db9d83b28080ce08dc8642f407 Component: engine --- .../builder/dockerfile/evaluator_test.go | 5 +-- .../dockerfile/instructions/parse_test.go | 31 +++++++++---------- .../dockerfile/internals_windows_test.go | 3 +- .../builder/remotecontext/remote_test.go | 3 +- components/engine/client/client_test.go | 3 +- .../engine/client/container_logs_test.go | 10 +++--- .../client/swarm_get_unlock_key_test.go | 3 +- .../engine/client/volume_inspect_test.go | 3 +- components/engine/cmd/dockerd/daemon_test.go | 5 ++- .../engine/daemon/config/config_test.go | 17 +++------- components/engine/daemon/delete_test.go | 9 +++--- .../jsonlog/time_marshalling_test.go | 5 ++- components/engine/daemon/trustkey_test.go | 3 +- .../engine/distribution/pull_v2_test.go | 5 +-- components/engine/image/fs_test.go | 25 +++++++-------- .../engine/integration/config/config_test.go | 5 ++- .../engine/integration/container/copy_test.go | 13 ++++---- .../integration/container/create_test.go | 9 +++--- .../integration/container/pause_test.go | 3 +- .../integration/container/remove_test.go | 7 ++--- .../integration/container/rename_test.go | 9 +++--- .../integration/container/resize_test.go | 3 +- .../integration/container/update_test.go | 3 +- .../engine/integration/image/remove_test.go | 3 +- .../engine/integration/image/tag_test.go | 17 +++++----- .../engine/integration/secret/secret_test.go | 9 +++--- .../engine/integration/volume/volume_test.go | 3 +- .../engine/internal/testutil/helpers.go | 16 ---------- 28 files changed, 95 insertions(+), 135 deletions(-) diff --git a/components/engine/builder/dockerfile/evaluator_test.go b/components/engine/builder/dockerfile/evaluator_test.go index 4d0f6c30ec..0a64b9fdd6 100644 --- a/components/engine/builder/dockerfile/evaluator_test.go +++ b/components/engine/builder/dockerfile/evaluator_test.go @@ -6,9 +6,10 @@ import ( "github.com/docker/docker/builder/dockerfile/instructions" "github.com/docker/docker/builder/remotecontext" - "github.com/docker/docker/internal/testutil" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/skip" ) @@ -139,5 +140,5 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) { b := newBuilderWithMockBackend() sb := newDispatchRequest(b, '`', context, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) err = dispatch(sb, testCase.cmd) - testutil.ErrorContains(t, err, testCase.expectedError) + assert.Check(t, is.ErrorContains(err, testCase.expectedError)) } diff --git a/components/engine/builder/dockerfile/instructions/parse_test.go b/components/engine/builder/dockerfile/instructions/parse_test.go index 242630f72a..f5eeea5719 100644 --- a/components/engine/builder/dockerfile/instructions/parse_test.go +++ b/components/engine/builder/dockerfile/instructions/parse_test.go @@ -6,7 +6,6 @@ import ( "github.com/docker/docker/builder/dockerfile/command" "github.com/docker/docker/builder/dockerfile/parser" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -19,11 +18,11 @@ func TestCommandsExactlyOneArgument(t *testing.T) { "STOPSIGNAL", } - for _, command := range commands { - ast, err := parser.Parse(strings.NewReader(command)) + for _, cmd := range commands { + ast, err := parser.Parse(strings.NewReader(cmd)) assert.NilError(t, err) _, err = ParseInstruction(ast.AST.Children[0]) - assert.Check(t, is.Error(err, errExactlyOneArgument(command).Error())) + assert.Check(t, is.Error(err, errExactlyOneArgument(cmd).Error())) } } @@ -37,11 +36,11 @@ func TestCommandsAtLeastOneArgument(t *testing.T) { "VOLUME", } - for _, command := range commands { - ast, err := parser.Parse(strings.NewReader(command)) + for _, cmd := range commands { + ast, err := parser.Parse(strings.NewReader(cmd)) assert.NilError(t, err) _, err = ParseInstruction(ast.AST.Children[0]) - assert.Check(t, is.Error(err, errAtLeastOneArgument(command).Error())) + assert.Check(t, is.Error(err, errAtLeastOneArgument(cmd).Error())) } } @@ -51,11 +50,11 @@ func TestCommandsNoDestinationArgument(t *testing.T) { "COPY", } - for _, command := range commands { - ast, err := parser.Parse(strings.NewReader(command + " arg1")) + for _, cmd := range commands { + ast, err := parser.Parse(strings.NewReader(cmd + " arg1")) assert.NilError(t, err) _, err = ParseInstruction(ast.AST.Children[0]) - assert.Check(t, is.Error(err, errNoDestinationArgument(command).Error())) + assert.Check(t, is.Error(err, errNoDestinationArgument(cmd).Error())) } } @@ -90,10 +89,10 @@ func TestCommandsBlankNames(t *testing.T) { "LABEL", } - for _, command := range commands { + for _, cmd := range commands { node := &parser.Node{ - Original: command + " =arg2", - Value: strings.ToLower(command), + Original: cmd + " =arg2", + Value: strings.ToLower(cmd), Next: &parser.Node{ Value: "", Next: &parser.Node{ @@ -102,7 +101,7 @@ func TestCommandsBlankNames(t *testing.T) { }, } _, err := ParseInstruction(node) - assert.Check(t, is.Error(err, errBlankCommandNames(command).Error())) + assert.Check(t, is.Error(err, errBlankCommandNames(cmd).Error())) } } @@ -134,7 +133,7 @@ func TestParseOptInterval(t *testing.T) { Value: "50ns", } _, err := parseOptInterval(flInterval) - testutil.ErrorContains(t, err, "cannot be less than 1ms") + assert.Check(t, is.ErrorContains(err, "cannot be less than 1ms")) flInterval.Value = "1ms" _, err = parseOptInterval(flInterval) @@ -194,6 +193,6 @@ func TestErrorCases(t *testing.T) { } n := ast.AST.Children[0] _, err = ParseInstruction(n) - testutil.ErrorContains(t, err, c.expectedError) + assert.Check(t, is.ErrorContains(err, c.expectedError)) } } diff --git a/components/engine/builder/dockerfile/internals_windows_test.go b/components/engine/builder/dockerfile/internals_windows_test.go index 1fc55c0752..ffe52fb132 100644 --- a/components/engine/builder/dockerfile/internals_windows_test.go +++ b/components/engine/builder/dockerfile/internals_windows_test.go @@ -6,7 +6,6 @@ import ( "fmt" "testing" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -48,7 +47,7 @@ func TestNormalizeDest(t *testing.T) { } assert.Check(t, is.Equal(testcase.expected, actual), msg) } else { - testutil.ErrorContains(t, err, testcase.etext) + assert.Check(t, is.ErrorContains(err, testcase.etext)) } } } diff --git a/components/engine/builder/remotecontext/remote_test.go b/components/engine/builder/remotecontext/remote_test.go index 5267d23969..64442d5107 100644 --- a/components/engine/builder/remotecontext/remote_test.go +++ b/components/engine/builder/remotecontext/remote_test.go @@ -10,7 +10,6 @@ import ( "testing" "github.com/docker/docker/builder" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" @@ -232,7 +231,7 @@ func TestGetWithStatusError(t *testing.T) { assert.NilError(t, err) assert.Check(t, is.Contains(string(body), testcase.expectedBody)) } else { - testutil.ErrorContains(t, err, testcase.expectedErr) + assert.Check(t, is.ErrorContains(err, testcase.expectedErr)) } } } diff --git a/components/engine/client/client_test.go b/components/engine/client/client_test.go index 7cca04ac72..fbf29d5639 100644 --- a/components/engine/client/client_test.go +++ b/components/engine/client/client_test.go @@ -10,7 +10,6 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/env" @@ -162,7 +161,7 @@ func TestParseHostURL(t *testing.T) { for _, testcase := range testcases { actual, err := ParseHostURL(testcase.host) if testcase.expectedErr != "" { - testutil.ErrorContains(t, err, testcase.expectedErr) + assert.Check(t, is.ErrorContains(err, testcase.expectedErr)) } assert.Check(t, is.DeepEqual(testcase.expected, actual)) } diff --git a/components/engine/client/container_logs_test.go b/components/engine/client/container_logs_test.go index b1f5c8ddba..cbdab168f6 100644 --- a/components/engine/client/container_logs_test.go +++ b/components/engine/client/container_logs_test.go @@ -2,6 +2,7 @@ package client // import "github.com/docker/docker/client" import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -12,10 +13,9 @@ import ( "testing" "time" - "context" - "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestContainerLogsNotFoundError(t *testing.T) { @@ -39,11 +39,11 @@ func TestContainerLogsError(t *testing.T) { _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) - testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) + assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`)) _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Until: "2006-01-02TZ", }) - testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) + assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`)) } func TestContainerLogs(t *testing.T) { diff --git a/components/engine/client/swarm_get_unlock_key_test.go b/components/engine/client/swarm_get_unlock_key_test.go index 41b067affd..be822cb8aa 100644 --- a/components/engine/client/swarm_get_unlock_key_test.go +++ b/components/engine/client/swarm_get_unlock_key_test.go @@ -11,7 +11,6 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -22,7 +21,7 @@ func TestSwarmGetUnlockKeyError(t *testing.T) { } _, err := client.SwarmGetUnlockKey(context.Background()) - testutil.ErrorContains(t, err, "Error response from daemon: Server error") + assert.Check(t, is.ErrorContains(err, "Error response from daemon: Server error")) } func TestSwarmGetUnlockKey(t *testing.T) { diff --git a/components/engine/client/volume_inspect_test.go b/components/engine/client/volume_inspect_test.go index 8c18bca07b..d0a3246689 100644 --- a/components/engine/client/volume_inspect_test.go +++ b/components/engine/client/volume_inspect_test.go @@ -11,7 +11,6 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" @@ -23,7 +22,7 @@ func TestVolumeInspectError(t *testing.T) { } _, err := client.VolumeInspect(context.Background(), "nothing") - testutil.ErrorContains(t, err, "Error response from daemon: Server error") + assert.Check(t, is.ErrorContains(err, "Error response from daemon: Server error")) } func TestVolumeInspectNotFound(t *testing.T) { diff --git a/components/engine/cmd/dockerd/daemon_test.go b/components/engine/cmd/dockerd/daemon_test.go index e5c2c2ec7c..539b442cbd 100644 --- a/components/engine/cmd/dockerd/daemon_test.go +++ b/components/engine/cmd/dockerd/daemon_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/docker/docker/daemon/config" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" @@ -58,7 +57,7 @@ func TestLoadDaemonCliConfigWithConflicts(t *testing.T) { assert.Check(t, flags.Set("label", "l2=baz")) _, err := loadDaemonCliConfig(opts) - testutil.ErrorContains(t, err, "as a flag and in the configuration file: labels") + assert.Check(t, is.ErrorContains(err, "as a flag and in the configuration file: labels")) } func TestLoadDaemonCliWithConflictingNodeGenericResources(t *testing.T) { @@ -74,7 +73,7 @@ func TestLoadDaemonCliWithConflictingNodeGenericResources(t *testing.T) { assert.Check(t, flags.Set("node-generic-resource", "r2=baz")) _, err := loadDaemonCliConfig(opts) - testutil.ErrorContains(t, err, "as a flag and in the configuration file: node-generic-resources") + assert.Check(t, is.ErrorContains(err, "as a flag and in the configuration file: node-generic-resources")) } func TestLoadDaemonCliWithConflictingLabels(t *testing.T) { diff --git a/components/engine/daemon/config/config_test.go b/components/engine/daemon/config/config_test.go index a18b84da97..cb7aa74071 100644 --- a/components/engine/daemon/config/config_test.go +++ b/components/engine/daemon/config/config_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/docker/docker/daemon/discovery" - "github.com/docker/docker/internal/testutil" "github.com/docker/docker/opts" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" @@ -62,10 +61,7 @@ func TestFindConfigurationConflicts(t *testing.T) { flags.String("authorization-plugins", "", "") assert.Check(t, flags.Set("authorization-plugins", "asdf")) - - testutil.ErrorContains(t, - findConfigurationConflicts(config, flags), - "authorization-plugins: (from flag: asdf, from file: foobar)") + assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "authorization-plugins: (from flag: asdf, from file: foobar)")) } func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) { @@ -76,8 +72,7 @@ func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) { flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, opts.ValidateHost), "host", "H", "Daemon socket(s) to connect to") assert.Check(t, flags.Set("host", "tcp://127.0.0.1:4444")) assert.Check(t, flags.Set("host", "unix:///var/run/docker.sock")) - - testutil.ErrorContains(t, findConfigurationConflicts(config, flags), "hosts") + assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "hosts")) } func TestDaemonConfigurationMergeConflicts(t *testing.T) { @@ -460,8 +455,7 @@ func TestReloadSetConfigFileNotExist(t *testing.T) { flags.Set("config-file", configFile) err := Reload(configFile, flags, func(c *Config) {}) - assert.Check(t, is.ErrorContains(err, "")) - testutil.ErrorContains(t, err, "unable to configure the Docker daemon with file") + assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) } // TestReloadDefaultConfigNotExist tests that if the default configuration file @@ -494,8 +488,7 @@ func TestReloadBadDefaultConfig(t *testing.T) { flags := pflag.NewFlagSet("test", pflag.ContinueOnError) flags.String("config-file", configFile, "") err = Reload(configFile, flags, func(c *Config) {}) - assert.Check(t, is.ErrorContains(err, "")) - testutil.ErrorContains(t, err, "unable to configure the Docker daemon with file") + assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) } func TestReloadWithConflictingLabels(t *testing.T) { @@ -508,7 +501,7 @@ func TestReloadWithConflictingLabels(t *testing.T) { flags.String("config-file", configFile, "") flags.StringSlice("labels", lbls, "") err := Reload(configFile, flags, func(c *Config) {}) - testutil.ErrorContains(t, err, "conflict labels for foo=baz and foo=bar") + assert.Check(t, is.ErrorContains(err, "conflict labels for foo=baz and foo=bar")) } func TestReloadWithDuplicateLabels(t *testing.T) { diff --git a/components/engine/daemon/delete_test.go b/components/engine/daemon/delete_test.go index 8bfa5d8170..4af206d9c6 100644 --- a/components/engine/daemon/delete_test.go +++ b/components/engine/daemon/delete_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func newDaemonWithTmpRoot(t *testing.T) (*Daemon, func()) { @@ -30,7 +30,6 @@ func newContainerWithState(state *container.State) *container.Container { State: state, Config: &containertypes.Config{}, } - } // TestContainerDelete tests that a useful error message and instructions is @@ -74,8 +73,8 @@ func TestContainerDelete(t *testing.T) { d.containers.Add(c.ID, c) err := d.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: false}) - testutil.ErrorContains(t, err, te.errMsg) - testutil.ErrorContains(t, err, te.fixMsg) + assert.Check(t, is.ErrorContains(err, te.errMsg)) + assert.Check(t, is.ErrorContains(err, te.fixMsg)) } } @@ -92,5 +91,5 @@ func TestContainerDoubleDelete(t *testing.T) { // Try to remove the container when its state is removalInProgress. // It should return an error indicating it is under removal progress. err := d.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true}) - testutil.ErrorContains(t, err, fmt.Sprintf("removal of container %s is already in progress", c.ID)) + assert.Check(t, is.ErrorContains(err, fmt.Sprintf("removal of container %s is already in progress", c.ID))) } diff --git a/components/engine/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go b/components/engine/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go index 76f299a0f6..3cfdcc33db 100644 --- a/components/engine/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go +++ b/components/engine/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go @@ -4,7 +4,6 @@ import ( "testing" "time" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -12,11 +11,11 @@ import ( func TestFastTimeMarshalJSONWithInvalidYear(t *testing.T) { aTime := time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local) _, err := fastTimeMarshalJSON(aTime) - testutil.ErrorContains(t, err, "year outside of range") + assert.Check(t, is.ErrorContains(err, "year outside of range")) anotherTime := time.Date(10000, 1, 1, 0, 0, 0, 0, time.Local) _, err = fastTimeMarshalJSON(anotherTime) - testutil.ErrorContains(t, err, "year outside of range") + assert.Check(t, is.ErrorContains(err, "year outside of range")) } func TestFastTimeMarshalJSON(t *testing.T) { diff --git a/components/engine/daemon/trustkey_test.go b/components/engine/daemon/trustkey_test.go index ebc7e28ee3..e13129e467 100644 --- a/components/engine/daemon/trustkey_test.go +++ b/components/engine/daemon/trustkey_test.go @@ -6,7 +6,6 @@ import ( "path/filepath" "testing" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" @@ -22,7 +21,7 @@ func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) { assert.NilError(t, err) _, err = loadOrCreateTrustKey(tmpKeyFile.Name()) - testutil.ErrorContains(t, err, "Error loading key file") + assert.Check(t, is.ErrorContains(err, "Error loading key file")) } func TestLoadOrCreateTrustKeyCreateKeyWhenFileDoesNotExist(t *testing.T) { diff --git a/components/engine/distribution/pull_v2_test.go b/components/engine/distribution/pull_v2_test.go index 28cbb3e9d3..1079b5fe53 100644 --- a/components/engine/distribution/pull_v2_test.go +++ b/components/engine/distribution/pull_v2_test.go @@ -10,7 +10,8 @@ import ( "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/reference" - "github.com/docker/docker/internal/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" ) @@ -104,7 +105,7 @@ func TestFixManifestLayersBadParent(t *testing.T) { } err := fixManifestLayers(&duplicateLayerManifest) - testutil.ErrorContains(t, err, "invalid parent ID") + assert.Check(t, is.ErrorContains(err, "invalid parent ID")) } // TestValidateManifest verifies the validateManifest function diff --git a/components/engine/image/fs_test.go b/components/engine/image/fs_test.go index dcf4da75f8..e8c120a003 100644 --- a/components/engine/image/fs_test.go +++ b/components/engine/image/fs_test.go @@ -10,10 +10,9 @@ import ( "path/filepath" "testing" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" ) func defaultFSStoreBackend(t *testing.T) (StoreBackend, func()) { @@ -39,7 +38,7 @@ func TestFSGetInvalidData(t *testing.T) { assert.Check(t, err) _, err = store.Get(id) - testutil.ErrorContains(t, err, "failed to verify") + assert.Check(t, is.ErrorContains(err, "failed to verify")) } func TestFSInvalidSet(t *testing.T) { @@ -51,7 +50,7 @@ func TestFSInvalidSet(t *testing.T) { assert.Check(t, err) _, err = store.Set([]byte("foobar")) - testutil.ErrorContains(t, err, "failed to write digest data") + assert.Check(t, is.ErrorContains(err, "failed to write digest data")) } func TestFSInvalidRoot(t *testing.T) { @@ -78,7 +77,7 @@ func TestFSInvalidRoot(t *testing.T) { f.Close() _, err = NewFSStoreBackend(root) - testutil.ErrorContains(t, err, "failed to create storage backend") + assert.Check(t, is.ErrorContains(err, "failed to create storage backend")) os.RemoveAll(root) } @@ -116,14 +115,14 @@ func TestFSMetadataGetSet(t *testing.T) { } _, err = store.GetMetadata(id2, "tkey2") - testutil.ErrorContains(t, err, "failed to read metadata") + assert.Check(t, is.ErrorContains(err, "failed to read metadata")) id3 := digest.FromBytes([]byte("baz")) err = store.SetMetadata(id3, "tkey", []byte("tval")) - testutil.ErrorContains(t, err, "failed to get digest") + assert.Check(t, is.ErrorContains(err, "failed to get digest")) _, err = store.GetMetadata(id3, "tkey") - testutil.ErrorContains(t, err, "failed to get digest") + assert.Check(t, is.ErrorContains(err, "failed to get digest")) } func TestFSInvalidWalker(t *testing.T) { @@ -191,7 +190,7 @@ func TestFSGetUnsetKey(t *testing.T) { for _, key := range []digest.Digest{"foobar:abc", "sha256:abc", "sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2a"} { _, err := store.Get(key) - testutil.ErrorContains(t, err, "failed to get digest") + assert.Check(t, is.ErrorContains(err, "failed to get digest")) } } @@ -201,7 +200,7 @@ func TestFSGetEmptyData(t *testing.T) { for _, emptyData := range [][]byte{nil, {}} { _, err := store.Set(emptyData) - testutil.ErrorContains(t, err, "invalid empty data") + assert.Check(t, is.ErrorContains(err, "invalid empty data")) } } @@ -219,7 +218,7 @@ func TestFSDelete(t *testing.T) { assert.Check(t, err) _, err = store.Get(id) - testutil.ErrorContains(t, err, "failed to get digest") + assert.Check(t, is.ErrorContains(err, "failed to get digest")) _, err = store.Get(id2) assert.Check(t, err) @@ -228,7 +227,7 @@ func TestFSDelete(t *testing.T) { assert.Check(t, err) _, err = store.Get(id2) - testutil.ErrorContains(t, err, "failed to get digest") + assert.Check(t, is.ErrorContains(err, "failed to get digest")) } func TestFSWalker(t *testing.T) { @@ -267,5 +266,5 @@ func TestFSWalkerStopOnError(t *testing.T) { err = store.Walk(func(id digest.Digest) error { return errors.New("what") }) - testutil.ErrorContains(t, err, "what") + assert.Check(t, is.ErrorContains(err, "what")) } diff --git a/components/engine/integration/config/config_test.go b/components/engine/integration/config/config_test.go index 949c3ae004..d698445f2b 100644 --- a/components/engine/integration/config/config_test.go +++ b/components/engine/integration/config/config_test.go @@ -13,7 +13,6 @@ import ( swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/swarm" - "github.com/docker/docker/internal/testutil" "github.com/docker/docker/pkg/stdcopy" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" @@ -136,7 +135,7 @@ func TestConfigsCreateAndDelete(t *testing.T) { assert.NilError(t, err) insp, _, err = client.ConfigInspectWithRaw(ctx, configID) - testutil.ErrorContains(t, err, "No such config") + assert.Check(t, is.ErrorContains(err, "No such config")) } func TestConfigsUpdate(t *testing.T) { @@ -190,7 +189,7 @@ func TestConfigsUpdate(t *testing.T) { // this test will produce an error in func UpdateConfig insp.Spec.Data = []byte("TESTINGDATA2") err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec) - testutil.ErrorContains(t, err, "only updates to Labels are allowed") + assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed")) } func TestTemplatedConfig(t *testing.T) { diff --git a/components/engine/integration/container/copy_test.go b/components/engine/integration/container/copy_test.go index 766c0a1762..6794f98555 100644 --- a/components/engine/integration/container/copy_test.go +++ b/components/engine/integration/container/copy_test.go @@ -8,7 +8,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/skip" @@ -22,9 +21,9 @@ func TestCopyFromContainerPathDoesNotExist(t *testing.T) { cid := container.Create(t, ctx, apiclient) _, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne") - assert.Assert(t, client.IsErrNotFound(err)) + assert.Check(t, client.IsErrNotFound(err)) expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne") - testutil.ErrorContains(t, err, expected) + assert.Check(t, is.ErrorContains(err, expected)) } func TestCopyFromContainerPathIsNotDir(t *testing.T) { @@ -36,7 +35,7 @@ func TestCopyFromContainerPathIsNotDir(t *testing.T) { cid := container.Create(t, ctx, apiclient) _, _, err := apiclient.CopyFromContainer(ctx, cid, "/etc/passwd/") - assert.Assert(t, is.Contains(err.Error(), "not a directory")) + assert.Assert(t, is.ErrorContains(err, "not a directory")) } func TestCopyToContainerPathDoesNotExist(t *testing.T) { @@ -48,9 +47,9 @@ func TestCopyToContainerPathDoesNotExist(t *testing.T) { cid := container.Create(t, ctx, apiclient) err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{}) - assert.Assert(t, client.IsErrNotFound(err)) + assert.Check(t, client.IsErrNotFound(err)) expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne") - testutil.ErrorContains(t, err, expected) + assert.Check(t, is.ErrorContains(err, expected)) } func TestCopyToContainerPathIsNotDir(t *testing.T) { @@ -62,5 +61,5 @@ func TestCopyToContainerPathIsNotDir(t *testing.T) { cid := container.Create(t, ctx, apiclient) err := apiclient.CopyToContainer(ctx, cid, "/etc/passwd/", nil, types.CopyToContainerOptions{}) - assert.Assert(t, is.Contains(err.Error(), "not a directory")) + assert.Assert(t, is.ErrorContains(err, "not a directory")) } diff --git a/components/engine/integration/container/create_test.go b/components/engine/integration/container/create_test.go index 61a07bcc97..cea8c059cc 100644 --- a/components/engine/integration/container/create_test.go +++ b/components/engine/integration/container/create_test.go @@ -8,7 +8,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/skip" ) @@ -48,7 +49,7 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) { &network.NetworkingConfig{}, "", ) - testutil.ErrorContains(t, err, tc.expectedError) + assert.Check(t, is.ErrorContains(err, tc.expectedError)) }) } } @@ -88,7 +89,7 @@ func TestCreateWithInvalidEnv(t *testing.T) { &network.NetworkingConfig{}, "", ) - testutil.ErrorContains(t, err, tc.expectedError) + assert.Check(t, is.ErrorContains(err, tc.expectedError)) }) } } @@ -133,6 +134,6 @@ func TestCreateTmpfsMountsTarget(t *testing.T) { &network.NetworkingConfig{}, "", ) - testutil.ErrorContains(t, err, tc.expectedError) + assert.Check(t, is.ErrorContains(err, tc.expectedError)) } } diff --git a/components/engine/integration/container/pause_test.go b/components/engine/integration/container/pause_test.go index e6ecbbc0b5..8854dd9fd9 100644 --- a/components/engine/integration/container/pause_test.go +++ b/components/engine/integration/container/pause_test.go @@ -12,7 +12,6 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/poll" @@ -62,7 +61,7 @@ func TestPauseFailsOnWindowsServerContainers(t *testing.T) { poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerPause(ctx, cID) - testutil.ErrorContains(t, err, "cannot pause Windows Server Containers") + assert.Check(t, is.ErrorContains(err, "cannot pause Windows Server Containers")) } func TestPauseStopPausedContainer(t *testing.T) { diff --git a/components/engine/integration/container/remove_test.go b/components/engine/integration/container/remove_test.go index 5596c88042..185f90cc00 100644 --- a/components/engine/integration/container/remove_test.go +++ b/components/engine/integration/container/remove_test.go @@ -10,7 +10,6 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" @@ -50,7 +49,7 @@ func TestRemoveContainerWithRemovedVolume(t *testing.T) { assert.NilError(t, err) _, _, err = client.ContainerInspectWithRaw(ctx, cID, true) - testutil.ErrorContains(t, err, "No such container") + assert.Check(t, is.ErrorContains(err, "No such container")) } // Test case for #2099/#2125 @@ -87,7 +86,7 @@ func TestRemoveContainerRunning(t *testing.T) { cID := container.Run(t, ctx, client) err := client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{}) - testutil.ErrorContains(t, err, "cannot remove a running container") + assert.Check(t, is.ErrorContains(err, "cannot remove a running container")) } func TestRemoveContainerForceRemoveRunning(t *testing.T) { @@ -109,5 +108,5 @@ func TestRemoveInvalidContainer(t *testing.T) { client := request.NewAPIClient(t) err := client.ContainerRemove(ctx, "unknown", types.ContainerRemoveOptions{}) - testutil.ErrorContains(t, err, "No such container") + assert.Check(t, is.ErrorContains(err, "No such container")) } diff --git a/components/engine/integration/container/rename_test.go b/components/engine/integration/container/rename_test.go index 4496b980b7..fd270052a5 100644 --- a/components/engine/integration/container/rename_test.go +++ b/components/engine/integration/container/rename_test.go @@ -11,7 +11,6 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/docker/docker/pkg/stringid" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" @@ -89,7 +88,7 @@ func TestRenameRunningContainerAndReuse(t *testing.T) { assert.Check(t, is.Equal("/"+newName, inspect.Name)) _, err = client.ContainerInspect(ctx, oldName) - testutil.ErrorContains(t, err, "No such container: "+oldName) + assert.Check(t, is.ErrorContains(err, "No such container: "+oldName)) cID = container.Run(t, ctx, client, container.WithName(oldName)) poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) @@ -109,7 +108,7 @@ func TestRenameInvalidName(t *testing.T) { poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerRename(ctx, oldName, "new:invalid") - testutil.ErrorContains(t, err, "Invalid container name") + assert.Check(t, is.ErrorContains(err, "Invalid container name")) inspect, err := client.ContainerInspect(ctx, oldName) assert.NilError(t, err) @@ -179,9 +178,9 @@ func TestRenameContainerWithSameName(t *testing.T) { poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerRename(ctx, oldName, oldName) - testutil.ErrorContains(t, err, "Renaming a container with the same name") + assert.Check(t, is.ErrorContains(err, "Renaming a container with the same name")) err = client.ContainerRename(ctx, cID, oldName) - testutil.ErrorContains(t, err, "Renaming a container with the same name") + assert.Check(t, is.ErrorContains(err, "Renaming a container with the same name")) } // Test case for GitHub issue 23973 diff --git a/components/engine/integration/container/resize_test.go b/components/engine/integration/container/resize_test.go index 892a5c1765..c6c4e81751 100644 --- a/components/engine/integration/container/resize_test.go +++ b/components/engine/integration/container/resize_test.go @@ -11,7 +11,6 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" req "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/poll" @@ -63,5 +62,5 @@ func TestResizeWhenContainerNotStarted(t *testing.T) { Height: 40, Width: 40, }) - testutil.ErrorContains(t, err, "is not running") + assert.Check(t, is.ErrorContains(err, "is not running")) } diff --git a/components/engine/integration/container/update_test.go b/components/engine/integration/container/update_test.go index 5e407e0c0b..2ac122aea7 100644 --- a/components/engine/integration/container/update_test.go +++ b/components/engine/integration/container/update_test.go @@ -8,7 +8,6 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/poll" @@ -61,5 +60,5 @@ func TestUpdateRestartWithAutoRemove(t *testing.T) { Name: "always", }, }) - testutil.ErrorContains(t, err, "Restart policy cannot be updated because AutoRemove is enabled for the container") + assert.Check(t, is.ErrorContains(err, "Restart policy cannot be updated because AutoRemove is enabled for the container")) } diff --git a/components/engine/integration/image/remove_test.go b/components/engine/integration/image/remove_test.go index 5ad09d51e0..172c27f54d 100644 --- a/components/engine/integration/image/remove_test.go +++ b/components/engine/integration/image/remove_test.go @@ -7,7 +7,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -56,5 +55,5 @@ func TestRemoveImageOrphaning(t *testing.T) { // check if the second image has been deleted _, _, err = client.ImageInspectWithRaw(ctx, commitResp2.ID) - testutil.ErrorContains(t, err, "No such image:") + assert.Check(t, is.ErrorContains(err, "No such image:")) } diff --git a/components/engine/integration/image/tag_test.go b/components/engine/integration/image/tag_test.go index e009114250..06431cd8a2 100644 --- a/components/engine/integration/image/tag_test.go +++ b/components/engine/integration/image/tag_test.go @@ -39,7 +39,7 @@ func TestTagInvalidReference(t *testing.T) { for _, repo := range invalidRepos { err := client.ImageTag(ctx, "busybox", repo) - testutil.ErrorContains(t, err, "not a valid repository/tag") + assert.Check(t, is.ErrorContains(err, "not a valid repository/tag")) } longTag := testutil.GenerateRandomAlphaOnlyString(121) @@ -48,24 +48,24 @@ func TestTagInvalidReference(t *testing.T) { for _, repotag := range invalidTags { err := client.ImageTag(ctx, "busybox", repotag) - testutil.ErrorContains(t, err, "not a valid repository/tag") + assert.Check(t, is.ErrorContains(err, "not a valid repository/tag")) } // test repository name begin with '-' err := client.ImageTag(ctx, "busybox:latest", "-busybox:test") - testutil.ErrorContains(t, err, "Error parsing reference") + assert.Check(t, is.ErrorContains(err, "Error parsing reference")) // test namespace name begin with '-' err = client.ImageTag(ctx, "busybox:latest", "-test/busybox:test") - testutil.ErrorContains(t, err, "Error parsing reference") + assert.Check(t, is.ErrorContains(err, "Error parsing reference")) // test index name begin with '-' err = client.ImageTag(ctx, "busybox:latest", "-index:5000/busybox:test") - testutil.ErrorContains(t, err, "Error parsing reference") + assert.Check(t, is.ErrorContains(err, "Error parsing reference")) // test setting tag fails err = client.ImageTag(ctx, "busybox:latest", "sha256:sometag") - testutil.ErrorContains(t, err, "refusing to create an ambiguous tag using digest algorithm as name") + assert.Check(t, is.ErrorContains(err, "refusing to create an ambiguous tag using digest algorithm as name")) } // ensure we allow the use of valid tags @@ -132,8 +132,9 @@ func TestTagMatchesDigest(t *testing.T) { digest := "busybox@sha256:abcdef76720241213f5303bda7704ec4c2ef75613173910a56fb1b6e20251507" // test setting tag fails err := client.ImageTag(ctx, "busybox:latest", digest) - testutil.ErrorContains(t, err, "refusing to create a tag with a digest reference") + assert.Check(t, is.ErrorContains(err, "refusing to create a tag with a digest reference")) + // check that no new image matches the digest _, _, err = client.ImageInspectWithRaw(ctx, digest) - testutil.ErrorContains(t, err, fmt.Sprintf("No such image: %s", digest)) + assert.Check(t, is.ErrorContains(err, fmt.Sprintf("No such image: %s", digest))) } diff --git a/components/engine/integration/secret/secret_test.go b/components/engine/integration/secret/secret_test.go index ea4dc7c7e9..96a02a6ec5 100644 --- a/components/engine/integration/secret/secret_test.go +++ b/components/engine/integration/secret/secret_test.go @@ -12,7 +12,6 @@ import ( swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/swarm" - "github.com/docker/docker/internal/testutil" "github.com/docker/docker/pkg/stdcopy" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" @@ -148,17 +147,17 @@ func TestSecretsCreateAndDelete(t *testing.T) { }, Data: []byte("TESTINGDATA"), }) - testutil.ErrorContains(t, err, "already exists") + assert.Check(t, is.ErrorContains(err, "already exists")) // Ported from original TestSecretsDelete err = client.SecretRemove(ctx, secretID) assert.NilError(t, err) _, _, err = client.SecretInspectWithRaw(ctx, secretID) - testutil.ErrorContains(t, err, "No such secret") + assert.Check(t, is.ErrorContains(err, "No such secret")) err = client.SecretRemove(ctx, "non-existin") - testutil.ErrorContains(t, err, "No such secret: non-existin") + assert.Check(t, is.ErrorContains(err, "No such secret: non-existin")) // Ported from original TestSecretsCreteaWithLabels testName = "test_secret_with_labels" @@ -223,7 +222,7 @@ func TestSecretsUpdate(t *testing.T) { // this test will produce an error in func UpdateSecret insp.Spec.Data = []byte("TESTINGDATA2") err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec) - testutil.ErrorContains(t, err, "only updates to Labels are allowed") + assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed")) } func TestTemplatedSecret(t *testing.T) { diff --git a/components/engine/integration/volume/volume_test.go b/components/engine/integration/volume/volume_test.go index d7014ec3ac..4acc6e8b1a 100644 --- a/components/engine/integration/volume/volume_test.go +++ b/components/engine/integration/volume/volume_test.go @@ -12,7 +12,6 @@ import ( volumetypes "github.com/docker/docker/api/types/volume" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/docker/docker/internal/testutil" "github.com/google/go-cmp/cmp/cmpopts" "github.com/gotestyourself/gotestyourself/assert" is "github.com/gotestyourself/gotestyourself/assert/cmp" @@ -63,7 +62,7 @@ func TestVolumesRemove(t *testing.T) { vname := c.Mounts[0].Name err = client.VolumeRemove(ctx, vname, false) - testutil.ErrorContains(t, err, "volume is in use") + assert.Check(t, is.ErrorContains(err, "volume is in use")) err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{ Force: true, diff --git a/components/engine/internal/testutil/helpers.go b/components/engine/internal/testutil/helpers.go index 89cb552fea..38cd1693f5 100644 --- a/components/engine/internal/testutil/helpers.go +++ b/components/engine/internal/testutil/helpers.go @@ -2,24 +2,8 @@ package testutil // import "github.com/docker/docker/internal/testutil" import ( "io" - - "github.com/gotestyourself/gotestyourself/assert" ) -type helperT interface { - Helper() -} - -// ErrorContains checks that the error is not nil, and contains the expected -// substring. -// Deprecated: use assert.Assert(t, cmp.ErrorContains(err, expected)) -func ErrorContains(t assert.TestingT, err error, expectedError string, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - assert.ErrorContains(t, err, expectedError, msgAndArgs...) -} - // DevZero acts like /dev/zero but in an OS-independent fashion. var DevZero io.Reader = devZero{} From f5c920960064f33700fe5c5182f75dabb1500c3e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 21 May 2018 00:37:54 +0200 Subject: [PATCH 8/9] bump libnetwork to eb6b2a57955e5c149d47c3973573216e8f8baa09 Changes included: - libnetwork#2147 Adding logs for ipam state - libnetwork#2143 Fix race conditions in the overlay network driver - possibly addresses moby#36743 services do not start: ingress-sbox is already present - possibly addresses moby#30427 Flaky Test: TestSwarmPublishDuplicatePorts on s390 - possibly addresses moby#36501 Flaky tests: Service "port" tests - libnetwork#2142 Add wait time into xtables lock warning - libnetwork#2135 filter xtables lock warnings when firewalld is active - libnetwork#2140 Switch from x/net/context to context - libnetwork#2134 Adding a recovery mechanism for a split gossip cluster Signed-off-by: Sebastiaan van Stijn Upstream-commit: 158ab95c17bed9e849e0d935899b75018cdf61a4 Component: engine --- components/engine/vendor.conf | 2 +- .../docker/libnetwork/bitseq/sequence.go | 5 +- .../docker/libnetwork/cluster/provider.go | 3 +- .../libnetwork/drivers/overlay/encryption.go | 15 ++--- .../libnetwork/drivers/overlay/ov_network.go | 55 +++++++++------- .../drivers/overlay/ovmanager/ovmanager.go | 10 +-- .../docker/libnetwork/ipam/allocator.go | 2 + .../docker/libnetwork/iptables/iptables.go | 33 +++++++--- .../docker/libnetwork/networkdb/cluster.go | 62 ++++++++++++++----- .../docker/libnetwork/networkdb/delegate.go | 15 ++--- .../docker/libnetwork/networkdb/networkdb.go | 6 +- .../github.com/docker/libnetwork/vendor.conf | 2 +- 12 files changed, 136 insertions(+), 74 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index a347459285..e6b14e7726 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -32,7 +32,7 @@ github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 #get libnetwork packages # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly -github.com/docker/libnetwork c15b372ef22125880d378167dde44f4b134e1a77 +github.com/docker/libnetwork eb6b2a57955e5c149d47c3973573216e8f8baa09 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec diff --git a/components/engine/vendor/github.com/docker/libnetwork/bitseq/sequence.go b/components/engine/vendor/github.com/docker/libnetwork/bitseq/sequence.go index 0069d495b7..4bd0f3ca5d 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/bitseq/sequence.go +++ b/components/engine/vendor/github.com/docker/libnetwork/bitseq/sequence.go @@ -326,7 +326,6 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial } h.Lock() // Acquire the lock back } - logrus.Debugf("Received set for ordinal %v, start %v, end %v, any %t, release %t, serial:%v curr:%d \n", ordinal, start, end, any, release, serial, h.curr) if serial { curr = h.curr } @@ -466,8 +465,8 @@ func (h *Handle) Unselected() uint64 { func (h *Handle) String() string { h.Lock() defer h.Unlock() - return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, bits: %d, unselected: %d, sequence: %s", - h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString()) + return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, Bits: %d, Unselected: %d, Sequence: %s Curr:%d", + h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString(), h.curr) } // MarshalJSON encodes Handle into json message diff --git a/components/engine/vendor/github.com/docker/libnetwork/cluster/provider.go b/components/engine/vendor/github.com/docker/libnetwork/cluster/provider.go index 491ccfd4b8..0259eb7005 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/cluster/provider.go +++ b/components/engine/vendor/github.com/docker/libnetwork/cluster/provider.go @@ -1,8 +1,9 @@ package cluster import ( + "context" + "github.com/docker/docker/api/types/network" - "golang.org/x/net/context" ) const ( diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go index 802d7bc36d..bcae0bd4e5 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go @@ -438,7 +438,7 @@ func (d *driver) setKeys(keys []*key) error { d.keys = keys d.secMap = &encrMap{nodes: map[string][]*spi{}} d.Unlock() - logrus.Debugf("Initial encryption keys: %v", d.keys) + logrus.Debugf("Initial encryption keys: %v", keys) return nil } @@ -458,6 +458,8 @@ func (d *driver) updateKeys(newKey, primary, pruneKey *key) error { ) d.Lock() + defer d.Unlock() + // add new if newKey != nil { d.keys = append(d.keys, newKey) @@ -471,7 +473,6 @@ func (d *driver) updateKeys(newKey, primary, pruneKey *key) error { delIdx = i } } - d.Unlock() if (newKey != nil && newIdx == -1) || (primary != nil && priIdx == -1) || @@ -480,17 +481,18 @@ func (d *driver) updateKeys(newKey, primary, pruneKey *key) error { "(newIdx,priIdx,delIdx):(%d, %d, %d)", newIdx, priIdx, delIdx) } + if priIdx != -1 && priIdx == delIdx { + return types.BadRequestErrorf("attempting to both make a key (index %d) primary and delete it", priIdx) + } + d.secMapWalk(func(rIPs string, spis []*spi) ([]*spi, bool) { rIP := net.ParseIP(rIPs) return updateNodeKey(lIP, aIP, rIP, spis, d.keys, newIdx, priIdx, delIdx), false }) - d.Lock() // swap primary if priIdx != -1 { - swp := d.keys[0] - d.keys[0] = d.keys[priIdx] - d.keys[priIdx] = swp + d.keys[0], d.keys[priIdx] = d.keys[priIdx], d.keys[0] } // prune if delIdx != -1 { @@ -499,7 +501,6 @@ func (d *driver) updateKeys(newKey, primary, pruneKey *key) error { } d.keys = append(d.keys[:delIdx], d.keys[delIdx+1:]...) } - d.Unlock() logrus.Debugf("Updated: %v", d.keys) diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go index 0be94a0708..9c83127d4f 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go @@ -203,6 +203,12 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d n.subnets = append(n.subnets, s) } + d.Lock() + defer d.Unlock() + if d.networks[n.id] != nil { + return fmt.Errorf("attempt to create overlay network %v that already exists", n.id) + } + if err := n.writeToStore(); err != nil { return fmt.Errorf("failed to update data store for network %v: %v", n.id, err) } @@ -217,11 +223,13 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d if nInfo != nil { if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil { + // XXX Undo writeToStore? No method to so. Why? return err } } - d.addNetwork(n) + d.networks[id] = n + return nil } @@ -235,7 +243,15 @@ func (d *driver) DeleteNetwork(nid string) error { return err } - n := d.network(nid) + d.Lock() + defer d.Unlock() + + // This is similar to d.network(), but we need to keep holding the lock + // until we are done removing this network. + n, ok := d.networks[nid] + if !ok { + n = d.restoreNetworkFromStore(nid) + } if n == nil { return fmt.Errorf("could not find network with id %s", nid) } @@ -255,7 +271,7 @@ func (d *driver) DeleteNetwork(nid string) error { } // flush the peerDB entries d.peerFlush(nid) - d.deleteNetwork(nid) + delete(d.networks, nid) vnis, err := n.releaseVxlanID() if err != nil { @@ -805,32 +821,25 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket, nsPath string) { } } -func (d *driver) addNetwork(n *network) { - d.Lock() - d.networks[n.id] = n - d.Unlock() -} - -func (d *driver) deleteNetwork(nid string) { - d.Lock() - delete(d.networks, nid) - d.Unlock() +// Restore a network from the store to the driver if it is present. +// Must be called with the driver locked! +func (d *driver) restoreNetworkFromStore(nid string) *network { + n := d.getNetworkFromStore(nid) + if n != nil { + n.driver = d + n.endpoints = endpointTable{} + n.once = &sync.Once{} + d.networks[nid] = n + } + return n } func (d *driver) network(nid string) *network { d.Lock() + defer d.Unlock() n, ok := d.networks[nid] - d.Unlock() if !ok { - n = d.getNetworkFromStore(nid) - if n != nil { - n.driver = d - n.endpoints = endpointTable{} - n.once = &sync.Once{} - d.Lock() - d.networks[nid] = n - d.Unlock() - } + n = d.restoreNetworkFromStore(nid) } return n diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go index 58cc687d4f..12deb22e44 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go @@ -125,8 +125,12 @@ func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, opts[netlabel.OverlayVxlanIDList] = val d.Lock() + defer d.Unlock() + if _, ok := d.networks[id]; ok { + n.releaseVxlanID() + return nil, fmt.Errorf("network %s already exists", id) + } d.networks[id] = n - d.Unlock() return opts, nil } @@ -137,8 +141,8 @@ func (d *driver) NetworkFree(id string) error { } d.Lock() + defer d.Unlock() n, ok := d.networks[id] - d.Unlock() if !ok { return fmt.Errorf("overlay network with id %s not found", id) @@ -147,9 +151,7 @@ func (d *driver) NetworkFree(id string) error { // Release all vxlan IDs in one shot. n.releaseVxlanID() - d.Lock() delete(d.networks, id) - d.Unlock() return nil } diff --git a/components/engine/vendor/github.com/docker/libnetwork/ipam/allocator.go b/components/engine/vendor/github.com/docker/libnetwork/ipam/allocator.go index d1a91c077f..c5e797aae6 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/ipam/allocator.go +++ b/components/engine/vendor/github.com/docker/libnetwork/ipam/allocator.go @@ -526,6 +526,7 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error { return types.InternalErrorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v", k.String(), address, poolID, err) } + defer logrus.Debugf("Released address PoolID:%s, Address:%v Sequence:%s", poolID, address, bm.String()) return bm.Unset(ipToUint64(h)) } @@ -537,6 +538,7 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres base *net.IPNet ) + logrus.Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", nw, bitmask.String(), serial, prefAddress) base = types.GetIPNetCopy(nw) if bitmask.Unselected() <= 0 { diff --git a/components/engine/vendor/github.com/docker/libnetwork/iptables/iptables.go b/components/engine/vendor/github.com/docker/libnetwork/iptables/iptables.go index b8126248b4..50896d935a 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/iptables/iptables.go +++ b/components/engine/vendor/github.com/docker/libnetwork/iptables/iptables.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/sirupsen/logrus" ) @@ -45,7 +46,7 @@ var ( iptablesPath string supportsXlock = false supportsCOpt = false - xLockWaitMsg = "Another app is currently holding the xtables lock; waiting" + xLockWaitMsg = "Another app is currently holding the xtables lock" // used to lock iptables commands if xtables lock is not supported bestEffortLock sync.Mutex // ErrIptablesNotFound is returned when the rule is not found. @@ -423,12 +424,32 @@ func existsRaw(table Table, chain string, rule ...string) bool { return strings.Contains(string(existingRules), ruleString) } +// Maximum duration that an iptables operation can take +// before flagging a warning. +const opWarnTime = 2 * time.Second + +func filterOutput(start time.Time, output []byte, args ...string) []byte { + // Flag operations that have taken a long time to complete + opTime := time.Since(start) + if opTime > opWarnTime { + logrus.Warnf("xtables contention detected while running [%s]: Waited for %.2f seconds and received %q", strings.Join(args, " "), float64(opTime)/float64(time.Second), string(output)) + } + // ignore iptables' message about xtables lock: + // it is a warning, not an error. + if strings.Contains(string(output), xLockWaitMsg) { + output = []byte("") + } + // Put further filters here if desired + return output +} + // Raw calls 'iptables' system command, passing supplied arguments. func Raw(args ...string) ([]byte, error) { if firewalldRunning { + startTime := time.Now() output, err := Passthrough(Iptables, args...) if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { - return output, err + return filterOutput(startTime, output, args...), err } } return raw(args...) @@ -447,17 +468,13 @@ func raw(args ...string) ([]byte, error) { logrus.Debugf("%s, %v", iptablesPath, args) + startTime := time.Now() output, err := exec.Command(iptablesPath, args...).CombinedOutput() if err != nil { return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) } - // ignore iptables' message about xtables lock - if strings.Contains(string(output), xLockWaitMsg) { - output = []byte("") - } - - return output, err + return filterOutput(startTime, output, args...), err } // RawCombinedOutput inernally calls the Raw function and returns a non nil diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/cluster.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/cluster.go index 198caceeb8..bd48fb9f18 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/cluster.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/cluster.go @@ -2,6 +2,7 @@ package networkdb import ( "bytes" + "context" "crypto/rand" "encoding/hex" "fmt" @@ -17,10 +18,12 @@ import ( ) const ( - reapPeriod = 5 * time.Second - retryInterval = 1 * time.Second - nodeReapInterval = 24 * time.Hour - nodeReapPeriod = 2 * time.Hour + reapPeriod = 5 * time.Second + rejoinClusterDuration = 10 * time.Second + rejoinInterval = 60 * time.Second + retryInterval = 1 * time.Second + nodeReapInterval = 24 * time.Hour + nodeReapPeriod = 2 * time.Hour ) type logWriter struct{} @@ -154,7 +157,7 @@ func (nDB *NetworkDB) clusterInit() error { return fmt.Errorf("failed to create memberlist: %v", err) } - nDB.stopCh = make(chan struct{}) + nDB.ctx, nDB.cancelCtx = context.WithCancel(context.Background()) nDB.memberlist = mlist for _, trigger := range []struct { @@ -166,16 +169,17 @@ func (nDB *NetworkDB) clusterInit() error { {config.PushPullInterval, nDB.bulkSyncTables}, {retryInterval, nDB.reconnectNode}, {nodeReapPeriod, nDB.reapDeadNode}, + {rejoinInterval, nDB.rejoinClusterBootStrap}, } { t := time.NewTicker(trigger.interval) - go nDB.triggerFunc(trigger.interval, t.C, nDB.stopCh, trigger.fn) + go nDB.triggerFunc(trigger.interval, t.C, trigger.fn) nDB.tickers = append(nDB.tickers, t) } return nil } -func (nDB *NetworkDB) retryJoin(members []string, stop <-chan struct{}) { +func (nDB *NetworkDB) retryJoin(ctx context.Context, members []string) { t := time.NewTicker(retryInterval) defer t.Stop() @@ -191,7 +195,7 @@ func (nDB *NetworkDB) retryJoin(members []string, stop <-chan struct{}) { continue } return - case <-stop: + case <-ctx.Done(): return } } @@ -202,8 +206,8 @@ func (nDB *NetworkDB) clusterJoin(members []string) error { mlist := nDB.memberlist if _, err := mlist.Join(members); err != nil { - // In case of failure, keep retrying join until it succeeds or the cluster is shutdown. - go nDB.retryJoin(members, nDB.stopCh) + // In case of failure, we no longer need to explicitly call retryJoin. + // rejoinClusterBootStrap, which runs every minute, will retryJoin for 10sec return fmt.Errorf("could not join node to memberlist: %v", err) } @@ -225,7 +229,8 @@ func (nDB *NetworkDB) clusterLeave() error { return err } - close(nDB.stopCh) + // cancel the context + nDB.cancelCtx() for _, t := range nDB.tickers { t.Stop() @@ -234,19 +239,19 @@ func (nDB *NetworkDB) clusterLeave() error { return mlist.Shutdown() } -func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, stop <-chan struct{}, f func()) { +func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, f func()) { // Use a random stagger to avoid syncronizing randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger)) select { case <-time.After(randStagger): - case <-stop: + case <-nDB.ctx.Done(): return } for { select { case <-C: f() - case <-stop: + case <-nDB.ctx.Done(): return } } @@ -270,6 +275,35 @@ func (nDB *NetworkDB) reapDeadNode() { } } +// rejoinClusterBootStrap is called periodically to check if all bootStrap nodes are active in the cluster, +// if not, call the cluster join to merge 2 separate clusters that are formed when all managers +// stopped/started at the same time +func (nDB *NetworkDB) rejoinClusterBootStrap() { + nDB.RLock() + if len(nDB.bootStrapIP) == 0 { + nDB.RUnlock() + return + } + + bootStrapIPs := make([]string, 0, len(nDB.bootStrapIP)) + for _, bootIP := range nDB.bootStrapIP { + for _, node := range nDB.nodes { + if node.Addr.Equal(bootIP) { + // One of the bootstrap nodes is part of the cluster, return + nDB.RUnlock() + return + } + } + bootStrapIPs = append(bootStrapIPs, bootIP.String()) + } + nDB.RUnlock() + // None of the bootStrap nodes are in the cluster, call memberlist join + logrus.Debugf("rejoinClusterBootStrap, calling cluster join with bootStrap %v", bootStrapIPs) + ctx, cancel := context.WithTimeout(nDB.ctx, rejoinClusterDuration) + defer cancel() + nDB.retryJoin(ctx, bootStrapIPs) +} + func (nDB *NetworkDB) reconnectNode() { nDB.RLock() if len(nDB.failedNodes) == 0 { diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/delegate.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/delegate.go index 1a57de3de6..9a379fe7cd 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/delegate.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/delegate.go @@ -38,16 +38,11 @@ func (nDB *NetworkDB) handleNodeEvent(nEvent *NodeEvent) bool { // If we are here means that the event is fresher and the node is known. Update the laport time n.ltime = nEvent.LTime - // If it is a node leave event for a manager and this is the only manager we - // know of we want the reconnect logic to kick in. In a single manager - // cluster manager's gossip can't be bootstrapped unless some other node - // connects to it. - if len(nDB.bootStrapIP) == 1 && nEvent.Type == NodeEventTypeLeave { - for _, ip := range nDB.bootStrapIP { - if ip.Equal(n.Addr) { - return true - } - } + // If the node is not known from memberlist we cannot process save any state of it else if it actually + // dies we won't receive any notification and we will remain stuck with it + if _, ok := nDB.nodes[nEvent.NodeName]; !ok { + logrus.Error("node: %s is unknown to memberlist", nEvent.NodeName) + return false } switch nEvent.Type { diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go index d1fa3b8d86..c433913a46 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go @@ -3,6 +3,7 @@ package networkdb //go:generate protoc -I.:../vendor/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork/networkdb,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. networkdb.proto import ( + "context" "fmt" "net" "os" @@ -77,9 +78,10 @@ type NetworkDB struct { // Broadcast queue for node event gossip. nodeBroadcasts *memberlist.TransmitLimitedQueue - // A central stop channel to stop all go routines running on + // A central context to stop all go routines running on // behalf of the NetworkDB instance. - stopCh chan struct{} + ctx context.Context + cancelCtx context.CancelFunc // A central broadcaster for all local watchers watching table // events. diff --git a/components/engine/vendor/github.com/docker/libnetwork/vendor.conf b/components/engine/vendor/github.com/docker/libnetwork/vendor.conf index 3e7181564b..9480699b87 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/vendor.conf +++ b/components/engine/vendor/github.com/docker/libnetwork/vendor.conf @@ -48,7 +48,7 @@ github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca -golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 +golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 From bd96bdb95388b0ef62d582ecd1487b1a93e19107 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 21 May 2018 01:07:22 +0200 Subject: [PATCH 9/9] Update authors and mailmap Signed-off-by: Sebastiaan van Stijn Upstream-commit: b7e722fe30ccde2a6081be4df3fa0d7354514ba3 Component: engine --- components/engine/.mailmap | 16 ++++++++++++++-- components/engine/AUTHORS | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/components/engine/.mailmap b/components/engine/.mailmap index 828ea78481..8b62c78d9a 100644 --- a/components/engine/.mailmap +++ b/components/engine/.mailmap @@ -13,8 +13,9 @@ Abhinandan Prativadi Adrien Gallouët Ahmed Kamal Ahmet Alp Balkan -AJ Bowen -AJ Bowen +AJ Bowen +AJ Bowen +AJ Bowen Akihiro Matsushima Akihiro Suda Aleksa Sarai @@ -24,6 +25,7 @@ Aleksandrs Fadins Alessandro Boch Alex Chen Alex Ellis +Alex Goodman Alexander Larsson Alexander Morozov Alexander Morozov @@ -121,6 +123,7 @@ Doug Davis Doug Tangren Elan Ruusamäe Elan Ruusamäe +Elango Sivanandam Eric G. Noriega Eric Hanchrow Eric Rosenberg @@ -128,12 +131,14 @@ Erica Windisch Erica Windisch Erik Hollensbe Erwin van der Koogh +Ethan Bell Euan Kemp Eugen Krizo Evan Hazlett Evelyn Xu Evgeny Shmarnev Faiz Khan +Fangming Fang Felix Hupfeld Felix Ruess Feng Yan @@ -155,6 +160,7 @@ Guillaume J. Charmes Guillaume J. Charmes Guillaume J. Charmes +Guri Gurjeet Singh Gustav Sinder Günther Jungbluth @@ -211,6 +217,8 @@ John Howard (VM) John Howard (VM) John Howard (VM) John Stephens +Jonathan Choy +Jonathan Choy Jon Surrell Jordan Arentsen Jordan Jennings @@ -286,6 +294,7 @@ Martin Redmond Mary Anthony Mary Anthony Mary Anthony moxiegirl +Masato Ohba Matt Bentley Matt Schurenko Matt Williams @@ -298,9 +307,12 @@ Mauricio Garavaglia Michael Crosby Michael Crosby Michael Crosby +Michał Gryko Michael Hudson-Doyle Michael Huettermann Michael Käufl +Michael Nussbaum +Michael Nussbaum Michael Spetsiotis Michal Minář Miguel Angel Alvarez Cabrerizo <30386061+doncicuto@users.noreply.github.com> diff --git a/components/engine/AUTHORS b/components/engine/AUTHORS index c5dafd7228..46102d7402 100644 --- a/components/engine/AUTHORS +++ b/components/engine/AUTHORS @@ -39,7 +39,7 @@ Ahmed Kamal Ahmet Alp Balkan Aidan Feldman Aidan Hobson Sayers -AJ Bowen +AJ Bowen Ajey Charantimath ajneu Akash Gupta @@ -54,6 +54,7 @@ Alan Scherger Alan Thompson Albert Callarisa Albert Zhang +Alejandro González Hevia Aleksa Sarai Aleksandrs Fadins Alena Prokharchyk @@ -65,6 +66,7 @@ Alex Coventry Alex Crawford Alex Ellis Alex Gaynor +Alex Goodman Alex Olshansky Alex Samorukov Alex Warhawk @@ -77,6 +79,7 @@ Alexander Shopov Alexandre Beslic Alexandre Garnier Alexandre González +Alexandre Jomin Alexandru Sfirlogea Alexey Guskov Alexey Kotlyarov @@ -98,11 +101,13 @@ Amir Goldstein Amit Bakshi Amit Krishnan Amit Shukla +Amr Gawish Amy Lindburg Anand Patil AnandkumarPatel Anatoly Borodin Anchal Agrawal +Anda Xu Anders Janmyr Andre Dublin <81dublin@gmail.com> Andre Granovsky @@ -197,6 +202,7 @@ Ben Toews Ben Wiklund Benjamin Atkin Benjamin Boudreau +Benjamin Yolken Benoit Chesneau Bernerd Schaefer Bernhard M. Wiedemann @@ -267,6 +273,7 @@ Carlos Sanchez Carol Fager-Higgins Cary Casey Bisson +Catalin Pirvu Ce Gao Cedric Davies Cezar Sa Espinola @@ -293,6 +300,8 @@ Chen Min Chen Mingjie Chen Qiu Cheng-mean Liu +Chengguang Xu +chenyuzhu Chetan Birajdar Chewey Chia-liang Kao @@ -313,6 +322,7 @@ Chris Snow Chris St. Pierre Chris Stivers Chris Swan +Chris Telfer Chris Wahl Chris Weyl Christian Berendt @@ -336,6 +346,7 @@ Chun Chen Ciro S. Costa Clayton Coleman Clinton Kitson +Cody Roseborough Coenraad Loubser Colin Dunklau Colin Hebert @@ -411,6 +422,7 @@ Dave MacDonald Dave Tucker David Anderson David Calavera +David Chung David Corking David Cramer David Currie @@ -510,6 +522,7 @@ Eike Herzbach Eivin Giske Skaaren Eivind Uggedal Elan Ruusamäe +Elango Sivanandam Elena Morozova Eli Uriegas Elias Faxö @@ -548,6 +561,7 @@ Erik St. Martin Erik Weathers Erno Hopearuoho Erwin van der Koogh +Ethan Bell Euan Kemp Eugen Krizo Eugene Yakubovich @@ -575,6 +589,7 @@ Fabrizio Regini Fabrizio Soppelsa Faiz Khan falmp +Fangming Fang Fangyuan Gao <21551127@zju.edu.cn> Fareed Dudhia Fathi Boudra @@ -673,6 +688,7 @@ Guilherme Salgado Guillaume Dufour Guillaume J. Charmes guoxiuyan +Guri Gurjeet Singh Guruprasad Gustav Sinder @@ -696,6 +712,7 @@ heartlock <21521209@zju.edu.cn> Hector Castro Helen Xie Henning Sprang +Hiroshi Hatake Hobofan Hollie Teal Hong Xu @@ -808,6 +825,7 @@ Jean-Pierre Huynh Jean-Tiare Le Bigot Jeeva S. Chelladhurai Jeff Anderson +Jeff Hajewski Jeff Johnston Jeff Lindsay Jeff Mickey @@ -893,6 +911,7 @@ Jonas Pfenniger Jonathan A. Sternberg Jonathan Boulle Jonathan Camp +Jonathan Choy Jonathan Dowland Jonathan Lebon Jonathan Lomas @@ -962,6 +981,7 @@ Kareem Khazem kargakis Karl Grzeszczak Karol Duleba +Karthik Karanth Karthik Nayak Kate Heddleston Katie McLaughlin @@ -1108,6 +1128,7 @@ Manfred Zabarauskas Manjunath A Kumatagi Mansi Nahar Manuel Meurer +Manuel Rüger Manuel Woelker mapk0y Marc Abramowitz @@ -1149,10 +1170,12 @@ Martin Mosegaard Amdisen Martin Redmond Mary Anthony Masahito Zembutsu +Masato Ohba Masayuki Morita Mason Malone Mateusz Sulima Mathias Monnerville +Mathieu Champlon Mathieu Le Marec - Pasquet Mathieu Parent Matt Apperson @@ -1209,6 +1232,7 @@ Michael Huettermann Michael Irwin Michael Käufl Michael Neale +Michael Nussbaum Michael Prokop Michael Scharf Michael Spetsiotis @@ -1223,6 +1247,7 @@ Michal Minář Michal Wieczorek Michaël Pailloncy Michał Czeraszkiewicz +Michał Gryko Michiel@unhosted Mickaël FORTUNATO Miguel Angel Fernández @@ -1239,6 +1264,7 @@ Mike Estes Mike Gaffney Mike Goelzer Mike Leone +Mike Lundy Mike MacCana Mike Naberezny Mike Snitzer @@ -1297,6 +1323,7 @@ Niall O'Higgins Nicholas E. Rabenau Nick DeCoursin Nick Irvine +Nick Neisen Nick Parker Nick Payne Nick Russo @@ -1322,6 +1349,7 @@ Nishant Totla NIWA Hideyuki Noah Meyerhans Noah Treuhaft +NobodyOnSE noducks Nolan Darilek nponeccop @@ -1329,7 +1357,6 @@ Nuutti Kotivuori nzwsch O.S. Tezer objectified -odk- Oguz Bilgic Oh Jinkyun Ohad Schneider @@ -1352,6 +1379,7 @@ Patrick Böänziger Patrick Devine Patrick Hemmer Patrick Stapleton +Patrik Cyvoct pattichen Paul paul @@ -1423,6 +1451,7 @@ Pradip Dhara Prasanna Gautam Pratik Karki Prayag Verma +Priya Wadhwa Przemek Hejman Pure White pysqz @@ -1838,11 +1867,13 @@ Wang Xing Wang Yuexiao Ward Vandewege WarheadsSE +Wassim Dhif Wayne Chang Wayne Song Weerasak Chongnguluam Wei Wu Wei-Ting Kuo +weipeng weiyan Weiyang Zhu Wen Cheng Ma @@ -1948,5 +1979,6 @@ Zunayed Ali Átila Camurça Alves 尹吉峰 徐俊杰 +慕陶 搏通 黄艳红00139573