commit e2ad376b6c44e1a91dacd6345bc88e64f33b217d Author: Kevin Franklin Kim Date: Tue Jan 3 15:37:15 2023 +0100 initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0977931 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yaml,yml,md,mdx}] +indent_style = space diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec358f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# General +.* +._* +bin/ +tmp/ +*.log +!.posh +!.gitignore +!.posh.yaml +!.editorconfig +!.golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..e050e2e --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,114 @@ +run: + go: "1.19" + skip-dirs: + - tmp + +linters-settings: + revive: + rules: + - name: indent-error-flow + disabled: true + # https://golangci-lint.run/usage/linters/#gocritic + gocritic: + disabled-checks: + - assignOp + - ifElseChain + - singleCaseSwitch + - commentFormatting +linters: + + enable: + # Enabled by default linters: + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false] + - gosimple # (megacheck): Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false] + - govet # (vet, vetshadow): Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false] + - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false] + - staticcheck # (megacheck): It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false] + - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: false, auto-fix: false] + - unused # (megacheck): Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] + + # Disabled by default linters: + - asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false] + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false] + - bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false] + - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] + #- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: true, auto-fix: false] + - contextcheck # check the function whether to use a non-inherited context [fast: false, auto-fix: false] + - cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false] + - decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false] + - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] + - dupl # Tool for code clone detection [fast: true, auto-fix: false] + - dupword # Checks for duplicate words in the source code [fast: true, auto-fix: true] + - durationcheck # check for two durations multiplied together [fast: false, auto-fix: false] + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted. [fast: false, auto-fix: false] + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false] + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false] + - execinquery # execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds [fast: false, auto-fix: false] + - exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false] + #- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false] + - exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false] + #- forbidigo # Forbids identifiers [fast: true, auto-fix: false] + - forcetypeassert # finds forced type assertions [fast: true, auto-fix: false] + #- funlen # Tool for detection of long functions [fast: true, auto-fix: false] + #- gci # Gci controls golang package import order and makes it always deterministic. [fast: true, auto-fix: false] + #- gochecknoglobals # check that no global variables exist [fast: true, auto-fix: false] + #- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] + - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] + - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] + - gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false] + - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] + #- godot # Check if comments end in a period [fast: true, auto-fix: true] + #- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false] + #- goerr113 # Golang linter to check the errors handling expressions [fast: false, auto-fix: false] + - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true] + #- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true] + - goheader # Checks is file header matches to pattern [fast: true, auto-fix: false] + - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt. [fast: true, auto-fix: true] + #- gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false] + #- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false] + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false] + - goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false] + - gosec # (gas): Inspects source code for security problems [fast: false, auto-fix: false] + - grouper # An analyzer to analyze expression groups. [fast: true, auto-fix: false] + - importas # Enforces consistent import aliases [fast: false, auto-fix: false] + - interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false] + #- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false] + - loggercheck # Checks key valur pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false] + - lll # Reports long lines [fast: true, auto-fix: false] + - maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false] + - makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false] + - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] + - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] + - nestif # Reports deeply nested if statements [fast: true, auto-fix: false] + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false] + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false] + #- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false] + - noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false] + - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false] + #- nonamedreturns # Reports all named returns [fast: false, auto-fix: false] + - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false] + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test [fast: false, auto-fix: false] + - prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false] + - predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false] + - promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false] + - reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false] + - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false] + - rowserrcheck # checks whether Err of rows is checked successfully [fast: false, auto-fix: false] + - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. [fast: false, auto-fix: false] + - stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false] + - tagliatelle # Checks the struct tags. [fast: true, auto-fix: false] + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false] + - testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false] + - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false] + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false] + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes [fast: false, auto-fix: false] + - unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false] + - unparam # Reports unused function parameters [fast: false, auto-fix: false] + - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false] + #- varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false] + - wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false] + - whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true] + # - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false] + #- wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false] + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b024ab4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Foomo web framework + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..74bd1fa --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +.DEFAULT_GOAL:=help + +## === Tasks === + +.PHONY: check +## Run tests and linters +check: tidy lint test + +.PHONY: tidy +## Run go mod tidy +tidy: files=$(shell find . -type f -name go.mod) +tidy: dirs=$(foreach file,$(files),$(dir $(file)) ) +tidy: + @for dir in $(dirs); do cd $$dir && go mod tidy; done + +.PHONY: outdated +## Show outdated direct dependencies +outdated: files=$(shell find . -type f -name go.mod) +outdated: dirs=$(foreach file,$(files),$(dir $(file)) ) +outdated: + @for dir in $(dirs); do cd $$dir && go list -u -m -json all | go-mod-outdated -update -direct; done + +.PHONY: test +## Run tests +test: + go test -v ./... + +.PHONY: lint +## Run linter +lint: files=$(shell find . -type f -name go.mod) +lint: dirs=$(foreach file,$(files),$(dir $(file)) ) +lint: + @for dir in $(dirs); do cd $$dir && golangci-lint run; done + +.PHONY: lint.fix +## Fix lint violations +lint.fix: files=$(shell find . -type f -name go.mod) +lint.fix: dirs=$(foreach file,$(files),$(dir $(file)) ) +lint.fix: + @for dir in $(dirs); do cd $$dir && golangci-lint run --fix; done + +.PHONY: lint.super +## Run super linter +lint.super: + docker run --rm -it \ + -e 'RUN_LOCAL=true' \ + -e 'DEFAULT_BRANCH=main' \ + -e 'IGNORE_GITIGNORED_FILES=true' \ + -e 'VALIDATE_JSCPD=false' \ + -e 'VALIDATE_GO=false' \ + -v $(PWD):/tmp/lint \ + github/super-linter + +## === Utils === + +## Show help text +help: + @awk '{ \ + if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \ + helpCommand = substr($$0, index($$0, ":") + 2); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \ + helpCommand = substr($$0, 0, index($$0, ":")); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage"\n"; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^##/) { \ + if (helpMessage) { \ + helpMessage = helpMessage"\n "substr($$0, 3); \ + } else { \ + helpMessage = substr($$0, 3); \ + } \ + } else { \ + if (helpMessage) { \ + print "\n "helpMessage"\n" \ + } \ + helpMessage = ""; \ + } \ + }' \ + $(MAKEFILE_LIST) diff --git a/README.md b/README.md new file mode 100644 index 0000000..05b1dba --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Project Oriented SHELL (posh) + +> think of `posh` as an interactive and hackable Makefile + +## Installing + +Install the latest release of the cli: + +````bash +$ brew update +$ brew install brew install foomo/posh +```` + +## Usage + +To start using posh, go into your project and run: + +```bash +$ cd your/project +$ posh init +``` + +This will generate the standard layout for posh which can be changed as required through `.posh.yaml`. + +Once initialized you can start posh through: + +```bash +$ posh prompt +``` diff --git a/cmd/depenencies.go b/cmd/depenencies.go new file mode 100644 index 0000000..2b11ddf --- /dev/null +++ b/cmd/depenencies.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/foomo/posh/internal/util" + "github.com/foomo/posh/pkg/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// dependenciesCmd represents the dependencies command +var dependenciesCmd = &cobra.Command{ + Use: "dependencies", + Short: "Run dependency validations", + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + var cfg config.Dependencies + if err := viper.UnmarshalKey("dependencies", &cfg); err != nil { + return err + } + + plg, err := util.LoadPlugin(cmd.Context(), m) + if err != nil { + return err + } + + return plg.Dependencies(cmd.Context(), cfg) + }, +} + +func init() { + rootCmd.AddCommand(dependenciesCmd) +} diff --git a/cmd/exec.go b/cmd/exec.go new file mode 100644 index 0000000..f888da1 --- /dev/null +++ b/cmd/exec.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "github.com/foomo/posh/internal/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// execCmd represents the exec command +var execCmd = &cobra.Command{ + Use: "exec", + Short: "Execute a single Project Oriented Shell command", + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing [cmd] argument") + } + + plg, err := util.LoadPlugin(cmd.Context(), m) + if err != nil { + return err + } + + return plg.Execute(cmd.Context(), args) + }, +} + +func init() { + rootCmd.AddCommand(execCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // execCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // execCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..1234802 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "os" + "path/filepath" + + "github.com/foomo/posh/embed" + "github.com/foomo/posh/pkg/scaffold" + "github.com/spf13/cobra" +) + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init [path]", + Short: "Initialize a Project Oriented Shell", + Long: `Initialize (posh init) will create a new Project Oriented Shell with the appropriate structure. + +Posh init must be run inside of a go module (please run "go mod init first)"`, + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + wd, err := os.Getwd() + if err != nil { + return err + } + + if len(args) > 0 && args[0] != "." { + wd = filepath.Join(wd, args[0]) + } + + fs, err := embed.Scaffold("init") + if err != nil { + return err + } + + sc, err := scaffold.New( + scaffold.WithLogger(l), + ) + if err != nil { + return err + } + + return sc.Render(fs, wd, nil) + }, +} + +func init() { + rootCmd.AddCommand(initCmd) +} diff --git a/cmd/packages.go b/cmd/packages.go new file mode 100644 index 0000000..cf47e89 --- /dev/null +++ b/cmd/packages.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/foomo/posh/internal/util" + "github.com/foomo/posh/pkg/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// packagesCmd represents the dependencies command +var packagesCmd = &cobra.Command{ + Use: "packages", + Short: "Check and install required packages.", + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + var cfg []config.Package + if err := viper.UnmarshalKey("packages", &cfg); err != nil { + return err + } + + plg, err := util.LoadPlugin(cmd.Context(), m) + if err != nil { + return err + } + + return plg.Packages(cmd.Context(), cfg) + }, +} + +func init() { + rootCmd.AddCommand(packagesCmd) +} diff --git a/cmd/prompt.go b/cmd/prompt.go new file mode 100644 index 0000000..9344251 --- /dev/null +++ b/cmd/prompt.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/foomo/posh/internal/util" + "github.com/foomo/posh/pkg/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// promptCmd represents the prompt command +var promptCmd = &cobra.Command{ + Use: "prompt", + Short: "Start the interactive Project Oriented Shell", + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + var cfg config.Prompt + if err := viper.UnmarshalKey("prompt", &cfg); err != nil { + return err + } + + plg, err := util.LoadPlugin(cmd.Context(), m) + if err != nil { + return err + } + + return plg.Prompt(cmd.Context(), cfg) + }, +} + +func init() { + rootCmd.AddCommand(promptCmd) +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..72f081b --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,134 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "os/signal" + "strings" + + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/plugin" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + EnvProjectRoot = "PROJECT_ROOT" +) + +var ( + l log.Logger + m *plugin.Manager + flagLevel string + flagConfig string + flagNoColor bool +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "posh", + Short: "Start the Project Oriented Shell", + Long: `Start the Project Oriented Shell in interactive mode.`, + Run: func(cmd *cobra.Command, args []string) { + // use the hypothetical UnknownArgs() pflag API + fmt.Println(args) + return + + }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + osInterrupt := make(chan os.Signal, 1) + signal.Notify(osInterrupt, os.Interrupt) + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + signal.Stop(osInterrupt) + cancel() + }() + go func() { + <-osInterrupt + l.Debug("received interrupt") + cancel() + }() + + if err := rootCmd.ExecuteContext(ctx); errors.Is(err, context.Canceled) { + l.Warn(err.Error()) + os.Exit(0) + } else if err != nil { + l.Error(err.Error()) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initialize) + + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + rootCmd.PersistentFlags().BoolVar(&flagNoColor, "no-color", false, "disabled colors (default is false)") + rootCmd.PersistentFlags().StringVar(&flagLevel, "level", "info", "set log level (default is warn)") + rootCmd.PersistentFlags().StringVar(&flagConfig, "config", "", "config file (default is $HOME/.posh.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + rootCmd.Flags().Bool("no-validate", false, "Skip validation") +} + +// initialize reads in config file and ENV variables if set. +func initialize() { + // setup logger + if value, err := log.NewPTerm( + log.PTermWithDisableColor(flagNoColor), + log.PTermWithLevel(log.GetLevel(flagLevel)), + ); err != nil { + cobra.CheckErr(err) + } else { + l = value + } + + // setup viper + if flagConfig != "" { + // Use config file from the flag. + viper.SetConfigFile(flagConfig) + } else { + wd, err := os.Getwd() + l.Must(err) + viper.AddConfigPath(wd) + viper.SetConfigType("yaml") + viper.SetConfigName(".posh") + } + + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() + + l.Must(viper.ReadInConfig()) + l.Debug("using config file:", viper.ConfigFileUsed()) + + // validate version + if v := viper.GetString("version"); v != "v1.0" { + l.Must(fmt.Errorf("invalid config version: %s (v1.0)", v)) + } + + // setup env + if value := os.Getenv(EnvProjectRoot); value != "" { + // continue + } else if value, err := os.Getwd(); err != nil { + l.Must(errors.Wrap(err, "failed to retrieve project root")) + } else if err := os.Setenv(EnvProjectRoot, value); err != nil { + l.Must(errors.Wrap(err, "failed to set project root env")) + } + for key, value := range viper.GetStringMapString("env") { + l.Must(os.Setenv(key, os.ExpandEnv(value))) + } + + // setup manager + var err error + m, err = plugin.NewManager(l) + l.Must(err) +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..f1d486c --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +const Version = "develop" + +// versionCmd represents the version command +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version", + Long: `If unsure which version of the CLI you are using, you can use this command to print the version of the CLI.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(Version) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/embed/embed.go b/embed/embed.go new file mode 100644 index 0000000..98464e4 --- /dev/null +++ b/embed/embed.go @@ -0,0 +1,14 @@ +package embed + +import ( + "embed" + "io/fs" + "path/filepath" +) + +//go:embed scaffold +var scaffold embed.FS + +func Scaffold(path string) (fs.FS, error) { + return fs.Sub(scaffold, filepath.Join("scaffold", path)) +} diff --git a/embed/scaffold/init/$.gitignore b/embed/scaffold/init/$.gitignore new file mode 100644 index 0000000..069a1e8 --- /dev/null +++ b/embed/scaffold/init/$.gitignore @@ -0,0 +1,4 @@ +*.so +*.lock +tmp/ +config/ diff --git a/embed/scaffold/init/$.posh.yaml b/embed/scaffold/init/$.posh.yaml new file mode 100644 index 0000000..d010c46 --- /dev/null +++ b/embed/scaffold/init/$.posh.yaml @@ -0,0 +1 @@ +Version: 1.0 diff --git a/embed/scaffold/init/plugin.go b/embed/scaffold/init/plugin.go new file mode 100644 index 0000000..73be0ce --- /dev/null +++ b/embed/scaffold/init/plugin.go @@ -0,0 +1,96 @@ +package main + +import ( + "context" + + "github.com/foomo/fender/fend" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/command" + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/ownbrew" + "github.com/foomo/posh/pkg/plugin" + "github.com/foomo/posh/pkg/prompt" + "github.com/foomo/posh/pkg/prompt/history" + "github.com/foomo/posh/pkg/validate" +) + +type Plugin struct { + l log.Logger + c cache.Cache + commands command.Commands +} + +func New(l log.Logger) (plugin.Plugin, error) { + inst := &Plugin{ + l: l, + commands: command.Commands{}, + } + + // add commands + inst.commands.Add( + command.NewExit(l), + command.NewHelp(l, inst.commands), + ) + return inst, nil +} + +func (p *Plugin) Prompt(ctx context.Context, cfg config.Prompt) error { + sh, err := prompt.New(p.l, + prompt.WithTitle(cfg.Title), + prompt.WithPrefix(cfg.Prefix), + prompt.WithContext(ctx), + prompt.WithCommands(p.commands), + prompt.WithFileHistory( + history.FileWithLimit(cfg.History.Limit), + history.FileWithFilename(cfg.History.Filename), + history.FileWithLockFilename(cfg.History.LockFilename), + ), + ) + if err != nil { + return err + } + return sh.Run() +} + +func (p *Plugin) Execute(ctx context.Context, args []string) error { + r, err := readline.New(p.l) + if err != nil { + return err + } + + if err := r.Parse(strings.Join(args, " ")); err != nil { + return err + } + + if c := p.commands.Get(r.Cmd()); c == nil { + return fmt.Errorf("invalid [cmd] argument: %s", r.Cmd()) + } else if err := c.Execute(ctx, r); err != nil { + return err + } + + return nil +} + +func (p *Plugin) Dependencies(ctx context.Context, cfg config.Dependencies) error { + var fends []fend.Fend + fends = append(fends, validate.DependenciesEnvs(p.l, cfg.Envs)...) + fends = append(fends, validate.DependenciesScripts(ctx, p.l, cfg.Scripts)...) + fends = append(fends, validate.DependenciesPackages(ctx, p.l, cfg.Packages)...) + if fendErr, err := fend.First(fends...); err != nil { + return err + } else if fendErr != nil { + return fendErr + } + return nil +} + +func (p *Plugin) Packages(ctx context.Context, cfg []config.Package) error { + brew, err := ownbrew.New(p.l, + ownbrew.WithPackages(cfg...), + ) + if err != nil { + return err + } + return brew.Install(ctx) +} diff --git a/example/.posh.yaml b/example/.posh.yaml new file mode 100644 index 0000000..b40fdce --- /dev/null +++ b/example/.posh.yaml @@ -0,0 +1,131 @@ +version: v1.0 + +prompt: + title: "Demo" + prefix: "demo >" + history: + limit: 100 + filename: .posh/history + lockFilename: .posh/history.lock + +env: + GOPROXY: "https://proxy.golang.org,direct" + PATH: "${PROJECT_ROOT}/node_modules/.bin:${PROJECT_ROOT}/bin:${PATH}" + +plugin: + source: .posh/plugin.go + provider: New + +packages: + - name: gotsrpc + version: 2.6.2 +# - name: helm +# version: 3.8.1 +# - name: husky +# version: 1.7.0 +# - name: hygen +# version: 6.1.0 +# - name: k6 +# version: 0.34.1 +# - name: k9s +# version: 0.24.15 +# + +dependencies: + envs: + - name: GITHUB_TOKEN + help: | + You need to export a GITHUB_TOKEN in your environment! + + To do so, create an access token on https://github.com/settings/tokens with the following permissions: + + - repo + - write:packages + - delete:packages + + and export it into your environment e.g. ZSH: + + $ vi ~/.zshenv + + export GITHUB_TOKEN=XXX +# scripts: +# - name: op +# command: op account --account get > /dev/null 2>&1 +# help: | +# You're not yet logged into the 1Password account! +# +# $ op signin .1password.eu +# - name: npm +# command: op account --account get > /dev/null 2>&1 +# help: | +# You're not yet logged into the github npm registry! +# +# $ npm login --scope=@ --registry=https://npm.pkg.github.com +# Username: [GITHUB_USERNAME] +# Password: [GITHUB_TOKEN] +# Email: [EMAIL] + packages: + - name: git + version: '~2' + command: git version | awk '{print $3}' + help: | + Please ensure you have 'git' installed in the required version: %s! + + $ brew update + $ brew install git + - name: go + version: '>=1.19.4' + command: go env GOVERSION | cut -c3- + help: | + Please ensure you have 'go' installed in the required version: %s! + + $ brew update + $ brew install go + - name: op + version: '~2' + command: op --version + help: | + Please ensure you have the 1Password cli 'op' installed in the required version: %s! + + $ brew update + $ brew install 1password-cli + - name: node + version: '~16' + command: node --version + help: | + Please ensure you have 'node' installed in the required version: %s! + + We strongly recommend installing 'nvm' trough: + + $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash + + If you already have installed 'nvm' just run the following command to read in the .nvmrc: + + $ nvm use + - name: yarn + version: '~1' + command: yarn --version + help: | + Please ensure you have 'yarn' installed in the required version: %s! + + $ npm install -g yarn + - name: tsh + version: '>=10' + command: tsh version --skip-version-check | awk 'NR==1{print $2}' + help: | + Please ensure you have 'teleport' installed in the required version: %s! + + $ brew update + $ brew install teleport + - name: goimports + version: '>=2022' + command: date -r $(which goimports) +%Y.%-m.%-d + help: | + Please ensure you have 'goimports' installed in a recent version: %s! + + $ go install golang.org/x/tools/cmd/goimports@latest + +# 1Password +onePassword: + account: + tokenFilename: .posh/config/.op diff --git a/example/.posh/.gitignore b/example/.posh/.gitignore new file mode 100644 index 0000000..069a1e8 --- /dev/null +++ b/example/.posh/.gitignore @@ -0,0 +1,4 @@ +*.so +*.lock +tmp/ +config/ diff --git a/example/.posh/command/gogenerate.go b/example/.posh/command/gogenerate.go new file mode 100644 index 0000000..829b6ea --- /dev/null +++ b/example/.posh/command/gogenerate.go @@ -0,0 +1,130 @@ +package command + +import ( + "context" + "io/fs" + "os" + + "github.com/charlievieth/fastwalk" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/prompt" + "github.com/foomo/posh/pkg/readline" + "github.com/foomo/posh/pkg/shell" + "github.com/pkg/errors" +) + +// GoGenerate command +type GoGenerate struct { + l log.Logger + name string + cache cache.Namespace +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +// NewGoGenerate command +func NewGoGenerate(l log.Logger, cache cache.Cache) *GoGenerate { + return &GoGenerate{ + l: l, + name: "gogenerate", + cache: cache.Get("gogenerate"), + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *GoGenerate) Name() string { + return c.name +} + +func (c *GoGenerate) Description() string { + return "run go generate on generate.go files" +} + +func (c *GoGenerate) Complete(ctx context.Context, r *readline.Readline, d prompt.Document) (suggests []prompt.Suggest) { + if r.Args().LenLte(1) { + suggests = c.completePaths() + } + return nil +} + +func (c *GoGenerate) Validate(ctx context.Context, r *readline.Readline) error { + switch { + case r.Args().LenIs(0): + return nil + case r.Args().LenGt(1): + return errors.New("too many parameters") + } + if info, err := os.Stat(r.Args().At(1)); err != nil || info.IsDir() { + return errors.Errorf("invalid [path] parameter: %s", r.Args().At(1)) + } + return nil +} + +func (c *GoGenerate) Execute(ctx context.Context, r *readline.Readline) error { + var paths []string + if r.Args().HasIndex(0) { + paths = append(paths, r.Args().At(0)) + } else { + paths = c.paths() + } + + for _, value := range paths { + c.l.Info("go generate:", value) + if err := shell.New(ctx, c.l, + "go", "generate", value, + ). + Args(r.AdditionalArgs()...). + Run(); err != nil { + return err + } + } + return nil +} + +func (c *GoGenerate) Help() string { + return `Looks for generate.go files and runs them. + +Usage: + gogenerate [path] + +Examples: + gogenerate tidy ./path/to/generate.go +` +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *GoGenerate) completePaths() (suggest []prompt.Suggest) { + for _, value := range c.paths() { + suggest = append(suggest, prompt.Suggest{Text: value}) + } + return suggest +} + +func (c *GoGenerate) paths() []string { + return c.cache.Get("paths", func() interface{} { + var ret []string + if err := fastwalk.Walk(&fastwalk.Config{ + Follow: false, + }, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.Name() == "generate.go" { + ret = append(ret, path) + } + return nil + }); err != nil { + c.l.Debug("failed to walk files", err.Error()) + } + return ret + }).([]string) +} diff --git a/example/.posh/command/gomod.go b/example/.posh/command/gomod.go new file mode 100644 index 0000000..acd2190 --- /dev/null +++ b/example/.posh/command/gomod.go @@ -0,0 +1,197 @@ +package command + +import ( + "context" + "io/fs" + "path" + + "github.com/charlievieth/fastwalk" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/command/tree" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/prompt" + "github.com/foomo/posh/pkg/readline" + "github.com/foomo/posh/pkg/shell" +) + +// GoMod command +type GoMod struct { + l log.Logger + cache cache.Namespace + parser *tree.Root +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +// NewGoMod command +func NewGoMod(l log.Logger, cache cache.Cache) *GoMod { + inst := &GoMod{ + l: l, + cache: cache.Get("gomod"), + } + + pathArg := &tree.Arg{ + Name: "path", + Optional: true, + Suggest: inst.completePaths, + } + + inst.parser = &tree.Root{ + Name: "gomod", + Nodes: []*tree.Node{ + { + Name: "tidy", + Description: "run go mod tidy", + Args: []*tree.Arg{pathArg}, + Execute: inst.tidy, + }, + { + Name: "download", + Description: "run go mod download", + Args: []*tree.Arg{pathArg}, + Execute: inst.download, + }, + { + Name: "outdated", + Description: "show go mod outdated", + Args: []*tree.Arg{pathArg}, + Execute: inst.outdated, + }, + }, + } + return inst +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *GoMod) Name() string { + return c.parser.Name +} + +func (c *GoMod) Description() string { + return "run go mod" +} + +func (c *GoMod) Complete(ctx context.Context, r *readline.Readline, d prompt.Document) (suggests []prompt.Suggest) { + return c.parser.Complete(ctx, r) +} + +func (c *GoMod) Execute(ctx context.Context, r *readline.Readline) error { + return c.parser.Execute(ctx, r) +} + +func (c *GoMod) Help() string { + return `Looks for go.mod files and runs the given command. + +Usage: + gomod [command] + +Available commands: + tidy run go mod tidy on specific or all paths + download run go mod download on specific or all paths + outdated list outdated packages on specific or all paths + +Examples: + gomod tidy ./path +` +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *GoMod) tidy(ctx context.Context, r *readline.Readline) error { + var paths []string + if r.Args().HasIndex(1) { + paths = []string{r.Args().At(1)} + } else { + paths = c.paths() + } + for _, value := range paths { + c.l.Info("go mod tidy:", value) + if err := shell.New(ctx, c.l, + "go", "mod", "tidy", + ). + Args(r.AdditionalArgs()...). + Dir(value). + Run(); err != nil { + return err + } + } + return nil +} + +func (c *GoMod) download(ctx context.Context, r *readline.Readline) error { + var paths []string + if r.Args().HasIndex(1) { + paths = []string{r.Args().At(1)} + } else { + paths = c.paths() + } + for _, value := range paths { + c.l.Info("go mod download:", value) + if err := shell.New(ctx, c.l, + "go", "mod", "tidy", + ). + Args(r.AdditionalArgs()...). + Dir(value). + Run(); err != nil { + return err + } + } + return nil +} + +func (c *GoMod) outdated(ctx context.Context, r *readline.Readline) error { + var paths []string + if r.Args().HasIndex(1) { + paths = []string{r.Args().At(1)} + } else { + paths = c.paths() + } + for _, value := range paths { + c.l.Info("go mod outdated:", value) + if err := shell.New(ctx, c.l, + "go", "list", + "-u", "-m", "-json", "all", + "|", "go-mod-outdated", "-update", "-direct", + ). + Args(r.AdditionalArgs()...). + Dir(value). + Run(); err != nil { + return err + } + } + return nil +} + +func (c *GoMod) completePaths(ctx context.Context, p *tree.Root, r *readline.Readline) (suggest []prompt.Suggest) { + for _, value := range c.paths() { + suggest = append(suggest, prompt.Suggest{Text: value}) + } + return suggest +} + +func (c *GoMod) paths() []string { + return c.cache.Get("paths", func() interface{} { + var ret []string + if err := fastwalk.Walk(&fastwalk.Config{ + Follow: false, + }, ".", func(p string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.Name() == "go.mod" { + ret = append(ret, path.Dir(p)) + } + return nil + }); err != nil { + c.l.Debug("failed to walk files", err.Error()) + } + return ret + }).([]string) +} diff --git a/example/.posh/go.mod b/example/.posh/go.mod new file mode 100644 index 0000000..a69df0c --- /dev/null +++ b/example/.posh/go.mod @@ -0,0 +1,63 @@ +module github.com/foomo/posh/example/posh + +go 1.19 + +require ( + github.com/charlievieth/fastwalk v1.0.1 + github.com/foomo/fender v0.4.4 + github.com/foomo/posh v0.0.0-00010101000000-000000000000 + github.com/pkg/errors v0.9.1 + github.com/spf13/viper v1.14.0 +) + +require ( + atomicgo.dev/cursor v0.1.1 // indirect + atomicgo.dev/keyboard v0.2.8 // indirect + github.com/1Password/connect-sdk-go v1.5.0 // indirect + github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/c-bata/go-prompt v0.2.6 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gookit/color v1.5.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/joho/godotenv v1.4.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lithammer/fuzzysearch v1.1.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-tty v0.0.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pkg/term v1.1.0 // indirect + github.com/pterm/pterm v0.12.51 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.1 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace ( + github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 + github.com/foomo/posh => ../../ +) diff --git a/example/.posh/go.sum b/example/.posh/go.sum new file mode 100644 index 0000000..9645ae3 --- /dev/null +++ b/example/.posh/go.sum @@ -0,0 +1,632 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= +atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.8 h1:Di09BitwZgdTV1hPyX/b9Cqxi8HVuJQwWivnZUEqlj4= +atomicgo.dev/keyboard v0.2.8/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/1Password/connect-sdk-go v1.5.0 h1:F0WJcLSzGg3iXEDY49/ULdszYKsQLGTzn+2cyYXqiyk= +github.com/1Password/connect-sdk-go v1.5.0/go.mod h1:TdynFeyvaRoackENbJ8RfJokH+WAowAu1MLmUbdMq6s= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.1 h1:a9Fqx6vQrHQ4CyiaLhktfTTelwGotmFWy8MNhyaohw8= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/charlievieth/fastwalk v1.0.1 h1:jW01w8OCFdKS9JvAcnI+JHhWU/FuIEmNb24Ri9p7OVg= +github.com/charlievieth/fastwalk v1.0.1/go.mod h1:dryXgMJyGHbMrAmmnF0/EJNBbZaihlwcNud5IuGyogU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/foomo/fender v0.4.4 h1:zs5jQGAVIygn552beaFXow4gRCuCKRl4D4qGiED6WeQ= +github.com/foomo/fender v0.4.4/go.mod h1:cUmK1Gg1wrue5ZzRdGj92Ong4jBcSbuuSbINqaBXZaE= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 h1:kXNtle4AoQnngdm+gwt4ku6Llbzw3EFHgZYpL618JaI= +github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5/go.mod h1:+syUfnvYJUO5A+6QMQYXAyzkxHMNlj9dH2LIeQfBSjc= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= +github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.0 h1:4ZexSFt8agMNzNisrsilL6RClWDC5YJnLHNIfTy4iuc= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= +github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.51 h1:iwhNG1FhQMgks+5kVyr/ClRk3WJCuL907nJN7RqmEpw= +github.com/pterm/pterm v0.12.51/go.mod h1:79BLm4vos2z+eOoHnDG7ZWuYtLaSStyaspKjGmSoxc4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= +github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/example/.posh/history b/example/.posh/history new file mode 100644 index 0000000..c888232 --- /dev/null +++ b/example/.posh/history @@ -0,0 +1,70 @@ +ls -loah +ls -loa +ls -lo +ping +demo +help foo +help exit | foo +help ex +help exit +help exit foo +help +help esit +help exit | bat +help exit -- exit +help exit -- --exit +help exit +gomod +gomod download ./.posh/go.mod +gomod download --foo -- bar +gomod download --foo -- --bar +gomod download +gomod download --foo +gomod download --foo xxx +gomod download --foo="xxx" +gomod download -foo barfoo="xxx" +gomod download -foo xx +gomod download --bar me +gomod download -bar me +gomod download -foo me +gomod download --foo=me +gomod download -foo=me +gomod download -foo +gomod download bar one -foo +gomod download bar one -foo me +gomod download bar one --foo me +gomod download one two --foo bar +gomod download one two --foo me --twice +gomod download one two --foo me +gomod download one two three +gomod download one two two --foo --bar +gomod download one two two --foo me --bar +gomod download one two two -foo me -bar you +gomod download one two two -foo=me -bar you +gomod download one two two -foo=yes -bar you +gomod download one two two -foo='yes' -bar you +gomod download one two two -foo="yes" -bar you +gomod download one two two -foo "yes" -bar you +gomod download one two two --foo "yes" --bar you +gomod download one two two --foo "yes" --bar -- --bar +gomod download one two two --foo yes --bar -- --bar +gomod download one two two --foo yes --bar -- foo --bar +gomod download one two two --foo yes --bar -- -one +gomod download one two two --foo yes --bar -- -on +gomod download one two two --foo yes --bar -- -o +gomod download one two two --foo yes --bar -- -- +gomod download one two two --foo yes --bar -- --one --two +gomod download one two two --foo yes --bar no -- --one +gomod download one two two --foo yes --bar no -- --one two +gomod outdated ./.posh/go.mod +gomod tidy ./.posh/go.mod +gogenerate +cache clear +gomod tidy +gomod tidy .posh +op signin kevin.kim@bestbytes.com +op signin +ls +ifconfig +ls -la +cache list \ No newline at end of file diff --git a/example/.posh/plugin.go b/example/.posh/plugin.go new file mode 100644 index 0000000..fea4394 --- /dev/null +++ b/example/.posh/plugin.go @@ -0,0 +1,131 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "github.com/foomo/fender/fend" + command2 "github.com/foomo/posh/example/posh/command" + "github.com/foomo/posh/integration/onepassword" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/command" + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/ownbrew" + "github.com/foomo/posh/pkg/plugin" + "github.com/foomo/posh/pkg/prompt" + "github.com/foomo/posh/pkg/prompt/history" + "github.com/foomo/posh/pkg/readline" + "github.com/foomo/posh/pkg/validate" + "github.com/spf13/viper" +) + +type Plugin struct { + l log.Logger + c cache.Cache + commands command.Commands +} + +func New(l log.Logger) (plugin.Plugin, error) { + inst := &Plugin{ + l: l, + c: cache.MemoryCache{}, + commands: command.Commands{}, + } + + var ( + err error + onePasswordCfg onepassword.Config + onePasswordClient *onepassword.OnePassword + ) + + // load configurations + l.Must(viper.UnmarshalKey("onePassword", &onePasswordCfg)) + + // create dependency instances + onePasswordClient, err = onepassword.New(l, inst.c, onepassword.WithTokenFilename(onePasswordCfg.TokenFilename)) + l.Must(err) + + // add commands + inst.commands.Add( + command2.NewGoMod(l, inst.c), + command2.NewGoGenerate(l, inst.c), + onepassword.NewCommand(l, onePasswordCfg, onePasswordClient), + command.NewCache(l, inst.c), + command.NewExit(l), + command.NewHelp(l, inst.commands), + ) + return inst, nil +} + +func (p *Plugin) Prompt(ctx context.Context, cfg config.Prompt) error { + sh, err := prompt.New(p.l, + prompt.WithTitle(cfg.Title), + prompt.WithPrefix(cfg.Prefix), + prompt.WithContext(ctx), + prompt.WithCommands(p.commands), + prompt.WithCheckers( + func(ctx context.Context, l log.Logger) (name, note string, ok bool) { + return "one", "", true + }, + func(ctx context.Context, l log.Logger) (name, note string, ok bool) { + return "two", "please install", false + }, + ), + prompt.WithFileHistory( + history.FileWithLimit(cfg.History.Limit), + history.FileWithFilename(cfg.History.Filename), + history.FileWithLockFilename(cfg.History.LockFilename), + ), + ) + if err != nil { + return err + } + return sh.Run() +} + +func (p *Plugin) Execute(ctx context.Context, args []string) error { + r, err := readline.New(p.l) + if err != nil { + return err + } + + if err := r.Parse(strings.Join(args, " ")); err != nil { + return err + } + + if c := p.commands.Get(r.Cmd()); c == nil { + return fmt.Errorf("invalid [cmd] argument: %s", r.Cmd()) + } else if err := c.Execute(ctx, r); err != nil { + return err + } + + return nil +} + +func (p *Plugin) Dependencies(ctx context.Context, cfg config.Dependencies) error { + var fends []fend.Fend + fends = append(fends, validate.DependenciesEnvs(p.l, cfg.Envs)...) + fends = append(fends, validate.DependenciesScripts(ctx, p.l, cfg.Scripts)...) + fends = append(fends, validate.DependenciesPackages(ctx, p.l, cfg.Packages)...) + fends = append(fends, + validate.GitUser(ctx, p.l, validate.GitUserName, validate.GitUserEmail(`(.*)@(bestbytes\.com)`)), + ) + if fendErr, err := fend.First(fends...); err != nil { + return err + } else if fendErr != nil { + return fendErr + } + return nil +} + +func (p *Plugin) Packages(ctx context.Context, cfg []config.Package) error { + brew, err := ownbrew.New(p.l, + ownbrew.WithPackages(cfg...), + ) + if err != nil { + return err + } + return brew.Install(ctx) +} diff --git a/example/.posh/scripts/ownbrew/gotsrpc.sh b/example/.posh/scripts/ownbrew/gotsrpc.sh new file mode 100755 index 0000000..89b48be --- /dev/null +++ b/example/.posh/scripts/ownbrew/gotsrpc.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +source .posh/scripts/ownbrew/include.sh + +curl -sfL "https://github.com/foomo/gotsrpc/releases/download/v${version}/gotsrpc_${version}_${os}_${arch}.tar.gz" -o "${TEMP_DIR}/gotsrpc.tar.gz" +curl -sfL "https://github.com/foomo/gotsrpc/releases/download/v${version}/gotsrpc_${version}_checksums.txt" -o "${TEMP_DIR}/gotsrpc.tar.gz.sha256" + +echo "$(cat "${TEMP_DIR}/gotsrpc.tar.gz.sha256" | grep "gotsrpc_${version}_${os}_${arch}.tar.gz" | awk '{print $1;}') ${TEMP_DIR}/gotsrpc.tar.gz" | shasum -a 256 --check --quiet + +tar -xzvf "${TEMP_DIR}/gotsrpc.tar.gz" -C "${TEMP_DIR}" gotsrpc +mv "${TEMP_DIR}/gotsrpc" "${BIN_DIR}/gotsrpc-${version}-${os}-${arch}" +chmod a+x "${BIN_DIR}/gotsrpc-${version}-${os}-${arch}" +rm "${TEMP_DIR}/gotsrpc.tar.gz" "${TEMP_DIR}/gotsrpc.tar.gz.sha256" diff --git a/example/.posh/scripts/ownbrew/include.sh b/example/.posh/scripts/ownbrew/include.sh new file mode 100644 index 0000000..7bc28e3 --- /dev/null +++ b/example/.posh/scripts/ownbrew/include.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +version="${version:-${1}}" +os="${os:-${2}}" +arch="${arch:-${3}}" diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..1757461 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,42 @@ +.DEFAULT_GOAL:=help + +## === Tasks === + +.PHONY: posh +posh: + @posh dependencies + @posh prompt + +## === Utils === + +## Show help text +help: + @awk '{ \ + if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \ + helpCommand = substr($$0, index($$0, ":") + 2); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \ + helpCommand = substr($$0, 0, index($$0, ":")); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage"\n"; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^##/) { \ + if (helpMessage) { \ + helpMessage = helpMessage"\n "substr($$0, 3); \ + } else { \ + helpMessage = substr($$0, 3); \ + } \ + } else { \ + if (helpMessage) { \ + print "\n "helpMessage"\n" \ + } \ + helpMessage = ""; \ + } \ + }' \ + $(MAKEFILE_LIST) diff --git a/example/cmd/posh/main.go b/example/cmd/posh/main.go new file mode 100644 index 0000000..28f37e0 --- /dev/null +++ b/example/cmd/posh/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("hello") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..804c766 --- /dev/null +++ b/go.mod @@ -0,0 +1,68 @@ +module github.com/foomo/posh + +go 1.19 + +require ( + github.com/1Password/connect-sdk-go v1.5.0 + github.com/Masterminds/semver/v3 v3.2.0 + github.com/Masterminds/sprig/v3 v3.2.3 + github.com/c-bata/go-prompt v0.2.6 + github.com/foomo/fender v0.4.4 + github.com/gofrs/flock v0.8.1 + github.com/joho/godotenv v1.4.0 + github.com/pkg/errors v0.9.1 + github.com/pterm/pterm v0.12.51 + github.com/spf13/cobra v1.6.1 + github.com/spf13/viper v1.14.0 +) + +require ( + atomicgo.dev/cursor v0.1.1 // indirect + atomicgo.dev/keyboard v0.2.8 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gookit/color v1.5.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lithammer/fuzzysearch v1.1.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-tty v0.0.3 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pkg/term v1.1.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.1 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e74d2d3 --- /dev/null +++ b/go.sum @@ -0,0 +1,661 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= +atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.8 h1:Di09BitwZgdTV1hPyX/b9Cqxi8HVuJQwWivnZUEqlj4= +atomicgo.dev/keyboard v0.2.8/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/1Password/connect-sdk-go v1.5.0 h1:F0WJcLSzGg3iXEDY49/ULdszYKsQLGTzn+2cyYXqiyk= +github.com/1Password/connect-sdk-go v1.5.0/go.mod h1:TdynFeyvaRoackENbJ8RfJokH+WAowAu1MLmUbdMq6s= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.1 h1:a9Fqx6vQrHQ4CyiaLhktfTTelwGotmFWy8MNhyaohw8= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/foomo/fender v0.4.4 h1:zs5jQGAVIygn552beaFXow4gRCuCKRl4D4qGiED6WeQ= +github.com/foomo/fender v0.4.4/go.mod h1:cUmK1Gg1wrue5ZzRdGj92Ong4jBcSbuuSbINqaBXZaE= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 h1:kXNtle4AoQnngdm+gwt4ku6Llbzw3EFHgZYpL618JaI= +github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5/go.mod h1:+syUfnvYJUO5A+6QMQYXAyzkxHMNlj9dH2LIeQfBSjc= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= +github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.0 h1:4ZexSFt8agMNzNisrsilL6RClWDC5YJnLHNIfTy4iuc= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= +github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.51 h1:iwhNG1FhQMgks+5kVyr/ClRk3WJCuL907nJN7RqmEpw= +github.com/pterm/pterm v0.12.51/go.mod h1:79BLm4vos2z+eOoHnDG7ZWuYtLaSStyaspKjGmSoxc4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= +github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/integration/onepassword/command.go b/integration/onepassword/command.go new file mode 100644 index 0000000..b8dc690 --- /dev/null +++ b/integration/onepassword/command.go @@ -0,0 +1,130 @@ +package onepassword + +import ( + "context" + + argparse2 "github.com/foomo/posh/pkg/command/tree" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/prompt" + "github.com/foomo/posh/pkg/readline" + "github.com/foomo/posh/pkg/shell" +) + +type Command struct { + l log.Logger + cfg Config + parser *argparse2.Root + client *OnePassword +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewCommand(l log.Logger, cfg Config, client *OnePassword) *Command { + inst := &Command{ + l: l, + cfg: cfg, + client: client, + } + + inst.parser = &argparse2.Root{ + Name: "op", + Nodes: []*argparse2.Node{ + { + Name: "get", + Description: "retrieve item", + Args: []*argparse2.Arg{ + { + Name: "id", + }, + }, + Execute: inst.get, + }, + { + Name: "signin", + Description: "sign into your account", + Execute: inst.signin, + }, + { + Name: "register", + Description: "register an account", + Args: []*argparse2.Arg{ + { + Name: "email", + }, + }, + Execute: inst.register, + }, + }, + } + return inst +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *Command) Name() string { + return c.parser.Name +} + +func (c *Command) Description() string { + return "run go mod" +} + +func (c *Command) Complete(ctx context.Context, r *readline.Readline, d prompt.Document) (suggests []prompt.Suggest) { + return c.parser.Complete(ctx, r) +} + +func (c *Command) Execute(ctx context.Context, r *readline.Readline) error { + return c.parser.Execute(ctx, r) +} + +func (c *Command) Help() string { + return `1Password session helper. + +Usage: + op [command] + +Available commands: + get [id] Retrieve an entry from your account + signin Sign into your 1Password account for the session + register [email] Add your 1Password account +` +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *Command) get(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, + "op", + "--account", c.cfg.Account, + "item", "get", r.Args().At(1), + "--format", "json", + ). + Args(r.AdditionalArgs()...). + Run() +} + +func (c *Command) register(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, + "op", "account", "add", + "--address", c.cfg.Account+".1password.eu", + "--email", r.Args().At(1), + ). + Args(r.AdditionalArgs()...). + Wait() +} + +func (c *Command) signin(ctx context.Context, r *readline.Readline) error { + if ok, _ := c.client.Session(c.cfg.Account); ok { + c.l.Info("Already signed in") + return nil + } else if err := c.client.SignIn(ctx, c.cfg.Account); err != nil { + return err + } + return nil +} diff --git a/integration/onepassword/config.go b/integration/onepassword/config.go new file mode 100644 index 0000000..9ee2fe8 --- /dev/null +++ b/integration/onepassword/config.go @@ -0,0 +1,6 @@ +package onepassword + +type Config struct { + Account string `json:"account" yaml:"account"` + TokenFilename string `json:"tokenFilename" yaml:"tokenFilename"` +} diff --git a/integration/onepassword/errors.go b/integration/onepassword/errors.go new file mode 100644 index 0000000..bb93a2c --- /dev/null +++ b/integration/onepassword/errors.go @@ -0,0 +1,7 @@ +package onepassword + +import ( + "github.com/pkg/errors" +) + +var ErrNotSignedIn = errors.New("you're not signed into your 1password account") diff --git a/integration/onepassword/onepassword.go b/integration/onepassword/onepassword.go new file mode 100644 index 0000000..bcfc572 --- /dev/null +++ b/integration/onepassword/onepassword.go @@ -0,0 +1,364 @@ +package onepassword + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "os" + "os/exec" + "path" + "regexp" + "strings" + "text/template" + "time" + + "github.com/1Password/connect-sdk-go/connect" + "github.com/1Password/connect-sdk-go/onepassword" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/log" + "github.com/joho/godotenv" + "github.com/pkg/errors" +) + +type ( + OnePassword struct { + l log.Logger + cache cache.Namespace + connect connect.Client + uuidRegex *regexp.Regexp + tokenFilename string + watching map[string]bool + last time.Time + } + Option func(*OnePassword) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func WithTokenFilename(v string) Option { + return func(o *OnePassword) error { + o.tokenFilename = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(l log.Logger, cache cache.Cache, opts ...Option) (*OnePassword, error) { + inst := &OnePassword{ + l: l, + cache: cache.Get("onePasswordClient"), + uuidRegex: regexp.MustCompile(`^[a-z0-9]{26}$`), + watching: map[string]bool{}, + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + if client, err := connect.NewClientFromEnvironment(); err != nil { + l.Debug("not able to create connect client", err.Error()) + } else { + inst.connect = client + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (op *OnePassword) Session(account string) (bool, error) { + var sessChanged bool + sess := os.Getenv("OP_SESSION_" + account) + + if op.tokenFilename != "" { + if err := godotenv.Overload(op.tokenFilename); err != nil { + op.l.Debug("could not load session from env file") + sessChanged = true + } else if value := os.Getenv("OP_SESSION_" + account); sess != value { + sessChanged = true + } else { + op.l.Debug("INFO: loaded op session from file " + value) + } + } + + if sessChanged || op.last.IsZero() || time.Since(op.last) > time.Minute*10 { + out, err := exec.Command("op", "account", "--account", account, "get", "--format", "json").Output() + if err != nil { + return false, fmt.Errorf("%w: %s", err, string(out)) + } + + var data struct { + Name string `json:"name"` + } + + if err := json.Unmarshal(out, &data); err != nil { + return false, err + } + + if data.Name == account { + op.last = time.Now() + op.watch(account) + return true, nil + } + } + return true, nil +} + +func (op *OnePassword) SignIn(ctx context.Context, account string) error { + if ok, _ := op.Session(account); ok { + return nil + } + + // create command + cmd := exec.CommandContext(ctx, + "op", "signin", + "--account", account, + "--raw", + ) + + var stdoutBuf bytes.Buffer + cmd.Stdout = &stdoutBuf + cmd.Stdin = os.Stdin + + // start the process and wait till it's finished + if err := cmd.Start(); err != nil { + return err + } else if err := cmd.Wait(); err != nil { + return err + } + + token := strings.TrimSuffix(stdoutBuf.String(), "\n") + if token == "" { + return errors.New("failed to retrieve 1password token!") + } else if err := os.Setenv(fmt.Sprintf("OP_SESSION_%s", account), token); err != nil { + return err + } else { + op.l.Infof(`If you need op outside the shell, run: + +$ export OP_SESSION_%s=%s +`, account, token) + } + + if op.tokenFilename != "" { + if err := os.MkdirAll(path.Dir(op.tokenFilename), os.ModePerm); err != nil { + return err + } else if err := os.WriteFile(op.tokenFilename, []byte(fmt.Sprintf("OP_SESSION_%s=%s\n", account, token)), 0600); err != nil { + return err + } else { + op.l.Infof(`Session env has been stored for your convenience at: + +%s +`, op.tokenFilename) + } + } + op.watch(account) + return nil +} + +func (op *OnePassword) Get(ctx context.Context, account, vaultUUID, itemUUID, field string) (string, error) { + if op.connect != nil { + if fields := op.connectGet(vaultUUID, itemUUID); len(fields) == 0 { + return "", fmt.Errorf("could not find secret '%s' '%s'", vaultUUID, itemUUID) + } else if value, ok := fields[field]; !ok { + return "", fmt.Errorf("could not find field %s", field) + } else { + return strings.ReplaceAll(strings.TrimSpace(value), "\\n", "\n"), nil + } + } else { + if ok, _ := op.Session(account); !ok { + return "", ErrNotSignedIn + } else if fields := op.clientGet(ctx, vaultUUID, itemUUID); len(fields) == 0 { + return "", fmt.Errorf("could not find secret '%s' '%s'", vaultUUID, itemUUID) + } else if value, ok := fields[field]; !ok { + return "", fmt.Errorf("could not find field %s", field) + } else { + return strings.ReplaceAll(strings.TrimSpace(value), "\\n", "\n"), nil + } + } +} + +func (op *OnePassword) GetOnetimePassword(ctx context.Context, account, uuid string) (string, error) { + if ok, _ := op.Session(account); !ok { + return "", ErrNotSignedIn + } + + out, err := exec.CommandContext(ctx, + "op", "item", "get", "--otp", uuid, + ).Output() + if err != nil { + return "", err + } + + return strings.ReplaceAll(strings.TrimSpace(string(out)), "\\n", "\n"), nil +} + +func (op *OnePassword) Render(ctx context.Context, source string) ([]byte, error) { + tpl, err := template.New("1password"). + Delims("<% ", " %>"). + Option("missingkey=error"). + Funcs( + template.FuncMap{ + "env": func(name string) (string, error) { + value := os.Getenv(name) + if value == "" { + return "", fmt.Errorf("env variable %q was empty", name) + } + return value, nil + }, + "op": func(account, vaultUUID, itemUUID, field string) (string, error) { + return op.Get(ctx, account, vaultUUID, itemUUID, field) + }, + "indent": func(spaces int, v string) string { + pad := strings.Repeat(" ", spaces) + return strings.ReplaceAll(v, "\n", "\n"+pad) + }, + "quote": func(v string) string { + return "'" + v + "'" + }, + "replace": func(old, new, v string) string { + return strings.ReplaceAll(v, old, new) + }, + "base64": func(v string) string { + return base64.StdEncoding.EncodeToString([]byte(v)) + }, + }, + ). + Parse(source) + if err != nil { + return nil, err + } + + out := bytes.NewBuffer([]byte{}) + if err := tpl.Execute(out, nil); err != nil { + return nil, err + } + + return out.Bytes(), nil +} + +func (op *OnePassword) RenderFile(ctx context.Context, source string) ([]byte, error) { + in, err := os.ReadFile(source) + if err != nil { + return nil, err + } + out, err := op.Render(ctx, string(in)) + if err != nil { + return nil, err + } + return out, nil +} + +func (op *OnePassword) RenderFileTo(ctx context.Context, source, target string) error { + out, err := op.RenderFile(ctx, source) + if err != nil { + return err + } + value := fmt.Sprintf( + "# Code generated by shell %s - DO NOT EDIT.\n%s", + time.Now().Format("2006-01-02 15:04:05"), + string(out), + ) + return os.WriteFile(target, []byte(value), 0600) +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (op *OnePassword) clientGet(ctx context.Context, vaultUUID string, itemUUID string) map[string]string { + return op.cache.Get(strings.Join([]string{vaultUUID, itemUUID}, "#"), func() interface{} { + ret := map[string]string{} + var v struct { + Vault struct { + ID string `json:"id"` + } `json:"vault"` + Fields []struct { + ID string `json:"id"` + Type string `json:"type"` // CONCEALED, STRING + Label string `json:"label"` + Value interface{} `json:"value"` + } `json:"fields"` + } + if res, err := exec.CommandContext(ctx, + "op", "item", "get", itemUUID, "--format", "json", + ).CombinedOutput(); err != nil { + op.l.Error("failed to retrieve item", err.Error()) + return ret + } else if err := json.Unmarshal(res, &v); err != nil { + op.l.Error("failed to retrieve item", err.Error()) + return ret + } else if v.Vault.ID != vaultUUID { + op.l.Error("failed to retrieve item", "wrong vault UUID %s for item %s", vaultUUID, itemUUID) + return ret + } else { + ret := map[string]string{} + aliases := map[string]string{ + "notesPlain": "notes", + } + for _, field := range v.Fields { + if alias, ok := aliases[field.Label]; ok { + ret[alias] = fmt.Sprintf("%v", field.Value) + } else { + ret[field.Label] = fmt.Sprintf("%v", field.Value) + } + } + return ret + } + }).(map[string]string) +} + +func (op *OnePassword) connectGet(vaultUUID, itemUUID string) map[string]string { + return op.cache.Get(strings.Join([]string{vaultUUID, itemUUID}, "#"), func() interface{} { + ret := map[string]string{} + var item *onepassword.Item + if op.uuidRegex.Match([]byte(itemUUID)) { + if v, err := op.connect.GetItem(itemUUID, vaultUUID); err != nil { + op.l.Error("failed to retrieve item:", err.Error()) + return ret + } else { + item = v + } + } else { + if v, err := op.connect.GetItemByTitle(itemUUID, vaultUUID); err != nil { + op.l.Error("failed to retrieve item by title:", err.Error()) + return ret + } else { + item = v + } + } + for _, f := range item.Fields { + ret[f.Label] = f.Value + } + return ret + }).(map[string]string) +} + +func (op *OnePassword) watch(account string) { + if v, ok := op.watching[account]; !ok || !v { + go func() { + for { + if ok, err := op.Session(account); err != nil { + op.l.Warnf("\n1password session keep alive failed for '%s' (%s)", account, err.Error()) + op.watching[account] = false + return + } else if !ok { + op.l.Warnf("\n1password session keep alive failed for '%s'", account) + op.watching[account] = false + return + } + time.Sleep(time.Minute * 15) + } + }() + op.watching[account] = true + } +} diff --git a/internal/cli/utils.go b/internal/cli/utils.go new file mode 100644 index 0000000..12d61ec --- /dev/null +++ b/internal/cli/utils.go @@ -0,0 +1,55 @@ +package cli + +import ( + "encoding/json" + "os/exec" + "path" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +type ( + Mod struct { + Path, Dir, GoMod string + } + CurDir struct { + Dir string + } +) + +func GetModImportPath() string { + mod, cd := parseModInfo() + return path.Join(mod.Path, fileToURL(strings.TrimPrefix(cd.Dir, mod.Dir))) +} + +func parseModInfo() (Mod, CurDir) { + var mod Mod + var dir CurDir + + m := modInfoJSON("-m") + cobra.CheckErr(json.Unmarshal(m, &mod)) + + // Unsure why, but if no module is present Path is set to this string. + if mod.Path == "command-line-arguments" { + cobra.CheckErr("Please run `go mod init ` before `cobra-cli init`") + } + + e := modInfoJSON("-e") + cobra.CheckErr(json.Unmarshal(e, &dir)) + + return mod, dir +} + +func fileToURL(in string) string { + i := strings.Split(in, string(filepath.Separator)) + return path.Join(i...) +} + +func modInfoJSON(args ...string) []byte { + cmdArgs := append([]string{"list", "-json"}, args...) + out, err := exec.Command("go", cmdArgs...).Output() + cobra.CheckErr(err) + return out +} diff --git a/internal/util/utils.go b/internal/util/utils.go new file mode 100644 index 0000000..a9821ad --- /dev/null +++ b/internal/util/utils.go @@ -0,0 +1,17 @@ +package util + +import ( + "context" + + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/plugin" + "github.com/spf13/viper" +) + +func LoadPlugin(ctx context.Context, m *plugin.Manager) (plugin.Plugin, error) { + var cfg config.Plugin + if err := viper.UnmarshalKey("plugin", &cfg); err != nil { + return nil, err + } + return m.BuildAndLoadPlugin(ctx, cfg.Source, cfg.Provider) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..58984e5 --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/foomo/posh/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go new file mode 100644 index 0000000..44ae5cb --- /dev/null +++ b/pkg/cache/cache.go @@ -0,0 +1,7 @@ +package cache + +type Cache interface { + Get(namespace string) Namespace + Clear(namespace string) + List() map[string]Namespace +} diff --git a/pkg/cache/memorycache.go b/pkg/cache/memorycache.go new file mode 100644 index 0000000..8d0dd49 --- /dev/null +++ b/pkg/cache/memorycache.go @@ -0,0 +1,28 @@ +package cache + +type MemoryCache map[string]MemoryNamespace + +func (c MemoryCache) Clear(namespace string) { + if namespace == "" { + for key := range c { + delete(c, key) + } + } else if _, ok := c[namespace]; ok { + delete(c, namespace) + } +} + +func (c MemoryCache) Get(namespace string) Namespace { + if _, ok := c[namespace]; !ok { + c[namespace] = MemoryNamespace{} + } + return c[namespace] +} + +func (c MemoryCache) List() map[string]Namespace { + ret := map[string]Namespace{} + for s, namespace := range c { + ret[s] = namespace + } + return ret +} diff --git a/pkg/cache/memorynamespace.go b/pkg/cache/memorynamespace.go new file mode 100644 index 0000000..0f559e8 --- /dev/null +++ b/pkg/cache/memorynamespace.go @@ -0,0 +1,34 @@ +package cache + +import "github.com/c-bata/go-prompt" + +type MemoryNamespace map[string]interface{} + +func (c MemoryNamespace) Delete(key string) { + delete(c, key) +} + +func (c MemoryNamespace) Get(key string, cb func() interface{}) interface{} { + if _, ok := c[key]; !ok { + if cb == nil { + return nil + } + c[key] = cb() + } + return c[key] +} + +func (c MemoryNamespace) Keys() []string { + keys := make([]string, 0, len(c)) + for k := range c { + keys = append(keys, k) + } + return keys +} + +func (c MemoryNamespace) GetSuggests(key string, cb func() interface{}) []prompt.Suggest { + if v, ok := c.Get(key, cb).([]prompt.Suggest); ok { + return v + } + return nil +} diff --git a/pkg/cache/namespace.go b/pkg/cache/namespace.go new file mode 100644 index 0000000..f6a1440 --- /dev/null +++ b/pkg/cache/namespace.go @@ -0,0 +1,12 @@ +package cache + +import ( + "github.com/c-bata/go-prompt" +) + +type Namespace interface { + Get(key string, cb func() interface{}) interface{} + Keys() []string + Delete(key string) + GetSuggests(key string, cb func() interface{}) []prompt.Suggest +} diff --git a/pkg/command/cache.go b/pkg/command/cache.go new file mode 100644 index 0000000..2f77b5a --- /dev/null +++ b/pkg/command/cache.go @@ -0,0 +1,107 @@ +package command + +import ( + "context" + "fmt" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/cache" + "github.com/foomo/posh/pkg/command/tree" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/readline" + "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" +) + +type Cache struct { + l log.Logger + tree *tree.Root + cache cache.Cache +} + +// NewCache command +func NewCache(l log.Logger, cache cache.Cache) *Cache { + inst := &Cache{ + l: l, + cache: cache, + } + inst.tree = &tree.Root{ + Name: "cache", + Nodes: tree.Nodes{ + { + Name: "clear", + Description: "clear all caches", + Execute: inst.clear, + }, + { + Name: "list", + Description: "list all caches", + Execute: inst.list, + }, + }, + } + + return inst +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *Cache) Name() string { + return c.tree.Name +} + +func (c *Cache) Description() string { + return "manage the internal cache" +} + +func (c *Cache) Complete(ctx context.Context, r *readline.Readline, d prompt.Document) (suggests []prompt.Suggest) { + return c.tree.Complete(ctx, r) +} + +func (c *Cache) Execute(ctx context.Context, args *readline.Readline) error { + return c.tree.Execute(ctx, args) +} + +func (c *Cache) Help() string { + return `Manage the internal cache. + +Usage: + cache [command] + +Available commands: + list List all caches + clear Clear all caches +` +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *Cache) clear(ctx context.Context, r *readline.Readline) error { + c.l.Info("clearing cache") + c.cache.Clear("") + return nil +} + +func (c *Cache) list(ctx context.Context, r *readline.Readline) error { + // Create a fork of the default table, fill it with data and print it. + // Data can also be generated and inserted later. + tree := pterm.LeveledList{} + for ns, value := range c.cache.List() { + tree = append(tree, pterm.LeveledListItem{Level: 0, Text: ns}) + for _, k := range value.Keys() { + tree = append(tree, pterm.LeveledListItem{Level: 1, Text: k}) + if c.l.Level() == log.LevelTrace { + tree = append(tree, pterm.LeveledListItem{Level: 2, Text: fmt.Sprintf("%v", value.Get(k, nil))}) + } + } + } + // Generate tree from LeveledList. + root := putils.TreeFromLeveledList(tree) + + // Render TreePrinter + return pterm.DefaultTree.WithRoot(root).Render() +} diff --git a/pkg/command/command.go b/pkg/command/command.go new file mode 100644 index 0000000..2c53020 --- /dev/null +++ b/pkg/command/command.go @@ -0,0 +1,40 @@ +package command + +import ( + "context" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/readline" +) + +type ( + Command interface { + Name() string + Description() string + Execute(ctx context.Context, r *readline.Readline) error + } + Helper interface { + Help() string + } + Validator interface { + Validate(ctx context.Context, r *readline.Readline) error + } + Completer interface { + Complete(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } + FlagCompleter interface { + CompleteFlags(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } + ArgumentCompleter interface { + CompleteArguments(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } + PassThroughArgsCompleter interface { + CompletePassTroughArgs(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } + PassThroughFlagsCompleter interface { + CompletePassTroughFlags(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } + AdditionalArgsCompleter interface { + CompleteAdditionalArgs(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest + } +) diff --git a/pkg/command/commands.go b/pkg/command/commands.go new file mode 100644 index 0000000..49b07a3 --- /dev/null +++ b/pkg/command/commands.go @@ -0,0 +1,54 @@ +package command + +import ( + "plugin" + "sort" + + "github.com/pkg/errors" +) + +type Commands map[string]Command + +func (c Commands) Get(name string) Command { + if value, ok := c[name]; ok { + return value + } else { + return nil + } +} + +func (c Commands) List() []Command { + ret := make([]Command, 0, len(c)) + for _, command := range c { + ret = append(ret, command) + } + sort.Slice(ret, func(i, j int) bool { + return ret[i].Name() < ret[j].Name() + }) + return ret +} + +func (c Commands) Has(name string) bool { + return c.Get(name) != nil +} + +func (c Commands) Add(commands ...Command) { + for _, command := range commands { + c[command.Name()] = command + } +} + +func (c Commands) Load(paths ...string) error { + for _, path := range paths { + if plg, err := plugin.Open(path); err != nil { + return errors.Wrapf(err, "failed to load plugin (%s)", path) + } else if sym, err := plg.Lookup("Commands"); err != nil { + return errors.Wrapf(err, "failed to lookup Commands from plugin (%s)", path) + } else if cmds, ok := sym.([]Command); !ok { + return errors.Wrapf(err, "failed to lookup Commands type from plugin (%s)", path) + } else { + c.Add(cmds...) + } + } + return nil +} diff --git a/pkg/command/exit.go b/pkg/command/exit.go new file mode 100644 index 0000000..1404444 --- /dev/null +++ b/pkg/command/exit.go @@ -0,0 +1,51 @@ +package command + +import ( + "context" + "os" + + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/readline" +) + +type Exit struct { + l log.Logger + name string +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewExit(l log.Logger) *Exit { + return &Exit{ + l: l, + name: "exit", + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *Exit) Name() string { + return c.name +} + +func (c *Exit) Description() string { + return "exit shell" +} + +func (c *Exit) Execute(ctx context.Context, args *readline.Readline) error { + c.l.Print("Bye.") + os.Exit(0) + return nil +} + +func (c *Exit) Help() string { + return `Exit the Project Oriented Shell. + +Usage: + exit +` +} diff --git a/pkg/command/help.go b/pkg/command/help.go new file mode 100644 index 0000000..464f2e2 --- /dev/null +++ b/pkg/command/help.go @@ -0,0 +1,122 @@ +package command + +import ( + "context" + "math" + "strings" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/readline" + "github.com/pkg/errors" +) + +type ( + Help struct { + l log.Logger + name string + commands Commands + } + + HelpOption func(*Help) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewHelp(l log.Logger, commands Commands) *Help { + return &Help{ + l: l, + name: "help", + commands: commands, + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (c *Help) Name() string { + return c.name +} + +func (c *Help) Description() string { + return "print help" +} + +func (c *Help) Complete(ctx context.Context, r *readline.Readline, d prompt.Document) (suggests []prompt.Suggest) { + switch { + case r.Args().LenLte(1): + for _, value := range c.list() { + suggests = append(suggests, prompt.Suggest{Text: value.Name(), Description: value.Description()}) + } + } + return suggests +} + +func (c *Help) Validate(ctx context.Context, r *readline.Readline) error { + switch { + case r.Args().LenIs(0): + // all good + case r.Args().LenIs(1): + for _, command := range c.list() { + if r.Args().At(0) == command.Name() { + return nil + } + } + return errors.Errorf("invalid [command] argument: %s", r.Args().At(0)) + case r.Args().LenGte(2): + return errors.New("too many arguments") + } + + return nil +} + +func (c *Help) Execute(ctx context.Context, r *readline.Readline) error { + switch r.Args().Len() { + case 0: + ret := `Help about all available commands. + +Usage: + help [command] + +Available Commands: +` + for _, value := range c.list() { + ret += c.format(value.Name(), value.Description()) + } + c.l.Print(ret) + case 1: + if helper, ok := c.commands.Get(r.Args().At(0)).(Helper); ok { + c.l.Print(helper.Help()) + } + } + return nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *Help) list() []Command { + var ret []Command + for _, value := range c.commands.List() { + if _, ok := value.(Helper); ok { + ret = append(ret, value) + } + } + return ret +} + +// print formatted output +func (c *Help) format(name, description string) string { + offset := int(math.Max(0, float64(20-len(name)))) + suffix := strings.Repeat(" ", offset) + prefix := "" + if offset == 0 { + suffix = "\n" + prefix = strings.Repeat(" ", 20) + } + return " " + name + suffix + prefix + description + "\n" +} diff --git a/pkg/command/tree/arg.go b/pkg/command/tree/arg.go new file mode 100644 index 0000000..4ba3b7c --- /dev/null +++ b/pkg/command/tree/arg.go @@ -0,0 +1,15 @@ +package tree + +import ( + "context" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/readline" +) + +type Arg struct { + Name string + Repeat bool + Optional bool + Suggest func(ctx context.Context, parser *Root, args *readline.Readline) (suggest []prompt.Suggest) +} diff --git a/pkg/command/tree/args.go b/pkg/command/tree/args.go new file mode 100644 index 0000000..0cbd1e9 --- /dev/null +++ b/pkg/command/tree/args.go @@ -0,0 +1,11 @@ +package tree + +type Args []*Arg + +func (a Args) Last() *Arg { + if len(a) > 0 { + return a[len(a)-1] + } else { + return nil + } +} diff --git a/pkg/command/tree/flag.go b/pkg/command/tree/flag.go new file mode 100644 index 0000000..95a95ff --- /dev/null +++ b/pkg/command/tree/flag.go @@ -0,0 +1,7 @@ +package tree + +type Flag struct { + Name string + Required bool + Value interface{} +} diff --git a/pkg/command/tree/node.go b/pkg/command/tree/node.go new file mode 100644 index 0000000..9cb57ec --- /dev/null +++ b/pkg/command/tree/node.go @@ -0,0 +1,93 @@ +package tree + +import ( + "context" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/readline" + + "github.com/pkg/errors" +) + +type Node struct { + Name string + Names func() []string + Args Args + Flags func(fs *readline.FlagSet) + PassThroughArgs Args + PassThroughFlags func(fs *readline.FlagSet) + Description string + Commands []*Node + Execute func(ctx context.Context, args *readline.Readline) error + //Suggest func(ctx context.Context, parser *Parser, args *prompt.Args) (suggest []prompt.Suggest) +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *Node) setFlags(r *readline.Readline, parse bool) error { + if c.Flags != nil { + r.SetFlags(readline.NewFlagSet(c.Flags)) + if parse { + if err := r.ParseFlags(); err != nil { + return errors.Wrap(err, "failed to parse flags") + } + } + } + if c.PassThroughFlags != nil { + r.SetParsePassThroughFlags(readline.NewFlagSet(c.PassThroughFlags)) + if parse { + if err := r.ParsePassThroughFlags(); err != nil { + return errors.Wrap(err, "failed to parse pass through flags") + } + } + } + return nil +} + +func (c *Node) completeArguments(ctx context.Context, p *Root, r *readline.Readline, i int) (suggest []prompt.Suggest) { + localArgs := r.Args()[i:] + if len(c.Commands) > 0 && len(localArgs) <= 1 { + for _, command := range c.Commands { + suggest = append(suggest, prompt.Suggest{Text: command.Name, Description: command.Description}) + } + } else if len(c.Args) >= len(localArgs) { + if fn := c.Args[len(localArgs)-1].Suggest; fn != nil { + suggest = fn(ctx, p, r) + } + } else if lastArg := c.Args.Last(); lastArg != nil && lastArg.Repeat { + if fn := lastArg.Suggest; fn != nil { + suggest = fn(ctx, p, r) + } + } + return suggest +} + +func (c *Node) completeFlags(r *readline.Readline) (suggest []prompt.Suggest) { + for _, f := range r.AllFlags() { + suggest = append(suggest, prompt.Suggest{Text: "--" + f.Name, Description: f.Usage}) + } + return suggest +} + +func (c *Node) completePassThroughFlags(r *readline.Readline) (suggest []prompt.Suggest) { + for _, f := range r.AllPassThroughFlags() { + suggest = append(suggest, prompt.Suggest{Text: "--" + f.Name, Description: f.Usage}) + } + return suggest +} + +func (c *Node) execute(ctx context.Context, r *readline.Readline, i int) error { + localArgs := r.Args()[i:] + if len(c.Commands) > 0 && len(localArgs) == 0 { + return errors.New("missing [command] argument") + } else if len(c.Args) > 0 { + for j, arg := range c.Args { + if !arg.Optional && len(localArgs) <= j+1 { + return errors.New("missing [" + arg.Name + "] argument") + } + } + } + return c.Execute(ctx, r) +} diff --git a/pkg/command/tree/nodes.go b/pkg/command/tree/nodes.go new file mode 100644 index 0000000..fba1ca9 --- /dev/null +++ b/pkg/command/tree/nodes.go @@ -0,0 +1,3 @@ +package tree + +type Nodes []*Node diff --git a/pkg/command/tree/root.go b/pkg/command/tree/root.go new file mode 100644 index 0000000..076e493 --- /dev/null +++ b/pkg/command/tree/root.go @@ -0,0 +1,108 @@ +package tree + +import ( + "context" + "sort" + + "github.com/c-bata/go-prompt" + "github.com/foomo/posh/pkg/readline" + + "github.com/pkg/errors" +) + +type Root struct { + Name string + Nodes []*Node +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (p *Root) Execute(ctx context.Context, r *readline.Readline) error { + if r.Args().LenIs(0) { + return errors.New("missing [command] argument") + } else if cmd, i := p.find(p.Nodes, r, 0); cmd == nil { + return errors.New("invalid [command] argument") + } else if err := cmd.setFlags(r, true); err != nil { + return err + } else if err := cmd.execute(ctx, r, i); err != nil { + return err + } + return nil +} + +func (p *Root) Complete(ctx context.Context, r *readline.Readline) (suggests []prompt.Suggest) { + switch r.Mode() { + case readline.ModeArgs: + if r.Args().LenLte(1) { + for _, command := range p.Nodes { + suggests = append(suggests, prompt.Suggest{Text: command.Name, Description: command.Description}) + } + } else if cmd, i := p.find(p.Nodes, r, 0); cmd == nil { + return nil + } else if err := cmd.setFlags(r, false); err != nil { + return nil + } else { + suggests = cmd.completeArguments(ctx, p, r, i+1) + } + sort.Slice(suggests, func(i, j int) bool { + return suggests[i].Text < suggests[j].Text + }) + case readline.ModeFlags: + if cmd, _ := p.find(p.Nodes, r, 0); cmd == nil { + return nil + } else if err := cmd.setFlags(r, false); err != nil { + return nil + } else { + suggests = cmd.completeFlags(r) + } + sort.Slice(suggests, func(i, j int) bool { + return suggests[i].Text < suggests[j].Text + }) + case readline.ModePassThroughArgs: + // TODO + case readline.ModePassThroughFlags: + if cmd, _ := p.find(p.Nodes, r, 0); cmd == nil { + return nil + } else if err := cmd.setFlags(r, false); err != nil { + return nil + } else { + suggests = cmd.completePassThroughFlags(r) + } + sort.Slice(suggests, func(i, j int) bool { + return suggests[i].Text < suggests[j].Text + }) + } + return suggests +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (p *Root) find(cmds []*Node, r *readline.Readline, i int) (*Node, int) { + if r.Args().LenLt(i + 1) { + return nil, i + } + arg := r.Args().At(i) + for _, cmd := range cmds { + if cmd.Name == arg { + if subCmd, j := p.find(cmd.Commands, r, i+1); subCmd != nil { + return subCmd, j + } + return cmd, i + } + if cmd.Names != nil { + for _, name := range cmd.Names() { + if name == arg { + if subCmd, j := p.find(cmd.Commands, r, i+1); subCmd != nil { + return subCmd, j + } + } + return cmd, i + } + } + } + return nil, i +} diff --git a/pkg/config/dependencies.go b/pkg/config/dependencies.go new file mode 100644 index 0000000..b66569b --- /dev/null +++ b/pkg/config/dependencies.go @@ -0,0 +1,40 @@ +package config + +import ( + "fmt" +) + +type ( + Dependencies struct { + Envs []DependenciesEnv `json:"envs" yaml:"envs"` + Scripts []DependenciesScript `json:"script" yaml:"script"` + Packages []DependenciesPackage `json:"packages" yaml:"packages"` + } + DependenciesEnv struct { + Name string `json:"name" yaml:"name"` + Help string `json:"help" yaml:"help"` + } + DependenciesScript struct { + Name string `json:"name" yaml:"name"` + Command string `json:"command" yaml:"command"` + Help string `json:"help" yaml:"help"` + } + DependenciesPackage struct { + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + Command string `json:"command" yaml:"command"` + Help string `json:"help" yaml:"help"` + } +) + +func (c DependenciesEnv) String() string { + return fmt.Sprintf("Name: %s", c.Name) +} + +func (c DependenciesScript) String() string { + return fmt.Sprintf("Name: %s, Command: %s", c.Name, c.Command) +} + +func (c DependenciesPackage) String() string { + return fmt.Sprintf("Name: %s, Version: %s Command: %s", c.Name, c.Version, c.Command) +} diff --git a/pkg/config/package.go b/pkg/config/package.go new file mode 100644 index 0000000..29824a4 --- /dev/null +++ b/pkg/config/package.go @@ -0,0 +1,15 @@ +package config + +import ( + "fmt" +) + +type Package struct { + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + Command string `json:"command" yaml:"command"` +} + +func (c Package) String() string { + return fmt.Sprintf("Name: %s, Version: %s Command: %s", c.Name, c.Version, c.Command) +} diff --git a/pkg/config/plugin.go b/pkg/config/plugin.go new file mode 100644 index 0000000..c41a7e3 --- /dev/null +++ b/pkg/config/plugin.go @@ -0,0 +1,15 @@ +package config + +import ( + "fmt" +) + +type Plugin struct { + Source string `json:"source" yaml:"source"` + Target string `json:"target" yaml:"target"` + Provider string `json:"provider" yaml:"provider"` +} + +func (c Plugin) String() string { + return fmt.Sprintf("Filename: %s, Provider: %s", c.Source, c.Provider) +} diff --git a/pkg/config/prompt.go b/pkg/config/prompt.go new file mode 100644 index 0000000..272ac73 --- /dev/null +++ b/pkg/config/prompt.go @@ -0,0 +1,14 @@ +package config + +type ( + Prompt struct { + Title string `json:"title" yaml:"title"` + Prefix string `json:"prefix" yaml:"prefix"` + History PromptHistory `json:"history" yaml:"history"` + } + PromptHistory struct { + Limit int `json:"limit" yaml:"limit"` + Filename string `json:"filename" yaml:"filename"` + LockFilename string `json:"lockFilename" yaml:"lockFilename"` + } +) diff --git a/pkg/log/fmt.go b/pkg/log/fmt.go new file mode 100644 index 0000000..231bf5f --- /dev/null +++ b/pkg/log/fmt.go @@ -0,0 +1,120 @@ +package log + +import ( + "fmt" + "os" +) + +type ( + Fmt struct { + level Level + } + FmtOption func(*Fmt) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func FmtWithLevel(v Level) FmtOption { + return func(o *Fmt) { + o.level = v + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewFmt(opts ...FmtOption) *Fmt { + inst := &Fmt{ + level: LevelError, + } + for _, opt := range opts { + if opt != nil { + opt(inst) + } + } + return inst +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (l *Fmt) Level() Level { + return l.level +} + +func (l *Fmt) Print(a ...interface{}) { + fmt.Println(append([]interface{}{"Info:"}, a...)...) +} + +func (l *Fmt) Printf(format string, a ...interface{}) { + fmt.Printf(format+"\n", a...) +} + +func (l *Fmt) Debug(a ...interface{}) { + if l.level <= LevelDebug { + fmt.Println(append([]interface{}{"Debug:"}, a...)...) + } +} + +func (l *Fmt) Debugf(format string, a ...interface{}) { + if l.level <= LevelDebug { + fmt.Printf("Debug: "+format+"\n", a...) + } +} + +func (l *Fmt) Info(a ...interface{}) { + if l.level <= LevelInfo { + fmt.Println(append([]interface{}{"Info:"}, a...)...) + } +} + +func (l *Fmt) Infof(format string, a ...interface{}) { + if l.level <= LevelInfo { + fmt.Printf("Info: "+format+"\n", a...) + } +} + +func (l *Fmt) Warn(a ...interface{}) { + if l.level <= LevelWarn { + fmt.Println(append([]interface{}{"Warn:"}, a...)...) + } +} + +func (l *Fmt) Warnf(format string, a ...interface{}) { + if l.level <= LevelWarn { + fmt.Printf("Warn: "+format+"\n", a...) + } +} + +func (l *Fmt) Error(a ...interface{}) { + if l.level <= LevelError { + fmt.Println(append([]interface{}{"Error:"}, a...)...) + } +} + +func (l *Fmt) Errorf(format string, a ...interface{}) { + if l.level <= LevelError { + fmt.Printf("Error: "+format+"\n", a...) + } +} + +func (l *Fmt) Fatal(a ...interface{}) { + fmt.Println(append([]interface{}{"Fatal:"}, a...)...) + os.Exit(1) +} + +func (l *Fmt) Fatalf(format string, args ...interface{}) { + fmt.Printf("Fatal: "+format+"\n", args...) + os.Exit(1) +} + +func (l *Fmt) Must(err error) { + if err != nil { + l.Error(err.Error()) + os.Exit(1) + } +} diff --git a/pkg/log/level.go b/pkg/log/level.go new file mode 100644 index 0000000..5a2193d --- /dev/null +++ b/pkg/log/level.go @@ -0,0 +1,28 @@ +package log + +type Level int + +const ( + LevelTrace Level = iota + LevelDebug + LevelInfo + LevelWarn + LevelError +) + +func GetLevel(v string) Level { + switch v { + case "trace": + return LevelTrace + case "debug": + return LevelDebug + case "info": + return LevelInfo + case "warn": + return LevelWarn + case "error": + return LevelError + default: + return LevelInfo + } +} diff --git a/pkg/log/logger.go b/pkg/log/logger.go new file mode 100644 index 0000000..ab98b1c --- /dev/null +++ b/pkg/log/logger.go @@ -0,0 +1,18 @@ +package log + +type Logger interface { + Level() Level + Print(a ...interface{}) + Printf(format string, a ...interface{}) + Debug(a ...interface{}) + Debugf(format string, a ...interface{}) + Info(a ...interface{}) + Infof(format string, a ...interface{}) + Warn(a ...interface{}) + Warnf(format string, a ...interface{}) + Error(a ...interface{}) + Errorf(format string, a ...interface{}) + Fatal(a ...interface{}) + Fatalf(format string, a ...interface{}) + Must(err error) +} diff --git a/pkg/log/pterm.go b/pkg/log/pterm.go new file mode 100644 index 0000000..22dcd76 --- /dev/null +++ b/pkg/log/pterm.go @@ -0,0 +1,140 @@ +package log + +import ( + "os" + + "github.com/pterm/pterm" +) + +type ( + PTerm struct { + level Level + } + PTermOption func(*PTerm) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func PTermWithDisableColor(v bool) PTermOption { + return func(o *PTerm) error { + if v { + pterm.DisableColor() + } + return nil + } +} + +func PTermWithLevel(v Level) PTermOption { + return func(o *PTerm) error { + o.level = v + switch { + case v <= LevelTrace: + pterm.Debug.ShowLineNumber = true + fallthrough + case v <= LevelDebug: + pterm.EnableDebugMessages() + fallthrough + default: + pterm.Debug.LineNumberOffset = 1 + } + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewPTerm(opts ...PTermOption) (*PTerm, error) { + inst := &PTerm{ + level: LevelError, + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (l *PTerm) Level() Level { + return l.level +} + +func (l *PTerm) Print(a ...interface{}) { + pterm.Println(a...) +} + +func (l *PTerm) Printf(format string, a ...interface{}) { + pterm.Printfln(format, a...) +} +func (l *PTerm) Debug(a ...interface{}) { + if l.level <= LevelDebug { + pterm.Debug.Println(a...) + } +} + +func (l *PTerm) Debugf(format string, a ...interface{}) { + if l.level <= LevelDebug { + pterm.Debug.Printfln(format, a...) + } +} + +func (l *PTerm) Info(a ...interface{}) { + if l.level <= LevelInfo { + pterm.Info.Println(a...) + } +} + +func (l *PTerm) Infof(format string, a ...interface{}) { + if l.level <= LevelInfo { + pterm.Info.Printfln(format, a...) + } +} + +func (l *PTerm) Warn(a ...interface{}) { + if l.level <= LevelWarn { + pterm.Warning.Println(a...) + } +} + +func (l *PTerm) Warnf(format string, a ...interface{}) { + if l.level <= LevelWarn { + pterm.Warning.Printfln(format, a...) + } +} + +func (l *PTerm) Error(a ...interface{}) { + if l.level <= LevelError { + pterm.Error.Println(a...) + } +} + +func (l *PTerm) Errorf(format string, a ...interface{}) { + if l.level <= LevelError { + pterm.Error.Printfln(format, a...) + } +} + +func (l *PTerm) Fatal(a ...interface{}) { + pterm.Fatal.Println(a...) +} + +func (l *PTerm) Fatalf(format string, args ...interface{}) { + pterm.Fatal.Printfln(format, args...) +} + +func (l *PTerm) Must(err error) { + if err != nil { + l.Error(err.Error()) + os.Exit(1) + } +} diff --git a/pkg/ownbrew/ownbrew.go b/pkg/ownbrew/ownbrew.go new file mode 100644 index 0000000..d6e7392 --- /dev/null +++ b/pkg/ownbrew/ownbrew.go @@ -0,0 +1,189 @@ +package ownbrew + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type ( + Ownbrew struct { + l log.Logger + binDir string + tempDir string + caskDir string + cellarDir string + packages []config.Package + } + Option func(*Ownbrew) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func WithPackages(v ...config.Package) Option { + return func(o *Ownbrew) error { + o.packages = append(o.packages, v...) + return nil + } +} + +func WithBinDir(v string) Option { + return func(o *Ownbrew) error { + o.binDir = v + return nil + } +} + +func WithTempDir(v string) Option { + return func(o *Ownbrew) error { + o.tempDir = v + return nil + } +} + +func WithCaskDir(v string) Option { + return func(o *Ownbrew) error { + o.caskDir = v + return nil + } +} + +func WithCellarDir(v string) Option { + return func(o *Ownbrew) error { + o.cellarDir = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(l log.Logger, opts ...Option) (*Ownbrew, error) { + inst := &Ownbrew{ + l: l, + binDir: "bin", + tempDir: ".posh/tmp", + caskDir: ".posh/scripts/ownbrew", + cellarDir: ".posh/bin", + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + + for _, dir := range []string{inst.binDir, inst.tempDir, inst.caskDir, inst.cellarDir} { + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return nil, err + } + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (o *Ownbrew) Install(ctx context.Context) error { + o.l.Debug("installing packages", runtime.GOOS, runtime.GOARCH) + + for _, pkg := range o.packages { + if pkg.Command == "" { + pkg.Command = filepath.Join(o.caskDir, pkg.Name+".sh") + } + o.l.Debug("installing:", pkg.String()) + + cellarFilename := filepath.Join(o.cellarDir, fmt.Sprintf("%s-%s-%s-%s", pkg.Name, pkg.Version, runtime.GOOS, runtime.GOARCH)) + exists, err := o.cellarExists(cellarFilename) + if err != nil { + return err + } + + if !exists { + if err := o.casksExists(pkg.Command); err != nil { + return err + } + + cmd := exec.CommandContext(ctx, "sh", + pkg.Command, + pkg.Version, + runtime.GOOS, + runtime.GOARCH, + ) + + cmd.Env = append( + os.Environ(), + "BIN_DIR="+o.cellarDir, + "TEMP_DIR="+o.tempDir, + ) + + output, err := cmd.CombinedOutput() + o.l.Debug(string(output)) + if err != nil { + return errors.Wrapf(err, "failed to install package: %s", pkg.Name) + } + } + + // create symlink + if err := o.symlink(cellarFilename, filepath.Join(o.binDir, pkg.Name)); err != nil { + return err + } + } + return nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (o *Ownbrew) symlink(source, target string) error { + if err := os.Remove(target); os.IsNotExist(err) { + // continue + } else if err != nil { + return err + } + + prefix, err := filepath.Rel(filepath.Base(target), "") + if err != nil { + return err + } + prefix = strings.TrimSuffix(prefix, ".") + + o.l.Debug("symlink:", prefix+source, target) + return os.Symlink(prefix+source, target) +} + +func (o *Ownbrew) casksExists(filename string) error { + if stat, err := os.Stat(filename); err != nil { + return errors.Wrap(err, "failed to stat cask") + } else if stat.IsDir() { + return fmt.Errorf("not an executeable: %s", filename) + } else { + return nil + } +} + +func (o *Ownbrew) cellarExists(filename string) (bool, error) { + if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { + o.l.Debug("install:", filename) + return false, nil + } else if err != nil { + return false, errors.Wrap(err, "failed to stat bin") + } else { + o.l.Debug("exists:", filename) + return true, nil + } +} diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go new file mode 100644 index 0000000..9d66b90 --- /dev/null +++ b/pkg/plugin/manager.go @@ -0,0 +1,101 @@ +package plugin + +import ( + "context" + "fmt" + "os/exec" + "path" + "path/filepath" + "plugin" + "strings" + + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type ( + Plugin interface { + Prompt(ctx context.Context, cfg config.Prompt) error + Execute(ctx context.Context, args []string) error + Packages(ctx context.Context, cfg []config.Package) error + Dependencies(ctx context.Context, cfg config.Dependencies) error + } + Provider func(l log.Logger) (Plugin, error) +) + +type Manager struct { + l log.Logger + plugins map[string]*plugin.Plugin +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewManager(l log.Logger) (*Manager, error) { + inst := &Manager{ + l: l, + plugins: map[string]*plugin.Plugin{}, + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (m *Manager) BuildAndLoadPlugin(ctx context.Context, filename, provider string) (Plugin, error) { + if err := m.Build(ctx, filename); err != nil { + return nil, err + } + return m.LoadPlugin(filename, provider) +} + +func (m *Manager) Build(ctx context.Context, filename string) error { + m.l.Debug("building:", filename) + + dir := filepath.Dir(filename) + base := path.Base(filename) + cmd := exec.CommandContext(ctx, "go", "build", + "-buildmode=plugin", + "-o", strings.ReplaceAll(base, ".go", ".so"), + base, + ) + cmd.Dir = dir + if output, err := cmd.CombinedOutput(); err != nil { + return errors.Wrap(err, string(output)) + } + return nil +} + +func (m *Manager) LoadPlugin(filename, provider string) (Plugin, error) { + m.l.Debug("loading plugin:", filename, provider) + filename = strings.ReplaceAll(filename, ".go", ".so") + if plg, err := m.Load(filename); err != nil { + return nil, err + } else if sym, err := plg.Lookup(provider); err != nil { + return nil, errors.Wrapf(err, "failed to lookup provider (%s)", provider) + } else if fn, ok := sym.(func(l log.Logger) (Plugin, error)); !ok { + return nil, fmt.Errorf("invalid provider type (%T) ", sym) + } else if inst, err := fn(m.l); err != nil { + return nil, errors.Wrap(err, "failed to create plugin instance") + } else if inst == nil { + return nil, errors.New("plugin can not be nil") + } else { + return inst, nil + } +} + +func (m *Manager) Load(filename string) (*plugin.Plugin, error) { + if value, ok := m.plugins[filename]; ok { + return value, nil + } + m.l.Debug("loading:", filename) + if plg, err := plugin.Open(filename); err != nil { + return nil, errors.Wrapf(err, "failed to load plugin (%s)", filename) + } else { + m.plugins[filename] = plg + return plg, nil + } +} diff --git a/pkg/prompt/aliases.go b/pkg/prompt/aliases.go new file mode 100644 index 0000000..1c9bd86 --- /dev/null +++ b/pkg/prompt/aliases.go @@ -0,0 +1,18 @@ +package prompt + +import ( + "github.com/c-bata/go-prompt" +) + +type ( + Filter = prompt.Filter + Suggest = prompt.Suggest + Document = prompt.Document +) + +var ( + FilterFuzzy = prompt.FilterFuzzy + FilterContains = prompt.FilterContains + FilterHasPrefix = prompt.FilterHasPrefix + FilterHasSuffix = prompt.FilterHasSuffix +) diff --git a/pkg/prompt/check/check.go b/pkg/prompt/check/check.go new file mode 100644 index 0000000..61c8758 --- /dev/null +++ b/pkg/prompt/check/check.go @@ -0,0 +1,9 @@ +package check + +import ( + "context" + + "github.com/foomo/posh/pkg/log" +) + +type Check func(ctx context.Context, l log.Logger, checkers []Checker) error diff --git a/pkg/prompt/check/checker.go b/pkg/prompt/check/checker.go new file mode 100644 index 0000000..08a1acd --- /dev/null +++ b/pkg/prompt/check/checker.go @@ -0,0 +1,9 @@ +package check + +import ( + "context" + + "github.com/foomo/posh/pkg/log" +) + +type Checker func(ctx context.Context, l log.Logger) (name, note string, ok bool) diff --git a/pkg/prompt/check/defaultcheck.go b/pkg/prompt/check/defaultcheck.go new file mode 100644 index 0000000..32e7277 --- /dev/null +++ b/pkg/prompt/check/defaultcheck.go @@ -0,0 +1,23 @@ +package check + +import ( + "context" + + "github.com/foomo/posh/pkg/log" + "github.com/pterm/pterm" +) + +func DefaultCheck(ctx context.Context, l log.Logger, checkers []Checker) error { + var data pterm.TableData + for _, checker := range checkers { + name, note, ok := checker(ctx, l) + data = append(data, []string{name, StatusFromBool(ok).String(), pterm.FgGray.Sprint(note)}) + } + table := pterm.DefaultTable + table.Separator = " " + if err := table.WithData(data).Render(); err != nil { + return err + } + pterm.Println() + return nil +} diff --git a/pkg/prompt/check/status.go b/pkg/prompt/check/status.go new file mode 100644 index 0000000..4a7dcf8 --- /dev/null +++ b/pkg/prompt/check/status.go @@ -0,0 +1,20 @@ +package check + +type Status string + +const ( + StatusSuccess = "✅" + StatusFailure = "❌" +) + +func (s Status) String() string { + return string(s) +} + +func StatusFromBool(v bool) Status { + if v { + return StatusSuccess + } else { + return StatusFailure + } +} diff --git a/pkg/prompt/flair/defaultflair.go b/pkg/prompt/flair/defaultflair.go new file mode 100644 index 0000000..faf225f --- /dev/null +++ b/pkg/prompt/flair/defaultflair.go @@ -0,0 +1,3 @@ +package flair + +type Flair func(title string) error diff --git a/pkg/prompt/flair/flair.go b/pkg/prompt/flair/flair.go new file mode 100644 index 0000000..886acf3 --- /dev/null +++ b/pkg/prompt/flair/flair.go @@ -0,0 +1,21 @@ +package flair + +import ( + "strings" + + "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" +) + +func DefaultFlair(title string) error { + pterm.FgGray.Println() + if err := pterm.DefaultBigText.WithLetters( + putils.LettersFromStringWithStyle(strings.ToUpper(title), pterm.NewStyle(pterm.FgCyan)), + putils.LettersFromStringWithStyle("$H", pterm.NewStyle(pterm.FgGreen))). + Render(); err != nil { + return err + } + pterm.FgGray.Println("Use `exit` or `Ctrl-D` to exit this program.") + pterm.Println() + return nil +} diff --git a/pkg/prompt/history/file.go b/pkg/prompt/history/file.go new file mode 100644 index 0000000..5eb2774 --- /dev/null +++ b/pkg/prompt/history/file.go @@ -0,0 +1,157 @@ +package history + +import ( + "context" + "os" + "strings" + "sync" + "time" + + "github.com/foomo/posh/pkg/log" + "github.com/gofrs/flock" + "github.com/pkg/errors" +) + +type ( + File struct { + l log.Logger + mu sync.RWMutex + lock *flock.Flock + limit int + filename string + lockFilename string + } + FileOption func(*File) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewFile(l log.Logger, opts ...FileOption) (*File, error) { + inst := &File{ + l: l, + limit: 100, + filename: ".posh/history", + lockFilename: ".posh/history.lock", + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + inst.lock = flock.New(inst.lockFilename) + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func FileWithLimit(v int) FileOption { + return func(h *File) error { + h.limit = v + return nil + } +} + +func FileWithFilename(v string) FileOption { + return func(h *File) error { + h.filename = v + return nil + } +} + +func FileWithLockFilename(v string) FileOption { + return func(h *File) error { + h.lockFilename = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (h *File) Load(ctx context.Context) ([]string, error) { + return h.read(ctx) +} + +func (h *File) Persist(ctx context.Context, record string) { + go func(ctx context.Context, record string) { + if lines, err := h.read(ctx); err != nil { + h.l.Warnf("failed to read history file (%s): %s", h.filename, err.Error()) + return + } else if err := h.write(ctx, h.truncate(h.unique(append(lines, record)))); err != nil { + h.l.Warnf("failed to write history file (%s): %s", h.filename, err.Error()) + return + } + }(ctx, record) +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (h *File) read(ctx context.Context) ([]string, error) { + h.mu.RLock() + defer h.mu.RUnlock() + if _, err := h.lock.TryRLockContext(ctx, 100*time.Millisecond); err != nil { + return nil, err + } + defer func() { + if err := h.lock.Unlock(); err != nil { + h.l.Warnf("failed to unlock on history file (%s): %s", h.filename, err.Error()) + } + }() + b, err := os.ReadFile(h.filename) + if errors.Is(err, os.ErrNotExist) { + return nil, nil + } else if err != nil { + return nil, err + } + var lines []string + for _, line := range strings.Split(string(b), "\n") { + lines = append(lines, line) + } + return lines, nil +} + +func (h *File) write(ctx context.Context, lines []string) error { + h.mu.Lock() + defer h.mu.Unlock() + if _, err := h.lock.TryLockContext(ctx, 100*time.Millisecond); err != nil { + return err + } + defer func() { + if err := h.lock.Unlock(); err != nil { + h.l.Warnf("failed to unlock on history file (%s): %s", h.filename, err.Error()) + } + }() + return os.WriteFile(h.filename, []byte(strings.Join(lines, "\n")), 0644) +} + +func (h *File) unique(lines []string) []string { + var list []string + var revList []string + keys := make(map[string]bool) + for i := len(lines) - 1; i >= 0; i-- { + if _, ok := keys[lines[i]]; !ok { + revList = append(revList, lines[i]) + keys[lines[i]] = true + } + } + for i := len(revList) - 1; i >= 0; i-- { + list = append(list, revList[i]) + } + return list +} + +func (h *File) truncate(lines []string) []string { + if len(lines) > h.limit { + lines = lines[len(lines)-h.limit:] + } + return lines +} diff --git a/pkg/prompt/history/history.go b/pkg/prompt/history/history.go new file mode 100644 index 0000000..f487a8a --- /dev/null +++ b/pkg/prompt/history/history.go @@ -0,0 +1,10 @@ +package history + +import ( + "context" +) + +type History interface { + Load(ctx context.Context) ([]string, error) + Persist(ctx context.Context, record string) +} diff --git a/pkg/prompt/history/noop.go b/pkg/prompt/history/noop.go new file mode 100644 index 0000000..54085e3 --- /dev/null +++ b/pkg/prompt/history/noop.go @@ -0,0 +1,32 @@ +package history + +import ( + "context" + + "github.com/foomo/posh/pkg/log" +) + +type Noop struct { + l log.Logger +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewNoop(l log.Logger) *Noop { + return &Noop{ + l: l, + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (h *Noop) Load(ctx context.Context) ([]string, error) { + return nil, nil +} + +func (h *Noop) Persist(ctx context.Context, record string) { +} diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go new file mode 100644 index 0000000..a76e623 --- /dev/null +++ b/pkg/prompt/prompt.go @@ -0,0 +1,318 @@ +package prompt + +import ( + "context" + "os" + "os/signal" + "strings" + + "github.com/c-bata/go-prompt" + "github.com/c-bata/go-prompt/completer" + "github.com/foomo/posh/pkg/command" + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/prompt/check" + "github.com/foomo/posh/pkg/prompt/flair" + "github.com/foomo/posh/pkg/prompt/history" + "github.com/foomo/posh/pkg/readline" + "github.com/foomo/posh/pkg/shell" +) + +type ( + Prompt struct { + l log.Logger + ctx context.Context + title string + flair flair.Flair + prefix string + check check.Check + checkers []check.Checker + filter Filter + readline *readline.Readline + history history.History + commands command.Commands + // inputRegex - split cmd into args + promptOptions []prompt.Option + } + Option func(*Prompt) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func WithTitle(v string) Option { + return func(o *Prompt) error { + o.title = v + return nil + } +} + +func WithFlair(v flair.Flair) Option { + return func(o *Prompt) error { + o.flair = v + return nil + } +} + +func WithPrefix(v string) Option { + return func(o *Prompt) error { + o.prefix = v + " " + return nil + } +} + +func WithCheck(v check.Check) Option { + return func(o *Prompt) error { + o.check = v + return nil + } +} + +func WithCheckers(v ...check.Checker) Option { + return func(o *Prompt) error { + o.checkers = append(o.checkers, v...) + return nil + } +} + +func WithContext(v context.Context) Option { + return func(o *Prompt) error { + o.ctx = v + return nil + } +} + +func WithHistory(v history.History) Option { + return func(o *Prompt) error { + o.history = v + return nil + } +} + +func WithFileHistory(v ...history.FileOption) Option { + return func(o *Prompt) error { + if value, err := history.NewFile(o.l, v...); err != nil { + return err + } else { + o.history = value + return nil + } + } +} + +func WithCommands(v command.Commands) Option { + return func(p *Prompt) error { + p.commands = v + return nil + } +} + +func WithFilter(v Filter) Option { + return func(o *Prompt) error { + o.filter = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(l log.Logger, opts ...Option) (*Prompt, error) { + inst := &Prompt{ + l: l, + ctx: context.Background(), + title: "posh", + prefix: "> ", + flair: flair.DefaultFlair, + filter: FilterFuzzy, + check: check.DefaultCheck, + history: history.NewNoop(l), + commands: command.Commands{}, + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + if value, err := readline.New(l); err != nil { + return nil, err + } else { + inst.readline = value + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Getter +// ------------------------------------------------------------------------------------------------ + +func (s *Prompt) Commands() command.Commands { + return s.commands +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (s *Prompt) Run() error { + histories, err := s.history.Load(s.ctx) + if err != nil { + return err + } + + p := prompt.New( + s.execute, + s.complete, + prompt.OptionTitle(s.title), + prompt.OptionPrefix(s.prefix), + prompt.OptionPrefixTextColor(prompt.Cyan), + prompt.OptionInputTextColor(prompt.DefaultColor), + prompt.OptionCompletionWordSeparator(completer.FilePathCompletionSeparator), + prompt.OptionHistoryIgnoreDuplicates(), + prompt.OptionHistory(histories), + // macos alt+left fix + prompt.OptionAddASCIICodeBind(prompt.ASCIICodeBind{ + ASCIICode: []byte{0x1b, 0x62}, + Fn: prompt.GoLeftWord, + }), + // macos alt+right fix + prompt.OptionAddASCIICodeBind(prompt.ASCIICodeBind{ + ASCIICode: []byte{0x1b, 0x66}, + Fn: prompt.GoRightWord, + }), + ) + + if err := s.flair(s.title); err != nil { + return err + } + + if err := s.check(s.ctx, s.l, s.checkers); err != nil { + return err + } + + p.Run() + + return nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (s *Prompt) execute(input string) { + input = strings.TrimSpace(input) + if input == "" { + return + } + + s.history.Persist(s.ctx, input) + + if err := s.readline.Parse(input); err != nil { + s.l.Error("failed to parse line:", err.Error()) + return + } + + ctx := s.context() + if cmd := s.Commands().Get(s.readline.Cmd()); cmd != nil { + s.l.Debugf(`executing command: + +> %s + +%s +`, input, s.readline.String()) + if value, ok := cmd.(command.Validator); ok { + if err := value.Validate(ctx, s.readline); err != nil { + s.l.Error(err.Error()) + } + } + if err := cmd.Execute(ctx, s.readline); err != nil { + s.l.Error(err.Error()) + } + } else if err := shell.New(ctx, s.l, input).Run(); err != nil { + s.l.Error(err.Error()) + } +} + +func (s *Prompt) complete(d prompt.Document) []prompt.Suggest { + input := d.TextBeforeCursor() + if input == "" { + return nil + } + + if err := s.readline.Parse(input); err != nil { + s.l.Debug("failed to parse line:", err.Error()) + return nil + } + word := d.GetWordBeforeCursor() + + // return root completion + if s.readline.IsModeDefault() && s.readline.Args().LenIs(0) { + var suggests []prompt.Suggest + for _, inst := range s.Commands().List() { + suggests = append(suggests, prompt.Suggest{Text: inst.Name(), Description: inst.Description()}) + } + return s.filter(suggests, word, true) + } + + // retrieve command instance + cmd := s.commands.Get(s.readline.Cmd()) + if cmd == nil { + return nil + } + + ctx := s.context() + + switch s.readline.Mode() { + case readline.ModeArgs: + if value, ok := cmd.(command.ArgumentCompleter); ok { + return s.filter(value.CompleteArguments(ctx, s.readline, d), word, true) + } else if value, ok := cmd.(command.Completer); ok { + return s.filter(value.Complete(ctx, s.readline, d), word, true) + } + case readline.ModeFlags: + if value, ok := cmd.(command.FlagCompleter); ok { + return s.filter(value.CompleteFlags(ctx, s.readline, d), word, true) + } else if value, ok := cmd.(command.Completer); ok { + return s.filter(value.Complete(ctx, s.readline, d), word, true) + } + case readline.ModePassThroughArgs: + if value, ok := cmd.(command.PassThroughArgsCompleter); ok { + return s.filter(value.CompletePassTroughArgs(ctx, s.readline, d), word, true) + } else if value, ok := cmd.(command.Completer); ok { + return s.filter(value.Complete(ctx, s.readline, d), word, true) + } + case readline.ModePassThroughFlags: + if value, ok := cmd.(command.PassThroughFlagsCompleter); ok { + return s.filter(value.CompletePassTroughFlags(ctx, s.readline, d), word, true) + } else if value, ok := cmd.(command.Completer); ok { + return s.filter(value.Complete(ctx, s.readline, d), word, true) + } + case readline.ModeAdditionalArgs: + if value, ok := cmd.(command.AdditionalArgsCompleter); ok { + return s.filter(value.CompleteAdditionalArgs(ctx, s.readline, d), word, true) + } else if value, ok := cmd.(command.Completer); ok { + return s.filter(value.Complete(ctx, s.readline, d), word, true) + } + } + return nil +} + +// context returns and watches over a new context +func (s *Prompt) context() context.Context { + ctx, cancel := context.WithCancel(s.ctx) + go func(ctx context.Context, cancel context.CancelFunc) { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt) + select { + case <-sigChan: + cancel() + return + case <-ctx.Done(): + return + } + }(ctx, cancel) + return ctx +} diff --git a/pkg/readline/arg.go b/pkg/readline/arg.go new file mode 100644 index 0000000..d36d7c7 --- /dev/null +++ b/pkg/readline/arg.go @@ -0,0 +1,34 @@ +package readline + +import ( + "strings" +) + +type Arg string + +func (a Arg) String() string { + return string(a) +} + +func (a Arg) IsPipe() bool { + return a == "|" +} + +func (a Arg) IsPass() bool { + return a == "--" +} + +func (a Arg) IsFlag() bool { + return strings.HasPrefix(a.String(), "-") && len(a) > 1 +} + +func (a Arg) IsRedirect() bool { + return a == ">" || a == ">>" || + a == "2>" || a == "2>>" || + a == "&>" || a == "&>>" || + a == "2>&1" +} + +func (a Arg) IsAdditional() bool { + return a.IsPipe() || a.IsRedirect() +} diff --git a/pkg/readline/args.go b/pkg/readline/args.go new file mode 100644 index 0000000..639d093 --- /dev/null +++ b/pkg/readline/args.go @@ -0,0 +1,76 @@ +package readline + +type Args []string + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewArgs(args []string) Args { + ret := make(Args, len(args)) + for i, arg := range args { + ret[i] = arg + } + return ret +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (a Args) At(v int) string { + if a.HasIndex(v) { + return a[v] + } else { + return "" + } +} + +func (a Args) Empty() bool { + return a == nil || a.LenIs(0) +} + +func (a Args) Has(v string) bool { + for _, arg := range a { + if arg == v { + return true + } + } + return false +} + +func (a Args) HasIndex(v int) bool { + return a.LenGte(v + 1) +} + +func (a Args) Len() int { + return len(a) +} + +func (a Args) LenIs(v int) bool { + return a.Len() == v +} + +func (a Args) LenGt(v int) bool { + return a.Len() > v +} + +func (a Args) LenGte(v int) bool { + return a.Len() >= v +} + +func (a Args) LenLt(v int) bool { + return a.Len() < v +} + +func (a Args) LenLte(v int) bool { + return a.Len() <= v +} + +func (a Args) Last() string { + if !a.Empty() { + return a[a.Len()-1] + } else { + return "" + } +} diff --git a/pkg/readline/mode.go b/pkg/readline/mode.go new file mode 100644 index 0000000..3beae74 --- /dev/null +++ b/pkg/readline/mode.go @@ -0,0 +1,11 @@ +package readline + +type Mode string + +const ( + ModeArgs Mode = "" + ModeFlags Mode = "flags" + ModePassThroughArgs Mode = "passThrough" + ModePassThroughFlags Mode = "passThroughFlags" + ModeAdditionalArgs Mode = "additional" +) diff --git a/pkg/readline/readline.go b/pkg/readline/readline.go new file mode 100644 index 0000000..b54ec4e --- /dev/null +++ b/pkg/readline/readline.go @@ -0,0 +1,330 @@ +package readline + +import ( + "flag" + "fmt" + "regexp" + "strconv" + "sync" + + "github.com/foomo/posh/pkg/log" +) + +type ( + Readline struct { + l log.Logger + mu sync.RWMutex + cmd string + mode Mode + args Args + flags Args + flagSet *FlagSet + passThroughArgs Args + passThroughFlags Args + passThroughFlagSet *FlagSet + additionalArgs Args + // regex - split cmd into args (https://regex101.com/r/EgiOzv/1) + regex *regexp.Regexp + } + Option func(*Readline) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func WithRegex(v *regexp.Regexp) Option { + return func(o *Readline) error { + o.regex = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(l log.Logger, opts ...Option) (*Readline, error) { + inst := &Readline{ + l: l, + regex: regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)'|(\s$)`), + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + return inst, nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Getter +// ------------------------------------------------------------------------------------------------ + +func (a *Readline) Mode() Mode { + a.mu.RLock() + defer a.mu.RUnlock() + return a.mode +} + +func (a *Readline) Cmd() string { + a.mu.RLock() + defer a.mu.RUnlock() + return a.cmd +} + +func (a *Readline) Args() Args { + a.mu.RLock() + defer a.mu.RUnlock() + return a.args +} + +func (a *Readline) Flags() Args { + a.mu.RLock() + defer a.mu.RUnlock() + return a.flags +} + +func (a *Readline) FlagSet() *FlagSet { + a.mu.RLock() + defer a.mu.RUnlock() + return a.flagSet +} + +func (a *Readline) PassThroughArgs() Args { + a.mu.RLock() + defer a.mu.RUnlock() + return a.passThroughArgs +} + +func (a *Readline) PassThroughFlags() Args { + a.mu.RLock() + defer a.mu.RUnlock() + return a.passThroughFlags +} + +func (a *Readline) PassThroughFlagSet() *FlagSet { + a.mu.RLock() + defer a.mu.RUnlock() + return a.passThroughFlagSet +} + +func (a *Readline) AdditionalArgs() Args { + a.mu.RLock() + defer a.mu.RUnlock() + return a.additionalArgs +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (a *Readline) Parse(input string) error { + a.mu.Lock() + defer a.mu.Unlock() + a.reset() + parts := a.regex.FindAllString(input, -1) + + if len(parts) == 0 { + return nil + } else { + a.cmd, parts = parts[0], parts[1:] + } + + last := len(parts) - 1 + for i, part := range parts { + if a.mode == ModeArgs && Arg(part).IsFlag() { + a.mode = ModeFlags + } + if i != last && (a.mode == ModeArgs || a.mode == ModeFlags) && Arg(part).IsPass() { + a.mode = ModePassThroughArgs + } + if a.mode == ModePassThroughArgs && Arg(part).IsFlag() { + a.mode = ModePassThroughFlags + } + if Arg(part).IsAdditional() && i < len(parts)-1 { + a.mode = ModeAdditionalArgs + } + + switch a.mode { + case ModeArgs: + a.args = append(a.args, part) + case ModeFlags: + a.flags = append(a.flags, part) + case ModePassThroughArgs: + a.passThroughArgs = append(a.passThroughArgs, part) + case ModePassThroughFlags: + a.passThroughFlags = append(a.passThroughFlags, part) + case ModeAdditionalArgs: + a.additionalArgs = append(a.additionalArgs, part) + } + } + + return nil +} + +func (a *Readline) SetFlags(fs *FlagSet) { + a.mu.Lock() + defer a.mu.Unlock() + a.flagSet = fs +} + +func (a *Readline) ParseFlags() error { + if fs := a.FlagSet(); fs == nil { + return nil + } else if err := fs.Parse(a.flags); err != nil { + return err + } + return nil +} + +func (a *Readline) SetParsePassThroughFlags(fs *FlagSet) { + a.mu.Lock() + defer a.mu.Unlock() + a.passThroughFlagSet = fs +} + +func (a *Readline) ParsePassThroughFlags() error { + if fs := a.PassThroughFlagSet(); fs == nil { + return nil + } else if err := fs.Parse(a.passThroughFlags); err != nil { + return err + } + return nil +} + +func (a *Readline) String() string { + return fmt.Sprintf(` +Cmd: %s +Mode %s +Args: %s +Flags: %s +PassThroughArgs: %s +PassThroughFlags: %s +AdditionalArgs %s +`, a.Cmd(), a.Mode(), a.Args(), a.Flags(), a.PassThroughArgs(), a.PassThroughFlags(), a.AdditionalArgs()) +} + +func (a *Readline) IsModeDefault() bool { + return a.Mode() == ModeArgs +} + +func (a *Readline) IsModePassThrough() bool { + return a.Mode() == ModePassThroughArgs +} + +func (a *Readline) IsModeAdditional() bool { + return a.Mode() == ModeAdditionalArgs +} + +func (a *Readline) AllFlags() []*flag.Flag { + var ret []*flag.Flag + if fs := a.FlagSet(); fs != nil { + fs.VisitAll(func(f *flag.Flag) { + ret = append(ret, f) + }) + } + return ret +} + +func (a *Readline) VisitedFlags() []*flag.Flag { + var ret []*flag.Flag + if fs := a.FlagSet(); fs != nil { + fs.Visit(func(f *flag.Flag) { + ret = append(ret, f) + }) + } + return ret +} + +func (a *Readline) AllPassThroughFlags() []*flag.Flag { + var ret []*flag.Flag + if fs := a.PassThroughFlagSet(); fs != nil { + fs.VisitAll(func(f *flag.Flag) { + ret = append(ret, f) + }) + } + return ret +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (a *Readline) reset() { + a.mode = ModeArgs + a.cmd = "" + a.args = nil + a.flags = nil + a.flagSet = nil + a.passThroughArgs = nil + a.passThroughFlags = nil + a.passThroughFlagSet = nil + a.additionalArgs = nil +} + +type FlagSet struct { + *flag.FlagSet +} + +func NewFlagSet(handler func(set *FlagSet)) *FlagSet { + inst := &FlagSet{ + FlagSet: flag.NewFlagSet("readline", flag.ContinueOnError), + } + if handler != nil { + handler(inst) + } + return inst +} + +func (a *FlagSet) GetString(name string) string { + if f := a.FlagSet.Lookup(name); f == nil { + return "" + } else if !a.flagIsSet(name) { + return f.DefValue + } else { + return f.Value.String() + } +} +func (a *FlagSet) GetInt64(name string) int64 { + if value := a.GetString(name); value == "" { + return 0 + } else if v, err := strconv.ParseInt(value, 10, 64); err != nil { + return 0 + } else { + return v + } +} + +func (a *FlagSet) GetFloat64(name string) float64 { + if value := a.GetString(name); value == "" { + return 0 + } else if v, err := strconv.ParseFloat(value, 64); err != nil { + return 0 + } else { + return v + } +} + +func (a *FlagSet) GetBool(name string) bool { + if value := a.GetString(name); value == "" { + return false + } else if v, err := strconv.ParseBool(value); err != nil { + return false + } else { + return v + } +} +func (a *FlagSet) flagIsSet(name string) bool { + found := false + if fs := a.FlagSet; fs != nil { + fs.Visit(func(f *flag.Flag) { + if f.Name == name { + found = true + } + }) + } + return found +} diff --git a/pkg/scaffold/scaffold.go b/pkg/scaffold/scaffold.go new file mode 100644 index 0000000..1cb0175 --- /dev/null +++ b/pkg/scaffold/scaffold.go @@ -0,0 +1,139 @@ +package scaffold + +import ( + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/Masterminds/sprig/v3" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type ( + Scaffold struct { + l log.Logger + dry bool + force bool + } + Option func(*Scaffold) error +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func WithDry(v bool) Option { + return func(o *Scaffold) error { + o.dry = v + return nil + } +} + +func WithForce(v bool) Option { + return func(o *Scaffold) error { + o.force = v + return nil + } +} + +func WithLogger(v log.Logger) Option { + return func(o *Scaffold) error { + o.l = v + return nil + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(opts ...Option) (*Scaffold, error) { + inst := &Scaffold{ + l: log.NewFmt(), + dry: false, + force: false, + } + for _, opt := range opts { + if opt != nil { + if err := opt(inst); err != nil { + return nil, err + } + } + } + return inst, nil +} + +func (s *Scaffold) Render(source fs.FS, target string, vars any) error { + // validate target + if stat, err := os.Stat(target); errors.Is(err, os.ErrNotExist) { + s.l.Print("scaffold:", target) + if err := os.MkdirAll(target, os.ModePerm); err != nil { + return errors.Wrapf(err, "failed to create target folder (%s)", target) + } + } else if err != nil { + return errors.Wrapf(err, "failed to stat target folder (%s)", target) + } else if !stat.IsDir() { + return fmt.Errorf("target not a directory (%s)", target) + } + + // iterate source + if err := fs.WalkDir(source, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return errors.Wrapf(err, "failed to walk fs dir") + } + + filename := filepath.Join(target, strings.ReplaceAll(path, "$", "")) + + if path == "." { + return nil + } else if d.IsDir() { + s.l.Print("scaffold:", filename) + return os.MkdirAll(filename, os.ModePerm) + } else { + tpl, err := template.New(d.Name()).Funcs(sprig.FuncMap()).ParseFS(source, path) + if err != nil { + return err + } + + if stat, err := os.Stat(filename); errors.Is(err, fs.ErrNotExist) { + // all good + } else if err != nil { + return errors.Wrapf(err, "failed to stat target (%s)", filename) + } else if stat.IsDir() { + return fmt.Errorf("target file is an existing directory (%s)", filename) + } else if !s.force { + return fmt.Errorf("target file already exists (%s)", filename) + } + + var out io.Writer + if s.dry { + out = os.Stdout + } else { + s.l.Print("scaffold:", filename) + if file, err := os.Create(filename); err != nil { + return errors.Wrapf(err, "failed to create target file (%s)", filename) + } else { + out = file + defer func() { + _ = file.Close() + }() + } + } + + if err := tpl.Execute(out, vars); err != nil { + return errors.Wrapf(err, "failed to render target file (%s)", filename) + } + + return nil + } + }); err != nil { + return errors.Wrapf(err, "failed to render scaffold to %s", target) + } + + return nil +} diff --git a/pkg/shell/shell.go b/pkg/shell/shell.go new file mode 100644 index 0000000..2444af8 --- /dev/null +++ b/pkg/shell/shell.go @@ -0,0 +1,134 @@ +package shell + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/foomo/posh/pkg/log" +) + +// Shell struct +type Shell struct { + l log.Logger + cmd *exec.Cmd + quiet bool + args []string + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func New(ctx context.Context, l log.Logger, inputs ...interface{}) *Shell { + var args []string + for _, input := range inputs { + if value, ok := input.(string); ok { + args = append(args, value) + } else if value, ok := input.([]string); ok { + args = append(args, value...) + } else { + args = append(args, fmt.Sprintf("%v", args)) + } + } + cmd := exec.CommandContext(ctx, "/bin/sh", "-c") + cmd.Env = os.Environ() + return &Shell{ + l: l, + cmd: cmd, + args: args, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Public methods +// ------------------------------------------------------------------------------------------------ + +func (s *Shell) Args(args ...string) *Shell { + s.args = append(s.args, args...) + return s +} + +func (s *Shell) Env(env []string) *Shell { + s.cmd.Env = append(s.cmd.Env, env...) + return s +} + +func (s *Shell) Dir(dir string) *Shell { + s.cmd.Dir = dir + return s +} + +func (s *Shell) Quiet() *Shell { + s.quiet = true + return s +} + +func (s *Shell) Run() error { + args := s.args + s.cmd.Args = append(s.cmd.Args, strings.Join(args, " ")) + s.cmd.Stdin = s.Stdin + if !s.quiet { + s.cmd.Stdout = s.Stdout + s.cmd.Stderr = s.Stderr + } + s.debug() + return s.cmd.Run() +} + +func (s *Shell) Output() ([]byte, error) { + args := s.args + s.cmd.Args = append(s.cmd.Args, strings.Join(args, " ")) + if !s.quiet { + s.cmd.Stdin = s.Stdin + s.cmd.Stderr = s.Stderr + } + s.debug() + return s.cmd.Output() +} + +// Wait ... +func (s *Shell) Wait() error { + args := s.args + s.cmd.Args = append(s.cmd.Args, strings.Join(args, " ")) + s.cmd.Stdin = s.Stdin + if !s.quiet { + s.cmd.Stdout = s.Stdout + s.cmd.Stderr = s.Stderr + } + s.debug() + // start the process and wait till it's finished + if err := s.cmd.Start(); err != nil { + return err + } else if err := s.cmd.Wait(); err != nil { + return err + } + return nil +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (s *Shell) debug() { + s.l.Debugf(`"Executing: +$ %s + +Directory: %s + +%s +`, + s.cmd.String(), + s.cmd.Dir, + strings.Join(s.cmd.Environ(), "\n"), + ) +} diff --git a/pkg/validate/dependenciesenv.go b/pkg/validate/dependenciesenv.go new file mode 100644 index 0000000..819ffc4 --- /dev/null +++ b/pkg/validate/dependenciesenv.go @@ -0,0 +1,41 @@ +package validate + +import ( + "errors" + "os" + + "github.com/foomo/fender/fend" + "github.com/foomo/fender/rule" + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" +) + +type DependenciesEnvRule func(l log.Logger, v config.DependenciesEnv) rule.Rule + +func DependenciesEnvs(l log.Logger, v []config.DependenciesEnv) []fend.Fend { + ret := make([]fend.Fend, len(v)) + for i, vv := range v { + ret[i] = DependenciesEnv(l, vv, DependenciesEnvExists) + } + return ret +} + +func DependenciesEnv(l log.Logger, v config.DependenciesEnv, rules ...DependenciesEnvRule) fend.Fend { + return func() []rule.Rule { + ret := make([]rule.Rule, len(rules)) + for i, r := range rules { + ret[i] = r(l, v) + } + return ret + } +} + +func DependenciesEnvExists(l log.Logger, v config.DependenciesEnv) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate env exists:", v.String()) + if value := os.Getenv(v.Name); value == "" { + return nil, errors.New(v.Help) + } + return nil, nil + } +} diff --git a/pkg/validate/dependenciespackage.go b/pkg/validate/dependenciespackage.go new file mode 100644 index 0000000..fdcd011 --- /dev/null +++ b/pkg/validate/dependenciespackage.go @@ -0,0 +1,70 @@ +package validate + +import ( + "context" + "fmt" + "os/exec" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/foomo/fender/fend" + "github.com/foomo/fender/rule" + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type DependenciesPackageRule func(ctx context.Context, l log.Logger, v config.DependenciesPackage) rule.Rule + +func DependenciesPackages(ctx context.Context, l log.Logger, v []config.DependenciesPackage) []fend.Fend { + ret := make([]fend.Fend, len(v)) + for i, vv := range v { + ret[i] = DependenciesPackage(ctx, l, vv, DependenciesPackageExists, DependenciesPackageVersion) + } + return ret +} + +func DependenciesPackage(ctx context.Context, l log.Logger, v config.DependenciesPackage, rules ...DependenciesPackageRule) fend.Fend { + return func() []rule.Rule { + ret := make([]rule.Rule, len(rules)) + for i, r := range rules { + ret[i] = r(ctx, l, v) + } + return ret + } +} + +func DependenciesPackageExists(ctx context.Context, l log.Logger, v config.DependenciesPackage) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate package exists:", v.String()) + if output, err := exec.LookPath(v.Name); err != nil { + l.Debug(err.Error(), output) + return nil, fmt.Errorf(v.Help, v.Version) + } else if output == "" { + l.Debugf("missing executable %s", v.Name) + return nil, fmt.Errorf(v.Help, v.Version) + } else { + return nil, nil + } + } +} + +func DependenciesPackageVersion(ctx context.Context, l log.Logger, v config.DependenciesPackage) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate package version:", v.String()) + if output, err := exec.CommandContext(ctx, "sh", "-c", v.Command).CombinedOutput(); err != nil { + return nil, err + } else if actual := strings.TrimPrefix(strings.TrimSpace(string(output)), "v"); actual == "" { + return nil, fmt.Errorf("failed to retrieve version: %s", string(output)) + } else if c, err := semver.NewConstraint(v.Version); err != nil { + return nil, errors.Wrapf(err, "failed to create version constraint: %s", v.Version) + } else if version, err := semver.NewVersion(actual); err != nil { + return nil, errors.Wrapf(err, "failed to create version") + } else if !c.Check(version) { + l.Debug("wrong package version:", actual) + return nil, fmt.Errorf(v.Help, v.Version) + } else { + return nil, nil + } + } +} diff --git a/pkg/validate/dependenciesscript.go b/pkg/validate/dependenciesscript.go new file mode 100644 index 0000000..b457a76 --- /dev/null +++ b/pkg/validate/dependenciesscript.go @@ -0,0 +1,43 @@ +package validate + +import ( + "context" + "os/exec" + + "github.com/foomo/fender/fend" + "github.com/foomo/fender/rule" + "github.com/foomo/posh/pkg/config" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type DependenciesScriptRule func(ctx context.Context, l log.Logger, v config.DependenciesScript) rule.Rule + +func DependenciesScripts(ctx context.Context, l log.Logger, v []config.DependenciesScript) []fend.Fend { + ret := make([]fend.Fend, len(v)) + for i, vv := range v { + ret[i] = DependenciesScript(ctx, l, vv, DependenciesScriptStatus) + } + return ret +} + +func DependenciesScript(ctx context.Context, l log.Logger, v config.DependenciesScript, rules ...DependenciesScriptRule) fend.Fend { + return func() []rule.Rule { + ret := make([]rule.Rule, len(rules)) + for i, r := range rules { + ret[i] = r(ctx, l, v) + } + return ret + } +} + +func DependenciesScriptStatus(ctx context.Context, l log.Logger, v config.DependenciesScript) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate script status:", v.String()) + if output, err := exec.CommandContext(ctx, "sh", "-c", v.Command).CombinedOutput(); err != nil { + l.Debug(string(output)) + return nil, errors.Wrap(err, v.Help) + } + return nil, nil + } +} diff --git a/pkg/validate/git.go b/pkg/validate/git.go new file mode 100644 index 0000000..a602b23 --- /dev/null +++ b/pkg/validate/git.go @@ -0,0 +1,64 @@ +package validate + +import ( + "context" + "os/exec" + "regexp" + "strings" + + "github.com/foomo/fender/fend" + "github.com/foomo/fender/rule" + "github.com/foomo/posh/pkg/log" + "github.com/pkg/errors" +) + +type GitRule func(ctx context.Context, l log.Logger) rule.Rule + +func GitUser(ctx context.Context, l log.Logger, rules ...GitRule) fend.Fend { + return func() []rule.Rule { + ret := make([]rule.Rule, len(rules)) + for i, r := range rules { + ret[i] = r(ctx, l) + } + return ret + } +} + +func GitUserName(ctx context.Context, l log.Logger) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate git user.name") + if output, err := exec.CommandContext(ctx, "git", "config", "user.name").CombinedOutput(); err != nil { + return nil, errors.Wrap(err, string(output)) + } else if parts := strings.Split(trim(string(output)), " "); len(parts) < 2 { + return nil, errors.New(` +Please configure a human readable git name e.g. "Max Mustermann" instead of "` + parts[0] + `". + +$ git config user.name "Max Mustermann" +`) + } + return nil, nil + } +} + +func GitUserEmail(pattern string) GitRule { + reg := regexp.MustCompile(pattern) + return func(ctx context.Context, l log.Logger) rule.Rule { + return func() (*rule.Error, error) { + l.Debug("validate git user.email") + if output, err := exec.CommandContext(ctx, "git", "config", "user.email").CombinedOutput(); err != nil { + return nil, errors.Wrap(err, string(output)) + } else if output := trim(string(output)); !reg.MatchString(output) { + return nil, errors.New(` +Please configure your github email to match the pattern "` + pattern + ` instead of "` + string(output) + `". + +$ git config user.email "max.muster@dev.null" +`) + } + return nil, nil + } + } +} + +func trim(s string) string { + return strings.TrimSuffix(strings.TrimSpace(s), "\n") +}