Merge pull request #642 from dnephin/use-upstream-spf13-cobra

Use upstream spf13 cobra
This commit is contained in:
Sebastiaan van Stijn 2017-10-26 10:05:11 +02:00 committed by GitHub
commit e59f3e2925
42 changed files with 1940 additions and 1475 deletions

View File

@ -17,6 +17,7 @@ func SetupRootCommand(rootCmd *cobra.Command) {
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands) cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages) cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
cobra.AddTemplateFunc("useLine", UseLine)
rootCmd.SetUsageTemplate(usageTemplate) rootCmd.SetUsageTemplate(usageTemplate)
rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetHelpTemplate(helpTemplate)
@ -97,9 +98,19 @@ func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
return cmds return cmds
} }
// UseLine returns the usage line for a command. This implementation is different
// from the default Command.UseLine in that it does not add a `[flags]` to the
// of the line.
func UseLine(cmd *cobra.Command) string {
if cmd.HasParent() {
return cmd.Parent().CommandPath() + " " + cmd.Use
}
return cmd.Use
}
var usageTemplate = `Usage: var usageTemplate = `Usage:
{{- if not .HasSubCommands}} {{.UseLine}}{{end}} {{- if not .HasSubCommands}} {{ useLine . }}{{end}}
{{- if .HasSubCommands}} {{ .CommandPath}} COMMAND{{end}} {{- if .HasSubCommands}} {{ .CommandPath}} COMMAND{{end}}
{{ .Short | trim }} {{ .Short | trim }}

View File

@ -13,7 +13,7 @@ func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command {
Short: "Manage checkpoints", Short: "Manage checkpoints",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"experimental": "", "version": "1.25"}, Annotations: map[string]string{"experimental": "", "version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View File

@ -15,7 +15,7 @@ func NewConfigCommand(dockerCli *command.DockerCli) *cobra.Command {
Short: "Manage Docker configs", Short: "Manage Docker configs",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.30"}, Annotations: map[string]string{"version": "1.30"},
} }
cmd.AddCommand( cmd.AddCommand(
newConfigListCommand(dockerCli), newConfigListCommand(dockerCli),

View File

@ -45,10 +45,7 @@ func TestValidateAttach(t *testing.T) {
// nolint: unparam // nolint: unparam
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) { func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
flags := pflag.NewFlagSet("run", pflag.ContinueOnError) flags, copts := setupRunFlags()
flags.SetOutput(ioutil.Discard)
flags.Usage = nil
copts := addFlags(flags)
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -60,6 +57,14 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *network
return containerConfig.Config, containerConfig.HostConfig, containerConfig.NetworkingConfig, err return containerConfig.Config, containerConfig.HostConfig, containerConfig.NetworkingConfig, err
} }
func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
flags.SetOutput(ioutil.Discard)
flags.Usage = nil
copts := addFlags(flags)
return flags, copts
}
func parseMustError(t *testing.T, args string) { func parseMustError(t *testing.T, args string) {
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) _, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
assert.Error(t, err, args) assert.Error(t, err, args)
@ -227,20 +232,21 @@ func TestParseWithMacAddress(t *testing.T) {
} }
} }
func TestParseWithMemory(t *testing.T) { func TestRunFlagsParseWithMemory(t *testing.T) {
invalidMemory := "--memory=invalid" flags, _ := setupRunFlags()
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}) args := []string{"--memory=invalid", "img", "cmd"}
testutil.ErrorContains(t, err, invalidMemory) err := flags.Parse(args)
testutil.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`)
_, hostconfig := mustParse(t, "--memory=1G") _, hostconfig := mustParse(t, "--memory=1G")
assert.Equal(t, int64(1073741824), hostconfig.Memory) assert.Equal(t, int64(1073741824), hostconfig.Memory)
} }
func TestParseWithMemorySwap(t *testing.T) { func TestParseWithMemorySwap(t *testing.T) {
invalidMemory := "--memory-swap=invalid" flags, _ := setupRunFlags()
args := []string{"--memory-swap=invalid", "img", "cmd"}
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}) err := flags.Parse(args)
testutil.ErrorContains(t, err, invalidMemory) testutil.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`)
_, hostconfig := mustParse(t, "--memory-swap=1G") _, hostconfig := mustParse(t, "--memory-swap=1G")
assert.Equal(t, int64(1073741824), hostconfig.MemorySwap) assert.Equal(t, int64(1073741824), hostconfig.MemorySwap)
@ -365,7 +371,10 @@ func TestParseDevice(t *testing.T) {
func TestParseModes(t *testing.T) { func TestParseModes(t *testing.T) {
// pid ko // pid ko
_, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}) flags, copts := setupRunFlags()
args := []string{"--pid=container:", "img", "cmd"}
require.NoError(t, flags.Parse(args))
_, err := parse(flags, copts)
testutil.ErrorContains(t, err, "--pid: invalid PID mode") testutil.ErrorContains(t, err, "--pid: invalid PID mode")
// pid ok // pid ok
@ -385,14 +394,18 @@ func TestParseModes(t *testing.T) {
if !hostconfig.UTSMode.Valid() { if !hostconfig.UTSMode.Valid() {
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode) t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
} }
}
func TestRunFlagsParseShmSize(t *testing.T) {
// shm-size ko // shm-size ko
expectedErr := `invalid argument "a128m" for --shm-size=a128m: invalid size: 'a128m'` flags, _ := setupRunFlags()
_, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}) args := []string{"--shm-size=a128m", "img", "cmd"}
expectedErr := `invalid argument "a128m" for "--shm-size" flag: invalid size: 'a128m'`
err := flags.Parse(args)
testutil.ErrorContains(t, err, expectedErr) testutil.ErrorContains(t, err, expectedErr)
// shm-size ok // shm-size ok
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"}) _, hostconfig, _, err := parseRun([]string{"--shm-size=128m", "img", "cmd"})
require.NoError(t, err) require.NoError(t, err)
if hostconfig.ShmSize != 134217728 { if hostconfig.ShmSize != 134217728 {
t.Fatalf("Expected a valid ShmSize, got %d", hostconfig.ShmSize) t.Fatalf("Expected a valid ShmSize, got %d", hostconfig.ShmSize)

View File

@ -35,7 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
return nil return nil
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -37,7 +37,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
return nil return nil
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -33,7 +33,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
} }
return nil return nil
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -18,7 +18,7 @@ func NewNodeCommand(dockerCli command.Cli) *cobra.Command {
Short: "Manage Swarm nodes", Short: "Manage Swarm nodes",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.24"}, Annotations: map[string]string{"version": "1.24"},
} }
cmd.AddCommand( cmd.AddCommand(
newDemoteCommand(dockerCli), newDemoteCommand(dockerCli),

View File

@ -14,7 +14,7 @@ func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
Short: "Manage plugins", Short: "Manage plugins",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(

View File

@ -26,7 +26,7 @@ func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
} }
return runUpgrade(dockerCli, options) return runUpgrade(dockerCli, options)
}, },
Tags: map[string]string{"version": "1.26"}, Annotations: map[string]string{"version": "1.26"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -15,7 +15,7 @@ func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
Short: "Manage Docker secrets", Short: "Manage Docker secrets",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(
newSecretListCommand(dockerCli), newSecretListCommand(dockerCli),

View File

@ -15,7 +15,7 @@ func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
Short: "Manage services", Short: "Manage services",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.24"}, Annotations: map[string]string{"version": "1.24"},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View File

@ -48,7 +48,7 @@ func newLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
opts.target = args[0] opts.target = args[0]
return runLogs(dockerCli, &opts) return runLogs(dockerCli, &opts)
}, },
Tags: map[string]string{"version": "1.29"}, Annotations: map[string]string{"version": "1.29"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -21,7 +21,7 @@ func newRollbackCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runRollback(dockerCli, options, args[0]) return runRollback(dockerCli, options, args[0])
}, },
Tags: map[string]string{"version": "1.31"}, Annotations: map[string]string{"version": "1.31"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -6,6 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/cli/internal/test/testutil"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount" mounttypes "github.com/docker/docker/api/types/mount"
@ -165,7 +166,7 @@ func TestUpdateDNSConfig(t *testing.T) {
// IPv6 // IPv6
flags.Set("dns-add", "2001:db8:abc8::1") flags.Set("dns-add", "2001:db8:abc8::1")
// Invalid dns record // Invalid dns record
assert.EqualError(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address") testutil.ErrorContains(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
// domains with duplicates // domains with duplicates
flags.Set("dns-search-add", "example.com") flags.Set("dns-search-add", "example.com")
@ -173,7 +174,7 @@ func TestUpdateDNSConfig(t *testing.T) {
flags.Set("dns-search-add", "example.org") flags.Set("dns-search-add", "example.org")
flags.Set("dns-search-rm", "example.org") flags.Set("dns-search-rm", "example.org")
// Invalid dns search domain // Invalid dns search domain
assert.EqualError(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain") testutil.ErrorContains(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
flags.Set("dns-option-add", "ndots:9") flags.Set("dns-option-add", "ndots:9")
flags.Set("dns-option-rm", "timeout:3") flags.Set("dns-option-rm", "timeout:3")
@ -362,7 +363,7 @@ func TestUpdateHosts(t *testing.T) {
// just hostname should work as well // just hostname should work as well
flags.Set("host-rm", "example.net") flags.Set("host-rm", "example.net")
// bad format error // bad format error
assert.EqualError(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`) testutil.ErrorContains(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`)
hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"} hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"}

View File

@ -14,7 +14,7 @@ func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
Short: "Manage Docker stacks", Short: "Manage Docker stacks",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(
newDeployCommand(dockerCli), newDeployCommand(dockerCli),
@ -31,6 +31,6 @@ func NewTopLevelDeployCommand(dockerCli command.Cli) *cobra.Command {
cmd := newDeployCommand(dockerCli) cmd := newDeployCommand(dockerCli)
// Remove the aliases at the top level // Remove the aliases at the top level
cmd.Aliases = []string{} cmd.Aliases = []string{}
cmd.Tags = map[string]string{"experimental": "", "version": "1.25"} cmd.Annotations = map[string]string{"experimental": "", "version": "1.25"}
return cmd return cmd
} }

View File

@ -36,7 +36,7 @@ func newCACommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCA(dockerCli, cmd.Flags(), opts) return runCA(dockerCli, cmd.Flags(), opts)
}, },
Tags: map[string]string{"version": "1.30"}, Annotations: map[string]string{"version": "1.30"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -15,7 +15,7 @@ func NewSwarmCommand(dockerCli command.Cli) *cobra.Command {
Short: "Manage Swarm", Short: "Manage Swarm",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.24"}, Annotations: map[string]string{"version": "1.24"},
} }
cmd.AddCommand( cmd.AddCommand(
newInitCommand(dockerCli), newInitCommand(dockerCli),

View File

@ -8,6 +8,7 @@ Flags:
--cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s) --cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)
--dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s) --dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)
--external-ca external-ca Specifications of one or more certificate signing endpoints --external-ca external-ca Specifications of one or more certificate signing endpoints
-h, --help help for update
--max-snapshots uint Number of additional Raft snapshots to retain --max-snapshots uint Number of additional Raft snapshots to retain
--snapshot-interval uint Number of log entries between Raft snapshots (default 10000) --snapshot-interval uint Number of log entries between Raft snapshots (default 10000)
--task-history-limit int Task history retention limit (default 5) --task-history-limit int Task history retention limit (default 5)

View File

@ -26,7 +26,7 @@ func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runDiskUsage(dockerCli, opts) return runDiskUsage(dockerCli, opts)
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -37,7 +37,7 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runPrune(dockerCli, options) return runPrune(dockerCli, options)
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -13,7 +13,7 @@ func NewVolumeCommand(dockerCli command.Cli) *cobra.Command {
Short: "Manage volumes", Short: "Manage volumes",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.21"}, Annotations: map[string]string{"version": "1.21"},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View File

@ -35,7 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
return nil return nil
}, },
Tags: map[string]string{"version": "1.25"}, Annotations: map[string]string{"version": "1.25"},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -88,6 +88,7 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p
} }
func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) {
defaultHelpFunc := cmd.HelpFunc()
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
initializeDockerCli(dockerCli, flags, opts) initializeDockerCli(dockerCli, flags, opts)
if err := isSupported(ccmd, dockerCli); err != nil { if err := isSupported(ccmd, dockerCli); err != nil {
@ -96,10 +97,7 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.
} }
hideUnsupportedFeatures(ccmd, dockerCli) hideUnsupportedFeatures(ccmd, dockerCli)
defaultHelpFunc(ccmd, args)
if err := ccmd.Help(); err != nil {
ccmd.Println(err)
}
}) })
} }
@ -225,13 +223,13 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) {
for _, subcmd := range cmd.Commands() { for _, subcmd := range cmd.Commands() {
// hide experimental subcommands // hide experimental subcommands
if !hasExperimental { if !hasExperimental {
if _, ok := subcmd.Tags["experimental"]; ok { if _, ok := subcmd.Annotations["experimental"]; ok {
subcmd.Hidden = true subcmd.Hidden = true
} }
} }
// hide subcommands not supported by the server // hide subcommands not supported by the server
if subcmdVersion, ok := subcmd.Tags["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) { if subcmdVersion, ok := subcmd.Annotations["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) {
subcmd.Hidden = true subcmd.Hidden = true
} }
} }
@ -244,10 +242,10 @@ func isSupported(cmd *cobra.Command, details versionDetails) error {
// Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack` // Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack`
for curr := cmd; curr != nil; curr = curr.Parent() { for curr := cmd; curr != nil; curr = curr.Parent() {
if cmdVersion, ok := curr.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) { if cmdVersion, ok := curr.Annotations["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
return fmt.Errorf("%s requires API version %s, but the Docker daemon API version is %s", cmd.CommandPath(), cmdVersion, clientVersion) return fmt.Errorf("%s requires API version %s, but the Docker daemon API version is %s", cmd.CommandPath(), cmdVersion, clientVersion)
} }
if _, ok := curr.Tags["experimental"]; ok && !hasExperimental { if _, ok := curr.Annotations["experimental"]; ok && !hasExperimental {
return fmt.Errorf("%s is only supported on a Docker daemon with experimental features enabled", cmd.CommandPath()) return fmt.Errorf("%s is only supported on a Docker daemon with experimental features enabled", cmd.CommandPath())
} }
} }
@ -300,7 +298,7 @@ func isOSTypeSupported(f *pflag.Flag, osType string) bool {
// hasTags return true if any of the command's parents has tags // hasTags return true if any of the command's parents has tags
func hasTags(cmd *cobra.Command) bool { func hasTags(cmd *cobra.Command) bool {
for curr := cmd; curr != nil; curr = curr.Parent() { for curr := cmd; curr != nil; curr = curr.Parent() {
if len(curr.Tags) > 0 { if len(curr.Annotations) > 0 {
return true return true
} }
} }

View File

@ -8,6 +8,7 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/docker/cli/cli"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
@ -53,7 +54,7 @@ func GenYamlTree(cmd *cobra.Command, dir string) error {
// GenYamlTreeCustom creates yaml structured ref files // GenYamlTreeCustom creates yaml structured ref files
func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string) error { func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string) error {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
if err := GenYamlTreeCustom(c, dir, filePrepender); err != nil { if err := GenYamlTreeCustom(c, dir, filePrepender); err != nil {
@ -92,7 +93,7 @@ func GenYamlCustom(cmd *cobra.Command, w io.Writer) error {
} }
if cmd.Runnable() { if cmd.Runnable() {
cliDoc.Usage = cmd.UseLine() cliDoc.Usage = cli.UseLine(cmd)
} }
if len(cmd.Example) > 0 { if len(cmd.Example) > 0 {
@ -103,10 +104,10 @@ func GenYamlCustom(cmd *cobra.Command, w io.Writer) error {
} }
// Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack` // Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack`
for curr := cmd; curr != nil; curr = curr.Parent() { for curr := cmd; curr != nil; curr = curr.Parent() {
if v, ok := curr.Tags["version"]; ok && cliDoc.MinAPIVersion == "" { if v, ok := curr.Annotations["version"]; ok && cliDoc.MinAPIVersion == "" {
cliDoc.MinAPIVersion = v cliDoc.MinAPIVersion = v
} }
if _, ok := curr.Tags["experimental"]; ok && !cliDoc.Experimental { if _, ok := curr.Annotations["experimental"]; ok && !cliDoc.Experimental {
cliDoc.Experimental = true cliDoc.Experimental = true
} }
} }
@ -137,7 +138,7 @@ func GenYamlCustom(cmd *cobra.Command, w io.Writer) error {
sort.Sort(byName(children)) sort.Sort(byName(children))
for _, child := range children { for _, child := range children {
if !child.IsAvailableCommand() || child.IsHelpCommand() { if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue continue
} }
currentChild := cliDoc.Name + " " + child.Name() currentChild := cliDoc.Name + " " + child.Name()
@ -207,7 +208,7 @@ func hasSeeAlso(cmd *cobra.Command) bool {
return true return true
} }
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
return true return true

View File

@ -37,8 +37,8 @@ github.com/pmezard/go-difflib v1.0.0
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438 github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77 github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
github.com/sirupsen/logrus v1.0.3 github.com/sirupsen/logrus v1.0.3
github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git github.com/spf13/cobra 7b2c5ac9fc04fc5efafb60700713d4fa609b777b
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7 github.com/spf13/pflag 97afa5e7ca8a08a383cb259e06636b5e2cc7897f
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a

View File

@ -8,6 +8,7 @@ Many of the most widely used Go projects are built using Cobra including:
* [Hugo](http://gohugo.io) * [Hugo](http://gohugo.io)
* [rkt](https://github.com/coreos/rkt) * [rkt](https://github.com/coreos/rkt)
* [etcd](https://github.com/coreos/etcd) * [etcd](https://github.com/coreos/etcd)
* [Moby (former Docker)](https://github.com/moby/moby)
* [Docker (distribution)](https://github.com/docker/distribution) * [Docker (distribution)](https://github.com/docker/distribution)
* [OpenShift](https://www.openshift.com/) * [OpenShift](https://www.openshift.com/)
* [Delve](https://github.com/derekparker/delve) * [Delve](https://github.com/derekparker/delve)
@ -15,16 +16,36 @@ Many of the most widely used Go projects are built using Cobra including:
* [CockroachDB](http://www.cockroachlabs.com/) * [CockroachDB](http://www.cockroachlabs.com/)
* [Bleve](http://www.blevesearch.com/) * [Bleve](http://www.blevesearch.com/)
* [ProjectAtomic (enterprise)](http://www.projectatomic.io/) * [ProjectAtomic (enterprise)](http://www.projectatomic.io/)
* [Parse (CLI)](https://parse.com/)
* [GiantSwarm's swarm](https://github.com/giantswarm/cli) * [GiantSwarm's swarm](https://github.com/giantswarm/cli)
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [rclone](http://rclone.org/)
* [nehm](https://github.com/bogem/nehm)
[![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![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) [![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 # Overview
@ -39,27 +60,16 @@ Cobra provides:
* Fully POSIX-compliant flags (including short & long versions) * Fully POSIX-compliant flags (including short & long versions)
* Nested subcommands * Nested subcommands
* Global, local and cascading flags * 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`?) * Intelligent suggestions (`app srver`... did you mean `app server`?)
* Automatic help generation for commands and flags * Automatic help generation for commands and flags
* Automatic detailed help for `app help [command]`
* Automatic help flag recognition of `-h`, `--help`, etc. * Automatic help flag recognition of `-h`, `--help`, etc.
* Automatically generated bash autocomplete for your application * Automatically generated bash autocomplete for your application
* Automatically generated man pages for your application * Automatically generated man pages for your application
* Command aliases so you can change things without breaking them * 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 * 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 isnt 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 # Concepts
Cobra is built on a structure of commands, arguments & flags. Cobra is built on a structure of commands, arguments & flags.
@ -78,11 +88,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: 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. In this command we are telling Git to clone the url bare.
> git clone URL --bare git clone URL --bare
## Commands ## Commands
@ -92,20 +102,11 @@ have children commands and optionally run an action.
In the example above, 'server' is the command. In the example above, 'server' is the command.
A Command has the following structure: [More about cobra.Command](https://godoc.org/github.com/spf13/cobra#Command)
```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 <this-command>' output.
Run func(cmd *Command, args []string) // Run runs the command.
}
```
## Flags ## 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/). 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 A Cobra command can define flags that persist through to children commands
and flags that are only available to that command. and flags that are only available to that command.
@ -113,23 +114,15 @@ and flags that are only available to that command.
In the example above, 'port' is the flag. In the example above, 'port' is the flag.
Flag functionality is provided by the [pflag 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. 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 # Installing
Using Cobra is easy. First, use `go get` to install the latest version Using Cobra is easy. First, use `go get` to install the latest version
of the library. This command will install the `cobra` generator executible of the library. This command will install the `cobra` generator executable
along with the library: 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: Next, include Cobra in your application:
@ -139,8 +132,8 @@ import "github.com/spf13/cobra"
# Getting Started # Getting Started
While you are welcome to provide your own organization, typically a Cobra based While you are welcome to provide your own organization, typically a Cobra-based
application will follow the following organizational structure. application will follow the following organizational structure:
``` ```
▾ appName/ ▾ appName/
@ -152,17 +145,22 @@ application will follow the following organizational structure.
main.go 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 ```go
package main package main
import "{pathToYourApp}/cmd" import (
"fmt"
"os"
"{pathToYourApp}/cmd"
)
func main() { func main() {
if err := cmd.RootCmd.Execute(); err != nil { if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(1)
} }
} }
``` ```
@ -172,93 +170,14 @@ func main() {
Cobra provides its own program that will create your application and add any 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. 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 ## Using the Cobra Library
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.
Cobra init is pretty smart. You can provide it a full path, or simply a path To manually implement Cobra you need to create a bare main.go file and a RootCmd file.
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 <spf@spf13.com>
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.
You will optionally provide additional commands as you see fit. You will optionally provide additional commands as you see fit.
### Create the root command ### Create rootCmd
The root command represents your binary itself.
#### Manually create rootCmd
Cobra doesn't require any special constructors. Simply create your commands. Cobra doesn't require any special constructors. Simply create your commands.
@ -279,9 +198,18 @@ var RootCmd = &cobra.Command{
You will additionally define flags and handle configuration in your init() function. You will additionally define flags and handle configuration in your init() function.
for example cmd/root.go: For example cmd/root.go:
```go ```go
import (
"fmt"
"os"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
@ -295,6 +223,34 @@ func init() {
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>") viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache") viper.SetDefault("license", "apache")
} }
func Execute() {
RootCmd.Execute()
}
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)
}
}
``` ```
### Create your main.go ### Create your main.go
@ -307,17 +263,21 @@ In a Cobra app, typically the main.go file is very bare. It serves, one purpose,
```go ```go
package main package main
import "{pathToYourApp}/cmd" import (
"fmt"
"os"
"{pathToYourApp}/cmd"
)
func main() { func main() {
if err := cmd.RootCmd.Execute(); err != nil { if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(1)
} }
} }
``` ```
### Create additional commands ### Create additional commands
Additional commands can be defined and typically are each given their own file Additional commands can be defined and typically are each given their own file
@ -331,6 +291,7 @@ package cmd
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"fmt"
) )
func init() { func init() {
@ -347,30 +308,6 @@ var versionCmd = &cobra.Command{
} }
``` ```
### 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 ## Working with Flags
Flags provide modifiers to control how the action command operates. Flags provide modifiers to control how the action command operates.
@ -406,39 +343,68 @@ A flag can also be assigned locally which will only apply to that specific comma
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 By default Cobra only parses local flags on the target command, any local flags on
one of the following values: 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).
## 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. - `NoArgs` - the command will report an error if there are any positional args.
- `ArbitraryArgs` - the command will accept any args. - `ArbitraryArgs` - the command will accept any args.
- `OnlyValidArgs` - the command will report an error if there are any positiona - `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`.
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.
- `MinimumNArgs(int)` - the command will report an error if there are not at - `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
least N positional args. - `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
- `MaximumNArgs(int)` - the command will report an error if there are more than - `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.
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
An example of setting the custom validator:
```go ```go
var HugoCmd = &cobra.Command{ var cmd = &cobra.Command{
Use: "hugo", Short: "hello",
Short: "Hugo is a very fast static site generator", Args: func(cmd *cobra.Command, args []string) error {
ValidArgs: []string{"one", "two"} if len(args) < 1 {
Args: cobra.OnlyValidArgs 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) { Run: func(cmd *cobra.Command, args []string) {
// args will only have the values one, two fmt.Println("Hello, World!")
// or the cmd.Execute() will fail.
}, },
} }
``` ```
@ -465,15 +431,14 @@ import (
) )
func main() { func main() {
var echoTimes int var echoTimes int
var cmdPrint = &cobra.Command{ var cmdPrint = &cobra.Command{
Use: "print [string to print]", Use: "print [string to print]",
Short: "Print anything to the screen", Short: "Print anything to the screen",
Long: `print is for printing anything back to the screen. Long: `print is for printing anything back to the screen.
For many years people have printed 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) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " ")) fmt.Println("Print: " + strings.Join(args, " "))
}, },
@ -483,8 +448,8 @@ func main() {
Use: "echo [string to echo]", Use: "echo [string to echo]",
Short: "Echo anything to the screen", Short: "Echo anything to the screen",
Long: `echo is for echoing anything back. Long: `echo is for echoing anything back.
Echo works a lot like print, except it has a child command. Echo works a lot like print, except it has a child command.`,
`, Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " ")) fmt.Println("Print: " + strings.Join(args, " "))
}, },
@ -495,6 +460,7 @@ func main() {
Short: "Echo anything to the screen more times", Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing Long: `echo things multiple times back to the user by providing
a count and a string.`, a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ { for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " ")) fmt.Println("Echo: " + strings.Join(args, " "))
@ -513,7 +479,7 @@ func main() {
For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/). 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. 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 This will be called when a user runs 'app help'. Additionally, help will also
@ -526,60 +492,28 @@ create' is called. Every command will automatically have the '--help' flag adde
The following output is automatically generated by Cobra. Nothing beyond the The following output is automatically generated by Cobra. Nothing beyond the
command and flag definitions are needed. command and flag definitions are needed.
> hugo help $ cobra help
hugo is the main command, used to build your Hugo site. Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
Hugo is a Fast and Flexible Static Site Generator to quickly create a Cobra application.
built with love by spf13 and friends in Go.
Complete documentation is available at http://gohugo.io/.
Usage: Usage:
hugo [flags] cobra [command]
hugo [command]
Available Commands: Available Commands:
server Hugo runs its own webserver to render the files add Add a command to a Cobra Application
version Print the version number of Hugo help Help about any command
config Print the site configuration init Initialize a Cobra Application
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.
Flags: Flags:
-b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ -a, --author string author name for copyright attribution (default "YOUR NAME")
-D, --buildDrafts[=false]: include content marked as draft --config string config file (default is $HOME/.cobra.yaml)
-F, --buildFuture[=false]: include content with publishdate in the future -h, --help help for cobra
--cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ -l, --license string name of license for the project
--canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL --viper use Viper for configuration (default true)
--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
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 Help is just a command like any other. There is no special logic or behavior
@ -587,38 +521,18 @@ around it. In fact, you can provide your own if you want.
### Defining your own help ### Defining your own help
You can provide your own Help command or your own template for the default command to use. You can provide your own Help command or your own template for the default command to use
with followind functions:
The default help command is
```go ```go
func (c *Command) initHelp() { cmd.SetHelpCommand(cmd *Command)
if c.helpCommand == nil { cmd.SetHelpFunc(f func(*Command, []string))
c.helpCommand = &Command{ cmd.SetHelpTemplate(s string)
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)
``` ```
The latter two will also apply to any children commands. 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 When the user provides an invalid flag or invalid command, Cobra responds by
showing the user the 'usage'. showing the user the 'usage'.
@ -627,73 +541,37 @@ showing the user the 'usage'.
You may recognize this from the help above. That's because the default help You may recognize this from the help above. That's because the default help
embeds the usage as part of its output. embeds the usage as part of its output.
$ cobra --invalid
Error: unknown flag: --invalid
Usage: Usage:
hugo [flags] cobra [command]
hugo [command]
Available Commands: Available Commands:
server Hugo runs its own webserver to render the files add Add a command to a Cobra Application
version Print the version number of Hugo help Help about any command
config Print the site configuration init Initialize a Cobra Application
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.
Flags: Flags:
-b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ -a, --author string author name for copyright attribution (default "YOUR NAME")
-D, --buildDrafts[=false]: include content marked as draft --config string config file (default is $HOME/.cobra.yaml)
-F, --buildFuture[=false]: include content with publishdate in the future -h, --help help for cobra
--cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ -l, --license string name of license for the project
--canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL --viper use Viper for configuration (default true)
--config="": config file (default is path/config.yaml|json|toml)
-d, --destination="": filesystem path to write files to Use "cobra [command] --help" for more information about a command.
--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
### Defining your own usage ### Defining your own usage
You can provide your own usage function or template for Cobra to use. 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: Like help, the function and template are overridable through public methods:
```go ```go
command.SetUsageFunc(f func(*Command) error) cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
command.SetUsageTemplate(s string)
``` ```
## PreRun or PostRun Hooks ## 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 inherrited by children if they do not declare their own. These function are run in the following order: 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` - `PersistentPreRun`
- `PreRun` - `PreRun`
@ -754,58 +632,26 @@ func main() {
rootCmd.AddCommand(subCmd) rootCmd.AddCommand(subCmd)
rootCmd.SetArgs([]string{""}) rootCmd.SetArgs([]string{""})
_ = rootCmd.Execute() rootCmd.Execute()
fmt.Print("\n") fmt.Println()
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
_ = rootCmd.Execute() 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 Inside rootCmd PersistentPreRun with args: [arg1 arg2]
Inside subCmd PreRun with args: [arg1 arg2]
Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, Inside subCmd Run with args: [arg1 arg2]
providing a way to handle the errors in one location. The current list of functions that return an error is: Inside subCmd PostRun with args: [arg1 arg2]
Inside subCmd PersistentPostRun with args: [arg1 arg2]
* 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)
}
}
``` ```
## Suggestions when "unknown command" happens ## Suggestions when "unknown command" happens
@ -848,81 +694,28 @@ Did you mean this?
Run 'kubectl help' for usage. 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
## Generating bash completions for your command
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). 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 # Contributing
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
1. Fork it 1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`)
3. Commit your changes (`git commit -am 'Add some feature'`) 3. Create your feature branch (`git checkout -b my-new-feature`)
4. Push to the branch (`git push origin my-new-feature`) 4. Make changes and add them (`git add .`)
5. Create new Pull Request 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 # License
Names in no particular order:
* [spf13](https://github.com/spf13),
[eparis](https://github.com/eparis),
[bep](https://github.com/bep), and many more!
## License
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) 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")

View File

@ -1,6 +1,7 @@
package cobra package cobra
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -10,19 +11,17 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// Annotations for Bash completion.
const ( const (
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions" BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions"
BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompCustom = "cobra_annotation_bash_completion_custom"
BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
) )
func preamble(out io.Writer, name string) error { func writePreamble(buf *bytes.Buffer, name string) {
_, err := fmt.Fprintf(out, "# bash completion for %-36s -*- shell-script -*-\n", name) buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
if err != nil { buf.WriteString(`
return err
}
_, err = fmt.Fprint(out, `
__debug() __debug()
{ {
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@ -87,13 +86,13 @@ __handle_reply()
local index flag local index flag
flag="${cur%%=*}" flag="${cur%%=*}"
__index_of_word "${flag}" "${flags_with_completion[@]}" __index_of_word "${flag}" "${flags_with_completion[@]}"
if [[ ${index} -ge 0 ]]; then
COMPREPLY=() COMPREPLY=()
if [[ ${index} -ge 0 ]]; then
PREFIX="" PREFIX=""
cur="${cur#*=}" cur="${cur#*=}"
${flags_completion[${index}]} ${flags_completion[${index}]}
if [ -n "${ZSH_VERSION}" ]; then if [ -n "${ZSH_VERSION}" ]; then
# zfs completion needs --flag= prefix # zsh completion needs --flag= prefix
eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )"
fi fi
fi fi
@ -133,7 +132,10 @@ __handle_reply()
declare -F __custom_func >/dev/null && __custom_func declare -F __custom_func >/dev/null && __custom_func
fi fi
# available in bash-completion >= 2, not always present on macOS
if declare -F __ltrim_colon_completions >/dev/null; then
__ltrim_colon_completions "$cur" __ltrim_colon_completions "$cur"
fi
} }
# The arguments should be in the form "ext1|ext2|extn" # The arguments should be in the form "ext1|ext2|extn"
@ -224,7 +226,7 @@ __handle_command()
fi fi
c=$((c+1)) c=$((c+1))
__debug "${FUNCNAME[0]}: looking for ${next_command}" __debug "${FUNCNAME[0]}: looking for ${next_command}"
declare -F $next_command >/dev/null && $next_command declare -F "$next_command" >/dev/null && $next_command
} }
__handle_word() __handle_word()
@ -247,16 +249,12 @@ __handle_word()
} }
`) `)
return err
} }
func postscript(w io.Writer, name string) error { func writePostscript(buf *bytes.Buffer, name string) {
name = strings.Replace(name, ":", "__", -1) name = strings.Replace(name, ":", "__", -1)
_, err := fmt.Fprintf(w, "__start_%s()\n", name) buf.WriteString(fmt.Sprintf("__start_%s()\n", name))
if err != nil { buf.WriteString(fmt.Sprintf(`{
return err
}
_, err = fmt.Fprintf(w, `{
local cur prev words cword local cur prev words cword
declare -A flaghash 2>/dev/null || : declare -A flaghash 2>/dev/null || :
if declare -F _init_completion >/dev/null 2>&1; then if declare -F _init_completion >/dev/null 2>&1; then
@ -280,318 +278,227 @@ func postscript(w io.Writer, name string) error {
__handle_word __handle_word
} }
`, name) `, name))
if err != nil { buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
return err
}
_, err = fmt.Fprintf(w, `if [[ $(type -t compopt) = "builtin" ]]; then
complete -o default -F __start_%s %s complete -o default -F __start_%s %s
else else
complete -o default -o nospace -F __start_%s %s complete -o default -o nospace -F __start_%s %s
fi fi
`, name, name, name, name) `, name, name, name, name))
if err != nil { buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n")
return err
}
_, err = fmt.Fprintf(w, "# ex: ts=4 sw=4 et filetype=sh\n")
return err
} }
func writeCommands(cmd *Command, w io.Writer) error { func writeCommands(buf *bytes.Buffer, cmd *Command) {
if _, err := fmt.Fprintf(w, " commands=()\n"); err != nil { buf.WriteString(" commands=()\n")
return err
}
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c == cmd.helpCommand { if !c.IsAvailableCommand() || c == cmd.helpCommand {
continue continue
} }
if _, err := fmt.Fprintf(w, " commands+=(%q)\n", c.Name()); err != nil { buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name()))
return err
} }
} buf.WriteString("\n")
_, err := fmt.Fprintf(w, "\n")
return err
} }
func writeFlagHandler(name string, annotations map[string][]string, w io.Writer) error { func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string) {
for key, value := range annotations { for key, value := range annotations {
switch key { switch key {
case BashCompFilenameExt: case BashCompFilenameExt:
_, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
if err != nil {
return err
}
var ext string
if len(value) > 0 { if len(value) > 0 {
ext := "__handle_filename_extension_flag " + strings.Join(value, "|") ext = "__handle_filename_extension_flag " + strings.Join(value, "|")
_, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext)
} else { } else {
ext := "_filedir" ext = "_filedir"
_, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext)
}
if err != nil {
return err
} }
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext))
case BashCompCustom: case BashCompCustom:
_, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
if err != nil {
return err
}
if len(value) > 0 { if len(value) > 0 {
handlers := strings.Join(value, "; ") handlers := strings.Join(value, "; ")
_, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", handlers) buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers))
} else { } else {
_, err = fmt.Fprintf(w, " flags_completion+=(:)\n") buf.WriteString(" flags_completion+=(:)\n")
}
if err != nil {
return err
} }
case BashCompSubdirsInDir: 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 { if len(value) == 1 {
ext := "__handle_subdirs_in_dir_flag " + value[0] ext = "__handle_subdirs_in_dir_flag " + value[0]
_, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext)
} else { } else {
ext := "_filedir -d" ext = "_filedir -d"
_, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext)
} }
if err != nil { buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext))
return err
} }
} }
} }
return nil
}
func writeShortFlag(flag *pflag.Flag, w io.Writer) error { func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
b := (len(flag.NoOptDefVal) > 0)
name := flag.Shorthand name := flag.Shorthand
format := " " format := " "
if !b { if len(flag.NoOptDefVal) == 0 {
format += "two_word_" format += "two_word_"
} }
format += "flags+=(\"-%s\")\n" format += "flags+=(\"-%s\")\n"
if _, err := fmt.Fprintf(w, format, name); err != nil { buf.WriteString(fmt.Sprintf(format, name))
return err writeFlagHandler(buf, "-"+name, flag.Annotations)
}
return writeFlagHandler("-"+name, flag.Annotations, w)
} }
func writeFlag(flag *pflag.Flag, w io.Writer) error { func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
b := (len(flag.NoOptDefVal) > 0)
name := flag.Name name := flag.Name
format := " flags+=(\"--%s" format := " flags+=(\"--%s"
if !b { if len(flag.NoOptDefVal) == 0 {
format += "=" format += "="
} }
format += "\")\n" format += "\")\n"
if _, err := fmt.Fprintf(w, format, name); err != nil { buf.WriteString(fmt.Sprintf(format, name))
return err writeFlagHandler(buf, "--"+name, flag.Annotations)
}
return writeFlagHandler("--"+name, flag.Annotations, w)
} }
func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error { func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
b := (len(flag.NoOptDefVal) > 0)
name := flag.Name name := flag.Name
format := " local_nonpersistent_flags+=(\"--%s" format := " local_nonpersistent_flags+=(\"--%s"
if !b { if len(flag.NoOptDefVal) == 0 {
format += "=" format += "="
} }
format += "\")\n" format += "\")\n"
if _, err := fmt.Fprintf(w, format, name); err != nil { buf.WriteString(fmt.Sprintf(format, name))
return err
}
return nil
} }
func writeFlags(cmd *Command, w io.Writer) error { func writeFlags(buf *bytes.Buffer, cmd *Command) {
_, err := fmt.Fprintf(w, ` flags=() buf.WriteString(` flags=()
two_word_flags=() two_word_flags=()
local_nonpersistent_flags=() local_nonpersistent_flags=()
flags_with_completion=() flags_with_completion=()
flags_completion=() flags_completion=()
`) `)
if err != nil {
return err
}
localNonPersistentFlags := cmd.LocalNonPersistentFlags() localNonPersistentFlags := cmd.LocalNonPersistentFlags()
var visitErr error
cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
if err := writeFlag(flag, w); err != nil { if nonCompletableFlag(flag) {
visitErr = err
return return
} }
writeFlag(buf, flag)
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
if err := writeShortFlag(flag, w); err != nil { writeShortFlag(buf, flag)
visitErr = err
return
}
} }
if localNonPersistentFlags.Lookup(flag.Name) != nil { if localNonPersistentFlags.Lookup(flag.Name) != nil {
if err := writeLocalNonPersistentFlag(flag, w); err != nil { writeLocalNonPersistentFlag(buf, flag)
visitErr = err
return
}
} }
}) })
if visitErr != nil {
return visitErr
}
cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
if err := writeFlag(flag, w); err != nil { if nonCompletableFlag(flag) {
visitErr = err
return return
} }
writeFlag(buf, flag)
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
if err := writeShortFlag(flag, w); err != nil { writeShortFlag(buf, flag)
visitErr = err
return
}
} }
}) })
if visitErr != nil {
return visitErr buf.WriteString("\n")
} }
_, err = fmt.Fprintf(w, "\n") func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
return err buf.WriteString(" must_have_one_flag=()\n")
}
func writeRequiredFlag(cmd *Command, w io.Writer) error {
if _, err := fmt.Fprintf(w, " must_have_one_flag=()\n"); err != nil {
return err
}
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
var visitErr error
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if nonCompletableFlag(flag) {
return
}
for key := range flag.Annotations { for key := range flag.Annotations {
switch key { switch key {
case BashCompOneRequiredFlag: case BashCompOneRequiredFlag:
format := " must_have_one_flag+=(\"--%s" format := " must_have_one_flag+=(\"--%s"
b := (flag.Value.Type() == "bool") if flag.Value.Type() != "bool" {
if !b {
format += "=" format += "="
} }
format += "\")\n" format += "\")\n"
if _, err := fmt.Fprintf(w, format, flag.Name); err != nil { buf.WriteString(fmt.Sprintf(format, flag.Name))
visitErr = err
return
}
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
if _, err := fmt.Fprintf(w, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand); err != nil { buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand))
visitErr = err
return
}
} }
} }
} }
}) })
return visitErr
} }
func writeRequiredNouns(cmd *Command, w io.Writer) error { func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
if _, err := fmt.Fprintf(w, " must_have_one_noun=()\n"); err != nil { buf.WriteString(" must_have_one_noun=()\n")
return err
}
sort.Sort(sort.StringSlice(cmd.ValidArgs)) sort.Sort(sort.StringSlice(cmd.ValidArgs))
for _, value := range cmd.ValidArgs { for _, value := range cmd.ValidArgs {
if _, err := fmt.Fprintf(w, " must_have_one_noun+=(%q)\n", value); err != nil { buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
return err
} }
} }
return nil
}
func writeArgAliases(cmd *Command, w io.Writer) error { func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
if _, err := fmt.Fprintf(w, " noun_aliases=()\n"); err != nil { buf.WriteString(" noun_aliases=()\n")
return err
}
sort.Sort(sort.StringSlice(cmd.ArgAliases)) sort.Sort(sort.StringSlice(cmd.ArgAliases))
for _, value := range cmd.ArgAliases { for _, value := range cmd.ArgAliases {
if _, err := fmt.Fprintf(w, " noun_aliases+=(%q)\n", value); err != nil { buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value))
return err
} }
} }
return nil
}
func gen(cmd *Command, w io.Writer) error { func gen(buf *bytes.Buffer, cmd *Command) {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c == cmd.helpCommand { if !c.IsAvailableCommand() || c == cmd.helpCommand {
continue continue
} }
if err := gen(c, w); err != nil { gen(buf, c)
return err
}
} }
commandName := cmd.CommandPath() commandName := cmd.CommandPath()
commandName = strings.Replace(commandName, " ", "_", -1) commandName = strings.Replace(commandName, " ", "_", -1)
commandName = strings.Replace(commandName, ":", "__", -1) commandName = strings.Replace(commandName, ":", "__", -1)
if _, err := fmt.Fprintf(w, "_%s()\n{\n", commandName); err != nil { buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
return err buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName))
} writeCommands(buf, cmd)
if _, err := fmt.Fprintf(w, " last_command=%q\n", commandName); err != nil { writeFlags(buf, cmd)
return err writeRequiredFlag(buf, cmd)
} writeRequiredNouns(buf, cmd)
if err := writeCommands(cmd, w); err != nil { writeArgAliases(buf, cmd)
return err buf.WriteString("}\n\n")
}
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
} }
func (cmd *Command) GenBashCompletion(w io.Writer) error { // GenBashCompletion generates bash completion file and writes to the passed writer.
if err := preamble(w, cmd.Name()); err != nil { 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")
}
gen(buf, c)
writePostscript(buf, c.Name())
_, err := buf.WriteTo(w)
return err return err
} }
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())
}
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) outFile, err := os.Create(filename)
if err != nil { if err != nil {
return err return err
} }
defer outFile.Close() defer outFile.Close()
return cmd.GenBashCompletion(outFile) return c.GenBashCompletion(outFile)
} }
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists. // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists.
func (cmd *Command) MarkFlagRequired(name string) error { func (c *Command) MarkFlagRequired(name string) error {
return MarkFlagRequired(cmd.Flags(), name) return MarkFlagRequired(c.Flags(), name)
} }
// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists. // MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists.
func (cmd *Command) MarkPersistentFlagRequired(name string) error { func (c *Command) MarkPersistentFlagRequired(name string) error {
return MarkFlagRequired(cmd.PersistentFlags(), name) 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 in the flag set, if it exists.
@ -601,20 +508,20 @@ func MarkFlagRequired(flags *pflag.FlagSet, name string) error {
// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists. // 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. // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
func (cmd *Command) MarkFlagFilename(name string, extensions ...string) error { func (c *Command) MarkFlagFilename(name string, extensions ...string) error {
return MarkFlagFilename(cmd.Flags(), name, extensions...) return MarkFlagFilename(c.Flags(), name, extensions...)
} }
// MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists.
// Generated bash autocompletion will call the bash function f for the flag. // Generated bash autocompletion will call the bash function f for the flag.
func (cmd *Command) MarkFlagCustom(name string, f string) error { func (c *Command) MarkFlagCustom(name string, f string) error {
return MarkFlagCustom(cmd.Flags(), name, f) return MarkFlagCustom(c.Flags(), name, f)
} }
// MarkPersistentFlagFilename adds the BashCompFilenameExt annotation to the named persistent flag, if it exists. // 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. // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
func (cmd *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error {
return MarkFlagFilename(cmd.PersistentFlags(), name, extensions...) return MarkFlagFilename(c.PersistentFlags(), name, extensions...)
} }
// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists. // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists.

View File

@ -29,6 +29,7 @@ import (
var templateFuncs = template.FuncMap{ var templateFuncs = template.FuncMap{
"trim": strings.TrimSpace, "trim": strings.TrimSpace,
"trimRightSpace": trimRightSpace, "trimRightSpace": trimRightSpace,
"trimTrailingWhitespaces": trimRightSpace,
"appendIfNotPresent": appendIfNotPresent, "appendIfNotPresent": appendIfNotPresent,
"rpad": rpad, "rpad": rpad,
"gt": Gt, "gt": Gt,
@ -37,21 +38,31 @@ var templateFuncs = template.FuncMap{
var initializers []func() var initializers []func()
// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools. // EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing
// Set this to true to enable it // to automatically enable in CLI tools.
// Set this to true to enable it.
var EnablePrefixMatching = false var EnablePrefixMatching = false
// EnableCommandSorting controls sorting of the slice of commands, which is turned on by default. // EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
// To disable sorting, set it to false. // To disable sorting, set it to false.
var EnableCommandSorting = true var EnableCommandSorting = true
// 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 // AddTemplateFunc adds a template function that's available to Usage and Help
// template generation. // template generation.
func AddTemplateFunc(name string, tmplFunc interface{}) { func AddTemplateFunc(name string, tmplFunc interface{}) {
templateFuncs[name] = tmplFunc templateFuncs[name] = tmplFunc
} }
//AddTemplateFuncs adds multiple template functions availalble to Usage and // AddTemplateFuncs adds multiple template functions that are available to Usage and
// Help template generation. // Help template generation.
func AddTemplateFuncs(tmplFuncs template.FuncMap) { func AddTemplateFuncs(tmplFuncs template.FuncMap) {
for k, v := range tmplFuncs { for k, v := range tmplFuncs {
@ -61,11 +72,11 @@ func AddTemplateFuncs(tmplFuncs template.FuncMap) {
// OnInitialize takes a series of func() arguments and appends them to a slice of func(). // OnInitialize takes a series of func() arguments and appends them to a slice of func().
func OnInitialize(y ...func()) { func OnInitialize(y ...func()) {
for _, x := range y { initializers = append(initializers, y...)
initializers = append(initializers, x)
}
} }
// 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, // 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 // Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
// ints and then compared. // ints and then compared.
@ -96,6 +107,8 @@ func Gt(a interface{}, b interface{}) bool {
return left > right return left > right
} }
// 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. // 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 { func Eq(a interface{}, b interface{}) bool {
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
@ -116,7 +129,9 @@ func trimRightSpace(s string) string {
return strings.TrimRightFunc(s, unicode.IsSpace) 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 { func appendIfNotPresent(s, stringToAppend string) string {
if strings.Contains(s, stringToAppend) { if strings.Contains(s, stringToAppend) {
return s return s
@ -124,7 +139,7 @@ func appendIfNotPresent(s, stringToAppend string) string {
return s + " " + stringToAppend 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 { func rpad(s string, padding int) string {
template := fmt.Sprintf("%%-%ds", padding) template := fmt.Sprintf("%%-%ds", padding)
return fmt.Sprintf(template, s) return fmt.Sprintf(template, s)
@ -138,7 +153,7 @@ func tmpl(w io.Writer, text string, data interface{}) error {
return t.Execute(w, data) 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 { func ld(s, t string, ignoreCase bool) int {
if ignoreCase { if ignoreCase {
s = strings.ToLower(s) s = strings.ToLower(s)

File diff suppressed because it is too large Load Diff

View File

@ -11,14 +11,8 @@ import (
var preExecHookFn = preExecHook 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) { func preExecHook(c *Command) {
if mousetrap.StartedByExplorer() { if MousetrapHelpText != "" && mousetrap.StartedByExplorer() {
c.Print(MousetrapHelpText) c.Print(MousetrapHelpText)
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
os.Exit(1) os.Exit(1)

View File

@ -23,21 +23,21 @@ import (
"strings" "strings"
"time" "time"
mangen "github.com/cpuguy83/go-md2man/md2man" "github.com/cpuguy83/go-md2man/md2man"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// GenManTree will generate a man page for this command and all descendants // GenManTree will generate a man page for this command and all descendants
// in the directory given. The header may be nil. This function may not work // in the directory given. The header may be nil. This function may not work
// correctly if your command names have - in them. If you have `cmd` with two // correctly if your command names have `-` in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third` // subcmds, `sub` and `sub-third`, and `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`. // it is undefined which help output will be in the file `cmd-sub-third.1`.
func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error { func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
return GenManTreeFromOpts(cmd, GenManTreeOptions{ return GenManTreeFromOpts(cmd, GenManTreeOptions{
Header: header, Header: header,
Path: dir, Path: dir,
CommandSeparator: "_", CommandSeparator: "-",
}) })
} }
@ -49,7 +49,7 @@ func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
header = &GenManHeader{} header = &GenManHeader{}
} }
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
if err := GenManTreeFromOpts(c, opts); err != nil { if err := GenManTreeFromOpts(c, opts); err != nil {
@ -77,6 +77,8 @@ func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
return GenMan(cmd, &headerCopy, f) return GenMan(cmd, &headerCopy, f)
} }
// GenManTreeOptions is the options for generating the man pages.
// Used only in GenManTreeFromOpts.
type GenManTreeOptions struct { type GenManTreeOptions struct {
Header *GenManHeader Header *GenManHeader
Path string Path string
@ -105,7 +107,7 @@ func GenMan(cmd *cobra.Command, header *GenManHeader, w io.Writer) error {
fillHeader(header, cmd.CommandPath()) fillHeader(header, cmd.CommandPath())
b := genMan(cmd, header) b := genMan(cmd, header)
_, err := w.Write(mangen.Render(b)) _, err := w.Write(md2man.Render(b))
return err return err
} }
@ -126,25 +128,25 @@ func fillHeader(header *GenManHeader, name string) {
} }
} }
func manPreamble(out io.Writer, header *GenManHeader, cmd *cobra.Command, dashedName string) { func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, dashedName string) {
description := cmd.Long description := cmd.Long
if len(description) == 0 { if len(description) == 0 {
description = cmd.Short description = cmd.Short
} }
fmt.Fprintf(out, `%% %s(%s)%s buf.WriteString(fmt.Sprintf(`%% %s(%s)%s
%% %s %% %s
%% %s %% %s
# NAME # NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual) `, header.Title, header.Section, header.date, header.Source, header.Manual))
fmt.Fprintf(out, "%s \\- %s\n\n", dashedName, cmd.Short) buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
fmt.Fprintf(out, "# SYNOPSIS\n") buf.WriteString("# SYNOPSIS\n")
fmt.Fprintf(out, "**%s**\n\n", cmd.UseLine()) buf.WriteString(fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
fmt.Fprintf(out, "# DESCRIPTION\n") buf.WriteString("# DESCRIPTION\n")
fmt.Fprintf(out, "%s\n\n", description) buf.WriteString(description + "\n\n")
} }
func manPrintFlags(out io.Writer, flags *pflag.FlagSet) { func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden { if len(flag.Deprecated) > 0 || flag.Hidden {
return return
@ -156,38 +158,41 @@ func manPrintFlags(out io.Writer, flags *pflag.FlagSet) {
format = fmt.Sprintf("**--%s**", flag.Name) format = fmt.Sprintf("**--%s**", flag.Name)
} }
if len(flag.NoOptDefVal) > 0 { if len(flag.NoOptDefVal) > 0 {
format = format + "[" format += "["
} }
if flag.Value.Type() == "string" { if flag.Value.Type() == "string" {
// put quotes on the value // put quotes on the value
format = format + "=%q" format += "=%q"
} else { } else {
format = format + "=%s" format += "=%s"
} }
if len(flag.NoOptDefVal) > 0 { if len(flag.NoOptDefVal) > 0 {
format = format + "]" format += "]"
} }
format = format + "\n\t%s\n\n" format += "\n\t%s\n\n"
fmt.Fprintf(out, format, flag.DefValue, flag.Usage) buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage))
}) })
} }
func manPrintOptions(out io.Writer, command *cobra.Command) { func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) {
flags := command.NonInheritedFlags() flags := command.NonInheritedFlags()
if flags.HasFlags() { if flags.HasFlags() {
fmt.Fprintf(out, "# OPTIONS\n") buf.WriteString("# OPTIONS\n")
manPrintFlags(out, flags) manPrintFlags(buf, flags)
fmt.Fprintf(out, "\n") buf.WriteString("\n")
} }
flags = command.InheritedFlags() flags = command.InheritedFlags()
if flags.HasFlags() { if flags.HasFlags() {
fmt.Fprintf(out, "# OPTIONS INHERITED FROM PARENT COMMANDS\n") buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(out, flags) manPrintFlags(buf, flags)
fmt.Fprintf(out, "\n") buf.WriteString("\n")
} }
} }
func genMan(cmd *cobra.Command, header *GenManHeader) []byte { func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()
// something like `rootcmd-subcmd1-subcmd2` // something like `rootcmd-subcmd1-subcmd2`
dashCommandName := strings.Replace(cmd.CommandPath(), " ", "-", -1) dashCommandName := strings.Replace(cmd.CommandPath(), " ", "-", -1)
@ -196,11 +201,11 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
manPreamble(buf, header, cmd, dashCommandName) manPreamble(buf, header, cmd, dashCommandName)
manPrintOptions(buf, cmd) manPrintOptions(buf, cmd)
if len(cmd.Example) > 0 { if len(cmd.Example) > 0 {
fmt.Fprintf(buf, "# EXAMPLE\n") buf.WriteString("# EXAMPLE\n")
fmt.Fprintf(buf, "\n%s\n\n", cmd.Example) buf.WriteString(fmt.Sprintf("```\n%s\n```\n", cmd.Example))
} }
if hasSeeAlso(cmd) { if hasSeeAlso(cmd) {
fmt.Fprintf(buf, "# SEE ALSO\n") buf.WriteString("# SEE ALSO\n")
seealsos := make([]string, 0) seealsos := make([]string, 0)
if cmd.HasParent() { if cmd.HasParent() {
parentPath := cmd.Parent().CommandPath() parentPath := cmd.Parent().CommandPath()
@ -216,16 +221,16 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
children := cmd.Commands() children := cmd.Commands()
sort.Sort(byName(children)) sort.Sort(byName(children))
for _, c := range children { for _, c := range children {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section) seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section)
seealsos = append(seealsos, seealso) seealsos = append(seealsos, seealso)
} }
fmt.Fprintf(buf, "%s\n", strings.Join(seealsos, ", ")) buf.WriteString(strings.Join(seealsos, ", ") + "\n")
} }
if !cmd.DisableAutoGenTag { if !cmd.DisableAutoGenTag {
fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006")) buf.WriteString(fmt.Sprintf("# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006")))
} }
return buf.Bytes() return buf.Bytes()
} }

View File

@ -14,6 +14,7 @@
package doc package doc
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -25,38 +26,36 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func printOptions(w io.Writer, cmd *cobra.Command, name string) error { func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.SetOutput(w) flags.SetOutput(buf)
if flags.HasFlags() { if flags.HasFlags() {
if _, err := fmt.Fprintf(w, "### Options\n\n```\n"); err != nil { buf.WriteString("### Options\n\n```\n")
return err
}
flags.PrintDefaults() flags.PrintDefaults()
if _, err := fmt.Fprintf(w, "```\n\n"); err != nil { buf.WriteString("```\n\n")
return err
}
} }
parentFlags := cmd.InheritedFlags() parentFlags := cmd.InheritedFlags()
parentFlags.SetOutput(w) parentFlags.SetOutput(buf)
if parentFlags.HasFlags() { if parentFlags.HasFlags() {
if _, err := fmt.Fprintf(w, "### Options inherited from parent commands\n\n```\n"); err != nil { buf.WriteString("### Options inherited from parent commands\n\n```\n")
return err
}
parentFlags.PrintDefaults() parentFlags.PrintDefaults()
if _, err := fmt.Fprintf(w, "```\n\n"); err != nil { buf.WriteString("```\n\n")
return err
}
} }
return nil return nil
} }
// GenMarkdown creates markdown output.
func GenMarkdown(cmd *cobra.Command, w io.Writer) error { func GenMarkdown(cmd *cobra.Command, w io.Writer) error {
return GenMarkdownCustom(cmd, w, func(s string) string { return s }) return GenMarkdownCustom(cmd, w, func(s string) string { return s })
} }
// GenMarkdownCustom creates custom markdown output.
func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error { func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()
buf := new(bytes.Buffer)
name := cmd.CommandPath() name := cmd.CommandPath()
short := cmd.Short short := cmd.Short
@ -65,49 +64,31 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
long = short long = short
} }
if _, err := fmt.Fprintf(w, "## %s\n\n", name); err != nil { buf.WriteString("## " + name + "\n\n")
return err buf.WriteString(short + "\n\n")
} buf.WriteString("### Synopsis\n\n")
if _, err := fmt.Fprintf(w, "%s\n\n", short); err != nil { buf.WriteString("\n" + long + "\n\n")
return err
}
if _, err := fmt.Fprintf(w, "### Synopsis\n\n"); err != nil {
return err
}
if _, err := fmt.Fprintf(w, "\n%s\n\n", long); err != nil {
return err
}
if cmd.Runnable() { if cmd.Runnable() {
if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine()); err != nil { buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine()))
return err
}
} }
if len(cmd.Example) > 0 { if len(cmd.Example) > 0 {
if _, err := fmt.Fprintf(w, "### Examples\n\n"); err != nil { buf.WriteString("### Examples\n\n")
return err buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
}
if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.Example); err != nil {
return err
}
} }
if err := printOptions(w, cmd, name); err != nil { if err := printOptions(buf, cmd, name); err != nil {
return err return err
} }
if hasSeeAlso(cmd) { if hasSeeAlso(cmd) {
if _, err := fmt.Fprintf(w, "### SEE ALSO\n"); err != nil { buf.WriteString("### SEE ALSO\n")
return err
}
if cmd.HasParent() { if cmd.HasParent() {
parent := cmd.Parent() parent := cmd.Parent()
pname := parent.CommandPath() pname := parent.CommandPath()
link := pname + ".md" link := pname + ".md"
link = strings.Replace(link, " ", "_", -1) link = strings.Replace(link, " ", "_", -1)
if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short); err != nil { buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short))
return err
}
cmd.VisitParents(func(c *cobra.Command) { cmd.VisitParents(func(c *cobra.Command) {
if c.DisableAutoGenTag { if c.DisableAutoGenTag {
cmd.DisableAutoGenTag = c.DisableAutoGenTag cmd.DisableAutoGenTag = c.DisableAutoGenTag
@ -119,37 +100,40 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
sort.Sort(byName(children)) sort.Sort(byName(children))
for _, child := range children { for _, child := range children {
if !child.IsAvailableCommand() || child.IsHelpCommand() { if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue continue
} }
cname := name + " " + child.Name() cname := name + " " + child.Name()
link := cname + ".md" link := cname + ".md"
link = strings.Replace(link, " ", "_", -1) link = strings.Replace(link, " ", "_", -1)
if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short); err != nil { buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short))
return err
}
}
if _, err := fmt.Fprintf(w, "\n"); err != nil {
return err
} }
buf.WriteString("\n")
} }
if !cmd.DisableAutoGenTag { if !cmd.DisableAutoGenTag {
if _, err := fmt.Fprintf(w, "###### Auto generated by spf13/cobra on %s\n", time.Now().Format("2-Jan-2006")); err != nil { buf.WriteString("###### Auto generated by spf13/cobra on " + time.Now().Format("2-Jan-2006") + "\n")
}
_, err := buf.WriteTo(w)
return err return err
} }
}
return nil
}
// GenMarkdownTree will generate a markdown page for this command and all
// descendants in the directory given. The header may be nil.
// This function may not work correctly if your command names have `-` in them.
// If you have `cmd` with two subcmds, `sub` and `sub-third`,
// and `sub` has a subcommand called `third`, it is undefined which
// help output will be in the file `cmd-sub-third.1`.
func GenMarkdownTree(cmd *cobra.Command, dir string) error { func GenMarkdownTree(cmd *cobra.Command, dir string) error {
identity := func(s string) string { return s } identity := func(s string) string { return s }
emptyStr := func(s string) string { return "" } emptyStr := func(s string) string { return "" }
return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity) return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
} }
// GenMarkdownTreeCustom is the the same as GenMarkdownTree, but
// with custom filePrepender and linkHandler.
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil { if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil {

185
vendor/github.com/spf13/cobra/doc/rest_docs.go generated vendored Normal file
View File

@ -0,0 +1,185 @@
//Copyright 2015 Red Hat Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doc
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/spf13/cobra"
)
func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
flags := cmd.NonInheritedFlags()
flags.SetOutput(buf)
if flags.HasFlags() {
buf.WriteString("Options\n")
buf.WriteString("~~~~~~~\n\n::\n\n")
flags.PrintDefaults()
buf.WriteString("\n")
}
parentFlags := cmd.InheritedFlags()
parentFlags.SetOutput(buf)
if parentFlags.HasFlags() {
buf.WriteString("Options inherited from parent commands\n")
buf.WriteString("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n")
parentFlags.PrintDefaults()
buf.WriteString("\n")
}
return nil
}
// linkHandler for default ReST hyperlink markup
func defaultLinkHandler(name, ref string) string {
return fmt.Sprintf("`%s <%s.rst>`_", name, ref)
}
// GenReST creates reStructured Text output.
func GenReST(cmd *cobra.Command, w io.Writer) error {
return GenReSTCustom(cmd, w, defaultLinkHandler)
}
// GenReSTCustom creates custom reStructured Text output.
func GenReSTCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string, string) string) error {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()
buf := new(bytes.Buffer)
name := cmd.CommandPath()
short := cmd.Short
long := cmd.Long
if len(long) == 0 {
long = short
}
ref := strings.Replace(name, " ", "_", -1)
buf.WriteString(".. _" + ref + ":\n\n")
buf.WriteString(name + "\n")
buf.WriteString(strings.Repeat("-", len(name)) + "\n\n")
buf.WriteString(short + "\n\n")
buf.WriteString("Synopsis\n")
buf.WriteString("~~~~~~~~\n\n")
buf.WriteString("\n" + long + "\n\n")
if cmd.Runnable() {
buf.WriteString(fmt.Sprintf("::\n\n %s\n\n", cmd.UseLine()))
}
if len(cmd.Example) > 0 {
buf.WriteString("Examples\n")
buf.WriteString("~~~~~~~~\n\n")
buf.WriteString(fmt.Sprintf("::\n\n%s\n\n", indentString(cmd.Example, " ")))
}
if err := printOptionsReST(buf, cmd, name); err != nil {
return err
}
if hasSeeAlso(cmd) {
buf.WriteString("SEE ALSO\n")
buf.WriteString("~~~~~~~~\n\n")
if cmd.HasParent() {
parent := cmd.Parent()
pname := parent.CommandPath()
ref = strings.Replace(pname, " ", "_", -1)
buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(pname, ref), parent.Short))
cmd.VisitParents(func(c *cobra.Command) {
if c.DisableAutoGenTag {
cmd.DisableAutoGenTag = c.DisableAutoGenTag
}
})
}
children := cmd.Commands()
sort.Sort(byName(children))
for _, child := range children {
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
cname := name + " " + child.Name()
ref = strings.Replace(cname, " ", "_", -1)
buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(cname, ref), child.Short))
}
buf.WriteString("\n")
}
if !cmd.DisableAutoGenTag {
buf.WriteString("*Auto generated by spf13/cobra on " + time.Now().Format("2-Jan-2006") + "*\n")
}
_, err := buf.WriteTo(w)
return err
}
// GenReSTTree will generate a ReST page for this command and all
// descendants in the directory given.
// This function may not work correctly if your command names have `-` in them.
// If you have `cmd` with two subcmds, `sub` and `sub-third`,
// and `sub` has a subcommand called `third`, it is undefined which
// help output will be in the file `cmd-sub-third.1`.
func GenReSTTree(cmd *cobra.Command, dir string) error {
emptyStr := func(s string) string { return "" }
return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler)
}
// GenReSTTreeCustom is the the same as GenReSTTree, but
// with custom filePrepender and linkHandler.
func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error {
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
if err := GenReSTTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
return err
}
}
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".rst"
filename := filepath.Join(dir, basename)
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
return err
}
if err := GenReSTCustom(cmd, f, linkHandler); err != nil {
return err
}
return nil
}
// adapted from: https://github.com/kr/text/blob/main/indent.go
func indentString(s, p string) string {
var res []byte
b := []byte(s)
prefix := []byte(p)
bol := true
for _, c := range b {
if bol && c != '\n' {
res = append(res, prefix...)
}
res = append(res, c)
bol = c == '\n'
}
return string(res)
}

View File

@ -13,7 +13,11 @@
package doc package doc
import "github.com/spf13/cobra" import (
"strings"
"github.com/spf13/cobra"
)
// Test to see if we have a reason to print See Also information in docs // Test to see if we have a reason to print See Also information in docs
// Basically this is a test for a parent commend or a subcommand which is // Basically this is a test for a parent commend or a subcommand which is
@ -23,7 +27,7 @@ func hasSeeAlso(cmd *cobra.Command) bool {
return true return true
} }
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
return true return true
@ -31,6 +35,15 @@ func hasSeeAlso(cmd *cobra.Command) bool {
return false return false
} }
// Temporary workaround for yaml lib generating incorrect yaml with long strings
// that do not contain \n.
func forceMultiLine(s string) string {
if len(s) > 60 && !strings.Contains(s, "\n") {
s = s + "\n"
}
return s
}
type byName []*cobra.Command type byName []*cobra.Command
func (s byName) Len() int { return len(s) } func (s byName) Len() int { return len(s) }

169
vendor/github.com/spf13/cobra/doc/yaml_docs.go generated vendored Normal file
View File

@ -0,0 +1,169 @@
// Copyright 2016 French Ben. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doc
import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
)
type cmdOption struct {
Name string
Shorthand string `yaml:",omitempty"`
DefaultValue string `yaml:"default_value,omitempty"`
Usage string `yaml:",omitempty"`
}
type cmdDoc struct {
Name string
Synopsis string `yaml:",omitempty"`
Description string `yaml:",omitempty"`
Options []cmdOption `yaml:",omitempty"`
InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"`
Example string `yaml:",omitempty"`
SeeAlso []string `yaml:"see_also,omitempty"`
}
// GenYamlTree creates yaml structured ref files for this command and all descendants
// in the directory given. This function may not work
// correctly if your command names have `-` in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`, and `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`.
func GenYamlTree(cmd *cobra.Command, dir string) error {
identity := func(s string) string { return s }
emptyStr := func(s string) string { return "" }
return GenYamlTreeCustom(cmd, dir, emptyStr, identity)
}
// GenYamlTreeCustom creates yaml structured ref files.
func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
if err := GenYamlTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
return err
}
}
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".yaml"
filename := filepath.Join(dir, basename)
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
return err
}
if err := GenYamlCustom(cmd, f, linkHandler); err != nil {
return err
}
return nil
}
// GenYaml creates yaml output.
func GenYaml(cmd *cobra.Command, w io.Writer) error {
return GenYamlCustom(cmd, w, func(s string) string { return s })
}
// GenYamlCustom creates custom yaml output.
func GenYamlCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()
yamlDoc := cmdDoc{}
yamlDoc.Name = cmd.CommandPath()
yamlDoc.Synopsis = forceMultiLine(cmd.Short)
yamlDoc.Description = forceMultiLine(cmd.Long)
if len(cmd.Example) > 0 {
yamlDoc.Example = cmd.Example
}
flags := cmd.NonInheritedFlags()
if flags.HasFlags() {
yamlDoc.Options = genFlagResult(flags)
}
flags = cmd.InheritedFlags()
if flags.HasFlags() {
yamlDoc.InheritedOptions = genFlagResult(flags)
}
if hasSeeAlso(cmd) {
result := []string{}
if cmd.HasParent() {
parent := cmd.Parent()
result = append(result, parent.CommandPath()+" - "+parent.Short)
}
children := cmd.Commands()
sort.Sort(byName(children))
for _, child := range children {
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
result = append(result, child.Name()+" - "+child.Short)
}
yamlDoc.SeeAlso = result
}
final, err := yaml.Marshal(&yamlDoc)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if _, err := w.Write(final); err != nil {
return err
}
return nil
}
func genFlagResult(flags *pflag.FlagSet) []cmdOption {
var result []cmdOption
flags.VisitAll(func(flag *pflag.Flag) {
// Todo, when we mark a shorthand is deprecated, but specify an empty message.
// The flag.ShorthandDeprecated is empty as the shorthand is deprecated.
// Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok.
if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 {
opt := cmdOption{
flag.Name,
flag.Shorthand,
flag.DefValue,
forceMultiLine(flag.Usage),
}
result = append(result, opt)
} else {
opt := cmdOption{
Name: flag.Name,
DefaultValue: forceMultiLine(flag.DefValue),
Usage: forceMultiLine(flag.Usage),
}
result = append(result, opt)
}
})
return result
}

126
vendor/github.com/spf13/cobra/zsh_completions.go generated vendored Normal file
View File

@ -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
}

View File

@ -246,6 +246,25 @@ It is possible to mark a flag as hidden, meaning it will still function as norma
flags.MarkHidden("secretFlag") 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 ## 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 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`). 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 You can see the full reference documentation of the pflag package
[at godoc.org][3], or through go's standard documentation system by [at godoc.org][3], or through go's standard documentation system by
running `godoc -http=:6060` and browsing to 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. installation.
[2]: http://localhost:6060/pkg/github.com/ogier/pflag [2]: http://localhost:6060/pkg/github.com/spf13/pflag
[3]: http://godoc.org/github.com/ogier/pflag [3]: http://godoc.org/github.com/spf13/pflag

View File

@ -11,13 +11,13 @@ func newCountValue(val int, p *int) *countValue {
} }
func (i *countValue) Set(s string) error { func (i *countValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64) // "+1" means that no specific value was passed, so increment
// -1 means that no specific value was passed, so increment if s == "+1" {
if v == -1 {
*i = countValue(*i + 1) *i = countValue(*i + 1)
} else { return nil
*i = countValue(v)
} }
v, err := strconv.ParseInt(s, 0, 0)
*i = countValue(v)
return err 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. // CountVarP is like CountVar only take a shorthand for the flag name.
func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) {
flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) 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 // 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 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 { func Count(name string, usage string) *int {
return CommandLine.CountP(name, "", usage) return CommandLine.CountP(name, "", usage)
} }

266
vendor/github.com/spf13/pflag/flag.go generated vendored
View File

@ -16,7 +16,7 @@ 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 pflag under the name "flag" then all code should continue to function
with no changes. 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. there is one more field "Shorthand" that you will need to set.
@ -134,14 +134,21 @@ type FlagSet struct {
// a custom error handler. // a custom error handler.
Usage func() Usage func()
// SortFlags is used to indicate, if user wants to have sorted flags in
// help/usage messages.
SortFlags bool
name string name string
parsed bool parsed bool
actual map[NormalizedName]*Flag actual map[NormalizedName]*Flag
orderedActual []*Flag
sortedActual []*Flag
formal map[NormalizedName]*Flag formal map[NormalizedName]*Flag
orderedFormal []*Flag
sortedFormal []*Flag
shorthands map[byte]*Flag shorthands map[byte]*Flag
args []string // arguments after flags args []string // arguments after flags
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- 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 errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor output io.Writer // nil means stderr; use out() accessor
interspersed bool // allow interspersed option/non-option args interspersed bool // allow interspersed option/non-option args
@ -194,11 +201,19 @@ func sortFlags(flags map[NormalizedName]*Flag) []*Flag {
// "--getUrl" which may also be translated to "geturl" and everything will work. // "--getUrl" which may also be translated to "geturl" and everything will work.
func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) {
f.normalizeNameFunc = n f.normalizeNameFunc = n
for k, v := range f.formal { f.sortedFormal = f.sortedFormal[:0]
delete(f.formal, k) for fname, flag := range f.formal {
nname := f.normalizeFlagName(string(k)) nname := f.normalizeFlagName(flag.Name)
f.formal[nname] = v if fname == nname {
v.Name = string(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,10 +244,25 @@ func (f *FlagSet) SetOutput(output io.Writer) {
f.output = output 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. // It visits all flags, even those not set.
func (f *FlagSet) VisitAll(fn func(*Flag)) { 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) fn(flag)
} }
} }
@ -253,22 +283,39 @@ func (f *FlagSet) HasAvailableFlags() bool {
return false return false
} }
// VisitAll visits the command-line flags in lexicographical order, calling // VisitAll visits the command-line flags in lexicographical order or
// fn for each. It visits all flags, even those not set. // 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)) { func VisitAll(fn func(*Flag)) {
CommandLine.VisitAll(fn) 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. // It visits only those flags that have been set.
func (f *FlagSet) Visit(fn func(*Flag)) { 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) fn(flag)
} }
} }
// Visit visits the command-line flags in lexicographical order, calling fn // Visit visits the command-line flags in lexicographical order or
// for each. It visits only those flags that have been set. // 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)) { func Visit(fn func(*Flag)) {
CommandLine.Visit(fn) CommandLine.Visit(fn)
} }
@ -278,6 +325,22 @@ func (f *FlagSet) Lookup(name string) *Flag {
return f.lookup(f.normalizeFlagName(name)) 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. // lookup returns the Flag structure of the named flag, returning nil if none exists.
func (f *FlagSet) lookup(name NormalizedName) *Flag { func (f *FlagSet) lookup(name NormalizedName) *Flag {
return f.formal[name] return f.formal[name]
@ -319,7 +382,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
if flag == nil { if flag == nil {
return fmt.Errorf("flag %q does not exist", name) 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) return fmt.Errorf("deprecated message for flag %q must be set", name)
} }
flag.Deprecated = usageMessage flag.Deprecated = usageMessage
@ -334,7 +397,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro
if flag == nil { if flag == nil {
return fmt.Errorf("flag %q does not exist", name) 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) return fmt.Errorf("deprecated message for flag %q must be set", name)
} }
flag.ShorthandDeprecated = usageMessage flag.ShorthandDeprecated = usageMessage
@ -358,6 +421,12 @@ func Lookup(name string) *Flag {
return CommandLine.Lookup(name) 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. // Set sets the value of the named flag.
func (f *FlagSet) Set(name, value string) error { func (f *FlagSet) Set(name, value string) error {
normalName := f.normalizeFlagName(name) normalName := f.normalizeFlagName(name)
@ -365,17 +434,30 @@ func (f *FlagSet) Set(name, value string) error {
if !ok { if !ok {
return fmt.Errorf("no such flag -%v", name) return fmt.Errorf("no such flag -%v", name)
} }
err := flag.Value.Set(value) err := flag.Value.Set(value)
if err != nil { 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 !flag.Changed {
if f.actual == nil { if f.actual == nil {
f.actual = make(map[NormalizedName]*Flag) f.actual = make(map[NormalizedName]*Flag)
} }
f.actual[normalName] = flag f.actual[normalName] = flag
f.orderedActual = append(f.orderedActual, flag)
flag.Changed = true 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 return nil
} }
@ -482,6 +564,10 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
name = "int" name = "int"
case "uint64": case "uint64":
name = "uint" name = "uint"
case "stringSlice":
name = "strings"
case "intSlice":
name = "ints"
} }
return return
@ -557,28 +643,28 @@ func wrap(i, w int, s string) string {
// for all flags in the FlagSet. Wrapped to `cols` columns (0 for no // for all flags in the FlagSet. Wrapped to `cols` columns (0 for no
// wrapping) // wrapping)
func (f *FlagSet) FlagUsagesWrapped(cols int) string { func (f *FlagSet) FlagUsagesWrapped(cols int) string {
x := new(bytes.Buffer) buf := new(bytes.Buffer)
lines := make([]string, 0, len(f.formal)) lines := make([]string, 0, len(f.formal))
maxlen := 0 maxlen := 0
f.VisitAll(func(flag *Flag) { f.VisitAll(func(flag *Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden { if flag.Deprecated != "" || flag.Hidden {
return return
} }
line := "" line := ""
if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 { if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
} else { } else {
line = fmt.Sprintf(" --%s", flag.Name) line = fmt.Sprintf(" --%s", flag.Name)
} }
varname, usage := UnquoteUsage(flag) varname, usage := UnquoteUsage(flag)
if len(varname) > 0 { if varname != "" {
line += " " + varname line += " " + varname
} }
if len(flag.NoOptDefVal) > 0 { if flag.NoOptDefVal != "" {
switch flag.Value.Type() { switch flag.Value.Type() {
case "string": case "string":
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
@ -586,6 +672,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
if flag.NoOptDefVal != "true" { if flag.NoOptDefVal != "true" {
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
} }
case "count":
if flag.NoOptDefVal != "+1" {
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
}
default: default:
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
} }
@ -601,7 +691,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
line += usage line += usage
if !flag.defaultIsZeroValue() { if !flag.defaultIsZeroValue() {
if flag.Value.Type() == "string" { if flag.Value.Type() == "string" {
line += fmt.Sprintf(" (default \"%s\")", flag.DefValue) line += fmt.Sprintf(" (default %q)", flag.DefValue)
} else { } else {
line += fmt.Sprintf(" (default %s)", flag.DefValue) line += fmt.Sprintf(" (default %s)", flag.DefValue)
} }
@ -614,10 +704,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
sidx := strings.Index(line, "\x00") sidx := strings.Index(line, "\x00")
spacing := strings.Repeat(" ", maxlen-sidx) spacing := strings.Repeat(" ", maxlen-sidx)
// maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in 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 // FlagUsages returns a string containing the usage information for all flags in
@ -714,11 +804,10 @@ func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
// AddFlag will add the flag to the FlagSet // AddFlag will add the flag to the FlagSet
func (f *FlagSet) AddFlag(flag *Flag) { func (f *FlagSet) AddFlag(flag *Flag) {
// Call normalizeFlagName function only once
normalizedFlagName := f.normalizeFlagName(flag.Name) normalizedFlagName := f.normalizeFlagName(flag.Name)
_, alreadythere := f.formal[normalizedFlagName] _, alreadyThere := f.formal[normalizedFlagName]
if alreadythere { if alreadyThere {
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
fmt.Fprintln(f.out(), msg) fmt.Fprintln(f.out(), msg)
panic(msg) // Happens only if flags are declared with identical names panic(msg) // Happens only if flags are declared with identical names
@ -729,28 +818,31 @@ func (f *FlagSet) AddFlag(flag *Flag) {
flag.Name = string(normalizedFlagName) flag.Name = string(normalizedFlagName)
f.formal[normalizedFlagName] = flag f.formal[normalizedFlagName] = flag
f.orderedFormal = append(f.orderedFormal, flag)
if len(flag.Shorthand) == 0 { if flag.Shorthand == "" {
return return
} }
if len(flag.Shorthand) > 1 { if len(flag.Shorthand) > 1 {
fmt.Fprintf(f.out(), "%s shorthand more than ASCII character: %s\n", f.name, flag.Shorthand) msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
panic("shorthand is more than one character") fmt.Fprintf(f.out(), msg)
panic(msg)
} }
if f.shorthands == nil { if f.shorthands == nil {
f.shorthands = make(map[byte]*Flag) f.shorthands = make(map[byte]*Flag)
} }
c := flag.Shorthand[0] c := flag.Shorthand[0]
old, alreadythere := f.shorthands[c] used, alreadyThere := f.shorthands[c]
if alreadythere { if alreadyThere {
fmt.Fprintf(f.out(), "%s shorthand reused: %q for %s already used for %s\n", f.name, c, flag.Name, old.Name) msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
panic("shorthand redefinition") fmt.Fprintf(f.out(), msg)
panic(msg)
} }
f.shorthands[c] = flag f.shorthands[c] = flag
} }
// AddFlagSet adds one FlagSet to another. If a flag is already present in f // 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) { func (f *FlagSet) AddFlagSet(newSet *FlagSet) {
if newSet == nil { if newSet == nil {
return return
@ -781,8 +873,10 @@ func VarP(value Value, name, shorthand, usage string) {
// returns the error. // returns the error.
func (f *FlagSet) failf(format string, a ...interface{}) error { func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...) err := fmt.Errorf(format, a...)
if f.errorHandling != ContinueOnError {
fmt.Fprintln(f.out(), err) fmt.Fprintln(f.out(), err)
f.usage() f.usage()
}
return err return err
} }
@ -798,34 +892,6 @@ 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)
}
// 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 --<flag_name>
if strings.HasPrefix(arg, "-") {
return false
}
arg = strings.SplitN(arg, "=", 2)[0]
return strings.Contains(arg, shorthand)
}
func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) { func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
a = args a = args
name := s[2:] name := s[2:]
@ -833,10 +899,11 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
err = f.failf("bad flag syntax: %s", s) err = f.failf("bad flag syntax: %s", s)
return return
} }
split := strings.SplitN(name, "=", 2) split := strings.SplitN(name, "=", 2)
name = split[0] name = split[0]
flag, alreadythere := f.formal[f.normalizeFlagName(name)] flag, exists := f.formal[f.normalizeFlagName(name)]
if !alreadythere { if !exists {
if name == "help" { // special case for nice help message. if name == "help" { // special case for nice help message.
f.usage() f.usage()
return a, ErrHelp return a, ErrHelp
@ -844,11 +911,12 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
err = f.failf("unknown flag: --%s", name) err = f.failf("unknown flag: --%s", name)
return return
} }
var value string var value string
if len(split) == 2 { if len(split) == 2 {
// '--flag=arg' // '--flag=arg'
value = split[1] value = split[1]
} else if len(flag.NoOptDefVal) > 0 { } else if flag.NoOptDefVal != "" {
// '--flag' (arg was optional) // '--flag' (arg was optional)
value = flag.NoOptDefVal value = flag.NoOptDefVal
} else if len(a) > 0 { } else if len(a) > 0 {
@ -860,7 +928,11 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
err = f.failf("flag needs an argument: %s", s) err = f.failf("flag needs an argument: %s", s)
return return
} }
err = fn(flag, value, s)
err = fn(flag, value)
if err != nil {
f.failf(err.Error())
}
return return
} }
@ -868,38 +940,52 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
if strings.HasPrefix(shorthands, "test.") { if strings.HasPrefix(shorthands, "test.") {
return return
} }
outArgs = args outArgs = args
outShorts = shorthands[1:] outShorts = shorthands[1:]
c := shorthands[0] c := shorthands[0]
flag, alreadythere := f.shorthands[c] flag, exists := f.shorthands[c]
if !alreadythere { if !exists {
if c == 'h' { // special case for nice help message. if c == 'h' { // special case for nice help message.
f.usage() f.usage()
err = ErrHelp err = ErrHelp
return return
} }
//TODO continue on error
err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
return return
} }
var value string var value string
if len(shorthands) > 2 && shorthands[1] == '=' { if len(shorthands) > 2 && shorthands[1] == '=' {
// '-f=arg'
value = shorthands[2:] value = shorthands[2:]
outShorts = "" outShorts = ""
} else if len(flag.NoOptDefVal) > 0 { } else if flag.NoOptDefVal != "" {
// '-f' (arg was optional)
value = flag.NoOptDefVal value = flag.NoOptDefVal
} else if len(shorthands) > 1 { } else if len(shorthands) > 1 {
// '-farg'
value = shorthands[1:] value = shorthands[1:]
outShorts = "" outShorts = ""
} else if len(args) > 0 { } else if len(args) > 0 {
// '-f arg'
value = args[0] value = args[0]
outArgs = args[1:] outArgs = args[1:]
} else { } else {
// '-f' (arg was required)
err = f.failf("flag needs an argument: %q in -%s", c, shorthands) err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
return 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 return
} }
@ -907,6 +993,7 @@ func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []stri
a = args a = args
shorthands := s[1:] shorthands := s[1:]
// "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv").
for len(shorthands) > 0 { for len(shorthands) > 0 {
shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn) shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn)
if err != nil { if err != nil {
@ -954,18 +1041,24 @@ func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
// The return value will be ErrHelp if -help was set but not defined. // The return value will be ErrHelp if -help was set but not defined.
func (f *FlagSet) Parse(arguments []string) error { func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true f.parsed = true
f.args = make([]string, 0, len(arguments))
assign := func(flag *Flag, value, origArg string) error { if len(arguments) < 0 {
return f.setFlag(flag, value, origArg) 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 { if err != nil {
switch f.errorHandling { switch f.errorHandling {
case ContinueOnError: case ContinueOnError:
return err return err
case ExitOnError: case ExitOnError:
fmt.Println(err)
os.Exit(2) os.Exit(2)
case PanicOnError: case PanicOnError:
panic(err) panic(err)
@ -974,7 +1067,7 @@ func (f *FlagSet) Parse(arguments []string) error {
return nil 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 // 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 // include the command name. The arguments for fn are flag and value. Must be
@ -985,11 +1078,7 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string)
f.parsed = true f.parsed = true
f.args = make([]string, 0, len(arguments)) f.args = make([]string, 0, len(arguments))
assign := func(flag *Flag, value, origArg string) error { err := f.parseArgs(arguments, fn)
return fn(flag, value)
}
err := f.parseArgs(arguments, assign)
if err != nil { if err != nil {
switch f.errorHandling { switch f.errorHandling {
case ContinueOnError: case ContinueOnError:
@ -1036,14 +1125,15 @@ func Parsed() bool {
// CommandLine is the default set of command-line flags, parsed from os.Args. // CommandLine is the default set of command-line flags, parsed from os.Args.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError) var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
// NewFlagSet returns a new, empty flag set with the specified name and // NewFlagSet returns a new, empty flag set with the specified name,
// error handling property. // error handling property and SortFlags set to true.
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
f := &FlagSet{ f := &FlagSet{
name: name, name: name,
errorHandling: errorHandling, errorHandling: errorHandling,
argsLenAtDash: -1, argsLenAtDash: -1,
interspersed: true, interspersed: true,
SortFlags: true,
} }
return f return f
} }

88
vendor/github.com/spf13/pflag/int16.go generated vendored Normal file
View File

@ -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)
}