Skip to content

Commit

Permalink
Update the repository with the latest commits from mitchellh/cli
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Sokolovsky committed Apr 8, 2020
1 parent c25d734 commit 081d372
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 104 deletions.
10 changes: 7 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ sudo: false

language: go

env:
- GO111MODULE=on

go:
- "1.8"
- "1.9"
- "1.10"
- "1.11"
- "1.12"
- "1.13"
- "1.14"

branches:
only:
Expand Down
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ testrace:

# updatedeps installs all the dependencies to run and build
updatedeps:
go list ./... \
| xargs go list -f '{{ join .Deps "\n" }}{{ printf "\n" }}{{ join .TestImports "\n" }}' \
| grep -v github.com/mitchellh/cli \
| xargs go get -f -u -v
go mod download

.PHONY: test testrace updatedeps
110 changes: 43 additions & 67 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package cli
import (
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"sort"
"strings"
"sync"
"text/tabwriter"
"text/template"

"github.com/armon/go-radix"
Expand Down Expand Up @@ -110,18 +110,23 @@ type CLI struct {
AutocompleteGlobalFlags complete.Flags
autocompleteInstaller autocompleteInstaller // For tests

// HelpFunc and HelpWriter are used to output help information, if
// requested.
//
// HelpFunc is the function called to generate the generic help
// text that is shown if help must be shown for the CLI that doesn't
// pertain to a specific command.
//
// HelpWriter is the Writer where the help text is outputted to. If
// not specified, it will default to Stderr.
HelpFunc HelpFunc
HelpFunc HelpFunc

// HelpWriter is used to print help text and version when requested.
// Defaults to os.Stderr for backwards compatibility.
// It is recommended that you set HelpWriter to os.Stdout, and
// ErrorWriter to os.Stderr.
HelpWriter io.Writer

// ErrorWriter used to output errors when a command can not be run.
// Defaults to the value of HelpWriter for backwards compatibility.
// It is recommended that you set HelpWriter to os.Stdout, and
// ErrorWriter to os.Stderr.
ErrorWriter io.Writer

//---------------------------------------------------------------
// Internal fields set automatically

Expand Down Expand Up @@ -229,7 +234,7 @@ func (c *CLI) Run() (int, error) {
// implementation. If the command is invalid or blank, it is an error.
raw, ok := c.commandTree.Get(c.Subcommand())
if !ok {
c.HelpWriter.Write([]byte(c.HelpFunc(c.helpCommands(c.subcommandParent())) + "\n"))
c.ErrorWriter.Write([]byte(c.HelpFunc(c.helpCommands(c.subcommandParent())) + "\n"))
return 127, nil
}

Expand All @@ -240,23 +245,23 @@ func (c *CLI) Run() (int, error) {

// If we've been instructed to just print the help, then print it
if c.IsHelp() {
c.commandHelp(command)
c.commandHelp(c.HelpWriter, command)
return 0, nil
}

// If there is an invalid flag, then error
if len(c.topFlags) > 0 {
c.HelpWriter.Write([]byte(
c.ErrorWriter.Write([]byte(
"Invalid flags before the subcommand. If these flags are for\n" +
"the subcommand, please put them after the subcommand.\n\n"))
c.commandHelp(command)
c.commandHelp(c.ErrorWriter, command)
return 1, nil
}

code := command.Run(c.SubcommandArgs())
if code == RunResultHelp {
// Requesting help
c.commandHelp(command)
c.commandHelp(c.ErrorWriter, command)
return 1, nil
}

Expand Down Expand Up @@ -311,6 +316,9 @@ func (c *CLI) init() {
if c.HelpWriter == nil {
c.HelpWriter = os.Stderr
}
if c.ErrorWriter == nil {
c.ErrorWriter = c.HelpWriter
}

// Build our hidden commands
if len(c.HiddenCommands) > 0 {
Expand Down Expand Up @@ -396,6 +404,16 @@ func (c *CLI) initAutocomplete() {
c.autocompleteInstaller = &realAutocompleteInstaller{}
}

// We first set c.autocomplete to a noop autocompleter that outputs
// to nul so that we can detect if we're autocompleting or not. If we're
// not, then we do nothing. This saves a LOT of compute cycles since
// initAutoCompleteSub has to walk every command.
c.autocomplete = complete.New(c.Name, complete.Command{})
c.autocomplete.Out = ioutil.Discard
if !c.autocomplete.Complete() {
return
}

// Build the root command
cmd := c.initAutocompleteSub("")

Expand All @@ -405,8 +423,8 @@ func (c *CLI) initAutocomplete() {
cmd.Flags = map[string]complete.Predictor{
"-" + c.AutocompleteInstall: complete.PredictNothing,
"-" + c.AutocompleteUninstall: complete.PredictNothing,
"-help": complete.PredictNothing,
"-version": complete.PredictNothing,
"-help": complete.PredictNothing,
"-version": complete.PredictNothing,
}
}
cmd.GlobalFlags = c.AutocompleteGlobalFlags
Expand Down Expand Up @@ -484,9 +502,9 @@ func (c *CLI) initAutocompleteSub(prefix string) complete.Command {
return cmd
}

func (c *CLI) commandHelp(command Command) {
func (c *CLI) commandHelp(out io.Writer, command Command) {
// Get the template to use
tpl := defaultHelpTemplate
tpl := strings.TrimSpace(defaultHelpTemplate)
if t, ok := command.(CommandHelpTemplate); ok {
tpl = t.HelpTemplate()
}
Expand All @@ -501,35 +519,10 @@ func (c *CLI) commandHelp(command Command) {
"Internal error! Failed to parse command help template: %s\n", err)))
}

flags := []*FlagWrapper{}
flagsGlobal := []*FlagWrapper{}
if tAutocomplete, ok := command.(CommandAutocomplete); ok {
for _, pw := range tAutocomplete.GetPredictorWrappers() {
if pw.Flag.Hidden {
continue
}
if pw.Flag.Global {
flagsGlobal = append(flagsGlobal, pw.Flag)
} else {
flags = append(flags, pw.Flag)
}
}
}
sort.Slice(flagsGlobal, func(i, j int) bool {
return flagsGlobal[i].Name < flagsGlobal[j].Name
})
sort.Slice(flags, func(i, j int) bool {
return flags[i].Name < flags[j].Name
})

// Template data
data := map[string]interface{}{
"Name": c.Name,
"Synopsis": command.Synopsis(),
"Cmd": c.Subcommand(),
"Help": command.Help(),
"Flags": flags,
"FlagsGlobal": flagsGlobal,
"Name": c.Name,
"Help": command.Help(),
}

// Build subcommand list if we have it
Expand Down Expand Up @@ -559,12 +552,12 @@ func (c *CLI) commandHelp(command Command) {
// Get the command
raw, ok := subcommands[k]
if !ok {
c.HelpWriter.Write([]byte(fmt.Sprintf(
c.ErrorWriter.Write([]byte(fmt.Sprintf(
"Error getting subcommand %q", k)))
}
sub, err := raw()
if err != nil {
c.HelpWriter.Write([]byte(fmt.Sprintf(
c.ErrorWriter.Write([]byte(fmt.Sprintf(
"Error instantiating %q: %s", k, err)))
}

Expand All @@ -585,17 +578,13 @@ func (c *CLI) commandHelp(command Command) {
data["Subcommands"] = subcommandsTpl

// Write
w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0)
if err = t.Execute(w, data); err == nil {
err = w.Flush()
}
// err = t.Execute(c.HelpWriter, data)
err = t.Execute(out, data)
if err == nil {
return
}

// An error, just output...
c.HelpWriter.Write([]byte(fmt.Sprintf(
c.ErrorWriter.Write([]byte(fmt.Sprintf(
"Internal error rendering help: %s", err)))
}

Expand Down Expand Up @@ -741,23 +730,10 @@ const defaultAutocompleteInstall = "autocomplete-install"
const defaultAutocompleteUninstall = "autocomplete-uninstall"

const defaultHelpTemplate = `
Cmd: {{.Cmd}} {{.Synopsis}}{{if ne .Help ""}}
{{.Help}}{{if gt (len .Subcommands) 0}}
{{.Help}}{{ end}}
{{if gt (len .Flags) 0}}
Flags:
{{- range $value := .Flags }}
--{{ $value.FriendlyName }}{{if ne $value.Shorthand ""}}, -{{$value.Shorthand}}{{end}} {{ $value.Usage }}{{ end }}
{{- end }}
{{if gt (len .FlagsGlobal) 0}}
Global:
{{- range $value := .FlagsGlobal }}
--{{ $value.FriendlyName }}{{if ne $value.Shorthand ""}}, -{{$value.Shorthand}}{{end}} {{ $value.Usage }}{{ end }}
{{- end }}
{{if gt (len .Subcommands) 0}}
Subcommands:
{{- range $value := .Subcommands }}
{{ $value.NameAligned }} {{ $value.Synopsis }}{{ end }}
{{ $value.NameAligned }} {{ $value.Synopsis }}{{ end }}
{{- end }}
`
16 changes: 10 additions & 6 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestCLIRun_prefix(t *testing.T) {
return command, nil
},
},
HelpWriter: buf,
ErrorWriter: buf,
}

exitCode, err := cli.Run()
Expand Down Expand Up @@ -257,7 +257,7 @@ func TestCLIRun_helpNested(t *testing.T) {

return ""
},
HelpWriter: buf,
ErrorWriter: buf,
}

code, err := cli.Run()
Expand Down Expand Up @@ -347,7 +347,7 @@ func TestCLIRun_nestedMissingParent(t *testing.T) {
return &MockCommand{SynopsisText: "hi!"}, nil
},
},
HelpWriter: buf,
ErrorWriter: buf,
}

exitCode, err := cli.Run()
Expand Down Expand Up @@ -530,7 +530,7 @@ func TestCLIRun_printHelpIllegal(t *testing.T) {

return helpText
},
HelpWriter: buf,
ErrorWriter: buf,
}

code, err := cli.Run()
Expand Down Expand Up @@ -849,7 +849,7 @@ func TestCLIRun_helpHiddenRoot(t *testing.T) {

return ""
},
HelpWriter: buf,
ErrorWriter: buf,
}

code, err := cli.Run()
Expand Down Expand Up @@ -1097,7 +1097,7 @@ func TestCLIRun_autocompleteHelpTab(t *testing.T) {
},

Name: "foo",
HelpWriter: buf,
ErrorWriter: buf,
Autocomplete: true,
autocompleteInstaller: installer,
}
Expand Down Expand Up @@ -1412,6 +1412,10 @@ func TestCLIAutocomplete_subcommandArgs(t *testing.T) {
Autocomplete: true,
}

// We need to initialize the autocomplete environment so that
// the cli doesn't no-op the autocomplete init
defer testAutocomplete(t, "must be non-empty")()

// Initialize
cli.init()

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/thycotic-rd/cli

go 1.11

require (
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310
github.com/bgentry/speakeasy v0.1.0
Expand Down
7 changes: 6 additions & 1 deletion ui_mock.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cli

import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
"sync"
)

Expand Down Expand Up @@ -35,9 +37,12 @@ func (u *MockUi) Ask(query string) (string, error) {

var result string
fmt.Fprint(u.OutputWriter, query)
if _, err := fmt.Fscanln(u.InputReader, &result); err != nil {
r := bufio.NewReader(u.InputReader)
line, err := r.ReadString('\n')
if err != nil {
return "", err
}
result = strings.TrimRight(line, "\r\n")

return result, nil
}
Expand Down
Loading

0 comments on commit 081d372

Please sign in to comment.