Merge pull request #3448 from hashicorp/sethvargo/updatecli
Update vendor to regroup by root commands
This commit is contained in:
commit
87fb782a01
|
@ -18,6 +18,9 @@ cli is the library that powers the CLI for
|
|||
* Optional support for default subcommands so `cli` does something
|
||||
other than error.
|
||||
|
||||
* Support for shell autocompletion of subcommands, flags, and arguments
|
||||
with callbacks in Go. You don't need to write any shell code.
|
||||
|
||||
* Automatic help generation for listing subcommands
|
||||
|
||||
* Automatic help flag recognition of `-h`, `--help`, etc.
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"github.com/posener/complete/cmd/install"
|
||||
)
|
||||
|
||||
// autocompleteInstaller is an interface to be implemented to perform the
|
||||
// autocomplete installation and uninstallation with a CLI.
|
||||
//
|
||||
// This interface is not exported because it only exists for unit tests
|
||||
// to be able to test that the installation is called properly.
|
||||
type autocompleteInstaller interface {
|
||||
Install(string) error
|
||||
Uninstall(string) error
|
||||
}
|
||||
|
||||
// realAutocompleteInstaller uses the real install package to do the
|
||||
// install/uninstall.
|
||||
type realAutocompleteInstaller struct{}
|
||||
|
||||
func (i *realAutocompleteInstaller) Install(cmd string) error {
|
||||
return install.Install(cmd)
|
||||
}
|
||||
|
||||
func (i *realAutocompleteInstaller) Uninstall(cmd string) error {
|
||||
return install.Uninstall(cmd)
|
||||
}
|
||||
|
||||
// mockAutocompleteInstaller is used for tests to record the install/uninstall.
|
||||
type mockAutocompleteInstaller struct {
|
||||
InstallCalled bool
|
||||
UninstallCalled bool
|
||||
}
|
||||
|
||||
func (i *mockAutocompleteInstaller) Install(cmd string) error {
|
||||
i.InstallCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockAutocompleteInstaller) Uninstall(cmd string) error {
|
||||
i.UninstallCalled = true
|
||||
return nil
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/armon/go-radix"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
// CLI contains the state necessary to run subcommands and parse the
|
||||
|
@ -66,6 +67,36 @@ type CLI struct {
|
|||
// Version of the CLI.
|
||||
Version string
|
||||
|
||||
// Autocomplete enables or disables subcommand auto-completion support.
|
||||
// This is enabled by default when NewCLI is called. Otherwise, this
|
||||
// must enabled explicitly.
|
||||
//
|
||||
// Autocomplete requires the "Name" option to be set on CLI. This name
|
||||
// should be set exactly to the binary name that is autocompleted.
|
||||
//
|
||||
// Autocompletion is supported via the github.com/posener/complete
|
||||
// library. This library supports both bash and zsh. To add support
|
||||
// for other shells, please see that library.
|
||||
//
|
||||
// AutocompleteInstall and AutocompleteUninstall are the global flag
|
||||
// names for installing and uninstalling the autocompletion handlers
|
||||
// for the user's shell. The flag should omit the hyphen(s) in front of
|
||||
// the value. Both single and double hyphens will automatically be supported
|
||||
// for the flag name. These default to `autocomplete-install` and
|
||||
// `autocomplete-uninstall` respectively.
|
||||
//
|
||||
// AutocompleteNoDefaultFlags is a boolean which controls if the default auto-
|
||||
// complete flags like -help and -version are added to the output.
|
||||
//
|
||||
// AutocompleteGlobalFlags are a mapping of global flags for
|
||||
// autocompletion. The help and version flags are automatically added.
|
||||
Autocomplete bool
|
||||
AutocompleteInstall string
|
||||
AutocompleteUninstall string
|
||||
AutocompleteNoDefaultFlags bool
|
||||
AutocompleteGlobalFlags complete.Flags
|
||||
autocompleteInstaller autocompleteInstaller // For tests
|
||||
|
||||
// HelpFunc and HelpWriter are used to output help information, if
|
||||
// requested.
|
||||
//
|
||||
|
@ -78,23 +109,32 @@ type CLI struct {
|
|||
HelpFunc HelpFunc
|
||||
HelpWriter io.Writer
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Internal fields set automatically
|
||||
|
||||
once sync.Once
|
||||
autocomplete *complete.Complete
|
||||
commandTree *radix.Tree
|
||||
commandNested bool
|
||||
isHelp bool
|
||||
subcommand string
|
||||
subcommandArgs []string
|
||||
topFlags []string
|
||||
|
||||
isVersion bool
|
||||
// These are true when special global flags are set. We can/should
|
||||
// probably use a bitset for this one day.
|
||||
isHelp bool
|
||||
isVersion bool
|
||||
isAutocompleteInstall bool
|
||||
isAutocompleteUninstall bool
|
||||
}
|
||||
|
||||
// NewClI returns a new CLI instance with sensible defaults.
|
||||
func NewCLI(app, version string) *CLI {
|
||||
return &CLI{
|
||||
Name: app,
|
||||
Version: version,
|
||||
HelpFunc: BasicHelpFunc(app),
|
||||
Name: app,
|
||||
Version: version,
|
||||
HelpFunc: BasicHelpFunc(app),
|
||||
Autocomplete: true,
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -117,6 +157,14 @@ func (c *CLI) IsVersion() bool {
|
|||
func (c *CLI) Run() (int, error) {
|
||||
c.once.Do(c.init)
|
||||
|
||||
// If this is a autocompletion request, satisfy it. This must be called
|
||||
// first before anything else since its possible to be autocompleting
|
||||
// -help or -version or other flags and we want to show completions
|
||||
// and not actually write the help or version.
|
||||
if c.Autocomplete && c.autocomplete.Complete() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Just show the version and exit if instructed.
|
||||
if c.IsVersion() && c.Version != "" {
|
||||
c.HelpWriter.Write([]byte(c.Version + "\n"))
|
||||
|
@ -125,10 +173,44 @@ func (c *CLI) Run() (int, error) {
|
|||
|
||||
// Just print the help when only '-h' or '--help' is passed.
|
||||
if c.IsHelp() && c.Subcommand() == "" {
|
||||
c.HelpWriter.Write([]byte(c.HelpFunc(c.Commands) + "\n"))
|
||||
c.HelpWriter.Write([]byte(c.HelpFunc(c.helpCommands(c.Subcommand())) + "\n"))
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// If we're attempting to install or uninstall autocomplete then handle
|
||||
if c.Autocomplete {
|
||||
// Autocomplete requires the "Name" to be set so that we know what
|
||||
// command to setup the autocomplete on.
|
||||
if c.Name == "" {
|
||||
return 1, fmt.Errorf(
|
||||
"internal error: CLI.Name must be specified for autocomplete to work")
|
||||
}
|
||||
|
||||
// If both install and uninstall flags are specified, then error
|
||||
if c.isAutocompleteInstall && c.isAutocompleteUninstall {
|
||||
return 1, fmt.Errorf(
|
||||
"Either the autocomplete install or uninstall flag may " +
|
||||
"be specified, but not both.")
|
||||
}
|
||||
|
||||
// If the install flag is specified, perform the install or uninstall
|
||||
if c.isAutocompleteInstall {
|
||||
if err := c.autocompleteInstaller.Install(c.Name); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if c.isAutocompleteUninstall {
|
||||
if err := c.autocompleteInstaller.Uninstall(c.Name); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to get the factory function for creating the command
|
||||
// implementation. If the command is invalid or blank, it is an error.
|
||||
raw, ok := c.commandTree.Get(c.Subcommand())
|
||||
|
@ -268,10 +350,108 @@ func (c *CLI) init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Setup autocomplete if we have it enabled. We have to do this after
|
||||
// the command tree is setup so we can use the radix tree to easily find
|
||||
// all subcommands.
|
||||
if c.Autocomplete {
|
||||
c.initAutocomplete()
|
||||
}
|
||||
|
||||
// Process the args
|
||||
c.processArgs()
|
||||
}
|
||||
|
||||
func (c *CLI) initAutocomplete() {
|
||||
if c.AutocompleteInstall == "" {
|
||||
c.AutocompleteInstall = defaultAutocompleteInstall
|
||||
}
|
||||
|
||||
if c.AutocompleteUninstall == "" {
|
||||
c.AutocompleteUninstall = defaultAutocompleteUninstall
|
||||
}
|
||||
|
||||
if c.autocompleteInstaller == nil {
|
||||
c.autocompleteInstaller = &realAutocompleteInstaller{}
|
||||
}
|
||||
|
||||
// Build the root command
|
||||
cmd := c.initAutocompleteSub("")
|
||||
|
||||
// For the root, we add the global flags to the "Flags". This way
|
||||
// they don't show up on every command.
|
||||
if !c.AutocompleteNoDefaultFlags {
|
||||
cmd.Flags = map[string]complete.Predictor{
|
||||
"-" + c.AutocompleteInstall: complete.PredictNothing,
|
||||
"-" + c.AutocompleteUninstall: complete.PredictNothing,
|
||||
"-help": complete.PredictNothing,
|
||||
"-version": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
cmd.GlobalFlags = c.AutocompleteGlobalFlags
|
||||
|
||||
c.autocomplete = complete.New(c.Name, cmd)
|
||||
}
|
||||
|
||||
// initAutocompleteSub creates the complete.Command for a subcommand with
|
||||
// the given prefix. This will continue recursively for all subcommands.
|
||||
// The prefix "" (empty string) can be used for the root command.
|
||||
func (c *CLI) initAutocompleteSub(prefix string) complete.Command {
|
||||
var cmd complete.Command
|
||||
walkFn := func(k string, raw interface{}) bool {
|
||||
// Keep track of the full key so that we can nest further if necessary
|
||||
fullKey := k
|
||||
|
||||
if len(prefix) > 0 {
|
||||
// If we have a prefix, trim the prefix + 1 (for the space)
|
||||
// Example: turns "sub one" to "one" with prefix "sub"
|
||||
k = k[len(prefix)+1:]
|
||||
}
|
||||
|
||||
if idx := strings.Index(k, " "); idx >= 0 {
|
||||
// If there is a space, we trim up to the space. This turns
|
||||
// "sub sub2 sub3" into "sub". The prefix trim above will
|
||||
// trim our current depth properly.
|
||||
k = k[:idx]
|
||||
}
|
||||
|
||||
if _, ok := cmd.Sub[k]; ok {
|
||||
// If we already tracked this subcommand then ignore
|
||||
return false
|
||||
}
|
||||
|
||||
if cmd.Sub == nil {
|
||||
cmd.Sub = complete.Commands(make(map[string]complete.Command))
|
||||
}
|
||||
subCmd := c.initAutocompleteSub(fullKey)
|
||||
|
||||
// Instantiate the command so that we can check if the command is
|
||||
// a CommandAutocomplete implementation. If there is an error
|
||||
// creating the command, we just ignore it since that will be caught
|
||||
// later.
|
||||
impl, err := raw.(CommandFactory)()
|
||||
if err != nil {
|
||||
impl = nil
|
||||
}
|
||||
|
||||
// Check if it implements ComandAutocomplete. If so, setup the autocomplete
|
||||
if c, ok := impl.(CommandAutocomplete); ok {
|
||||
subCmd.Args = c.AutocompleteArgs()
|
||||
subCmd.Flags = c.AutocompleteFlags()
|
||||
}
|
||||
|
||||
cmd.Sub[k] = subCmd
|
||||
return false
|
||||
}
|
||||
|
||||
walkPrefix := prefix
|
||||
if walkPrefix != "" {
|
||||
walkPrefix += " "
|
||||
}
|
||||
|
||||
c.commandTree.WalkPrefix(walkPrefix, walkFn)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *CLI) commandHelp(command Command) {
|
||||
// Get the template to use
|
||||
tpl := strings.TrimSpace(defaultHelpTemplate)
|
||||
|
@ -398,6 +578,25 @@ func (c *CLI) processArgs() {
|
|||
break
|
||||
}
|
||||
|
||||
// Check for help flags.
|
||||
if arg == "-h" || arg == "-help" || arg == "--help" {
|
||||
c.isHelp = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for autocomplete flags
|
||||
if c.Autocomplete {
|
||||
if arg == "-"+c.AutocompleteInstall || arg == "--"+c.AutocompleteInstall {
|
||||
c.isAutocompleteInstall = true
|
||||
continue
|
||||
}
|
||||
|
||||
if arg == "-"+c.AutocompleteUninstall || arg == "--"+c.AutocompleteUninstall {
|
||||
c.isAutocompleteUninstall = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if c.subcommand == "" {
|
||||
// Check for version flags if not in a subcommand.
|
||||
if arg == "-v" || arg == "-version" || arg == "--version" {
|
||||
|
@ -405,12 +604,6 @@ func (c *CLI) processArgs() {
|
|||
continue
|
||||
}
|
||||
|
||||
// Check for help flags.
|
||||
if arg == "-h" || arg == "-help" || arg == "--help" {
|
||||
c.isHelp = true
|
||||
continue
|
||||
}
|
||||
|
||||
if arg != "" && arg[0] == '-' {
|
||||
// Record the arg...
|
||||
c.topFlags = append(c.topFlags, arg)
|
||||
|
@ -456,6 +649,11 @@ func (c *CLI) processArgs() {
|
|||
}
|
||||
}
|
||||
|
||||
// defaultAutocompleteInstall and defaultAutocompleteUninstall are the
|
||||
// default values for the autocomplete install and uninstall flags.
|
||||
const defaultAutocompleteInstall = "autocomplete-install"
|
||||
const defaultAutocompleteUninstall = "autocomplete-uninstall"
|
||||
|
||||
const defaultHelpTemplate = `
|
||||
{{.Help}}{{if gt (len .Subcommands) 0}}
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
const (
|
||||
// RunResultHelp is a value that can be returned from Run to signal
|
||||
// to the CLI to render the help output.
|
||||
|
@ -26,6 +30,22 @@ type Command interface {
|
|||
Synopsis() string
|
||||
}
|
||||
|
||||
// CommandAutocomplete is an extension of Command that enables fine-grained
|
||||
// autocompletion. Subcommand autocompletion will work even if this interface
|
||||
// is not implemented. By implementing this interface, more advanced
|
||||
// autocompletion is enabled.
|
||||
type CommandAutocomplete interface {
|
||||
// AutocompleteArgs returns the argument predictor for this command.
|
||||
// If argument completion is not supported, this should return
|
||||
// complete.PredictNothing.
|
||||
AutocompleteArgs() complete.Predictor
|
||||
|
||||
// AutocompleteFlags returns a mapping of supported flags and autocomplete
|
||||
// options for this command. The map key for the Flags map should be the
|
||||
// complete flag such as "-foo" or "--foo".
|
||||
AutocompleteFlags() complete.Flags
|
||||
}
|
||||
|
||||
// CommandHelpTemplate is an extension of Command that also has a function
|
||||
// for returning a template for the help rather than the help itself. In
|
||||
// this scenario, both Help and HelpTemplate should be implemented.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
// MockCommand is an implementation of Command that can be used for tests.
|
||||
// It is publicly exported from this package in case you want to use it
|
||||
// externally.
|
||||
|
@ -29,6 +33,23 @@ func (c *MockCommand) Synopsis() string {
|
|||
return c.SynopsisText
|
||||
}
|
||||
|
||||
// MockCommandAutocomplete is an implementation of CommandAutocomplete.
|
||||
type MockCommandAutocomplete struct {
|
||||
MockCommand
|
||||
|
||||
// Settable
|
||||
AutocompleteArgsValue complete.Predictor
|
||||
AutocompleteFlagsValue complete.Flags
|
||||
}
|
||||
|
||||
func (c *MockCommandAutocomplete) AutocompleteArgs() complete.Predictor {
|
||||
return c.AutocompleteArgsValue
|
||||
}
|
||||
|
||||
func (c *MockCommandAutocomplete) AutocompleteFlags() complete.Flags {
|
||||
return c.AutocompleteFlagsValue
|
||||
}
|
||||
|
||||
// MockCommandHelpTemplate is an implementation of CommandHelpTemplate.
|
||||
type MockCommandHelpTemplate struct {
|
||||
MockCommand
|
||||
|
|
|
@ -100,8 +100,12 @@ func (b *syncBuffer) Reset() {
|
|||
}
|
||||
|
||||
func (b *syncBuffer) String() string {
|
||||
return string(b.Bytes())
|
||||
}
|
||||
|
||||
func (b *syncBuffer) Bytes() []byte {
|
||||
b.RLock()
|
||||
data := b.b.Bytes()
|
||||
b.RUnlock()
|
||||
return string(data)
|
||||
return data
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Eyal Posener
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,75 @@
|
|||
package complete
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Args describes command line arguments
|
||||
type Args struct {
|
||||
// All lists of all arguments in command line (not including the command itself)
|
||||
All []string
|
||||
// Completed lists of all completed arguments in command line,
|
||||
// If the last one is still being typed - no space after it,
|
||||
// it won't appear in this list of arguments.
|
||||
Completed []string
|
||||
// Last argument in command line, the one being typed, if the last
|
||||
// character in the command line is a space, this argument will be empty,
|
||||
// otherwise this would be the last word.
|
||||
Last string
|
||||
// LastCompleted is the last argument that was fully typed.
|
||||
// If the last character in the command line is space, this would be the
|
||||
// last word, otherwise, it would be the word before that.
|
||||
LastCompleted string
|
||||
}
|
||||
|
||||
// Directory gives the directory of the current written
|
||||
// last argument if it represents a file name being written.
|
||||
// in case that it is not, we fall back to the current directory.
|
||||
func (a Args) Directory() string {
|
||||
if info, err := os.Stat(a.Last); err == nil && info.IsDir() {
|
||||
return fixPathForm(a.Last, a.Last)
|
||||
}
|
||||
dir := filepath.Dir(a.Last)
|
||||
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
|
||||
return "./"
|
||||
}
|
||||
return fixPathForm(a.Last, dir)
|
||||
}
|
||||
|
||||
func newArgs(line []string) Args {
|
||||
completed := removeLast(line[1:])
|
||||
return Args{
|
||||
All: line[1:],
|
||||
Completed: completed,
|
||||
Last: last(line),
|
||||
LastCompleted: last(completed),
|
||||
}
|
||||
}
|
||||
|
||||
func (a Args) from(i int) Args {
|
||||
if i > len(a.All) {
|
||||
i = len(a.All)
|
||||
}
|
||||
a.All = a.All[i:]
|
||||
|
||||
if i > len(a.Completed) {
|
||||
i = len(a.Completed)
|
||||
}
|
||||
a.Completed = a.Completed[i:]
|
||||
return a
|
||||
}
|
||||
|
||||
func removeLast(a []string) []string {
|
||||
if len(a) > 0 {
|
||||
return a[:len(a)-1]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func last(args []string) (last string) {
|
||||
if len(args) > 0 {
|
||||
last = args[len(args)-1]
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// Package cmd used for command line options for the complete tool
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/posener/complete/cmd/install"
|
||||
)
|
||||
|
||||
// CLI for command line
|
||||
type CLI struct {
|
||||
Name string
|
||||
InstallName string
|
||||
UninstallName string
|
||||
|
||||
install bool
|
||||
uninstall bool
|
||||
yes bool
|
||||
}
|
||||
|
||||
const (
|
||||
defaultInstallName = "install"
|
||||
defaultUninstallName = "uninstall"
|
||||
)
|
||||
|
||||
// Run is used when running complete in command line mode.
|
||||
// this is used when the complete is not completing words, but to
|
||||
// install it or uninstall it.
|
||||
func (f *CLI) Run() bool {
|
||||
err := f.validate()
|
||||
if err != nil {
|
||||
os.Stderr.WriteString(err.Error() + "\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.install:
|
||||
f.prompt()
|
||||
err = install.Install(f.Name)
|
||||
case f.uninstall:
|
||||
f.prompt()
|
||||
err = install.Uninstall(f.Name)
|
||||
default:
|
||||
// non of the action flags matched,
|
||||
// returning false should make the real program execute
|
||||
return false
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("%s failed! %s\n", f.action(), err)
|
||||
os.Exit(3)
|
||||
}
|
||||
fmt.Println("Done!")
|
||||
return true
|
||||
}
|
||||
|
||||
// prompt use for approval
|
||||
// exit if approval was not given
|
||||
func (f *CLI) prompt() {
|
||||
defer fmt.Println(f.action() + "ing...")
|
||||
if f.yes {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s completion for %s? ", f.action(), f.Name)
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
|
||||
switch strings.ToLower(answer) {
|
||||
case "y", "yes":
|
||||
return
|
||||
default:
|
||||
fmt.Println("Cancelling...")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlags adds the CLI flags to the flag set.
|
||||
// If flags is nil, the default command line flags will be taken.
|
||||
// Pass non-empty strings as installName and uninstallName to override the default
|
||||
// flag names.
|
||||
func (f *CLI) AddFlags(flags *flag.FlagSet) {
|
||||
if flags == nil {
|
||||
flags = flag.CommandLine
|
||||
}
|
||||
|
||||
if f.InstallName == "" {
|
||||
f.InstallName = defaultInstallName
|
||||
}
|
||||
if f.UninstallName == "" {
|
||||
f.UninstallName = defaultUninstallName
|
||||
}
|
||||
|
||||
if flags.Lookup(f.InstallName) == nil {
|
||||
flags.BoolVar(&f.install, f.InstallName, false,
|
||||
fmt.Sprintf("Install completion for %s command", f.Name))
|
||||
}
|
||||
if flags.Lookup(f.UninstallName) == nil {
|
||||
flags.BoolVar(&f.uninstall, f.UninstallName, false,
|
||||
fmt.Sprintf("Uninstall completion for %s command", f.Name))
|
||||
}
|
||||
if flags.Lookup("y") == nil {
|
||||
flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'")
|
||||
}
|
||||
}
|
||||
|
||||
// validate the CLI
|
||||
func (f *CLI) validate() error {
|
||||
if f.install && f.uninstall {
|
||||
return errors.New("Install and uninstall are mutually exclusive")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// action name according to the CLI values.
|
||||
func (f *CLI) action() string {
|
||||
switch {
|
||||
case f.install:
|
||||
return "Install"
|
||||
case f.uninstall:
|
||||
return "Uninstall"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package install
|
||||
|
||||
import "fmt"
|
||||
|
||||
// (un)install in bash
|
||||
// basically adds/remove from .bashrc:
|
||||
//
|
||||
// complete -C </path/to/completion/command> <command>
|
||||
type bash struct {
|
||||
rc string
|
||||
}
|
||||
|
||||
func (b bash) Install(cmd, bin string) error {
|
||||
completeCmd := b.cmd(cmd, bin)
|
||||
if lineInFile(b.rc, completeCmd) {
|
||||
return fmt.Errorf("already installed in %s", b.rc)
|
||||
}
|
||||
return appendToFile(b.rc, completeCmd)
|
||||
}
|
||||
|
||||
func (b bash) Uninstall(cmd, bin string) error {
|
||||
completeCmd := b.cmd(cmd, bin)
|
||||
if !lineInFile(b.rc, completeCmd) {
|
||||
return fmt.Errorf("does not installed in %s", b.rc)
|
||||
}
|
||||
|
||||
return removeFromFile(b.rc, completeCmd)
|
||||
}
|
||||
|
||||
func (bash) cmd(cmd, bin string) string {
|
||||
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package install
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
type installer interface {
|
||||
Install(cmd, bin string) error
|
||||
Uninstall(cmd, bin string) error
|
||||
}
|
||||
|
||||
// Install complete command given:
|
||||
// cmd: is the command name
|
||||
func Install(cmd string) error {
|
||||
is := installers()
|
||||
if len(is) == 0 {
|
||||
return errors.New("Did not found any shells to install")
|
||||
}
|
||||
bin, err := getBinaryPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, i := range is {
|
||||
errI := i.Install(cmd, bin)
|
||||
if errI != nil {
|
||||
err = multierror.Append(err, errI)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Uninstall complete command given:
|
||||
// cmd: is the command name
|
||||
func Uninstall(cmd string) error {
|
||||
is := installers()
|
||||
if len(is) == 0 {
|
||||
return errors.New("Did not found any shells to uninstall")
|
||||
}
|
||||
bin, err := getBinaryPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, i := range is {
|
||||
errI := i.Uninstall(cmd, bin)
|
||||
if errI != nil {
|
||||
multierror.Append(err, errI)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func installers() (i []installer) {
|
||||
for _, rc := range [...]string{".bashrc", ".bash_profile"} {
|
||||
if f := rcFile(rc); f != "" {
|
||||
i = append(i, bash{f})
|
||||
break
|
||||
}
|
||||
}
|
||||
if f := rcFile(".zshrc"); f != "" {
|
||||
i = append(i, zsh{f})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getBinaryPath() (string, error) {
|
||||
bin, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Abs(bin)
|
||||
}
|
||||
|
||||
func rcFile(name string) string {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
path := filepath.Join(u.HomeDir, name)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package install
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func lineInFile(name string, lookFor string) bool {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
r := bufio.NewReader(f)
|
||||
prefix := []byte{}
|
||||
for {
|
||||
line, isPrefix, err := r.ReadLine()
|
||||
if err == io.EOF {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if isPrefix {
|
||||
prefix = append(prefix, line...)
|
||||
continue
|
||||
}
|
||||
line = append(prefix, line...)
|
||||
if string(line) == lookFor {
|
||||
return true
|
||||
}
|
||||
prefix = prefix[:0]
|
||||
}
|
||||
}
|
||||
|
||||
func appendToFile(name string, content string) error {
|
||||
f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(fmt.Sprintf("\n%s\n", content))
|
||||
return err
|
||||
}
|
||||
|
||||
func removeFromFile(name string, content string) error {
|
||||
backup := name + ".bck"
|
||||
err := copyFile(name, backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
temp, err := removeContentToTempFile(name, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = copyFile(temp, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(backup)
|
||||
}
|
||||
|
||||
func removeContentToTempFile(name, content string) (string, error) {
|
||||
rf, err := os.Open(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rf.Close()
|
||||
wf, err := ioutil.TempFile("/tmp", "complete-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer wf.Close()
|
||||
|
||||
r := bufio.NewReader(rf)
|
||||
prefix := []byte{}
|
||||
for {
|
||||
line, isPrefix, err := r.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isPrefix {
|
||||
prefix = append(prefix, line...)
|
||||
continue
|
||||
}
|
||||
line = append(prefix, line...)
|
||||
str := string(line)
|
||||
if str == content {
|
||||
continue
|
||||
}
|
||||
wf.WriteString(str + "\n")
|
||||
prefix = prefix[:0]
|
||||
}
|
||||
return wf.Name(), nil
|
||||
}
|
||||
|
||||
func copyFile(src string, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package install
|
||||
|
||||
import "fmt"
|
||||
|
||||
// (un)install in zsh
|
||||
// basically adds/remove from .zshrc:
|
||||
//
|
||||
// autoload -U +X bashcompinit && bashcompinit"
|
||||
// complete -C </path/to/completion/command> <command>
|
||||
type zsh struct {
|
||||
rc string
|
||||
}
|
||||
|
||||
func (z zsh) Install(cmd, bin string) error {
|
||||
completeCmd := z.cmd(cmd, bin)
|
||||
if lineInFile(z.rc, completeCmd) {
|
||||
return fmt.Errorf("already installed in %s", z.rc)
|
||||
}
|
||||
|
||||
bashCompInit := "autoload -U +X bashcompinit && bashcompinit"
|
||||
if !lineInFile(z.rc, bashCompInit) {
|
||||
completeCmd = bashCompInit + "\n" + completeCmd
|
||||
}
|
||||
|
||||
return appendToFile(z.rc, completeCmd)
|
||||
}
|
||||
|
||||
func (z zsh) Uninstall(cmd, bin string) error {
|
||||
completeCmd := z.cmd(cmd, bin)
|
||||
if !lineInFile(z.rc, completeCmd) {
|
||||
return fmt.Errorf("does not installed in %s", z.rc)
|
||||
}
|
||||
|
||||
return removeFromFile(z.rc, completeCmd)
|
||||
}
|
||||
|
||||
func (zsh) cmd(cmd, bin string) string {
|
||||
return fmt.Sprintf("complete -o nospace -C %s %s", bin, cmd)
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package complete
|
||||
|
||||
import "github.com/posener/complete/match"
|
||||
|
||||
// Command represents a command line
|
||||
// It holds the data that enables auto completion of command line
|
||||
// Command can also be a sub command.
|
||||
type Command struct {
|
||||
// Sub is map of sub commands of the current command
|
||||
// The key refer to the sub command name, and the value is it's
|
||||
// Command descriptive struct.
|
||||
Sub Commands
|
||||
|
||||
// Flags is a map of flags that the command accepts.
|
||||
// The key is the flag name, and the value is it's predictions.
|
||||
Flags Flags
|
||||
|
||||
// GlobalFlags is a map of flags that the command accepts.
|
||||
// Global flags that can appear also after a sub command.
|
||||
GlobalFlags Flags
|
||||
|
||||
// Args are extra arguments that the command accepts, those who are
|
||||
// given without any flag before.
|
||||
Args Predictor
|
||||
}
|
||||
|
||||
// Predict returns all possible predictions for args according to the command struct
|
||||
func (c *Command) Predict(a Args) (predictions []string) {
|
||||
predictions, _ = c.predict(a)
|
||||
return
|
||||
}
|
||||
|
||||
// Commands is the type of Sub member, it maps a command name to a command struct
|
||||
type Commands map[string]Command
|
||||
|
||||
// Predict completion of sub command names names according to command line arguments
|
||||
func (c Commands) Predict(a Args) (prediction []string) {
|
||||
for sub := range c {
|
||||
if match.Prefix(sub, a.Last) {
|
||||
prediction = append(prediction, sub)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions.
|
||||
type Flags map[string]Predictor
|
||||
|
||||
// Predict completion of flags names according to command line arguments
|
||||
func (f Flags) Predict(a Args) (prediction []string) {
|
||||
for flag := range f {
|
||||
// If the flag starts with a hyphen, we avoid emitting the prediction
|
||||
// unless the last typed arg contains a hyphen as well.
|
||||
flagHyphenStart := len(flag) != 0 && flag[0] == '-'
|
||||
lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-'
|
||||
if flagHyphenStart && !lastHyphenStart {
|
||||
continue
|
||||
}
|
||||
|
||||
if match.Prefix(flag, a.Last) {
|
||||
prediction = append(prediction, flag)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// predict options
|
||||
// only is set to true if no more options are allowed to be returned
|
||||
// those are in cases of special flag that has specific completion arguments,
|
||||
// and other flags or sub commands can't come after it.
|
||||
func (c *Command) predict(a Args) (options []string, only bool) {
|
||||
|
||||
// search sub commands for predictions first
|
||||
subCommandFound := false
|
||||
for i, arg := range a.Completed {
|
||||
if cmd, ok := c.Sub[arg]; ok {
|
||||
subCommandFound = true
|
||||
|
||||
// recursive call for sub command
|
||||
options, only = cmd.predict(a.from(i))
|
||||
if only {
|
||||
return
|
||||
}
|
||||
|
||||
// We matched so stop searching. Continuing to search can accidentally
|
||||
// match a subcommand with current set of commands, see issue #46.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if last completed word is a global flag that we need to complete
|
||||
if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil {
|
||||
Log("Predicting according to global flag %s", a.LastCompleted)
|
||||
return predictor.Predict(a), true
|
||||
}
|
||||
|
||||
options = append(options, c.GlobalFlags.Predict(a)...)
|
||||
|
||||
// if a sub command was entered, we won't add the parent command
|
||||
// completions and we return here.
|
||||
if subCommandFound {
|
||||
return
|
||||
}
|
||||
|
||||
// if last completed word is a command flag that we need to complete
|
||||
if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
|
||||
Log("Predicting according to flag %s", a.LastCompleted)
|
||||
return predictor.Predict(a), true
|
||||
}
|
||||
|
||||
options = append(options, c.Sub.Predict(a)...)
|
||||
options = append(options, c.Flags.Predict(a)...)
|
||||
if c.Args != nil {
|
||||
options = append(options, c.Args.Predict(a)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// Package complete provides a tool for bash writing bash completion in go.
|
||||
//
|
||||
// Writing bash completion scripts is a hard work. This package provides an easy way
|
||||
// to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||
// the completion of the command.
|
||||
package complete
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/posener/complete/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
envComplete = "COMP_LINE"
|
||||
envDebug = "COMP_DEBUG"
|
||||
)
|
||||
|
||||
// Complete structs define completion for a command with CLI options
|
||||
type Complete struct {
|
||||
Command Command
|
||||
cmd.CLI
|
||||
}
|
||||
|
||||
// New creates a new complete command.
|
||||
// name is the name of command we want to auto complete.
|
||||
// IMPORTANT: it must be the same name - if the auto complete
|
||||
// completes the 'go' command, name must be equal to "go".
|
||||
// command is the struct of the command completion.
|
||||
func New(name string, command Command) *Complete {
|
||||
return &Complete{
|
||||
Command: command,
|
||||
CLI: cmd.CLI{Name: name},
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the completion and add installation flags beforehand.
|
||||
// The flags are added to the main flag CommandLine variable.
|
||||
func (c *Complete) Run() bool {
|
||||
c.AddFlags(nil)
|
||||
flag.Parse()
|
||||
return c.Complete()
|
||||
}
|
||||
|
||||
// Complete a command from completion line in environment variable,
|
||||
// and print out the complete options.
|
||||
// returns success if the completion ran or if the cli matched
|
||||
// any of the given flags, false otherwise
|
||||
// For installation: it assumes that flags were added and parsed before
|
||||
// it was called.
|
||||
func (c *Complete) Complete() bool {
|
||||
line, ok := getLine()
|
||||
if !ok {
|
||||
// make sure flags parsed,
|
||||
// in case they were not added in the main program
|
||||
return c.CLI.Run()
|
||||
}
|
||||
Log("Completing line: %s", line)
|
||||
|
||||
a := newArgs(line)
|
||||
|
||||
options := c.Command.Predict(a)
|
||||
|
||||
Log("Completion: %s", options)
|
||||
output(options)
|
||||
return true
|
||||
}
|
||||
|
||||
func getLine() ([]string, bool) {
|
||||
line := os.Getenv(envComplete)
|
||||
if line == "" {
|
||||
return nil, false
|
||||
}
|
||||
return strings.Split(line, " "), true
|
||||
}
|
||||
|
||||
func output(options []string) {
|
||||
Log("")
|
||||
// stdout of program defines the complete options
|
||||
for _, option := range options {
|
||||
fmt.Println(option)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package complete
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Log is used for debugging purposes
|
||||
// since complete is running on tab completion, it is nice to
|
||||
// have logs to the stderr (when writing your own completer)
|
||||
// to write logs, set the COMP_DEBUG environment variable and
|
||||
// use complete.Log in the complete program
|
||||
var Log = getLogger()
|
||||
|
||||
func getLogger() func(format string, args ...interface{}) {
|
||||
var logfile io.Writer = ioutil.Discard
|
||||
if os.Getenv(envDebug) != "" {
|
||||
logfile = os.Stderr
|
||||
}
|
||||
return log.New(logfile, "complete ", log.Flags()).Printf
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package match
|
||||
|
||||
import "strings"
|
||||
|
||||
// File returns true if prefix can match the file
|
||||
func File(file, prefix string) bool {
|
||||
// special case for current directory completion
|
||||
if file == "./" && (prefix == "." || prefix == "") {
|
||||
return true
|
||||
}
|
||||
if prefix == "." && strings.HasPrefix(file, ".") {
|
||||
return true
|
||||
}
|
||||
|
||||
file = strings.TrimPrefix(file, "./")
|
||||
prefix = strings.TrimPrefix(prefix, "./")
|
||||
|
||||
return strings.HasPrefix(file, prefix)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package match
|
||||
|
||||
// Match matches two strings
|
||||
// it is used for comparing a term to the last typed
|
||||
// word, the prefix, and see if it is a possible auto complete option.
|
||||
type Match func(term, prefix string) bool
|
|
@ -0,0 +1,9 @@
|
|||
package match
|
||||
|
||||
import "strings"
|
||||
|
||||
// Prefix is a simple Matcher, if the word is it's prefix, there is a match
|
||||
// Match returns true if a has the prefix as prefix
|
||||
func Prefix(long, prefix string) bool {
|
||||
return strings.HasPrefix(long, prefix)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"Vendor": true,
|
||||
"DisableAll": true,
|
||||
"Enable": [
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"interfacer",
|
||||
"goconst",
|
||||
"misspell",
|
||||
"unconvert",
|
||||
"gosimple",
|
||||
"golint",
|
||||
"structcheck",
|
||||
"deadcode",
|
||||
"vet"
|
||||
],
|
||||
"Exclude": [
|
||||
"initTests is unused"
|
||||
],
|
||||
"Deadline": "2m"
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package complete
|
||||
|
||||
// Predictor implements a predict method, in which given
|
||||
// command line arguments returns a list of options it predicts.
|
||||
type Predictor interface {
|
||||
Predict(Args) []string
|
||||
}
|
||||
|
||||
// PredictOr unions two predicate functions, so that the result predicate
|
||||
// returns the union of their predication
|
||||
func PredictOr(predictors ...Predictor) Predictor {
|
||||
return PredictFunc(func(a Args) (prediction []string) {
|
||||
for _, p := range predictors {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
prediction = append(prediction, p.Predict(a)...)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// PredictFunc determines what terms can follow a command or a flag
|
||||
// It is used for auto completion, given last - the last word in the already
|
||||
// in the command line, what words can complete it.
|
||||
type PredictFunc func(Args) []string
|
||||
|
||||
// Predict invokes the predict function and implements the Predictor interface
|
||||
func (p PredictFunc) Predict(a Args) []string {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p(a)
|
||||
}
|
||||
|
||||
// PredictNothing does not expect anything after.
|
||||
var PredictNothing Predictor
|
||||
|
||||
// PredictAnything expects something, but nothing particular, such as a number
|
||||
// or arbitrary name.
|
||||
var PredictAnything = PredictFunc(func(Args) []string { return nil })
|
|
@ -0,0 +1,108 @@
|
|||
package complete
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/posener/complete/match"
|
||||
)
|
||||
|
||||
// PredictDirs will search for directories in the given started to be typed
|
||||
// path, if no path was started to be typed, it will complete to directories
|
||||
// in the current working directory.
|
||||
func PredictDirs(pattern string) Predictor {
|
||||
return files(pattern, false)
|
||||
}
|
||||
|
||||
// PredictFiles will search for files matching the given pattern in the started to
|
||||
// be typed path, if no path was started to be typed, it will complete to files that
|
||||
// match the pattern in the current working directory.
|
||||
// To match any file, use "*" as pattern. To match go files use "*.go", and so on.
|
||||
func PredictFiles(pattern string) Predictor {
|
||||
return files(pattern, true)
|
||||
}
|
||||
|
||||
func files(pattern string, allowFiles bool) PredictFunc {
|
||||
|
||||
// search for files according to arguments,
|
||||
// if only one directory has matched the result, search recursively into
|
||||
// this directory to give more results.
|
||||
return func(a Args) (prediction []string) {
|
||||
prediction = predictFiles(a, pattern, allowFiles)
|
||||
|
||||
// if the number of prediction is not 1, we either have many results or
|
||||
// have no results, so we return it.
|
||||
if len(prediction) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// only try deeper, if the one item is a directory
|
||||
if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
a.Last = prediction[0]
|
||||
return predictFiles(a, pattern, allowFiles)
|
||||
}
|
||||
}
|
||||
|
||||
func predictFiles(a Args, pattern string, allowFiles bool) []string {
|
||||
if strings.HasSuffix(a.Last, "/..") {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := a.Directory()
|
||||
files := listFiles(dir, pattern, allowFiles)
|
||||
|
||||
// add dir if match
|
||||
files = append(files, dir)
|
||||
|
||||
return PredictFilesSet(files).Predict(a)
|
||||
}
|
||||
|
||||
// PredictFilesSet predict according to file rules to a given set of file names
|
||||
func PredictFilesSet(files []string) PredictFunc {
|
||||
return func(a Args) (prediction []string) {
|
||||
// add all matching files to prediction
|
||||
for _, f := range files {
|
||||
f = fixPathForm(a.Last, f)
|
||||
|
||||
// test matching of file to the argument
|
||||
if match.File(f, a.Last) {
|
||||
prediction = append(prediction, f)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func listFiles(dir, pattern string, allowFiles bool) []string {
|
||||
// set of all file names
|
||||
m := map[string]bool{}
|
||||
|
||||
// list files
|
||||
if files, err := filepath.Glob(filepath.Join(dir, pattern)); err == nil {
|
||||
for _, f := range files {
|
||||
if stat, err := os.Stat(f); err != nil || stat.IsDir() || allowFiles {
|
||||
m[f] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// list directories
|
||||
if dirs, err := ioutil.ReadDir(dir); err == nil {
|
||||
for _, d := range dirs {
|
||||
if d.IsDir() {
|
||||
m[filepath.Join(dir, d.Name())] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
list = append(list, k)
|
||||
}
|
||||
return list
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package complete
|
||||
|
||||
import "github.com/posener/complete/match"
|
||||
|
||||
// PredictSet expects specific set of terms, given in the options argument.
|
||||
func PredictSet(options ...string) Predictor {
|
||||
return predictSet(options)
|
||||
}
|
||||
|
||||
type predictSet []string
|
||||
|
||||
func (p predictSet) Predict(a Args) (prediction []string) {
|
||||
for _, m := range p {
|
||||
if match.Prefix(m, a.Last) {
|
||||
prediction = append(prediction, m)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
# complete
|
||||
|
||||
[![Build Status](https://travis-ci.org/posener/complete.svg?branch=master)](https://travis-ci.org/posener/complete)
|
||||
[![codecov](https://codecov.io/gh/posener/complete/branch/master/graph/badge.svg)](https://codecov.io/gh/posener/complete)
|
||||
[![GoDoc](https://godoc.org/github.com/posener/complete?status.svg)](http://godoc.org/github.com/posener/complete)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/posener/complete)](https://goreportcard.com/report/github.com/posener/complete)
|
||||
|
||||
A tool for bash writing bash completion in go.
|
||||
|
||||
Writing bash completion scripts is a hard work. This package provides an easy way
|
||||
to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||
the completion of the command.
|
||||
|
||||
## go command bash completion
|
||||
|
||||
In [gocomplete](./gocomplete) there is an example for bash completion for the `go` command line.
|
||||
|
||||
This is an example that uses the `complete` package on the `go` command - the `complete` package
|
||||
can also be used to implement any completions, see [Usage](#usage).
|
||||
|
||||
### Install
|
||||
|
||||
1. Type in your shell:
|
||||
```
|
||||
go get -u github.com/posener/complete/gocomplete
|
||||
gocomplete -install
|
||||
```
|
||||
|
||||
2. Restart your shell
|
||||
|
||||
Uninstall by `gocomplete -uninstall`
|
||||
|
||||
### Features
|
||||
|
||||
- Complete `go` command, including sub commands and all flags.
|
||||
- Complete packages names or `.go` files when necessary.
|
||||
- Complete test names after `-run` flag.
|
||||
|
||||
## complete package
|
||||
|
||||
Supported shells:
|
||||
|
||||
- [x] bash
|
||||
- [x] zsh
|
||||
|
||||
### Usage
|
||||
|
||||
Assuming you have program called `run` and you want to have bash completion
|
||||
for it, meaning, if you type `run` then space, then press the `Tab` key,
|
||||
the shell will suggest relevant complete options.
|
||||
|
||||
In that case, we will create a program called `runcomplete`, a go program,
|
||||
with a `func main()` and so, that will make the completion of the `run`
|
||||
program. Once the `runcomplete` will be in a binary form, we could
|
||||
`runcomplete -install` and that will add to our shell all the bash completion
|
||||
options for `run`.
|
||||
|
||||
So here it is:
|
||||
|
||||
```go
|
||||
import "github.com/posener/complete"
|
||||
|
||||
func main() {
|
||||
|
||||
// create a Command object, that represents the command we want
|
||||
// to complete.
|
||||
run := complete.Command{
|
||||
|
||||
// Sub defines a list of sub commands of the program,
|
||||
// this is recursive, since every command is of type command also.
|
||||
Sub: complete.Commands{
|
||||
|
||||
// add a build sub command
|
||||
"build": complete.Command {
|
||||
|
||||
// define flags of the build sub command
|
||||
Flags: complete.Flags{
|
||||
// build sub command has a flag '-cpus', which
|
||||
// expects number of cpus after it. in that case
|
||||
// anything could complete this flag.
|
||||
"-cpus": complete.PredictAnything,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// define flags of the 'run' main command
|
||||
Flags: complete.Flags{
|
||||
// a flag -o, which expects a file ending with .out after
|
||||
// it, the tab completion will auto complete for files matching
|
||||
// the given pattern.
|
||||
"-o": complete.PredictFiles("*.out"),
|
||||
},
|
||||
|
||||
// define global flags of the 'run' main command
|
||||
// those will show up also when a sub command was entered in the
|
||||
// command line
|
||||
GlobalFlags: complete.Flags{
|
||||
|
||||
// a flag '-h' which does not expects anything after it
|
||||
"-h": complete.PredictNothing,
|
||||
},
|
||||
}
|
||||
|
||||
// run the command completion, as part of the main() function.
|
||||
// this triggers the autocompletion when needed.
|
||||
// name must be exactly as the binary that we want to complete.
|
||||
complete.New("run", run).Run()
|
||||
}
|
||||
```
|
||||
|
||||
### Self completing program
|
||||
|
||||
In case that the program that we want to complete is written in go we
|
||||
can make it self completing.
|
||||
|
||||
Here is an [example](./example/self/main.go)
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
for d in $(go list ./... | grep -v vendor); do
|
||||
go test -v -race -coverprofile=profile.out -covermode=atomic $d
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,46 @@
|
|||
package complete
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// fixPathForm changes a file name to a relative name
|
||||
func fixPathForm(last string, file string) string {
|
||||
// get wording directory for relative name
|
||||
workDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return file
|
||||
}
|
||||
|
||||
abs, err := filepath.Abs(file)
|
||||
if err != nil {
|
||||
return file
|
||||
}
|
||||
|
||||
// if last is absolute, return path as absolute
|
||||
if filepath.IsAbs(last) {
|
||||
return fixDirPath(abs)
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(workDir, abs)
|
||||
if err != nil {
|
||||
return file
|
||||
}
|
||||
|
||||
// fix ./ prefix of path
|
||||
if rel != "." && strings.HasPrefix(last, ".") {
|
||||
rel = "./" + rel
|
||||
}
|
||||
|
||||
return fixDirPath(rel)
|
||||
}
|
||||
|
||||
func fixDirPath(path string) string {
|
||||
info, err := os.Stat(path)
|
||||
if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") {
|
||||
path += "/"
|
||||
}
|
||||
return path
|
||||
}
|
|
@ -2,89 +2,93 @@
|
|||
"comment": "",
|
||||
"ignore": "test appengine",
|
||||
"package": [
|
||||
{"path":"github.com/DataDog/datadog-go/statsd","checksumSHA1":"JhyS/zIicgtrSasHSZ6WtXGWJVk=","revision":"cc2f4770f4d61871e19bfee967bc767fe730b0d9","revisionTime":"2016-03-29T13:52:53Z"},
|
||||
{"path":"github.com/Microsoft/go-winio","checksumSHA1":"AzjRkOQtVBTwIw4RJLTygFhJs3s=","revision":"c4dc1301f1dc0307acd38e611aa375a64dfe0642","revisionTime":"2017-07-12T04:46:15Z"},
|
||||
{"path":"github.com/StackExchange/wmi","checksumSHA1":"9NR0rrcAT5J76C5xMS4AVksS9o0=","revision":"e54cbda6595d7293a7a468ccf9525f6bc8887f99","revisionTime":"2016-08-11T21:45:55Z"},
|
||||
{"path":"github.com/armon/circbuf","checksumSHA1":"l0iFqayYAaEip6Olaq3/LCOa/Sg=","revision":"bbbad097214e2918d8543d5201d12bfd7bca254d","revisionTime":"2015-08-27T00:49:46Z"},
|
||||
{"path":"github.com/armon/go-metrics","checksumSHA1":"0et4hA6AYqZCgYiY+c6Z17t3k3k=","revision":"023a4bbe4bb9bfb23ee7e1afc8d0abad217641f3","revisionTime":"2017-08-09T01:16:44Z"},
|
||||
{"path":"github.com/armon/go-metrics/circonus","checksumSHA1":"xCsGGM9TKBogZDfSN536KtQdLko=","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||
{"path":"github.com/armon/go-metrics/datadog","checksumSHA1":"Dt0n1sSivvvdZQdzc4Hu/yOG+T0=","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||
{"path":"github.com/armon/go-radix","checksumSHA1":"gNO0JNpLzYOdInGeq7HqMZUzx9M=","revision":"4239b77079c7b5d1243b7b4736304ce8ddb6f0f2","revisionTime":"2016-01-15T23:47:25Z"},
|
||||
{"path":"github.com/bgentry/speakeasy","checksumSHA1":"dvd7Su+WNmHRP1+w1HezrPUCDsc=","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||
{"path":"github.com/bgentry/speakeasy/example","checksumSHA1":"twtRfb6484vfr2qqjiFkLThTjcQ=","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||
{"path":"github.com/boltdb/bolt","checksumSHA1":"C1BwfmTAUmldO4V3++tvYWzT49Y=","comment":"v1.2.1","revision":"dfb21201d9270c1082d5fb0f07f500311ff72f18","revisionTime":"2016-05-16T15:40:46Z","version":"v1.2.1","versionExact":"v1.2.1"},
|
||||
{"path":"github.com/circonus-labs/circonus-gometrics","checksumSHA1":"szvY4u7TlXkrQ3PW8wmyJaIFy0U=","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"path":"github.com/circonus-labs/circonus-gometrics/api","checksumSHA1":"WUE6oF152uN5FcLmmq+nO3Yl7pU=","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"path":"github.com/circonus-labs/circonus-gometrics/checkmgr","checksumSHA1":"beRBHHy2+V6Ht4cACIMmZOCnzgg=","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"path":"github.com/circonus-labs/circonusllhist","checksumSHA1":"C4Z7+l5GOpOCW5DcvNYzheGvQRE=","revision":"365d370cc1459bdcaceda3499453668373dea1d0","revisionTime":"2016-11-10T00:26:50Z"},
|
||||
{"path":"github.com/elazarl/go-bindata-assetfs","checksumSHA1":"5ftkjfUwI9A6xCQ1PwIAd5+qlo0=","revision":"e1a2a7ec64b07d04ac9ebb072404fe8b7b60de1b","revisionTime":"2016-08-03T19:23:04Z"},
|
||||
{"path":"github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs","checksumSHA1":"CaThXbumVxZtNlItiXma5B78PwQ=","revision":"e1a2a7ec64b07d04ac9ebb072404fe8b7b60de1b","revisionTime":"2016-08-03T19:23:04Z"},
|
||||
{"path":"github.com/go-ole/go-ole","checksumSHA1":"oyULO1B/l2OLy2xereo+2w3hYk4=","revision":"02d3668a0cf01f58411cc85cd37c174c257ec7c2","revisionTime":"2017-06-01T13:56:11Z"},
|
||||
{"path":"github.com/go-ole/go-ole/oleutil","checksumSHA1":"IvdiJE1NIogRmGi3WmteEKZQJB8=","revision":"5e9c030faf78847db7aa77e3661b9cc449de29b7","revisionTime":"2016-11-16T06:46:58Z"},
|
||||
{"path":"github.com/google/gofuzz","checksumSHA1":"PFtXkXPO7pwRtykVUUXtc07wc7U=","revision":"24818f796faf91cd76ec7bddd72458fbced7a6c1","revisionTime":"2017-06-12T17:47:53Z"},
|
||||
{"path":"github.com/hashicorp/errwrap","checksumSHA1":"cdOCt0Yb+hdErz8NAQqayxPmRsY=","revision":"7554cd9344cec97297fa6649b055a8c98c2a1e55","revisionTime":"2014-10-28T05:47:10Z"},
|
||||
{"path":"github.com/hashicorp/go-checkpoint","checksumSHA1":"nd3S1qkFv7zZxA9be0bw4nT0pe0=","revision":"e4b2dc34c0f698ee04750bf2035d8b9384233e1b","revisionTime":"2015-10-22T18:15:14Z"},
|
||||
{"path":"github.com/hashicorp/go-cleanhttp","checksumSHA1":"b8F628srIitj5p7Y130xc9k0QWs=","revision":"3573b8b52aa7b37b9358d966a898feb387f62437","revisionTime":"2017-02-11T01:34:15Z"},
|
||||
{"path":"github.com/hashicorp/go-discover","checksumSHA1":"uNoQWG5h2hzWHjaLi376ZXVaCr4=","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z"},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/aws","checksumSHA1":"6wdwVsYyTqW4ReHsWs4mEwoz1FI=","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/azure","checksumSHA1":"r97P32e+VmNMh2vwLkZa1zPEDQU=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/gce","checksumSHA1":"KC/MepQsQF17904UShiM61jmaEs=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/softlayer","checksumSHA1":"SIyZ44AHIUTBfI336ACpCeybsLg=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-immutable-radix","checksumSHA1":"Cas2nprG6pWzf05A2F/OlnjUu2Y=","revision":"8aac2701530899b64bdea735a1de8da899815220","revisionTime":"2017-07-25T22:12:15Z"},
|
||||
{"path":"github.com/hashicorp/go-memdb","checksumSHA1":"T65qvYBTy4rYks7oN+U0muEqtRw=","revision":"2b2d6c35e14e7557ea1003e707d5e179fa315028","revisionTime":"2017-07-25T22:15:03Z"},
|
||||
{"path":"github.com/hashicorp/go-msgpack/codec","checksumSHA1":"TNlVzNR1OaajcNi3CbQ3bGbaLGU=","revision":"fa3f63826f7c23912c15263591e65d54d080b458","revisionTime":"2015-05-18T23:42:57Z"},
|
||||
{"path":"github.com/hashicorp/go-multierror","checksumSHA1":"lrSl49G23l6NhfilxPM0XFs5rZo=","revision":"d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5","revisionTime":"2015-09-16T20:57:42Z"},
|
||||
{"path":"github.com/hashicorp/go-retryablehttp","checksumSHA1":"ErJHGU6AVPZM9yoY/xV11TwSjQs=","revision":"6e85be8fee1dcaa02c0eaaac2df5a8fbecf94145","revisionTime":"2016-09-30T03:51:02Z"},
|
||||
{"path":"github.com/hashicorp/go-rootcerts","checksumSHA1":"A1PcINvF3UiwHRKn8UcgARgvGRs=","revision":"6bb64b370b90e7ef1fa532be9e591a81c3493e00","revisionTime":"2016-05-03T14:34:40Z"},
|
||||
{"path":"github.com/hashicorp/go-sockaddr","checksumSHA1":"GP24Vz4EmZAL1ZH2TYTkDiiCO94=","revision":"2d10d7c10258d11196c0ebf2943509e4afd06cd4","revisionTime":"2017-05-23T22:50:28Z"},
|
||||
{"path":"github.com/hashicorp/go-sockaddr/template","checksumSHA1":"mIUCMmRHslN2bxQZ0uObMnXxk9E=","revision":"2d10d7c10258d11196c0ebf2943509e4afd06cd4","revisionTime":"2017-05-23T22:50:28Z"},
|
||||
{"path":"github.com/hashicorp/go-syslog","checksumSHA1":"xZ7Ban1x//6uUIU1xtrTbCYNHBc=","revision":"42a2b573b664dbf281bd48c3cc12c086b17a39ba","revisionTime":"2015-02-18T18:19:46Z"},
|
||||
{"path":"github.com/hashicorp/go-uuid","checksumSHA1":"mAkPa/RLuIwN53GbwIEMATexams=","revision":"64130c7a86d732268a38cb04cfbaf0cc987fda98","revisionTime":"2016-07-17T02:21:40Z"},
|
||||
{"path":"github.com/hashicorp/go-version","checksumSHA1":"tUGxc7rfX0cmhOOUDhMuAZ9rWsA=","revision":"03c5bf6be031b6dd45afec16b1cf94fc8938bc77","revisionTime":"2017-02-02T08:07:59Z"},
|
||||
{"path":"github.com/hashicorp/golang-lru","checksumSHA1":"d9PxF1XQGLMJZRct2R8qVM/eYlE=","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4","revisionTime":"2016-02-07T21:47:19Z"},
|
||||
{"path":"github.com/hashicorp/golang-lru/simplelru","checksumSHA1":"2nOpYjx8Sn57bqlZq17yM4YJuM4=","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4","revisionTime":"2016-02-07T21:47:19Z"},
|
||||
{"path":"github.com/hashicorp/hcl","checksumSHA1":"ydHBPi04mEh+Tir+2JkpSIMckcw=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/hcl/ast","checksumSHA1":"IxyvRpCFeoJBGl2obLKJV7RCGjg=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/hcl/parser","checksumSHA1":"l2oQxBsZRwn6eZjf+whXr8c9+8c=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/hcl/scanner","checksumSHA1":"vjhDQVlgHhdxml1V8/cj0vOe+j8=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/hcl/strconv","checksumSHA1":"JlZmnzqdmFFyb1+2afLyR3BOE/8=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/hcl/token","checksumSHA1":"c6yprzj06ASwCo18TtbbNNBHljA=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/json/parser","checksumSHA1":"jQ45CCc1ed/nlV7bbSnx6z72q1M=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/json/scanner","checksumSHA1":"S1e0F9ZKSnqgOLfjDTYazRL28tA=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hcl/json/token","checksumSHA1":"fNlXQCQEnb+B3k5UDL/r15xtSJY=","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"path":"github.com/hashicorp/hil","checksumSHA1":"kqCMCHy2b+RBMKC+ER+OPqp8C3E=","revision":"1e86c6b523c55d1fa6c6e930ce80b548664c95c2","revisionTime":"2016-07-11T23:18:37Z"},
|
||||
{"path":"github.com/hashicorp/hil/ast","checksumSHA1":"UICubs001+Q4MsUf9zl2vcMzWQQ=","revision":"1e86c6b523c55d1fa6c6e930ce80b548664c95c2","revisionTime":"2016-07-11T23:18:37Z"},
|
||||
{"path":"github.com/hashicorp/logutils","checksumSHA1":"vt+P9D2yWDO3gdvdgCzwqunlhxU=","revision":"0dc08b1671f34c4250ce212759ebd880f743d883","revisionTime":"2015-06-09T07:04:31Z"},
|
||||
{"path":"github.com/hashicorp/memberlist","checksumSHA1":"ml0MTqOsKTrsqv/mZhy78Vz4SfA=","revision":"d6c1fb0b99c33d0a8e22acea9da9709b369b5d39","revisionTime":"2017-08-15T22:46:17Z"},
|
||||
{"path":"github.com/hashicorp/net-rpc-msgpackrpc","checksumSHA1":"qnlqWJYV81ENr61SZk9c65R1mDo=","revision":"a14192a58a694c123d8fe5481d4a4727d6ae82f3","revisionTime":"2015-11-16T02:03:38Z"},
|
||||
{"path":"github.com/hashicorp/raft","checksumSHA1":"f2QYddVWZ2eWxdlCEhearTH4XOs=","revision":"c837e57a6077e74a4a3749959fb6cfefc26d7705","revisionTime":"2017-08-30T14:31:53Z","version":"library-v2-stage-one","versionExact":"library-v2-stage-one"},
|
||||
{"path":"github.com/hashicorp/raft-boltdb","checksumSHA1":"QAxukkv54/iIvLfsUP6IK4R0m/A=","revision":"d1e82c1ec3f15ee991f7cc7ffd5b67ff6f5bbaee","revisionTime":"2015-02-01T20:08:39Z"},
|
||||
{"path":"github.com/hashicorp/serf/coordinate","checksumSHA1":"/oss17GO4hXGM7QnUdI3VzcAHzA=","comment":"v0.7.0-66-g6c4672d","revision":"c2e4be24cdc9031eb0ad869c5d160775efdf7d7a","revisionTime":"2017-05-25T23:15:04Z"},
|
||||
{"path":"github.com/hashicorp/serf/serf","checksumSHA1":"3WPnGSL9ZK6EmkAE6tEW5SCxrd8=","comment":"v0.7.0-66-g6c4672d","revision":"b84a66cc5575994cb672940d244a2404141688c0","revisionTime":"2017-08-17T21:22:02Z"},
|
||||
{"path":"github.com/hashicorp/yamux","checksumSHA1":"ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=","revision":"d1caa6c97c9fc1cc9e83bbe34d0603f9ff0ce8bd","revisionTime":"2016-07-20T23:31:40Z"},
|
||||
{"path":"github.com/mattn/go-isatty","checksumSHA1":"xZuhljnmBysJPta/lMyYmJdujCg=","revision":"66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8","revisionTime":"2016-08-06T12:27:52Z"},
|
||||
{"path":"github.com/miekg/dns","checksumSHA1":"OUZ1FFXyKs+Cfg9M9rmXqqweQck=","revision":"db96a2b759cdef4f11a34506a42eb8d1290c598e","revisionTime":"2016-07-26T03:20:27Z"},
|
||||
{"path":"github.com/mitchellh/cli","checksumSHA1":"bUuI7AVR3IZPLlBaEKmw/ke7wqA=","revision":"b481eac70eea3ad671b7c360a013f89bb759b252","revisionTime":"2017-05-23T17:27:49Z"},
|
||||
{"path":"github.com/mitchellh/copystructure","checksumSHA1":"86nE93o1VIND0Doe8PuhCXnhUx0=","revision":"cdac8253d00f2ecf0a0b19fbff173a9a72de4f82","revisionTime":"2016-08-04T03:23:30Z"},
|
||||
{"path":"github.com/mitchellh/go-homedir","checksumSHA1":"V/quM7+em2ByJbWBLOsEwnY3j/Q=","revision":"b8bc1bf767474819792c23f32d8286a45736f1c6","revisionTime":"2016-12-03T19:45:07Z"},
|
||||
{"path":"github.com/mitchellh/mapstructure","checksumSHA1":"LUrnGREfnifW4WDMaavmc9MlLI0=","revision":"ca63d7c062ee3c9f34db231e352b60012b4fd0c1","revisionTime":"2016-08-08T18:12:53Z"},
|
||||
{"path":"github.com/mitchellh/reflectwalk","checksumSHA1":"mrqMlK6gqe//WsJSrJ1HgkPM0lM=","revision":"eecf4c70c626c7cfbb95c90195bc34d386c74ac6","revisionTime":"2015-05-27T15:31:53Z"},
|
||||
{"path":"github.com/pascaldekloe/goe/verify","checksumSHA1":"5h+ERzHw3Rl2G0kFPxoJzxiA9s0=","revision":"07ebd1e2481f616a278ab431cf04cc5cf5ab3ebe","revisionTime":"2017-03-28T18:37:59Z"},
|
||||
{"path":"github.com/pkg/errors","checksumSHA1":"ynJSWoF6v+3zMnh9R0QmmG6iGV8=","revision":"ff09b135c25aae272398c51a07235b90a75aa4f0","revisionTime":"2017-03-16T20:15:38Z","tree":true},
|
||||
{"path":"github.com/ryanuber/columnize","checksumSHA1":"ExnVEVNT8APpFTm26cUb5T09yR4=","comment":"v2.0.1-8-g983d3a5","revision":"9b3edd62028f107d7cabb19353292afd29311a4e","revisionTime":"2016-07-12T16:32:29Z"},
|
||||
{"path":"github.com/sean-/seed","checksumSHA1":"A/YUMbGg1LHIeK2+NLZBt+MIAao=","revision":"3c72d44db0c567f7c901f9c5da5fe68392227750","revisionTime":"2017-02-08T16:47:21Z"},
|
||||
{"path":"github.com/shirou/gopsutil/cpu","checksumSHA1":"zW2k8E1gkuySzTz2eXuSEDhpffY=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"path":"github.com/shirou/gopsutil/host","checksumSHA1":"GsqEEmGv6sj8DreS2SYXRkoZ9NI=","revision":"b62e301a8b9958eebb7299683eb57fab229a9501","revisionTime":"2017-02-08T02:55:55Z"},
|
||||
{"path":"github.com/shirou/gopsutil/internal/common","checksumSHA1":"hz9RxkaV3Tnju2eiHBWO/Yv7n5c=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"path":"github.com/shirou/gopsutil/mem","checksumSHA1":"XQwjGKI51Y3aQ3/jNyRh9Gnprgg=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"path":"github.com/shirou/gopsutil/net","checksumSHA1":"OSvOZs5uK5iolCOeS46nB2InVy8=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"path":"github.com/shirou/gopsutil/process","checksumSHA1":"JX0bRK/BdKVfbm4XOxMducVdY58=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"path":"github.com/shirou/w32","checksumSHA1":"Nve7SpDmjsv6+rhkXAkfg/UQx94=","revision":"bb4de0191aa41b5507caa14b0650cdbddcd9280b","revisionTime":"2016-09-30T03:27:40Z"},
|
||||
{"path":"github.com/tonnerre/golang-text","checksumSHA1":"t24KnvC9jRxiANVhpw2pqFpmEu8=","revision":"048ed3d792f7104850acbc8cfc01e5a6070f4c04","revisionTime":"2013-09-25T19:58:46Z"},
|
||||
{"path":"golang.org/x/net/context","checksumSHA1":"9jjO5GjLa0XF/nfWihF02RoH4qc=","revision":"075e191f18186a8ff2becaf64478e30f4545cdad","revisionTime":"2016-08-05T06:12:51Z"},
|
||||
{"path":"golang.org/x/net/context/ctxhttp","checksumSHA1":"WHc3uByvGaMcnSoI21fhzYgbOgg=","revision":"075e191f18186a8ff2becaf64478e30f4545cdad","revisionTime":"2016-08-05T06:12:51Z"},
|
||||
{"path":"golang.org/x/sys/unix","checksumSHA1":"vlicYp+fe4ECQ+5QqpAk36VRA3s=","revision":"cd2c276457edda6df7fb04895d3fd6a6add42926","revisionTime":"2017-07-17T10:05:24Z"},
|
||||
{"path":"golang.org/x/time/rate","checksumSHA1":"vGfePfr0+weQUeTM/71mu+LCFuE=","revision":"8be79e1e0910c292df4e79c241bb7e8f7e725959","revisionTime":"2017-04-24T23:28:54Z"}
|
||||
{"checksumSHA1":"JhyS/zIicgtrSasHSZ6WtXGWJVk=","path":"github.com/DataDog/datadog-go/statsd","revision":"cc2f4770f4d61871e19bfee967bc767fe730b0d9","revisionTime":"2016-03-29T13:52:53Z"},
|
||||
{"checksumSHA1":"AzjRkOQtVBTwIw4RJLTygFhJs3s=","path":"github.com/Microsoft/go-winio","revision":"c4dc1301f1dc0307acd38e611aa375a64dfe0642","revisionTime":"2017-07-12T04:46:15Z"},
|
||||
{"checksumSHA1":"9NR0rrcAT5J76C5xMS4AVksS9o0=","path":"github.com/StackExchange/wmi","revision":"e54cbda6595d7293a7a468ccf9525f6bc8887f99","revisionTime":"2016-08-11T21:45:55Z"},
|
||||
{"checksumSHA1":"l0iFqayYAaEip6Olaq3/LCOa/Sg=","path":"github.com/armon/circbuf","revision":"bbbad097214e2918d8543d5201d12bfd7bca254d","revisionTime":"2015-08-27T00:49:46Z"},
|
||||
{"checksumSHA1":"0et4hA6AYqZCgYiY+c6Z17t3k3k=","path":"github.com/armon/go-metrics","revision":"023a4bbe4bb9bfb23ee7e1afc8d0abad217641f3","revisionTime":"2017-08-09T01:16:44Z"},
|
||||
{"checksumSHA1":"xCsGGM9TKBogZDfSN536KtQdLko=","path":"github.com/armon/go-metrics/circonus","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||
{"checksumSHA1":"Dt0n1sSivvvdZQdzc4Hu/yOG+T0=","path":"github.com/armon/go-metrics/datadog","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||
{"checksumSHA1":"gNO0JNpLzYOdInGeq7HqMZUzx9M=","path":"github.com/armon/go-radix","revision":"4239b77079c7b5d1243b7b4736304ce8ddb6f0f2","revisionTime":"2016-01-15T23:47:25Z"},
|
||||
{"checksumSHA1":"dvd7Su+WNmHRP1+w1HezrPUCDsc=","path":"github.com/bgentry/speakeasy","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||
{"checksumSHA1":"twtRfb6484vfr2qqjiFkLThTjcQ=","path":"github.com/bgentry/speakeasy/example","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||
{"checksumSHA1":"C1BwfmTAUmldO4V3++tvYWzT49Y=","comment":"v1.2.1","path":"github.com/boltdb/bolt","revision":"dfb21201d9270c1082d5fb0f07f500311ff72f18","revisionTime":"2016-05-16T15:40:46Z","version":"v1.2.1","versionExact":"v1.2.1"},
|
||||
{"checksumSHA1":"szvY4u7TlXkrQ3PW8wmyJaIFy0U=","path":"github.com/circonus-labs/circonus-gometrics","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"checksumSHA1":"WUE6oF152uN5FcLmmq+nO3Yl7pU=","path":"github.com/circonus-labs/circonus-gometrics/api","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"checksumSHA1":"beRBHHy2+V6Ht4cACIMmZOCnzgg=","path":"github.com/circonus-labs/circonus-gometrics/checkmgr","revision":"d17a8420c36e800fcb46bbd4d2a15b93c68605ea","revisionTime":"2016-11-09T19:23:37Z"},
|
||||
{"checksumSHA1":"C4Z7+l5GOpOCW5DcvNYzheGvQRE=","path":"github.com/circonus-labs/circonusllhist","revision":"365d370cc1459bdcaceda3499453668373dea1d0","revisionTime":"2016-11-10T00:26:50Z"},
|
||||
{"checksumSHA1":"5ftkjfUwI9A6xCQ1PwIAd5+qlo0=","path":"github.com/elazarl/go-bindata-assetfs","revision":"e1a2a7ec64b07d04ac9ebb072404fe8b7b60de1b","revisionTime":"2016-08-03T19:23:04Z"},
|
||||
{"checksumSHA1":"CaThXbumVxZtNlItiXma5B78PwQ=","path":"github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs","revision":"e1a2a7ec64b07d04ac9ebb072404fe8b7b60de1b","revisionTime":"2016-08-03T19:23:04Z"},
|
||||
{"checksumSHA1":"oyULO1B/l2OLy2xereo+2w3hYk4=","path":"github.com/go-ole/go-ole","revision":"02d3668a0cf01f58411cc85cd37c174c257ec7c2","revisionTime":"2017-06-01T13:56:11Z"},
|
||||
{"checksumSHA1":"IvdiJE1NIogRmGi3WmteEKZQJB8=","path":"github.com/go-ole/go-ole/oleutil","revision":"5e9c030faf78847db7aa77e3661b9cc449de29b7","revisionTime":"2016-11-16T06:46:58Z"},
|
||||
{"checksumSHA1":"PFtXkXPO7pwRtykVUUXtc07wc7U=","path":"github.com/google/gofuzz","revision":"24818f796faf91cd76ec7bddd72458fbced7a6c1","revisionTime":"2017-06-12T17:47:53Z"},
|
||||
{"checksumSHA1":"cdOCt0Yb+hdErz8NAQqayxPmRsY=","path":"github.com/hashicorp/errwrap","revision":"7554cd9344cec97297fa6649b055a8c98c2a1e55","revisionTime":"2014-10-28T05:47:10Z"},
|
||||
{"checksumSHA1":"nd3S1qkFv7zZxA9be0bw4nT0pe0=","path":"github.com/hashicorp/go-checkpoint","revision":"e4b2dc34c0f698ee04750bf2035d8b9384233e1b","revisionTime":"2015-10-22T18:15:14Z"},
|
||||
{"checksumSHA1":"b8F628srIitj5p7Y130xc9k0QWs=","path":"github.com/hashicorp/go-cleanhttp","revision":"3573b8b52aa7b37b9358d966a898feb387f62437","revisionTime":"2017-02-11T01:34:15Z"},
|
||||
{"checksumSHA1":"uNoQWG5h2hzWHjaLi376ZXVaCr4=","path":"github.com/hashicorp/go-discover","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z"},
|
||||
{"checksumSHA1":"6wdwVsYyTqW4ReHsWs4mEwoz1FI=","path":"github.com/hashicorp/go-discover/provider/aws","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z","tree":true},
|
||||
{"checksumSHA1":"r97P32e+VmNMh2vwLkZa1zPEDQU=","path":"github.com/hashicorp/go-discover/provider/azure","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"checksumSHA1":"KC/MepQsQF17904UShiM61jmaEs=","path":"github.com/hashicorp/go-discover/provider/gce","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"checksumSHA1":"SIyZ44AHIUTBfI336ACpCeybsLg=","path":"github.com/hashicorp/go-discover/provider/softlayer","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"checksumSHA1":"Cas2nprG6pWzf05A2F/OlnjUu2Y=","path":"github.com/hashicorp/go-immutable-radix","revision":"8aac2701530899b64bdea735a1de8da899815220","revisionTime":"2017-07-25T22:12:15Z"},
|
||||
{"checksumSHA1":"T65qvYBTy4rYks7oN+U0muEqtRw=","path":"github.com/hashicorp/go-memdb","revision":"2b2d6c35e14e7557ea1003e707d5e179fa315028","revisionTime":"2017-07-25T22:15:03Z"},
|
||||
{"checksumSHA1":"TNlVzNR1OaajcNi3CbQ3bGbaLGU=","path":"github.com/hashicorp/go-msgpack/codec","revision":"fa3f63826f7c23912c15263591e65d54d080b458","revisionTime":"2015-05-18T23:42:57Z"},
|
||||
{"checksumSHA1":"lrSl49G23l6NhfilxPM0XFs5rZo=","path":"github.com/hashicorp/go-multierror","revision":"d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5","revisionTime":"2015-09-16T20:57:42Z"},
|
||||
{"checksumSHA1":"ErJHGU6AVPZM9yoY/xV11TwSjQs=","path":"github.com/hashicorp/go-retryablehttp","revision":"6e85be8fee1dcaa02c0eaaac2df5a8fbecf94145","revisionTime":"2016-09-30T03:51:02Z"},
|
||||
{"checksumSHA1":"A1PcINvF3UiwHRKn8UcgARgvGRs=","path":"github.com/hashicorp/go-rootcerts","revision":"6bb64b370b90e7ef1fa532be9e591a81c3493e00","revisionTime":"2016-05-03T14:34:40Z"},
|
||||
{"checksumSHA1":"GP24Vz4EmZAL1ZH2TYTkDiiCO94=","path":"github.com/hashicorp/go-sockaddr","revision":"2d10d7c10258d11196c0ebf2943509e4afd06cd4","revisionTime":"2017-05-23T22:50:28Z"},
|
||||
{"checksumSHA1":"mIUCMmRHslN2bxQZ0uObMnXxk9E=","path":"github.com/hashicorp/go-sockaddr/template","revision":"2d10d7c10258d11196c0ebf2943509e4afd06cd4","revisionTime":"2017-05-23T22:50:28Z"},
|
||||
{"checksumSHA1":"xZ7Ban1x//6uUIU1xtrTbCYNHBc=","path":"github.com/hashicorp/go-syslog","revision":"42a2b573b664dbf281bd48c3cc12c086b17a39ba","revisionTime":"2015-02-18T18:19:46Z"},
|
||||
{"checksumSHA1":"mAkPa/RLuIwN53GbwIEMATexams=","path":"github.com/hashicorp/go-uuid","revision":"64130c7a86d732268a38cb04cfbaf0cc987fda98","revisionTime":"2016-07-17T02:21:40Z"},
|
||||
{"checksumSHA1":"tUGxc7rfX0cmhOOUDhMuAZ9rWsA=","path":"github.com/hashicorp/go-version","revision":"03c5bf6be031b6dd45afec16b1cf94fc8938bc77","revisionTime":"2017-02-02T08:07:59Z"},
|
||||
{"checksumSHA1":"d9PxF1XQGLMJZRct2R8qVM/eYlE=","path":"github.com/hashicorp/golang-lru","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4","revisionTime":"2016-02-07T21:47:19Z"},
|
||||
{"checksumSHA1":"2nOpYjx8Sn57bqlZq17yM4YJuM4=","path":"github.com/hashicorp/golang-lru/simplelru","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4","revisionTime":"2016-02-07T21:47:19Z"},
|
||||
{"checksumSHA1":"ydHBPi04mEh+Tir+2JkpSIMckcw=","path":"github.com/hashicorp/hcl","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"IxyvRpCFeoJBGl2obLKJV7RCGjg=","path":"github.com/hashicorp/hcl/hcl/ast","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"l2oQxBsZRwn6eZjf+whXr8c9+8c=","path":"github.com/hashicorp/hcl/hcl/parser","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"vjhDQVlgHhdxml1V8/cj0vOe+j8=","path":"github.com/hashicorp/hcl/hcl/scanner","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"JlZmnzqdmFFyb1+2afLyR3BOE/8=","path":"github.com/hashicorp/hcl/hcl/strconv","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"c6yprzj06ASwCo18TtbbNNBHljA=","path":"github.com/hashicorp/hcl/hcl/token","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"jQ45CCc1ed/nlV7bbSnx6z72q1M=","path":"github.com/hashicorp/hcl/json/parser","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"S1e0F9ZKSnqgOLfjDTYazRL28tA=","path":"github.com/hashicorp/hcl/json/scanner","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"fNlXQCQEnb+B3k5UDL/r15xtSJY=","path":"github.com/hashicorp/hcl/json/token","revision":"d8c773c4cba11b11539e3d45f93daeaa5dcf1fa1","revisionTime":"2016-07-11T23:17:52Z"},
|
||||
{"checksumSHA1":"kqCMCHy2b+RBMKC+ER+OPqp8C3E=","path":"github.com/hashicorp/hil","revision":"1e86c6b523c55d1fa6c6e930ce80b548664c95c2","revisionTime":"2016-07-11T23:18:37Z"},
|
||||
{"checksumSHA1":"UICubs001+Q4MsUf9zl2vcMzWQQ=","path":"github.com/hashicorp/hil/ast","revision":"1e86c6b523c55d1fa6c6e930ce80b548664c95c2","revisionTime":"2016-07-11T23:18:37Z"},
|
||||
{"checksumSHA1":"vt+P9D2yWDO3gdvdgCzwqunlhxU=","path":"github.com/hashicorp/logutils","revision":"0dc08b1671f34c4250ce212759ebd880f743d883","revisionTime":"2015-06-09T07:04:31Z"},
|
||||
{"checksumSHA1":"ml0MTqOsKTrsqv/mZhy78Vz4SfA=","path":"github.com/hashicorp/memberlist","revision":"d6c1fb0b99c33d0a8e22acea9da9709b369b5d39","revisionTime":"2017-08-15T22:46:17Z"},
|
||||
{"checksumSHA1":"qnlqWJYV81ENr61SZk9c65R1mDo=","path":"github.com/hashicorp/net-rpc-msgpackrpc","revision":"a14192a58a694c123d8fe5481d4a4727d6ae82f3","revisionTime":"2015-11-16T02:03:38Z"},
|
||||
{"checksumSHA1":"f2QYddVWZ2eWxdlCEhearTH4XOs=","path":"github.com/hashicorp/raft","revision":"c837e57a6077e74a4a3749959fb6cfefc26d7705","revisionTime":"2017-08-30T14:31:53Z","version":"library-v2-stage-one","versionExact":"library-v2-stage-one"},
|
||||
{"checksumSHA1":"QAxukkv54/iIvLfsUP6IK4R0m/A=","path":"github.com/hashicorp/raft-boltdb","revision":"d1e82c1ec3f15ee991f7cc7ffd5b67ff6f5bbaee","revisionTime":"2015-02-01T20:08:39Z"},
|
||||
{"checksumSHA1":"/oss17GO4hXGM7QnUdI3VzcAHzA=","comment":"v0.7.0-66-g6c4672d","path":"github.com/hashicorp/serf/coordinate","revision":"c2e4be24cdc9031eb0ad869c5d160775efdf7d7a","revisionTime":"2017-05-25T23:15:04Z"},
|
||||
{"checksumSHA1":"3WPnGSL9ZK6EmkAE6tEW5SCxrd8=","comment":"v0.7.0-66-g6c4672d","path":"github.com/hashicorp/serf/serf","revision":"b84a66cc5575994cb672940d244a2404141688c0","revisionTime":"2017-08-17T21:22:02Z"},
|
||||
{"checksumSHA1":"ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=","path":"github.com/hashicorp/yamux","revision":"d1caa6c97c9fc1cc9e83bbe34d0603f9ff0ce8bd","revisionTime":"2016-07-20T23:31:40Z"},
|
||||
{"checksumSHA1":"xZuhljnmBysJPta/lMyYmJdujCg=","path":"github.com/mattn/go-isatty","revision":"66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8","revisionTime":"2016-08-06T12:27:52Z"},
|
||||
{"checksumSHA1":"OUZ1FFXyKs+Cfg9M9rmXqqweQck=","path":"github.com/miekg/dns","revision":"db96a2b759cdef4f11a34506a42eb8d1290c598e","revisionTime":"2016-07-26T03:20:27Z"},
|
||||
{"checksumSHA1":"GzfpPGtV2UJH9hFsKwzGjKrhp/A=","path":"github.com/mitchellh/cli","revision":"dff723fff508858a44c1f4bd0911f00d73b0202f","revisionTime":"2017-09-05T22:10:09Z"},
|
||||
{"checksumSHA1":"86nE93o1VIND0Doe8PuhCXnhUx0=","path":"github.com/mitchellh/copystructure","revision":"cdac8253d00f2ecf0a0b19fbff173a9a72de4f82","revisionTime":"2016-08-04T03:23:30Z"},
|
||||
{"checksumSHA1":"V/quM7+em2ByJbWBLOsEwnY3j/Q=","path":"github.com/mitchellh/go-homedir","revision":"b8bc1bf767474819792c23f32d8286a45736f1c6","revisionTime":"2016-12-03T19:45:07Z"},
|
||||
{"checksumSHA1":"LUrnGREfnifW4WDMaavmc9MlLI0=","path":"github.com/mitchellh/mapstructure","revision":"ca63d7c062ee3c9f34db231e352b60012b4fd0c1","revisionTime":"2016-08-08T18:12:53Z"},
|
||||
{"checksumSHA1":"mrqMlK6gqe//WsJSrJ1HgkPM0lM=","path":"github.com/mitchellh/reflectwalk","revision":"eecf4c70c626c7cfbb95c90195bc34d386c74ac6","revisionTime":"2015-05-27T15:31:53Z"},
|
||||
{"checksumSHA1":"5h+ERzHw3Rl2G0kFPxoJzxiA9s0=","path":"github.com/pascaldekloe/goe/verify","revision":"07ebd1e2481f616a278ab431cf04cc5cf5ab3ebe","revisionTime":"2017-03-28T18:37:59Z"},
|
||||
{"checksumSHA1":"ynJSWoF6v+3zMnh9R0QmmG6iGV8=","path":"github.com/pkg/errors","revision":"ff09b135c25aae272398c51a07235b90a75aa4f0","revisionTime":"2017-03-16T20:15:38Z","tree":true},
|
||||
{"checksumSHA1":"rTNABfFJ9wtLQRH8uYNkEZGQOrY=","path":"github.com/posener/complete","revision":"9f41f7636a724791a3b8b1d35e84caa1124f0d3c","revisionTime":"2017-08-29T17:11:12Z"},
|
||||
{"checksumSHA1":"NB7uVS0/BJDmNu68vPAlbrq4TME=","path":"github.com/posener/complete/cmd","revision":"9f41f7636a724791a3b8b1d35e84caa1124f0d3c","revisionTime":"2017-08-29T17:11:12Z"},
|
||||
{"checksumSHA1":"gSX86Xl0w9hvtntdT8h23DZtSag=","path":"github.com/posener/complete/cmd/install","revision":"9f41f7636a724791a3b8b1d35e84caa1124f0d3c","revisionTime":"2017-08-29T17:11:12Z"},
|
||||
{"checksumSHA1":"DMo94FwJAm9ZCYCiYdJU2+bh4no=","path":"github.com/posener/complete/match","revision":"9f41f7636a724791a3b8b1d35e84caa1124f0d3c","revisionTime":"2017-08-29T17:11:12Z"},
|
||||
{"checksumSHA1":"ExnVEVNT8APpFTm26cUb5T09yR4=","comment":"v2.0.1-8-g983d3a5","path":"github.com/ryanuber/columnize","revision":"9b3edd62028f107d7cabb19353292afd29311a4e","revisionTime":"2016-07-12T16:32:29Z"},
|
||||
{"checksumSHA1":"A/YUMbGg1LHIeK2+NLZBt+MIAao=","path":"github.com/sean-/seed","revision":"3c72d44db0c567f7c901f9c5da5fe68392227750","revisionTime":"2017-02-08T16:47:21Z"},
|
||||
{"checksumSHA1":"zW2k8E1gkuySzTz2eXuSEDhpffY=","path":"github.com/shirou/gopsutil/cpu","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"checksumSHA1":"GsqEEmGv6sj8DreS2SYXRkoZ9NI=","path":"github.com/shirou/gopsutil/host","revision":"b62e301a8b9958eebb7299683eb57fab229a9501","revisionTime":"2017-02-08T02:55:55Z"},
|
||||
{"checksumSHA1":"hz9RxkaV3Tnju2eiHBWO/Yv7n5c=","path":"github.com/shirou/gopsutil/internal/common","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"checksumSHA1":"XQwjGKI51Y3aQ3/jNyRh9Gnprgg=","path":"github.com/shirou/gopsutil/mem","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"checksumSHA1":"OSvOZs5uK5iolCOeS46nB2InVy8=","path":"github.com/shirou/gopsutil/net","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"checksumSHA1":"JX0bRK/BdKVfbm4XOxMducVdY58=","path":"github.com/shirou/gopsutil/process","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"},
|
||||
{"checksumSHA1":"Nve7SpDmjsv6+rhkXAkfg/UQx94=","path":"github.com/shirou/w32","revision":"bb4de0191aa41b5507caa14b0650cdbddcd9280b","revisionTime":"2016-09-30T03:27:40Z"},
|
||||
{"checksumSHA1":"t24KnvC9jRxiANVhpw2pqFpmEu8=","path":"github.com/tonnerre/golang-text","revision":"048ed3d792f7104850acbc8cfc01e5a6070f4c04","revisionTime":"2013-09-25T19:58:46Z"},
|
||||
{"checksumSHA1":"9jjO5GjLa0XF/nfWihF02RoH4qc=","path":"golang.org/x/net/context","revision":"075e191f18186a8ff2becaf64478e30f4545cdad","revisionTime":"2016-08-05T06:12:51Z"},
|
||||
{"checksumSHA1":"WHc3uByvGaMcnSoI21fhzYgbOgg=","path":"golang.org/x/net/context/ctxhttp","revision":"075e191f18186a8ff2becaf64478e30f4545cdad","revisionTime":"2016-08-05T06:12:51Z"},
|
||||
{"checksumSHA1":"vlicYp+fe4ECQ+5QqpAk36VRA3s=","path":"golang.org/x/sys/unix","revision":"cd2c276457edda6df7fb04895d3fd6a6add42926","revisionTime":"2017-07-17T10:05:24Z"},
|
||||
{"checksumSHA1":"vGfePfr0+weQUeTM/71mu+LCFuE=","path":"golang.org/x/time/rate","revision":"8be79e1e0910c292df4e79c241bb7e8f7e725959","revisionTime":"2017-04-24T23:28:54Z"}
|
||||
],
|
||||
"rootPath": "github.com/hashicorp/consul"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue