├── MAINTAINERS.md ├── testdata_test.go ├── Dockerfile ├── internal ├── event │ ├── types.go │ ├── testdata.go │ ├── main_test.go │ ├── errors.go │ ├── marshalling.go │ ├── enum.go │ ├── main.go │ └── marshalling_test.go ├── util │ └── util.go ├── versioninfo │ └── versioninfo.go └── git │ └── git.go ├── .devcontainer ├── devcontainer.json └── Dockerfile ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── cmdShowFlags_test.go ├── cmdVersion.go ├── cmdVersion_test.go ├── cmdShowFlags.go ├── main.go ├── cmdGet.go ├── LICENSE.md ├── value.go ├── go.mod ├── Makefile ├── cmdUnset.go ├── errors.go ├── cmdSet.go ├── cmdUnset_test.go ├── cmdList.go ├── cmdGet_test.go ├── cmdRoot.go ├── cmdSet_test.go ├── cmdList_test.go ├── testutils_test.go ├── cmdRoot_test.go ├── README.md ├── common_test.go ├── CONTRIBUTING.md ├── common.go └── go.sum /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | Scott Guymer 2 | -------------------------------------------------------------------------------- /testdata_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var ( 4 | TestDataDummyHash = "DUMMY_HASH\n" 5 | TestDataDummyRef = "DUMMY_REF" 6 | TestDataDummyValue = "DUMMY_VALUE" 7 | TestDataEmptyString = "" 8 | ) 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.16.2 2 | 3 | RUN \ 4 | apk update && \ 5 | apk add --no-cache git=2.32.0-r0 && \ 6 | rm /var/cache/apk/* 7 | 8 | COPY build/gino-keva /usr/local/bin/ 9 | CMD [ "gino-keva", "list" ] 10 | -------------------------------------------------------------------------------- /internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | // Event represents an event stored in git notes 4 | type Event struct { 5 | EventType Type `json:"type"` 6 | Key string `json:"key"` 7 | Value *string `json:"value,omitempty"` 8 | } 9 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerFile": "Dockerfile", 3 | "extensions": [ 4 | "golang.Go", 5 | "ms-azuretools.vscode-docker", 6 | "yzhang.markdown-all-in-one", 7 | "exiasr.hadolint", 8 | "me-dutour-mathieu.vscode-github-actions" 9 | ] 10 | } -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG HADOLINT_VERSION=2.4.1 2 | FROM hadolint/hadolint:${HADOLINT_VERSION}-alpine as hadolint 3 | 4 | FROM mcr.microsoft.com/vscode/devcontainers/go:1.19 5 | 6 | RUN go install github.com/ahmetb/govvv@v0.3.0 && go install github.com/go-delve/delve/cmd/dlv@master 7 | 8 | COPY --from=hadolint /bin/hadolint /bin/ 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | coverage.txt 14 | 15 | # Build output directory 16 | build/ 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | 21 | .vscode/ -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "docker" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "docker" 8 | directory: "/.devcontainer/" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "gomod" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /cmdShowFlags_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestShowFlags(t *testing.T) { 11 | t.Run("Error when trying to show unknown flag", func(t *testing.T) { 12 | root := NewRootCommand() 13 | args := []string{"show-flag", "Unknown_Flag"} 14 | 15 | ctx := ContextWithGitWrapper(context.Background(), ¬esStub{}) 16 | 17 | _, err := executeCommandContext(ctx, root, args...) 18 | 19 | assert.Error(t, err) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /cmdVersion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/philips-software/gino-keva/internal/versioninfo" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func addVersionCommandTo(root *cobra.Command) { 11 | var versionCommand = &cobra.Command{ 12 | Use: "version", 13 | Short: "Show version info", 14 | Long: `Show version info`, 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | fmt.Fprintln(cmd.OutOrStdout(), versioninfo.Get()) 17 | return nil 18 | }, 19 | Args: cobra.NoArgs, 20 | } 21 | 22 | root.AddCommand(versionCommand) 23 | } 24 | -------------------------------------------------------------------------------- /cmdVersion_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var dummyVersionInfo = "version.Info{Version: \"\", BuildDate: \"\", GitCommit: \"\", GitState: \"\"}\n" 11 | 12 | func TestVersion(t *testing.T) { 13 | t.Run("Version command returns an empty version string", func(t *testing.T) { 14 | root := NewRootCommand() 15 | args := []string{"version"} 16 | ctx := ContextWithGitWrapper(context.Background(), ¬esStub{}) 17 | 18 | out, err := executeCommandContext(ctx, root, args...) 19 | 20 | assert.NoError(t, err) 21 | assert.Equal(t, dummyVersionInfo, out) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /cmdShowFlags.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func addShowFlagCommandTo(root *cobra.Command) { 10 | var showFlagCommand = &cobra.Command{ 11 | Use: "show-flag [name]", 12 | Short: "Show flag value", 13 | Long: `Show the value of a flag`, 14 | Hidden: true, // For testing purposes only 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | flagName := args[0] 17 | 18 | flag := cmd.Flag(flagName) 19 | if flag == nil { 20 | return fmt.Errorf("unknown flag [%v]", flagName) 21 | } 22 | 23 | flagValue := flag.Value.String() 24 | fmt.Fprint(cmd.OutOrStdout(), flagValue) 25 | 26 | return nil 27 | }, 28 | Args: cobra.ExactArgs(1), 29 | } 30 | root.AddCommand(showFlagCommand) 31 | } 32 | -------------------------------------------------------------------------------- /internal/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // GetSlicesIntersect returns the intersect of s1 and s2 4 | func GetSlicesIntersect(s1, s2 []string) (intersect []string) { 5 | intersect = []string{} 6 | 7 | for i := 0; i < len(s1); i++ { 8 | if Contains(s2, s1[i]) { 9 | intersect = append(intersect, s1[i]) 10 | } 11 | } 12 | return intersect 13 | } 14 | 15 | // LimitStringSlice limits the slice of strings to a maximum length 16 | func LimitStringSlice(slice []string, limit uint) []string { 17 | if len(slice) <= int(limit) { 18 | return slice 19 | } 20 | 21 | return slice[:limit] 22 | } 23 | 24 | // Contains returns true if s is in the slice 25 | func Contains(slice []string, s string) bool { 26 | for _, a := range slice { 27 | if a == s { 28 | return true 29 | } 30 | } 31 | return false 32 | } 33 | -------------------------------------------------------------------------------- /internal/versioninfo/versioninfo.go: -------------------------------------------------------------------------------- 1 | package versioninfo 2 | 3 | import "fmt" 4 | 5 | //// Following variables will be statically linked at the time of compiling 6 | 7 | // Version holds contents of ./VERSION file, if exists, or the value passed via the -version option 8 | var Version = "" 9 | 10 | // BuildDate holds RFC3339 formatted UTC date (build time) 11 | var BuildDate = "" 12 | 13 | // GitCommit holds short commit hash of source tree 14 | var GitCommit = "" 15 | 16 | // GitState shows whether there are uncommitted changes 17 | var GitState = "" 18 | 19 | // Get returns the version information 20 | func Get() string { 21 | return fmt.Sprintf( 22 | `version.Info{`+ 23 | `Version: "%v", `+ 24 | `BuildDate: "%v", `+ 25 | `GitCommit: "%v", `+ 26 | `GitState: "%v"`+ 27 | `}`, 28 | Version, BuildDate, GitCommit, GitState) 29 | } 30 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | log "github.com/sirupsen/logrus" 8 | 9 | "github.com/philips-software/gino-keva/internal/git" 10 | ) 11 | 12 | const ( 13 | envPrefix = "GINO_KEVA" 14 | ) 15 | 16 | func checkIfError(err error) { 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | } 21 | 22 | func main() { 23 | log.SetOutput(os.Stderr) 24 | 25 | attemptsLeft := maxRetryAttempts 26 | 27 | var err error 28 | for { 29 | root := NewRootCommand() 30 | root.SilenceUsage = true 31 | root.SilenceErrors = true 32 | 33 | ctx := ContextWithGitWrapper(context.Background(), &git.GoGitCmdWrapper{}) 34 | 35 | err = root.ExecuteContext(ctx) 36 | attemptsLeft-- 37 | uc, upstreamChanged := err.(*UpstreamChanged) 38 | 39 | if attemptsLeft > 0 && upstreamChanged && uc.fetchEnabled { 40 | log.WithField("attemptsLeft", attemptsLeft).Info("Upstream has changed in the meanwhile. Starting again from fetch") 41 | } else { 42 | break 43 | } 44 | } 45 | 46 | if err != nil { 47 | log.Fatal(err) 48 | os.Exit(1) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/event/testdata.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | // TestData string constants 4 | var ( 5 | // TestDataFoo 6 | TestDataFoo = "foo" 7 | // TestDataBar 8 | TestDataBar = "bar" 9 | // TestDataKey 10 | TestDataKey = "key" 11 | // TestDataValue 12 | TestDataValue = "value" 13 | // TestDataOtherValue 14 | TestDataOtherValue = "otherValue" 15 | ) 16 | 17 | // TestDataSetFooBar is an event which sets foo=bar 18 | var TestDataSetFooBar = Event{ 19 | EventType: Set, 20 | Key: TestDataFoo, 21 | Value: &TestDataBar, 22 | } 23 | 24 | // TestDataSetKeyValue is an event which sets key=value 25 | var TestDataSetKeyValue = Event{ 26 | EventType: Set, 27 | Key: TestDataKey, 28 | Value: &TestDataValue, 29 | } 30 | 31 | // TestDataSetKeyOtherValue is an event which sets key=otherValue 32 | var TestDataSetKeyOtherValue = Event{ 33 | EventType: Set, 34 | Key: TestDataKey, 35 | Value: &TestDataOtherValue, 36 | } 37 | 38 | // TestDataUnsetKey is an event which unsets key 39 | var TestDataUnsetKey = Event{ 40 | EventType: Unset, 41 | Key: TestDataKey, 42 | } 43 | -------------------------------------------------------------------------------- /cmdGet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func addGetCommandTo(root *cobra.Command) { 10 | var getCommand = &cobra.Command{ 11 | Use: "get [key]", 12 | Short: "Get the value of a specific key", 13 | Long: `Get the value of a specific key`, 14 | RunE: func(cmd *cobra.Command, args []string) (err error) { 15 | key := args[0] 16 | 17 | gitWrapper := GetGitWrapperFrom(cmd.Context()) 18 | 19 | if globalFlags.Fetch { 20 | err = fetchNotes(gitWrapper) 21 | if err != nil { 22 | return err 23 | } 24 | } 25 | 26 | out, err := getValue(gitWrapper, globalFlags.NotesRef, key) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | fmt.Fprint(cmd.OutOrStdout(), out) 32 | return nil 33 | }, 34 | Args: cobra.ExactArgs(1), 35 | } 36 | 37 | root.AddCommand(getCommand) 38 | } 39 | 40 | func getValue(gitWrapper GitWrapper, notesRef string, key string) (string, error) { 41 | values, err := calculateKeyValues(gitWrapper, notesRef) 42 | if err != nil { 43 | return "", err 44 | } 45 | 46 | return string(values.Get(key)), nil 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2021] Koninklijke Philips N.V., https://www.philips.com 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | 11 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Values represents a collection of values 4 | type Values struct { 5 | values map[string]Value 6 | } 7 | 8 | // Add a key/value to the collection 9 | func (v *Values) Add(key string, value Value) { 10 | v.values[key] = value 11 | } 12 | 13 | // Count returns number of items in collection 14 | func (v Values) Count() int { 15 | return len(v.values) 16 | } 17 | 18 | // Get returns a single value from the collection 19 | func (v Values) Get(key string) Value { 20 | return v.values[key] 21 | } 22 | 23 | // HasKey indicates if key exists in collection 24 | func (v Values) HasKey(key string) bool { 25 | _, ok := v.values[key] 26 | return ok 27 | } 28 | 29 | // Iterate the collection value data 30 | func (v Values) Iterate() map[string]Value { 31 | return v.values 32 | } 33 | 34 | // Remove a key from the collection 35 | func (v *Values) Remove(key string) { 36 | delete(v.values, key) 37 | } 38 | 39 | // NewValues returns a new values map 40 | func NewValues() *Values { 41 | return &Values{ 42 | values: make(map[string]Value), 43 | } 44 | } 45 | 46 | // Value represents the parsed value as stored in git notes 47 | type Value string 48 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/philips-software/gino-keva 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/ldez/go-git-cmd-wrapper/v2 v2.3.0 7 | github.com/sirupsen/logrus v1.9.0 8 | github.com/spf13/cobra v1.3.0 9 | github.com/spf13/pflag v1.0.5 10 | github.com/spf13/viper v1.11.0 11 | github.com/stretchr/testify v1.8.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/fsnotify/fsnotify v1.5.1 // indirect 17 | github.com/hashicorp/hcl v1.0.0 // indirect 18 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 19 | github.com/magiconair/properties v1.8.6 // indirect 20 | github.com/mitchellh/mapstructure v1.4.3 // indirect 21 | github.com/pelletier/go-toml v1.9.4 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | github.com/spf13/afero v1.8.2 // indirect 25 | github.com/spf13/cast v1.4.1 // indirect 26 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 27 | github.com/subosito/gotenv v1.2.0 // indirect 28 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect 29 | golang.org/x/text v0.3.7 // indirect 30 | gopkg.in/ini.v1 v1.66.4 // indirect 31 | gopkg.in/yaml.v2 v2.4.0 // indirect 32 | gopkg.in/yaml.v3 v3.0.1 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /internal/event/main_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestValidateKey(t *testing.T) { 10 | testCases := []struct { 11 | name string 12 | key string 13 | valid bool 14 | }{ 15 | { 16 | name: "Key can end on number", 17 | key: "TheAnswerIs42", 18 | valid: true, 19 | }, 20 | { 21 | name: "Key can container underscores and dashes", 22 | key: "This-key_is-valid", 23 | valid: true, 24 | }, 25 | { 26 | name: "Key cannot be empty", 27 | key: "", 28 | valid: false, 29 | }, 30 | { 31 | name: "Key contains an invalid character", 32 | key: "foo!", 33 | valid: false, 34 | }, 35 | { 36 | name: "First character of key is not a letter", 37 | key: "2BeOrNot2Be", 38 | valid: false, 39 | }, 40 | { 41 | name: "Last character of key is not a letter of number", 42 | key: "invalid-", 43 | valid: false, 44 | }, 45 | } 46 | 47 | for _, tc := range testCases { 48 | t.Run(tc.name, func(t *testing.T) { 49 | err := validateKey(tc.key) 50 | 51 | if tc.valid { 52 | assert.NoError(t, err) 53 | } else { 54 | if assert.Error(t, err) { 55 | assert.IsType(t, &InvalidKey{}, err) 56 | } 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/event/errors.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import "fmt" 4 | 5 | // NoEventsInNote error indicates Gino keva ran into a note which doesn't have an "events" key 6 | type NoEventsInNote struct { 7 | } 8 | 9 | func (NoEventsInNote) Error() string { 10 | return "Cannot find events key in JSON. Old syntax?" 11 | } 12 | 13 | // UnknownType error indicates Gino keva ran into an unknown event type stored in Git Notes 14 | type UnknownType struct { 15 | EventType string 16 | } 17 | 18 | func (u UnknownType) Error() string { 19 | return fmt.Sprintf("Unknown event type: %s", u.EventType) 20 | } 21 | 22 | // KeyMissing error indicates Gino keva ran into an event with a missing or empty key 23 | type KeyMissing struct { 24 | event Event 25 | } 26 | 27 | func (k KeyMissing) Error() string { 28 | return fmt.Sprintf("Key missing from event: %v", k.event) 29 | } 30 | 31 | // ValueMissing error indicates Gino keva ran into an event with a missing value 32 | type ValueMissing struct { 33 | event Event 34 | } 35 | 36 | func (v ValueMissing) Error() string { 37 | return fmt.Sprintf("Value missing from event: %v", v.event) 38 | } 39 | 40 | // InvalidKey error indicates the key is not valid 41 | type InvalidKey struct { 42 | msg string 43 | } 44 | 45 | func (i InvalidKey) Error() string { 46 | return fmt.Sprintf("Invalid key: %v", i.msg) 47 | } 48 | -------------------------------------------------------------------------------- /internal/event/marshalling.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | log "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // Marshal a list of Event objects into a string 11 | func Marshal(events *[]Event) (string, error) { 12 | if events == nil { 13 | events = &[]Event{} 14 | } 15 | 16 | wrappedEvents := map[string]*[]Event{"events": events} 17 | result, err := json.Marshal(wrappedEvents) 18 | if err != nil { 19 | return "", err 20 | } 21 | 22 | return fmt.Sprintf("%s\n", result), nil 23 | } 24 | 25 | // Unmarshal a string into a list of Event objects 26 | func Unmarshal(s string, events *[]Event) error { 27 | r := make(map[string]json.RawMessage) 28 | if err := json.Unmarshal([]byte(s), &r); err != nil { 29 | return err 30 | } 31 | 32 | if eventsJSON, ok := r["events"]; ok { 33 | if err := json.Unmarshal(eventsJSON, &events); err != nil { 34 | return err 35 | } 36 | } else { 37 | return &NoEventsInNote{} 38 | } 39 | 40 | for _, e := range *events { 41 | switch e.EventType { 42 | case Set: 43 | if e.Value == nil { 44 | return &ValueMissing{e} 45 | } 46 | fallthrough 47 | case Unset: 48 | if e.Key == "" { 49 | return &KeyMissing{e} 50 | } 51 | default: 52 | log.Fatal("Fatal: Unknown event type encountered") 53 | } 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /internal/event/enum.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | ) 7 | 8 | // Type represents the type of an event as stored by Gino Keva 9 | type Type int 10 | 11 | const ( 12 | // Invalid represents an invalid eventtype 13 | Invalid Type = iota 14 | // Set represents the value for a key to be set or overwritten if already set 15 | Set 16 | // Unset represents a key to be unset if present 17 | Unset 18 | ) 19 | 20 | func (t Type) String() string { 21 | return toString[t] 22 | } 23 | 24 | var toString = map[Type]string{ 25 | Set: "set", 26 | Unset: "unset", 27 | } 28 | 29 | var toID = map[string]Type{ 30 | "set": Set, 31 | "unset": Unset, 32 | } 33 | 34 | // MarshalJSON marshals the enum as a quoted json string 35 | func (t Type) MarshalJSON() ([]byte, error) { 36 | buffer := bytes.NewBufferString(`"`) 37 | buffer.WriteString(toString[t]) 38 | buffer.WriteString(`"`) 39 | return buffer.Bytes(), nil 40 | } 41 | 42 | // UnmarshalJSON unmashals a quoted json string to the enum value 43 | func (t *Type) UnmarshalJSON(b []byte) error { 44 | var j string 45 | 46 | if err := json.Unmarshal(b, &j); err != nil { 47 | return err 48 | } 49 | 50 | // Note that if the string cannot be found then it will be set to the zero value 51 | *t = toID[j] 52 | 53 | if *t == Invalid { 54 | return &UnknownType{EventType: j} 55 | } 56 | 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME := "gino-keva" 2 | VERSION := "$(shell git describe --tags --match "v*.*.*")" 3 | LAST_VERSION := "$(shell git describe --tags --match "v*.*.*" --abbrev=0 HEAD^)" 4 | PKG := "github.com/philips-software/$(PROJECT_NAME)" 5 | PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/) 6 | GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go) 7 | 8 | .PHONY: all dep lint vet test test-coverage build clean 9 | 10 | all: build lint test 11 | 12 | dep: ## Get the dependencies 13 | @go mod tidy 14 | 15 | lint: ## Lint Golang files 16 | @golint -set_exit_status ${PKG_LIST} 17 | @staticcheck ./... 18 | 19 | vet: ## Run go vet 20 | @go vet ${PKG_LIST} 21 | 22 | test: ## Run unittests 23 | @go test -short ${PKG_LIST} 24 | 25 | test-coverage: ## Run tests with coverage 26 | @go test -short -coverprofile cover.out -covermode=atomic ${PKG_LIST} 27 | @cat cover.out >> coverage.txt 28 | 29 | build: dep ## Build the binary file 30 | @CGO_ENABLED=0 govvv build -pkg $(PKG)/internal/versioninfo -version $(VERSION) -o build/$(PROJECT_NAME) $(PKG) 31 | 32 | clean: ## Remove previous build 33 | @rm -f $(PROJECT_NAME)/build 34 | 35 | release-notes: ## Generate release notes 36 | @git log $(LAST_VERSION)..HEAD --pretty=format:%s 37 | 38 | help: ## Display this help screen 39 | @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 40 | -------------------------------------------------------------------------------- /cmdUnset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/philips-software/gino-keva/internal/event" 5 | log "github.com/sirupsen/logrus" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func addUnsetCommandTo(root *cobra.Command) { 10 | var ( 11 | push bool 12 | ) 13 | 14 | var unsetCommand = &cobra.Command{ 15 | Use: "unset [key]", 16 | Short: "Unset a key", 17 | Long: `Unset a key`, 18 | RunE: func(cmd *cobra.Command, args []string) (err error) { 19 | key := args[0] 20 | gitWrapper := GetGitWrapperFrom(cmd.Context()) 21 | 22 | if globalFlags.Fetch { 23 | err = fetchNotes(gitWrapper) 24 | if err != nil { 25 | return err 26 | } 27 | } 28 | 29 | err = unset(gitWrapper, globalFlags.NotesRef, key) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | if push { 35 | err = pushNotes(gitWrapper, globalFlags.NotesRef) 36 | } 37 | 38 | return err 39 | }, 40 | Args: cobra.ExactArgs(1), 41 | } 42 | 43 | unsetCommand.Flags().BoolVar(&push, "push", false, "Push notes to upstream") 44 | root.AddCommand(unsetCommand) 45 | } 46 | 47 | func unset(gitWrapper GitWrapper, notesRef string, key string) error { 48 | unsetEvent, err := event.NewUnsetEvent(key) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | events, err := getEvents(gitWrapper, notesRef) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | *events = event.AddNewEvent(events, unsetEvent) 59 | 60 | err = persistEvents(gitWrapper, notesRef, events) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | log.WithFields(log.Fields{ 66 | "key": key, 67 | }).Debug("Unset event added successfully") 68 | 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | // UpstreamChanged error indicates there's been a change in the upstream preventing a fetch/push without force 9 | type UpstreamChanged struct { 10 | fetchEnabled bool 11 | } 12 | 13 | func (UpstreamChanged) Error() string { 14 | return "Upstream has changed in the meanwhile" 15 | } 16 | 17 | func checkIfErrorStringIsUpstreamChanged(s string) bool { 18 | return strings.Contains(s, "! [rejected]") || strings.Contains(s, "! [remote rejected]") 19 | } 20 | 21 | // NoRemoteRef error indicates that the remote reference isn't there 22 | type NoRemoteRef struct { 23 | } 24 | 25 | func (NoRemoteRef) Error() string { 26 | return "No remote reference found" 27 | } 28 | 29 | func checkIfErrorStringIsNoRemoteRef(s string) bool { 30 | return strings.HasPrefix(strings.ToLower(s), "fatal: couldn't find remote ref refs/notes/") 31 | } 32 | 33 | // NoNotePresent error indicates there's no note present on HEAD commit 34 | type NoNotePresent struct { 35 | } 36 | 37 | func (NoNotePresent) Error() string { 38 | return "No note present on HEAD commit" 39 | } 40 | 41 | func checkIfErrorStringIsNoNotePresent(s string) bool { 42 | return strings.HasPrefix(strings.ToLower(s), "error: no note found for object ") 43 | } 44 | 45 | func convertGitOutputToError(out string, errorCode error) (err error) { 46 | if errorCode == nil { 47 | return nil 48 | } 49 | 50 | if checkIfErrorStringIsNoRemoteRef(out) { 51 | err = &NoRemoteRef{} 52 | } else if checkIfErrorStringIsNoNotePresent(out) { 53 | err = &NoNotePresent{} 54 | } else if checkIfErrorStringIsUpstreamChanged(out) { 55 | err = &UpstreamChanged{fetchEnabled: globalFlags.Fetch} 56 | } else { 57 | err = errors.New(out) 58 | } 59 | 60 | return err 61 | } 62 | -------------------------------------------------------------------------------- /internal/event/main.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | // AddNewEvent to start of list 8 | func AddNewEvent(events *[]Event, e *Event) []Event { 9 | return append([]Event{*e}, *events...) 10 | } 11 | 12 | // NewSetEvent will create a new event of type Set 13 | func NewSetEvent(key string, value string) (*Event, error) { 14 | err := validateKey(key) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return &Event{ 20 | EventType: Set, 21 | Key: key, 22 | Value: &value, 23 | }, nil 24 | } 25 | 26 | // NewUnsetEvent will create a new event of type Unset 27 | func NewUnsetEvent(key string) (*Event, error) { 28 | err := validateKey(key) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | return &Event{ 34 | EventType: Unset, 35 | Key: key, 36 | }, nil 37 | } 38 | 39 | func validateKey(key string) error { 40 | if key == "" { 41 | return &InvalidKey{msg: "key cannot be empty"} 42 | } 43 | 44 | { 45 | pattern := `[^A-Za-z0-9_-]` 46 | matched, err := regexp.Match(pattern, []byte(key)) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | if matched { 52 | return &InvalidKey{msg: "key contains invalid characters"} 53 | } 54 | } 55 | 56 | { 57 | pattern := `^[^A-Za-z]` 58 | matched, err := regexp.Match(pattern, []byte(key)) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | if matched { 64 | return &InvalidKey{msg: "first character is not a letter"} 65 | } 66 | } 67 | 68 | { 69 | pattern := `[^A-Za-z0-9]$` 70 | matched, err := regexp.Match(pattern, []byte(key)) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if matched { 76 | return &InvalidKey{msg: "last character is not a letter or number"} 77 | } 78 | } 79 | 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /cmdSet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/philips-software/gino-keva/internal/event" 5 | log "github.com/sirupsen/logrus" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func addSetCommandTo(root *cobra.Command) { 10 | var ( 11 | push bool 12 | ) 13 | 14 | var setCommand = &cobra.Command{ 15 | Use: "set [key] [value]", 16 | Short: "Set the value of a key", 17 | Long: `Set the value of a key`, 18 | RunE: func(cmd *cobra.Command, args []string) (err error) { 19 | key := args[0] 20 | value := args[1] 21 | gitWrapper := GetGitWrapperFrom(cmd.Context()) 22 | 23 | if globalFlags.Fetch { 24 | err = fetchNotes(gitWrapper) 25 | if err != nil { 26 | return err 27 | } 28 | } 29 | 30 | err = set(gitWrapper, globalFlags.NotesRef, key, value) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = pruneNotes(gitWrapper, globalFlags.NotesRef) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | if push { 41 | err = pushNotes(gitWrapper, globalFlags.NotesRef) 42 | } 43 | 44 | return err 45 | }, 46 | Args: cobra.ExactArgs(2), 47 | } 48 | 49 | setCommand.Flags().BoolVar(&push, "push", false, "Push notes to upstream") 50 | root.AddCommand(setCommand) 51 | } 52 | 53 | func set(gitWrapper GitWrapper, notesRef string, key string, value string) error { 54 | setEvent, err := event.NewSetEvent(key, value) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | events, err := getEvents(gitWrapper, notesRef) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | *events = event.AddNewEvent(events, setEvent) 65 | 66 | err = persistEvents(gitWrapper, notesRef, events) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | log.WithFields(log.Fields{ 72 | "key": key, 73 | "value": value, 74 | }).Debug("Set event added successfully") 75 | 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /cmdUnset_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/philips-software/gino-keva/internal/event" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestUnsetCommand(t *testing.T) { 12 | testCases := []struct { 13 | name string 14 | args []string 15 | start []event.Event 16 | wanted []event.Event 17 | }{ 18 | { 19 | name: "Unset key", 20 | start: []event.Event{}, 21 | args: []string{"unset", "key"}, 22 | wanted: []event.Event{event.TestDataUnsetKey}, 23 | }, 24 | } 25 | 26 | for _, tc := range testCases { 27 | t.Run(tc.name, func(t *testing.T) { 28 | starteventsJSON, _ := event.Marshal(&tc.start) 29 | wantedeventsJSON, _ := event.Marshal(&tc.wanted) 30 | root := NewRootCommand() 31 | 32 | var notesAddArgMsg string 33 | gitWrapper := ¬esStub{ 34 | logCommitsImplementation: responseStubArgsNone(simpleLogCommitsResponse), 35 | notesListImplementation: responseStubArgsString(simpleNotesListResponse), 36 | notesAddImplementation: spyArgsStringString(nil, nil, ¬esAddArgMsg), 37 | notesShowImplementation: responseStubArgsStringString(starteventsJSON), 38 | revParseHeadImplementation: responseStubArgsNone(TestDataDummyHash), 39 | } 40 | 41 | ctx := ContextWithGitWrapper(context.Background(), gitWrapper) 42 | 43 | args := disableFetch(tc.args) 44 | _, err := executeCommandContext(ctx, root, args...) 45 | 46 | assert.NoError(t, err) 47 | assert.Equal(t, wantedeventsJSON, notesAddArgMsg) 48 | }) 49 | } 50 | } 51 | 52 | func TestUnsetInvalidKey(t *testing.T) { 53 | gitWrapper := ¬esStub{ 54 | notesAddImplementation: dummyStubArgsStringString, 55 | revParseHeadImplementation: responseStubArgsNone(TestDataDummyHash), 56 | logCommitsImplementation: dummyStubArgsNone, 57 | notesListImplementation: dummyStubArgsString, 58 | notesShowImplementation: dummyStubArgsStringString, 59 | } 60 | 61 | t.Run("Key cannot be empty", func(t *testing.T) { 62 | err := unset(gitWrapper, TestDataDummyRef, TestDataEmptyString) 63 | if assert.Error(t, err) { 64 | assert.IsType(t, &event.InvalidKey{}, err) 65 | } 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /cmdList.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // InvalidOutputFormat error indicates the specified output format is invalid 11 | type InvalidOutputFormat struct { 12 | } 13 | 14 | func (InvalidOutputFormat) Error() string { 15 | return "Invalid output format specified" 16 | } 17 | 18 | func addListCommandTo(root *cobra.Command) { 19 | var ( 20 | outputFormat string 21 | ) 22 | 23 | var listCommand = &cobra.Command{ 24 | Use: "list", 25 | Short: "List", 26 | Long: `List all of the keys and values currently stored`, 27 | RunE: func(cmd *cobra.Command, args []string) (err error) { 28 | gitWrapper := GetGitWrapperFrom(cmd.Context()) 29 | 30 | if globalFlags.Fetch { 31 | err = fetchNotes(gitWrapper) 32 | if err != nil { 33 | return err 34 | } 35 | } 36 | 37 | out, err := getListOutput(gitWrapper, globalFlags.NotesRef, outputFormat) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | fmt.Fprint(cmd.OutOrStdout(), out) 43 | return nil 44 | }, 45 | Args: cobra.NoArgs, 46 | } 47 | listCommand.Flags().StringVarP(&outputFormat, "output", "o", "plain", "Set output format (plain/json)") 48 | 49 | root.AddCommand(listCommand) 50 | } 51 | 52 | func getListOutput(gitWrapper GitWrapper, notesRef string, outputFormat string) (out string, err error) { 53 | values, err := calculateKeyValues(gitWrapper, notesRef) 54 | if err != nil { 55 | return "", err 56 | } 57 | 58 | return convertValuesToOutput(values, outputFormat) 59 | } 60 | 61 | func convertValuesToOutput(values *Values, outputFlag string) (out string, err error) { 62 | switch outputFlag { 63 | 64 | case "plain": 65 | if values.Count() == 0 { 66 | out = "" 67 | } else { 68 | for k, v := range values.Iterate() { 69 | out += fmt.Sprintf("%s=%s\n", k, v) 70 | } 71 | } 72 | 73 | case "json": 74 | out, err = marshalJSON(values) 75 | 76 | default: 77 | err = &InvalidOutputFormat{} 78 | } 79 | 80 | return out, err 81 | } 82 | 83 | func marshalJSON(values *Values) (string, error) { 84 | result, err := json.MarshalIndent(values.Iterate(), "", " ") 85 | if err != nil { 86 | return "", err 87 | } 88 | 89 | return fmt.Sprintf("%s\n", result), nil 90 | } 91 | -------------------------------------------------------------------------------- /internal/git/git.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ldez/go-git-cmd-wrapper/v2/fetch" 7 | gitCmdWrapper "github.com/ldez/go-git-cmd-wrapper/v2/git" 8 | "github.com/ldez/go-git-cmd-wrapper/v2/notes" 9 | "github.com/ldez/go-git-cmd-wrapper/v2/push" 10 | "github.com/ldez/go-git-cmd-wrapper/v2/revparse" 11 | "github.com/ldez/go-git-cmd-wrapper/v2/types" 12 | ) 13 | 14 | // GoGitCmdWrapper implements the Wrapper interface using go-git-cmd-wrapper 15 | type GoGitCmdWrapper struct { 16 | } 17 | 18 | // FetchNotes notes 19 | func (GoGitCmdWrapper) FetchNotes(notesRef string, force bool) (string, error) { 20 | refSpec := fmt.Sprintf("refs/notes/%v:refs/notes/%v", notesRef, notesRef) 21 | if force { 22 | // Add + to force fetch 23 | refSpec = fmt.Sprintf("+%v", refSpec) 24 | } 25 | return gitCmdWrapper.Fetch(fetch.NoTags, fetch.Remote("origin"), fetch.RefSpec(refSpec)) 26 | } 27 | 28 | // LogCommits returns log output with commit hashes 29 | func (GoGitCmdWrapper) LogCommits() (string, error) { 30 | return gitCmdWrapper.Raw("log", func(g *types.Cmd) { 31 | g.AddOptions("--pretty=format:%H") 32 | }) 33 | } 34 | 35 | // NotesAdd sets/overwrites a note 36 | func (GoGitCmdWrapper) NotesAdd(notesRef, msg string) (string, error) { 37 | return gitCmdWrapper.Notes(notes.Ref(notesRef), notes.Add("", notes.Message(msg), notes.Force)) 38 | } 39 | 40 | // NotesList returns all the notes 41 | func (GoGitCmdWrapper) NotesList(notesRef string) (string, error) { 42 | return gitCmdWrapper.Notes(notes.Ref(notesRef), notes.List("")) 43 | } 44 | 45 | // NotesPrune prunes unreachable notes 46 | func (GoGitCmdWrapper) NotesPrune(notesRef string) (string, error) { 47 | return gitCmdWrapper.Notes(notes.Ref(notesRef), notes.Prune()) 48 | } 49 | 50 | // NotesShow returns the note for provided hash, or error if there is none 51 | func (GoGitCmdWrapper) NotesShow(notesRef, hash string) (string, error) { 52 | return gitCmdWrapper.Notes(notes.Ref(notesRef), notes.Show(hash)) 53 | } 54 | 55 | // PushNotes notes 56 | func (GoGitCmdWrapper) PushNotes(notesRef string) (string, error) { 57 | refSpec := fmt.Sprintf("refs/notes/%v:refs/notes/%v", notesRef, notesRef) 58 | return gitCmdWrapper.Push(push.Remote("origin"), push.RefSpec(refSpec)) 59 | } 60 | 61 | // RevParseHead returns the HEAD commit hash 62 | func (g GoGitCmdWrapper) RevParseHead() (string, error) { 63 | return gitCmdWrapper.RevParse(revparse.Args("HEAD")) 64 | } 65 | -------------------------------------------------------------------------------- /cmdGet_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/philips-software/gino-keva/internal/event" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestGetCommand(t *testing.T) { 12 | 13 | testCases := []struct { 14 | name string 15 | args []string 16 | start []event.Event 17 | wantOutput string 18 | }{ 19 | { 20 | name: "Get value of a key", 21 | args: []string{"get", "key"}, 22 | start: []event.Event{event.TestDataSetKeyValue}, 23 | wantOutput: "value", 24 | }, 25 | } 26 | 27 | for _, tc := range testCases { 28 | t.Run(tc.name, func(t *testing.T) { 29 | eventsJSON, _ := event.Marshal(&tc.start) 30 | 31 | root := NewRootCommand() 32 | ctx := ContextWithGitWrapper(context.Background(), ¬esStub{ 33 | logCommitsImplementation: responseStubArgsNone(simpleLogCommitsResponse), 34 | notesListImplementation: responseStubArgsString(simpleNotesListResponse), 35 | notesShowImplementation: responseStubArgsStringString(eventsJSON), 36 | }) 37 | args := disableFetch(tc.args) 38 | gotOutput, err := executeCommandContext(ctx, root, args...) 39 | assert.NoError(t, err) 40 | assert.Equal(t, tc.wantOutput, gotOutput) 41 | }) 42 | } 43 | } 44 | 45 | func TestGetValue(t *testing.T) { 46 | testCases := []struct { 47 | name string 48 | key string 49 | depth uint 50 | start []event.Event 51 | wantValue string 52 | }{ 53 | { 54 | name: "Get value of an existing key", 55 | key: "key", 56 | depth: 0, 57 | start: []event.Event{event.TestDataSetKeyValue}, 58 | wantValue: "value", 59 | }, 60 | { 61 | name: "Get value of a non-existing key", 62 | key: "nonExistingKey", 63 | depth: 0, 64 | start: []event.Event{event.TestDataSetKeyValue}, 65 | wantValue: "", 66 | }, 67 | } 68 | 69 | for _, tc := range testCases { 70 | t.Run(tc.name, func(t *testing.T) { 71 | eventsJSON, _ := event.Marshal(&[]event.Event{event.TestDataSetKeyValue}) 72 | 73 | gitWrapper := notesStub{ 74 | logCommitsImplementation: responseStubArgsNone(simpleLogCommitsResponse), 75 | notesListImplementation: responseStubArgsString(simpleNotesListResponse), 76 | notesShowImplementation: responseStubArgsStringString(eventsJSON), 77 | } 78 | gotValue, err := getValue(&gitWrapper, TestDataDummyRef, tc.key) 79 | 80 | assert.NoError(t, err) 81 | assert.Equal(t, tc.wantValue, gotValue) 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cmdRoot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | log "github.com/sirupsen/logrus" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/spf13/pflag" 11 | "github.com/spf13/viper" 12 | ) 13 | 14 | // NewRootCommand builds the cobra command that handles our command line tool. 15 | func NewRootCommand() *cobra.Command { 16 | rootCommand := &cobra.Command{ 17 | Use: "gino-keva", 18 | Short: "A tool to store key value data as git notes", 19 | Long: `Git Notes Key Value (gino-keva) is a tool used to store and manage key values 20 | in git notes. You can store any sort of data you want against each commit in your 21 | repository`, 22 | PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { 23 | initializeConfig(cmd) 24 | return err 25 | }, 26 | } 27 | 28 | addRootFlagsTo(rootCommand) 29 | addShowFlagCommandTo(rootCommand) 30 | addListCommandTo(rootCommand) 31 | addGetCommandTo(rootCommand) 32 | addSetCommandTo(rootCommand) 33 | addUnsetCommandTo(rootCommand) 34 | addVersionCommandTo(rootCommand) 35 | 36 | return rootCommand 37 | } 38 | 39 | func initializeConfig(cmd *cobra.Command) { 40 | v := viper.New() 41 | 42 | v.SetEnvPrefix(envPrefix) 43 | v.AutomaticEnv() // read in environment variables that match 44 | 45 | // Bind the current command's flags to viper 46 | bindFlags(cmd, v) 47 | 48 | verboseLogs, err := cmd.Flags().GetBool("verbose") 49 | checkIfError(err) 50 | 51 | if verboseLogs { 52 | log.SetLevel(log.DebugLevel) 53 | } 54 | } 55 | 56 | // Bind each cobra flag to its associated viper configuration 57 | func bindFlags(cmd *cobra.Command, v *viper.Viper) { 58 | cmd.Flags().VisitAll(func(f *pflag.Flag) { 59 | // Environment variables can't have dashes in them, so bind them to their equivalent 60 | // keys with underscores 61 | if strings.Contains(f.Name, "-") { 62 | envVarSuffix := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_")) 63 | v.BindEnv(f.Name, fmt.Sprintf("%s_%s", envPrefix, envVarSuffix)) 64 | } 65 | 66 | // Apply the viper config value to the flag when the flag is not set and viper has a value 67 | if !f.Changed && v.IsSet(f.Name) { 68 | val := v.Get(f.Name) 69 | cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) 70 | } 71 | }) 72 | } 73 | 74 | func addRootFlagsTo(cmd *cobra.Command) { 75 | cmd.PersistentFlags().StringVar(&globalFlags.NotesRef, "ref", "gino_keva", "Name of notes reference") 76 | cmd.PersistentFlags().BoolVarP(&globalFlags.VerboseLog, "verbose", "v", false, "Turn on verbose logging") 77 | 78 | cmd.PersistentFlags().BoolVar(&globalFlags.Fetch, "fetch", true, "Fetch notes from upstream") 79 | } 80 | -------------------------------------------------------------------------------- /cmdSet_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/philips-software/gino-keva/internal/event" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestSetCommand(t *testing.T) { 13 | testCases := []struct { 14 | name string 15 | args []string 16 | startEvents []event.Event 17 | wantedEvents []event.Event 18 | }{ 19 | { 20 | name: "Start empty, set key=value (default ref)", 21 | startEvents: []event.Event{}, 22 | args: []string{"set", "key", "value"}, 23 | wantedEvents: []event.Event{event.TestDataSetKeyValue}, 24 | }, 25 | { 26 | name: "Start key=value, set foo=bar (non-default ref)", 27 | startEvents: []event.Event{event.TestDataSetKeyValue}, 28 | args: []string{"set", "foo", "bar", "--ref", "non_default"}, 29 | wantedEvents: []event.Event{event.TestDataSetFooBar, event.TestDataSetKeyValue}, 30 | }, 31 | } 32 | 33 | for _, tc := range testCases { 34 | t.Run(tc.name, func(t *testing.T) { 35 | starteventsJSON, _ := event.Marshal(&tc.startEvents) 36 | wantedeventsJSON, _ := event.Marshal(&tc.wantedEvents) 37 | 38 | root := NewRootCommand() 39 | var notesAddArgMsg string 40 | gitWrapper := ¬esStub{ 41 | notesAddImplementation: spyArgsStringString(nil, nil, ¬esAddArgMsg), 42 | notesShowImplementation: responseStubArgsStringString(starteventsJSON), 43 | revParseHeadImplementation: responseStubArgsNone(TestDataDummyHash), 44 | } 45 | ctx := ContextWithGitWrapper(context.Background(), gitWrapper) 46 | 47 | args := disableFetch(tc.args) 48 | _, err := executeCommandContext(ctx, root, args...) 49 | 50 | assert.NoError(t, err) 51 | assert.Equal(t, wantedeventsJSON, notesAddArgMsg) 52 | }) 53 | } 54 | } 55 | 56 | func TestSetInvalidKey(t *testing.T) { 57 | gitWrapper := ¬esStub{} 58 | 59 | t.Run("Key cannot be empty", func(t *testing.T) { 60 | err := set(gitWrapper, TestDataDummyRef, TestDataEmptyString, TestDataDummyValue) 61 | if assert.Error(t, err) { 62 | assert.IsType(t, &event.InvalidKey{}, err) 63 | } 64 | }) 65 | } 66 | 67 | func TestSetWithoutHeadEvents(t *testing.T) { 68 | var showStubNoNote = func(string, string) (response string, err error) { 69 | // Mimic error when ther's no note available 70 | err = errors.New("exit status 128") 71 | response = "error: no note found for object DUMMY_HASH.\n" 72 | 73 | return response, err 74 | } 75 | 76 | gitWrapper := ¬esStub{ 77 | notesShowImplementation: showStubNoNote, 78 | revParseHeadImplementation: responseStubArgsNone(TestDataDummyHash), 79 | notesAddImplementation: dummyStubArgsStringString, 80 | } 81 | 82 | t.Run("Set without prior events on HEAD commit doesn't fail", func(t *testing.T) { 83 | err := set(gitWrapper, TestDataDummyRef, event.TestDataFoo, event.TestDataBar) 84 | assert.NoError(t, err) 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /cmdList_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/philips-software/gino-keva/internal/event" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestListCommand(t *testing.T) { 12 | td := []event.Event{event.TestDataSetKeyValue} 13 | 14 | testCases := []struct { 15 | name string 16 | args []string 17 | start []event.Event 18 | input string 19 | wantOutput string 20 | }{ 21 | { 22 | name: "List all notes (plain output)", 23 | args: []string{"list"}, 24 | start: td, 25 | wantOutput: "key=value\n", 26 | }, 27 | { 28 | name: "List all notes (json output)", 29 | args: []string{"list", "--output", "json"}, 30 | start: td, 31 | wantOutput: "{\n \"key\": \"value\"\n}\n", 32 | }, 33 | } 34 | 35 | for _, tc := range testCases { 36 | t.Run(tc.name, func(t *testing.T) { 37 | eventsJSON, _ := event.Marshal(&tc.start) 38 | 39 | root := NewRootCommand() 40 | ctx := ContextWithGitWrapper(context.Background(), ¬esStub{ 41 | logCommitsImplementation: responseStubArgsNone(simpleLogCommitsResponse), 42 | notesListImplementation: responseStubArgsString(simpleNotesListResponse), 43 | notesShowImplementation: responseStubArgsStringString(eventsJSON), 44 | }) 45 | 46 | args := disableFetch(tc.args) 47 | gotOutput, err := executeCommandContext(ctx, root, args...) 48 | 49 | assert.NoError(t, err) 50 | assert.Equal(t, tc.wantOutput, gotOutput) 51 | }) 52 | } 53 | } 54 | 55 | func TestGetListOutputTestDataEmpty(t *testing.T) { 56 | td := []event.Event{} 57 | 58 | testCases := []struct { 59 | name string 60 | outputFormat string 61 | start []event.Event 62 | wantText string 63 | }{ 64 | { 65 | name: "Empty note (plain)", 66 | outputFormat: "plain", 67 | start: td, 68 | wantText: TestDataEmptyString, 69 | }, 70 | { 71 | name: "Empty note (json)", 72 | outputFormat: "json", 73 | start: td, 74 | wantText: "{}\n", 75 | }, 76 | { 77 | name: "Empty note (plain)", 78 | outputFormat: "plain", 79 | start: td, 80 | wantText: TestDataEmptyString, 81 | }, 82 | { 83 | name: "Empty note (json)", 84 | outputFormat: "json", 85 | start: td, 86 | wantText: "{}\n", 87 | }, 88 | } 89 | 90 | for _, tc := range testCases { 91 | t.Run(tc.name, func(t *testing.T) { 92 | eventsJSON, _ := event.Marshal(&tc.start) 93 | 94 | gitWrapper := notesStub{ 95 | logCommitsImplementation: dummyStubArgsNone, 96 | notesListImplementation: dummyStubArgsString, 97 | notesShowImplementation: responseStubArgsStringString(eventsJSON), 98 | } 99 | gotOutput, err := getListOutput(&gitWrapper, TestDataDummyRef, tc.outputFormat) 100 | 101 | assert.NoError(t, err) 102 | assert.Equal(t, tc.wantText, gotOutput) 103 | }) 104 | } 105 | } 106 | 107 | func TestInvalidOutputFormat(t *testing.T) { 108 | t.Run("InvalidOutputFormat error raised when specifying invalid output format", func(t *testing.T) { 109 | gitWrapper := notesStub{ 110 | logCommitsImplementation: dummyStubArgsNone, 111 | notesListImplementation: dummyStubArgsString, 112 | notesShowImplementation: dummyStubArgsStringString, 113 | } 114 | 115 | _, err := getListOutput(&gitWrapper, TestDataDummyRef, "invalid format") 116 | if assert.Error(t, err) { 117 | assert.IsType(t, &InvalidOutputFormat{}, err) 118 | } 119 | }) 120 | } 121 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | # This workflow will run on main branch and on any pull requests targeting main 4 | on: 5 | push: 6 | branches: 7 | - '**' 8 | tags: 9 | - 'v*.*.*' 10 | pull_request: 11 | branches: 12 | - 'main' 13 | 14 | jobs: 15 | build_linux: 16 | name: Build - Linux 17 | runs-on: ubuntu-latest 18 | container: golang:1.17-buster 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | 24 | - uses: actions/setup-go@v3 25 | with: 26 | go-version: 1.17 27 | 28 | - name: Build 29 | run: | 30 | go get -u golang.org/x/lint/golint 31 | go install honnef.co/go/tools/cmd/staticcheck@2021.1 32 | go install github.com/ahmetb/govvv@v0.3.0 33 | make all 34 | 35 | - name: Generate release notes 36 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 37 | run: | 38 | echo 'RELEASE_NOTES<> $GITHUB_ENV 39 | make release-notes >> $GITHUB_ENV 40 | echo >> $GITHUB_ENV 41 | echo 'EOF' >> $GITHUB_ENV 42 | 43 | - name: Add binaries to release 44 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 45 | uses: softprops/action-gh-release@v1 46 | with: 47 | body: ${{ env.RELEASE_NOTES }} 48 | files: build/* 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | build_windows: 53 | name: Build - Windows 54 | runs-on: windows-2019 55 | steps: 56 | - uses: actions/checkout@v3 57 | with: 58 | fetch-depth: 0 59 | 60 | - uses: actions/setup-go@v3 61 | with: 62 | go-version: 1.17 63 | 64 | - name: Build 65 | run: | 66 | go install github.com/ahmetb/govvv@v0.3.0 67 | make build 68 | mv build/gino-keva build/gino-keva.exe 69 | 70 | - name: Add binaries to release 71 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 72 | uses: softprops/action-gh-release@v1 73 | with: 74 | files: build/* 75 | env: 76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | 78 | docker: 79 | name: Docker build/push 80 | needs: 81 | - build_linux 82 | - build_windows 83 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 84 | runs-on: ubuntu-latest 85 | steps: 86 | - uses: actions/checkout@v3 87 | 88 | - uses: robinraju/release-downloader@v1 89 | with: 90 | repository: ${{ github.repository }} 91 | latest: true 92 | fileName: gino-keva 93 | out-file-path: build/ 94 | token: '${{ secrets.GITHUB_TOKEN }}' 95 | 96 | - run: chmod +x build/gino-keva 97 | 98 | - name: Docker meta 99 | id: meta 100 | uses: docker/metadata-action@v4 101 | with: 102 | images: | 103 | philipssoftware/gino-keva 104 | tags: | 105 | type=semver,pattern={{version}} 106 | 107 | - name: Login to DockerHub 108 | uses: azure/docker-login@v1 109 | with: 110 | username: ${{ secrets.DOCKER_USERNAME }} 111 | password: ${{ secrets.DOCKER_PASSWORD }} 112 | 113 | - name: Build and push container 114 | uses: docker/build-push-action@v3 115 | with: 116 | context: . 117 | file: Dockerfile 118 | tags: ${{ steps.meta.outputs.tags }} 119 | labels: ${{ steps.meta.outputs.labels }} 120 | push: true 121 | 122 | - name: Update repo description 123 | uses: peter-evans/dockerhub-description@v3 124 | with: 125 | username: ${{ secrets.DOCKER_USERNAME }} 126 | password: ${{ secrets.DOCKER_PASSWORD }} 127 | repository: philipssoftware/gino-keva 128 | short-description: ${{ github.event.repository.description }} -------------------------------------------------------------------------------- /testutils_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "strconv" 7 | 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | type notesStub struct { 12 | fetchNotesImplementation func(string) (string, error) 13 | logCommitsImplementation func() (string, error) 14 | notesAddImplementation func(string, string) (string, error) 15 | notesListImplementation func(string) (string, error) 16 | notesShowImplementation func(string, string) (string, error) 17 | pushNotesImplementation func(string) (string, error) 18 | revParseHeadImplementation func() (string, error) 19 | } 20 | 21 | // FetchNotes test-double 22 | func (n notesStub) FetchNotes(notesRef string, force bool) (string, error) { 23 | return n.fetchNotesImplementation(notesRef) 24 | } 25 | 26 | // LogCommits test-double 27 | func (n notesStub) LogCommits() (string, error) { 28 | return n.logCommitsImplementation() 29 | } 30 | 31 | // NotesAdd test-double 32 | func (n notesStub) NotesAdd(notesRef string, msg string) (string, error) { 33 | return n.notesAddImplementation(notesRef, msg) 34 | } 35 | 36 | // NotesList test-double 37 | func (n notesStub) NotesList(notesRef string) (string, error) { 38 | return n.notesListImplementation(notesRef) 39 | } 40 | 41 | // NotesPrune dummy 42 | func (notesStub) NotesPrune(string) (string, error) { 43 | return "", nil 44 | } 45 | 46 | //NotesShow test-double calls the stub implementation 47 | func (n *notesStub) NotesShow(notesRef, hash string) (response string, err error) { 48 | return n.notesShowImplementation(notesRef, hash) 49 | } 50 | 51 | // PushNotes test-double 52 | func (n notesStub) PushNotes(notesRef string) (string, error) { 53 | return n.pushNotesImplementation(notesRef) 54 | } 55 | 56 | // RevParseHead test-double 57 | func (n notesStub) RevParseHead() (string, error) { 58 | return n.revParseHeadImplementation() 59 | } 60 | 61 | var dummyStubArgsNone = func() (string, error) { return "", nil } 62 | var dummyStubArgsString = func(string) (string, error) { return "", nil } 63 | var dummyStubArgsStringString = func(string, string) (string, error) { return "", nil } 64 | 65 | var responseStubArgsNone = func(expectedResponse string) func() (string, error) { 66 | return func() (string, error) { 67 | return expectedResponse, nil 68 | } 69 | } 70 | 71 | var responseStubArgsString = func(expectedResponse string) func(string) (string, error) { 72 | return func(string) (string, error) { 73 | return expectedResponse, nil 74 | } 75 | } 76 | 77 | var responseStubArgsStringString = func(expectedResponse string) func(string, string) (string, error) { 78 | return func(string, string) (string, error) { 79 | return expectedResponse, nil 80 | } 81 | } 82 | 83 | var spyArgsString = func(isCalled *bool, arg1 *string) func(string) (string, error) { 84 | return func(a1 string) (string, error) { 85 | if isCalled != nil { 86 | *isCalled = true 87 | } 88 | if arg1 != nil { 89 | *arg1 = a1 90 | } 91 | return "", nil 92 | } 93 | } 94 | 95 | var spyArgsStringString = func(isCalled *bool, arg1, arg2 *string) func(string, string) (string, error) { 96 | return func(a1, a2 string) (string, error) { 97 | if isCalled != nil { 98 | *isCalled = true 99 | } 100 | if arg1 != nil { 101 | *arg1 = a1 102 | } 103 | if arg2 != nil { 104 | *arg2 = a2 105 | } 106 | return "", nil 107 | } 108 | } 109 | 110 | // Simple dummy responses for logCommits and notesList 111 | var ( 112 | simpleLogCommitsResponse = "COMMIT_REFERENCE\n" 113 | simpleNotesListResponse = "NOTES_OBJECT_ID COMMIT_REFERENCE\n" 114 | ) 115 | 116 | func executeCommandContext(ctx context.Context, root *cobra.Command, args ...string) (output string, err error) { 117 | buf := new(bytes.Buffer) 118 | 119 | root.SetOut(buf) 120 | root.SetErr(buf) 121 | root.SetArgs(args) 122 | 123 | err = root.ExecuteContext(ctx) 124 | return buf.String(), err 125 | } 126 | 127 | func disableFetch(args []string) []string { 128 | return append(args, "--fetch=false") 129 | } 130 | 131 | func enablePush(args []string) []string { 132 | return append(args, "--push") 133 | } 134 | 135 | func generateIncrementingNumbersListOfLength(length int) []string { 136 | output := []string{} 137 | for i := 0; i < length; i++ { 138 | output = append(output, strconv.Itoa(i)) 139 | } 140 | 141 | return output 142 | } 143 | -------------------------------------------------------------------------------- /internal/event/marshalling_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var ( 12 | // Correct events 13 | rawEventSetFooBar = "{\"type\":\"set\",\"key\":\"foo\",\"value\":\"bar\"}" 14 | rawEventSetKeyValue = "{\"type\":\"set\",\"key\":\"key\",\"value\":\"value\"}" 15 | rawEventUnsetKey = "{\"type\":\"unset\",\"key\":\"key\"}" 16 | 17 | // Incorrect events 18 | rawEventTypeUnknown = "{\"type\":\"unknown\"}" 19 | rawEventSetFooMissingValue = "{\"type\":\"set\",\"key\":\"foo\"}" 20 | rawEventSetMissingKeyValueBar = "{\"type\":\"set\",\"value\":\"bar\"}" 21 | rawEventUnsetMissingKey = "{\"type\":\"unset\"}" 22 | ) 23 | 24 | func wrapEvents(events ...string) string { 25 | return fmt.Sprintf("{\"events\":[%v]}\n", strings.Join(events, ",")) 26 | } 27 | 28 | func TestMarshal(t *testing.T) { 29 | testCases := []struct { 30 | name string 31 | input *[]Event 32 | wanted string 33 | }{ 34 | { 35 | name: "nil input", 36 | input: nil, 37 | wanted: wrapEvents(), 38 | }, 39 | { 40 | name: "no events", 41 | input: &[]Event{}, 42 | wanted: wrapEvents(), 43 | }, 44 | { 45 | name: "set foo=bar", 46 | input: &[]Event{TestDataSetFooBar}, 47 | wanted: wrapEvents(rawEventSetFooBar), 48 | }, 49 | { 50 | name: "set foo=bar, set key=value", 51 | input: &[]Event{TestDataSetFooBar, TestDataSetKeyValue}, 52 | wanted: wrapEvents(rawEventSetFooBar, rawEventSetKeyValue), 53 | }, 54 | { 55 | name: "set key=value, unset key", 56 | input: &[]Event{TestDataSetKeyValue, TestDataUnsetKey}, 57 | wanted: wrapEvents(rawEventSetKeyValue, rawEventUnsetKey), 58 | }, 59 | } 60 | 61 | for _, tc := range testCases { 62 | t.Run(tc.name, func(t *testing.T) { 63 | got, err := Marshal(tc.input) 64 | 65 | assert.NoError(t, err) 66 | assert.Equal(t, tc.wanted, got) 67 | }) 68 | } 69 | } 70 | 71 | func TestUnMarshalInvalidFormat(t *testing.T) { 72 | testCases := []struct { 73 | name string 74 | input string 75 | wantedErrorType error 76 | }{ 77 | { 78 | name: "unexpected JSON structure", 79 | input: `{"FOO": "bar"}`, 80 | wantedErrorType: &NoEventsInNote{}, 81 | }, 82 | { 83 | name: "unknown event", 84 | input: wrapEvents(rawEventTypeUnknown), 85 | wantedErrorType: &UnknownType{}, 86 | }, 87 | { 88 | name: "Set with missing value", 89 | input: wrapEvents(rawEventSetFooMissingValue), 90 | wantedErrorType: &ValueMissing{}, 91 | }, 92 | { 93 | name: "Set with missing key", 94 | input: wrapEvents(rawEventSetMissingKeyValueBar), 95 | wantedErrorType: &KeyMissing{}, 96 | }, 97 | { 98 | name: "Unset with missing key", 99 | input: wrapEvents(rawEventUnsetMissingKey), 100 | wantedErrorType: &KeyMissing{}, 101 | }, 102 | } 103 | 104 | for _, tc := range testCases { 105 | t.Run(tc.name, func(t *testing.T) { 106 | var got []Event 107 | err := Unmarshal(tc.input, &got) 108 | 109 | if assert.Error(t, err) { 110 | assert.IsType(t, tc.wantedErrorType, err) 111 | } 112 | }) 113 | } 114 | } 115 | 116 | func TestUnMarshal(t *testing.T) { 117 | testCases := []struct { 118 | name string 119 | input string 120 | wanted []Event 121 | }{ 122 | { 123 | name: "nil input", 124 | input: wrapEvents(), 125 | wanted: []Event{}, 126 | }, 127 | { 128 | name: "no events", 129 | input: wrapEvents(), 130 | wanted: []Event{}, 131 | }, 132 | { 133 | name: "set foo=bar", 134 | input: wrapEvents(rawEventSetFooBar), 135 | wanted: []Event{ 136 | { 137 | EventType: Set, 138 | Key: TestDataFoo, 139 | Value: &TestDataBar, 140 | }, 141 | }, 142 | }, 143 | { 144 | name: "set foo=bar, set key=value", 145 | input: wrapEvents(rawEventSetFooBar, rawEventSetKeyValue), 146 | wanted: []Event{TestDataSetFooBar, TestDataSetKeyValue}, 147 | }, 148 | { 149 | name: "set key=value, unset key", 150 | input: wrapEvents(rawEventSetKeyValue, rawEventUnsetKey), 151 | wanted: []Event{TestDataSetKeyValue, TestDataUnsetKey}, 152 | }, 153 | } 154 | 155 | for _, tc := range testCases { 156 | t.Run(tc.name, func(t *testing.T) { 157 | var got []Event 158 | err := Unmarshal(tc.input, &got) 159 | 160 | assert.NoError(t, err) 161 | assert.Equal(t, tc.wanted, got) 162 | }) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /cmdRoot_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "os" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestFlagResolution(t *testing.T) { 13 | testCases := []struct { 14 | name string 15 | envVar string 16 | flagArgs []string 17 | wantOutput string 18 | }{ 19 | { 20 | name: "If no flag value is provided, the default value (gino_keva) should be used", 21 | envVar: "", 22 | flagArgs: []string{}, 23 | wantOutput: "gino_keva", 24 | }, 25 | { 26 | name: "Set notes ref flag via command line", 27 | envVar: "", 28 | flagArgs: []string{"--ref", "From_Command_Line"}, 29 | wantOutput: "From_Command_Line", 30 | }, 31 | { 32 | name: "Set notes ref flag with an environment variable", 33 | envVar: "From_Environment", 34 | flagArgs: []string{}, 35 | wantOutput: "From_Environment", 36 | }, 37 | { 38 | name: "Overwrite notes ref flag set in environment via command line", 39 | envVar: "From_Environment", 40 | flagArgs: []string{"--ref", "From_Command_Line"}, 41 | wantOutput: "From_Command_Line", 42 | }, 43 | } 44 | 45 | for _, tc := range testCases { 46 | t.Run(tc.name, func(t *testing.T) { 47 | root := NewRootCommand() 48 | 49 | os.Setenv("GINO_KEVA_REF", tc.envVar) 50 | defer os.Unsetenv("GINO_KEVA_REF") 51 | 52 | listFlagArgs := []string{"show-flag", "ref"} 53 | args := append(listFlagArgs, tc.flagArgs...) 54 | 55 | ctx := ContextWithGitWrapper(context.Background(), ¬esStub{}) 56 | gotOutput, err := executeCommandContext(ctx, root, args...) 57 | 58 | assert.NoError(t, err) 59 | assert.Equal(t, tc.wantOutput, gotOutput) 60 | }) 61 | } 62 | } 63 | 64 | func TestFetchFlag(t *testing.T) { 65 | testCases := []struct { 66 | name string 67 | args []string 68 | wantFetchCalled bool 69 | }{ 70 | { 71 | name: "Fetch is called when calling list (default)", 72 | args: []string{"list"}, 73 | wantFetchCalled: true, 74 | }, 75 | { 76 | name: "Fetch is NOT called if set to false", 77 | args: disableFetch([]string{"list"}), 78 | wantFetchCalled: false, 79 | }} 80 | 81 | for _, tc := range testCases { 82 | t.Run(tc.name, func(t *testing.T) { 83 | var fetchCalled bool 84 | gitWrapper := ¬esStub{ 85 | fetchNotesImplementation: spyArgsString(&fetchCalled, nil), 86 | logCommitsImplementation: dummyStubArgsNone, 87 | notesListImplementation: dummyStubArgsString, 88 | notesShowImplementation: dummyStubArgsStringString, 89 | } 90 | ctx := ContextWithGitWrapper(context.Background(), gitWrapper) 91 | 92 | root := NewRootCommand() 93 | _, err := executeCommandContext(ctx, root, tc.args...) 94 | 95 | assert.NoError(t, err) 96 | assert.Equal(t, tc.wantFetchCalled, fetchCalled) 97 | }) 98 | } 99 | } 100 | 101 | func TestPushFlag(t *testing.T) { 102 | testCases := []struct { 103 | name string 104 | args []string 105 | wantPushCalled bool 106 | }{ 107 | { 108 | name: "Push is NOT called when flag is unset (default)", 109 | args: []string{"set", "foo", "bar"}, 110 | wantPushCalled: false, 111 | }, 112 | { 113 | name: "Push is called when flag is set", 114 | args: enablePush([]string{"set", "foo", "bar"}), 115 | wantPushCalled: true, 116 | }} 117 | 118 | for _, tc := range testCases { 119 | t.Run(tc.name, func(t *testing.T) { 120 | var pushCalled bool 121 | gitWrapper := ¬esStub{ 122 | pushNotesImplementation: spyArgsString(&pushCalled, nil), 123 | revParseHeadImplementation: responseStubArgsNone(TestDataDummyHash), 124 | logCommitsImplementation: dummyStubArgsNone, 125 | notesAddImplementation: dummyStubArgsStringString, 126 | notesListImplementation: dummyStubArgsString, 127 | notesShowImplementation: dummyStubArgsStringString, 128 | } 129 | ctx := ContextWithGitWrapper(context.Background(), gitWrapper) 130 | 131 | root := NewRootCommand() 132 | 133 | args := disableFetch(tc.args) 134 | _, err := executeCommandContext(ctx, root, args...) 135 | 136 | assert.NoError(t, err) 137 | assert.Equal(t, tc.wantPushCalled, pushCalled) 138 | }) 139 | } 140 | } 141 | 142 | func TestFetchNoUpstreamRef(t *testing.T) { 143 | var fetchStubNoUpstreamRef = func(string) (response string, err error) { 144 | // Mimic error when there's no upstream ref present yet 145 | err = errors.New("exit status 128") 146 | response = "fatal: couldn't find remote ref refs/notes/foo" 147 | 148 | return response, err 149 | } 150 | 151 | t.Run("Fetch without upstream notesref doesn't result in error", func(t *testing.T) { 152 | root := NewRootCommand() 153 | gitWrapper := ¬esStub{ 154 | fetchNotesImplementation: fetchStubNoUpstreamRef, 155 | logCommitsImplementation: dummyStubArgsNone, 156 | notesListImplementation: dummyStubArgsString, 157 | notesShowImplementation: dummyStubArgsStringString, 158 | } 159 | ctx := ContextWithGitWrapper(context.Background(), gitWrapper) 160 | 161 | args := []string{"list"} 162 | gotOutput, err := executeCommandContext(ctx, root, args...) 163 | 164 | assert.NoError(t, err) 165 | assert.Equal(t, TestDataEmptyString, gotOutput) 166 | }) 167 | } 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build and Test](https://github.com/philips-software/gino-keva/actions/workflows/main.yml/badge.svg)](https://github.com/philips-software/gino-keva/actions/workflows/main.yml) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/philips-software/gino-keva)](https://goreportcard.com/report/github.com/philips-software/gino-keva) 3 | 4 | 5 | 6 | # Gino Keva - Git Notes Key Values 7 | 8 | Gino Keva works as a simple Key Value store built on top of Git Notes, using an event sourcing architecture. 9 | - Events are added to the current commit when manipulating key/values via get or unset actions 10 | - Gino Keva compiles a snapshot of all historical key/values by replaying all events up to the current commit 11 | 12 | 13 | 14 | ## Table of Contents 15 | 16 | - [Gino Keva - Git Notes Key Values](#gino-keva---git-notes-key-values) 17 | - [Table of Contents](#table-of-contents) 18 | - [Use case](#use-case) 19 | - [Use case - Store new component version](#use-case---store-new-component-version) 20 | - [Use case - List all component versions corresponding for a certain commit](#use-case---list-all-component-versions-corresponding-for-a-certain-commit) 21 | - [Requirements](#requirements) 22 | - [How to use](#how-to-use) 23 | - [Warning: Push your changes](#warning-push-your-changes) 24 | - [Set key/value pairs](#set-keyvalue-pairs) 25 | - [List all key/value pairs](#list-all-keyvalue-pairs) 26 | - [Use custom notes reference](#use-custom-notes-reference) 27 | - [FAQ](#faq) 28 | - [I need additional git configuration? How can I do that?](#i-need-additional-git-configuration-how-can-i-do-that) 29 | - [I need a custom output format](#i-need-a-custom-output-format) 30 | 31 | ## Use case 32 | 33 | _Although Gino Keva was written with the below use case in mind, it intends to be a generic tool. Don't get discouraged if your intended use is very different. Instead feel free to open a ticket, so we can discuss if we can make it work._ 34 | 35 | The need for Gino Keva was born in an environment where ~20 components (some would call micro-services) live together in a single repository. Every component is deployed in a docker container; together they form an application/service. There's a single build pipeline that triggers upon any change. The pipeline will then fan out and trigger an independent build (and test) for each component impacted by the change. For each component, this results in a new docker container which is versioned and pushed to the registry. Once all components are rebuilt, the set of containers (of which some newly built) can be deployed and tested and eventually be promoted to production. 36 | 37 | Due to the selective build mechanism, the versions of components are not coupled. Some will rarely change, others frequently. Now how to keep track of the set of containers that make up the application? It makes sense to keep this build metadata inside the version control system, so we have it available for each commit that was built. But we'd hate to see the build pipeline polluting the git history with artificial commits. This is where Gino Keva was born. 38 | 39 | 40 | 41 | ### Use case - Store new component version 42 | 43 | Gino Keva is used to store the newly built version of any component as a key/value pair in git notes, linked to commit it was built from: `COMPONENT_foo=1.1.0`. 44 | 45 | 46 | 47 | ### Use case - List all component versions corresponding for a certain commit 48 | 49 | For each deployment, the list of containers which make up the application is simply collected based on the output of `gino-keva list`: 50 | 51 | | Before | After | 52 | | ------------------- | ------------------------------- | 53 | | COMPONENT_foo=1.0.0 | COMPONENT_foo=1.1.0 (updated) | 54 | | COMPONENT_BAR=1.2.3 | COMPONENT_BAR=1.2.3 (untouched) | 55 | | .... | .... | 56 | 57 | ## Requirements 58 | 59 | - Git CLI: Gino Keva uses the git CLI as installed on the host. Tested with version 2.32.0, however any recent version should do. 60 | 61 | ## How to use 62 | 63 | See below examples on how to use gino-keva, or run `gino-keva --help` for help. 64 | 65 | ### Warning: Push your changes 66 | 67 | By default, gino-keva will not push your changes to the upstream. You likely would like to change this behaviour by specifying `--push`, or setting the environment variable `GINO_KEVA_PUSH=1`. 68 | If you do not do this, subsequent fetches will overwrite any local changes made. 69 | 70 | ### Set key/value pairs 71 | 72 | 73 | ````console 74 | foo@bar (f10b970d):~$ gino-keva set key my_value 75 | foo@bar (f10b970d):~$ gino-keva set counter 12 76 | foo@bar (f10b970d):~$ gino-keva set foo bar 77 | ```` 78 | 79 | ### List all key/value pairs 80 | 81 | ```console 82 | foo@bar (f10b970d):~$ gino-keva list 83 | counter=12 84 | foo=bar 85 | key=my_value 86 | ``` 87 | 88 | foo@bar (f10b970d):~$ git commit --allow-empty -m "Dummy commit" 89 | foo@bar (a8517558):~$ gino-keva set pi 3.14 90 | foo@bar (a8517558):~$ gino-keva list --output=json 91 | { 92 | "counter": "12", 93 | "foo": "bar", 94 | "key": "my_value", 95 | "pi": "3.14" 96 | } 97 | ``` 98 | 99 | ### Unset keys 100 | 101 | Finally, you can unset keys using `unset`: 102 | 103 | ```console 104 | foo@bar (a8517558):~$ gino-keva unset foo 105 | foo@bar (a8517558):~$ gino-keva list 106 | counter=12 107 | key=my_value 108 | pi=3.14 109 | ``` 110 | 111 | ### Use custom notes reference 112 | 113 | By default the notes are saved to `refs/notes/gino-keva`, but this can be changed with the `--ref` command-line switch. To store your key/value under `refs/notes/banana`: 114 | 115 | ```console 116 | foo@bar (a8517558):~$ gino-keva --ref=banana set color yellow 117 | ``` 118 | 119 | ## FAQ 120 | 121 | ### I need additional git configuration? How can I do that? 122 | 123 | Since Gino Keva simply uses the git CLI, you can use (most of) the options it provides to set/override the configuration. You could either use `git config` to setup the system is desired, or use environment variables to achieve the same. 124 | 125 | Example: Add a key/value pair as the "whatever \" user 126 | 127 | ``` 128 | GIT_CONFIG_COUNT=2 \ 129 | GIT_CONFIG_KEY_0="user.name" GIT_CONFIG_VALUE_0="whatever" \ 130 | GIT_CONFIG_KEY_1="user.email" GIT_CONFIG_VALUE_1="whatever@example.com" \ 131 | gino-keva set foo bar 132 | ``` 133 | 134 | ### I need a custom output format 135 | 136 | Gino Keva supports just simple `key=value` format (default), or json (`--output=json`). However, you can parse the output in any format you'd like. 137 | 138 | Example: Use gino-keva as part of a GitHub action: 139 | 140 | ```console 141 | foo@bar:~$ gino-keva list | awk -F= '{print "::set-output name="$1"::"$2}' 142 | ::set-output name=COUNTER::12 143 | ::set-output name=key::my_value 144 | ::set-output name=PI::3.14 145 | ``` 146 | 147 | Example: Use gino-keva as part of an Azure Devops pipeline: 148 | 149 | ```console 150 | foo@bar:~$ gino-keva list | awk -F= '{print "##vso[task.setvariable variable="$1"]"$2}' 151 | ##vso[task.setvariable variable=COUNTER]12 152 | ##vso[task.setvariable variable=key]my_value 153 | ##vso[task.setvariable variable=PI]3.14 154 | ``` 155 | -------------------------------------------------------------------------------- /common_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/philips-software/gino-keva/internal/event" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGetCommitHashes(t *testing.T) { 13 | testCases := []struct { 14 | name string 15 | gitLogCommitsOutput string 16 | wantedGitCommits []string 17 | }{ 18 | { 19 | name: "Get commit hashes - no history", 20 | gitLogCommitsOutput: "", 21 | wantedGitCommits: []string{}, 22 | }, 23 | { 24 | name: "Get commit hashes - 1 commit history", 25 | gitLogCommitsOutput: "COMMIT_HASH\n", 26 | wantedGitCommits: []string{"COMMIT_HASH"}, 27 | }, 28 | { 29 | name: "Get commit hashes - 2 commit history", 30 | gitLogCommitsOutput: "1234567\n890abcd\n", 31 | wantedGitCommits: []string{"1234567", "890abcd"}, 32 | }, 33 | } 34 | 35 | for _, tc := range testCases { 36 | t.Run(tc.name, func(t *testing.T) { 37 | gitWrapper := ¬esStub{ 38 | logCommitsImplementation: func() (response string, err error) { 39 | return tc.gitLogCommitsOutput, nil 40 | }, 41 | } 42 | 43 | hashes, err := getCommitHashes(gitWrapper) 44 | 45 | assert.NoError(t, err) 46 | assert.EqualValues(t, tc.wantedGitCommits, hashes) 47 | }) 48 | } 49 | } 50 | 51 | func TestGetNotesHashes(t *testing.T) { 52 | testCases := []struct { 53 | name string 54 | gitNotesListOutput string 55 | wantedNotesCommits []string 56 | }{ 57 | { 58 | name: "Get notes hashes - no notes", 59 | gitNotesListOutput: "", 60 | wantedNotesCommits: []string{}, 61 | }, 62 | { 63 | name: "Get notes hashes - 1 note", 64 | gitNotesListOutput: "NOTE_OBJECT_HASH ANNOTATED_OBJECT_HASH\n", 65 | wantedNotesCommits: []string{"ANNOTATED_OBJECT_HASH"}, 66 | }, 67 | { 68 | name: "Get notes hashes - 1 note", 69 | gitNotesListOutput: "NOTE_OBJECT_HASH ANNOTATED_OBJECT_HASH\n01234567 890abcd\n", 70 | wantedNotesCommits: []string{"ANNOTATED_OBJECT_HASH", "890abcd"}, 71 | }, 72 | } 73 | 74 | for _, tc := range testCases { 75 | t.Run(tc.name, func(t *testing.T) { 76 | gitWrapper := ¬esStub{ 77 | notesListImplementation: func(string) (response string, err error) { 78 | return tc.gitNotesListOutput, nil 79 | }, 80 | } 81 | 82 | hashes, err := getNotesHashes(gitWrapper, TestDataDummyRef) 83 | 84 | assert.NoError(t, err) 85 | assert.EqualValues(t, tc.wantedNotesCommits, hashes) 86 | }) 87 | } 88 | } 89 | 90 | func TestFindNoteText(t *testing.T) { 91 | testCases := []struct { 92 | name string 93 | getCommitHashesOutput []string 94 | getNotesHashesOutput []string 95 | expectedNotes []string 96 | }{ 97 | { 98 | name: "No commits, no notes", 99 | expectedNotes: []string{}, 100 | }, 101 | { 102 | name: "Some commits, no notes", 103 | getCommitHashesOutput: generateIncrementingNumbersListOfLength(4), 104 | expectedNotes: []string{}, 105 | }, 106 | { 107 | name: "Some notes, no commits", 108 | getNotesHashesOutput: generateIncrementingNumbersListOfLength(4), 109 | expectedNotes: []string{}, 110 | }, 111 | { 112 | name: "Note at depth 1", 113 | getCommitHashesOutput: generateIncrementingNumbersListOfLength(2), 114 | getNotesHashesOutput: []string{"1"}, 115 | expectedNotes: []string{"1"}, 116 | }, 117 | { 118 | name: "Note at depth 10", 119 | getCommitHashesOutput: generateIncrementingNumbersListOfLength(11), 120 | getNotesHashesOutput: []string{"10"}, 121 | expectedNotes: []string{"10"}, 122 | }, 123 | { 124 | name: "Note at depth 0 and 1", 125 | getCommitHashesOutput: generateIncrementingNumbersListOfLength(2), 126 | getNotesHashesOutput: generateIncrementingNumbersListOfLength(2), 127 | expectedNotes: generateIncrementingNumbersListOfLength(2), 128 | }, 129 | { 130 | name: "Note at depth 0 and 1 (notes output reversed)", 131 | getCommitHashesOutput: generateIncrementingNumbersListOfLength(2), 132 | getNotesHashesOutput: []string{"1", "0"}, 133 | expectedNotes: generateIncrementingNumbersListOfLength(2), 134 | }, 135 | } 136 | 137 | for _, tc := range testCases { 138 | t.Run(tc.name, func(t *testing.T) { 139 | getCommitHashes = func(GitWrapper) ([]string, error) { 140 | return tc.getCommitHashesOutput, nil 141 | } 142 | 143 | getNotesHashes = func(GitWrapper, string) ([]string, error) { 144 | return tc.getNotesHashesOutput, nil 145 | } 146 | 147 | var notesShowCalled bool 148 | var hashArg string 149 | gitWrapper := ¬esStub{ 150 | notesShowImplementation: spyArgsStringString(¬esShowCalled, nil, &hashArg), 151 | } 152 | notes, err := getRelevantNotes(gitWrapper, TestDataDummyRef) 153 | 154 | assert.NoError(t, err) 155 | assert.EqualValues(t, tc.expectedNotes, notes) 156 | }) 157 | } 158 | } 159 | 160 | func TestCalculateKeyValues(t *testing.T) { 161 | testCases := []struct { 162 | name string 163 | events [][]event.Event 164 | wanted map[string]Value 165 | }{ 166 | { 167 | name: "No notes", 168 | events: [][]event.Event{{}}, 169 | wanted: map[string]Value{}, 170 | }, 171 | { 172 | name: "Note with single set event", 173 | events: [][]event.Event{ 174 | {event.TestDataSetFooBar}, 175 | }, 176 | wanted: map[string]Value{ 177 | event.TestDataFoo: Value(event.TestDataBar), 178 | }, 179 | }, 180 | { 181 | name: "Note with 2 set event", 182 | events: [][]event.Event{ 183 | {event.TestDataSetFooBar, event.TestDataSetKeyValue}, 184 | }, 185 | wanted: map[string]Value{ 186 | event.TestDataFoo: Value(event.TestDataBar), 187 | event.TestDataKey: Value(event.TestDataValue), 188 | }, 189 | }, 190 | { 191 | name: "Overwrite value in same note", 192 | events: [][]event.Event{ 193 | {event.TestDataSetKeyOtherValue, event.TestDataSetKeyValue}, 194 | }, 195 | wanted: map[string]Value{ 196 | event.TestDataKey: Value(event.TestDataOtherValue), 197 | }, 198 | }, 199 | { 200 | name: "Set and unset value in same note", 201 | events: [][]event.Event{ 202 | {event.TestDataUnsetKey, event.TestDataSetKeyValue}, 203 | }, 204 | wanted: map[string]Value{}, 205 | }, 206 | { 207 | name: "Set events across 2 notes", 208 | events: [][]event.Event{ 209 | {event.TestDataSetFooBar}, 210 | {event.TestDataSetKeyValue}, 211 | }, 212 | wanted: map[string]Value{ 213 | event.TestDataFoo: Value(event.TestDataBar), 214 | event.TestDataKey: Value(event.TestDataValue), 215 | }, 216 | }, 217 | { 218 | name: "Overwrite value across 2 notes", 219 | events: [][]event.Event{ 220 | {event.TestDataSetKeyOtherValue}, 221 | {event.TestDataSetKeyValue}, 222 | }, 223 | wanted: map[string]Value{ 224 | event.TestDataKey: Value(event.TestDataOtherValue), 225 | }, 226 | }, 227 | } 228 | 229 | for _, tc := range testCases { 230 | t.Run(tc.name, func(t *testing.T) { 231 | getCommitHashes = func(GitWrapper) ([]string, error) { 232 | return generateIncrementingNumbersListOfLength(len(tc.events)), nil 233 | } 234 | 235 | getNotesHashes = func(GitWrapper, string) ([]string, error) { 236 | return generateIncrementingNumbersListOfLength(len(tc.events)), nil 237 | } 238 | 239 | gitWrapper := ¬esStub{ 240 | logCommitsImplementation: dummyStubArgsNone, 241 | notesListImplementation: dummyStubArgsString, 242 | notesShowImplementation: func(string, hash string) (string, error) { 243 | i, _ := strconv.Atoi(hash) 244 | return event.Marshal(&tc.events[i]) 245 | }, 246 | } 247 | 248 | got, err := calculateKeyValues(gitWrapper, TestDataDummyRef) 249 | 250 | assert.NoError(t, err) 251 | assert.Truef(t, reflect.DeepEqual(got.values, tc.wanted), "Got %v, wanted %v", got.values, tc.wanted) 252 | }) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contributing to Gino keva 4 | 5 | First off, thanks for taking the time to contribute! ❤️ 6 | 7 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 8 | 9 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: 10 | > 11 | > - Star the project 12 | > - Tweet about it 13 | > - Refer this project in your project's readme 14 | > - Mention the project at local meetups and tell your friends/colleagues 15 | 16 | 17 | 18 | ## Table of Contents 19 | 20 | - [I Have a Question](#i-have-a-question) 21 | - [I Want To Contribute](#i-want-to-contribute) 22 | - [Reporting Bugs](#reporting-bugs) 23 | - [Suggesting Enhancements](#suggesting-enhancements) 24 | 25 | ## I Have a Question 26 | 27 | > If you want to ask a question, we assume that you have read the available [Documentation](README.md). 28 | 29 | Before you ask a question, it is best to search for existing [Issues](https://github.com/philips-software/gino-keva/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. 30 | 31 | If you then still feel the need to ask a question and need clarification, we recommend the following: 32 | 33 | - Open an [Issue](https://github.com/philips-software/gino-keva/issues/new). 34 | - Provide as much context as you can about what you're running into. 35 | 36 | We will then take care of the issue as soon as possible. 37 | 38 | ## I Want To Contribute 39 | 40 | > ### Legal Notice 41 | > 42 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. 43 | 44 | ### Reporting Bugs 45 | 46 | 47 | 48 | #### Before Submitting a Bug Report 49 | 50 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. 51 | 52 | - Make sure that you are using the latest version. 53 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](README.md). If you are looking for support, you might want to check [this section](#i-have-a-question)). 54 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/philips-software/gino-keva/issues?q=label%3Abug). 55 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. 56 | - Collect information about the bug: 57 | - Stack trace (Traceback) 58 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) 59 | - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. 60 | - Possibly your input and the output 61 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 62 | 63 | 64 | 65 | #### How Do I Submit a Good Bug Report? 66 | 67 | > You must never report security related issues, vulnerabilities or bugs to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . 68 | 69 | We use GitHub issues to track bugs and errors. If you run into an issue with the project: 70 | 71 | - Open an [Issue](https://github.com/philips-software/gino-keva/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) 72 | - Explain the behavior you would expect and the actual behavior. 73 | - Please provide as much context as possible and describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. 74 | - Provide the information you collected in the previous section. 75 | 76 | Once it's filed: 77 | 78 | - The project team will label the issue accordingly. 79 | - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. 80 | - If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). 81 | 82 | 83 | 84 | ### Suggesting Enhancements 85 | 86 | This section guides you through submitting an enhancement suggestion for Gino keva, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. 87 | 88 | 89 | 90 | #### Before Submitting an Enhancement 91 | 92 | - Make sure that you are using the latest version. 93 | - Read the [documentation](README.md) carefully and find out if the functionality is already covered, maybe by an individual configuration. 94 | - Perform a [search](https://github.com/philips-software/gino-keva/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. 95 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. 96 | 97 | 98 | 99 | #### How Do I Submit a Good Enhancement Suggestion? 100 | 101 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/philips-software/gino-keva/issues). 102 | 103 | - Use a **clear and descriptive title** for the issue to identify the suggestion. 104 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. 105 | - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. 106 | - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. 107 | - **Explain why this enhancement would be useful** to most Gino keva users. You may also want to point out the other projects that solved it better and which could serve as inspiration. 108 | 109 | 110 | 111 | ## Attribution 112 | 113 | This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! 114 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | log "github.com/sirupsen/logrus" 8 | 9 | "github.com/philips-software/gino-keva/internal/event" 10 | "github.com/philips-software/gino-keva/internal/util" 11 | ) 12 | 13 | const ( 14 | maxRetryAttempts = 3 15 | ) 16 | 17 | type contextKey string 18 | 19 | var ( 20 | notesContextKey contextKey = "gitNotesKey" 21 | ) 22 | 23 | // GitWrapper interface 24 | type GitWrapper interface { 25 | FetchNotes(notesRef string, force bool) (string, error) 26 | LogCommits() (string, error) 27 | NotesAdd(notesRef, msg string) (string, error) 28 | NotesList(notesRef string) (string, error) 29 | NotesPrune(notesRef string) (string, error) 30 | NotesShow(notesRef, hash string) (string, error) 31 | PushNotes(notesRef string) (string, error) 32 | RevParseHead() (string, error) 33 | } 34 | 35 | // ContextWithGitWrapper returns a new context with the git wrapper object added 36 | func ContextWithGitWrapper(ctx context.Context, gitWrapper GitWrapper) context.Context { 37 | return context.WithValue(ctx, notesContextKey, gitWrapper) 38 | } 39 | 40 | // GetGitWrapperFrom returns the git wrapper object from the provided context 41 | func GetGitWrapperFrom(ctx context.Context) GitWrapper { 42 | v := ctx.Value(notesContextKey) 43 | if v == nil { 44 | log.Fatal("No Notes interface found in context") 45 | } 46 | 47 | return v.(GitWrapper) 48 | } 49 | 50 | var globalFlags = struct { 51 | NotesRef string 52 | VerboseLog bool 53 | 54 | Fetch bool 55 | }{} 56 | 57 | func getEvents(gitWrapper GitWrapper, notesRef string) (*[]event.Event, error) { 58 | var commitHash string 59 | { 60 | out, err := gitWrapper.RevParseHead() 61 | if err != nil { 62 | return nil, convertGitOutputToError(out, err) 63 | } 64 | commitHash = strings.TrimSuffix(out, "\n") 65 | } 66 | 67 | log.WithField("hash", commitHash).Debug("Retrieving events from git note...") 68 | events, err := getEventsFromNote(gitWrapper, notesRef, commitHash) 69 | 70 | if _, ok := err.(*NoNotePresent); ok { 71 | log.WithField("notesRef", globalFlags.NotesRef).Debug("No git note present yet") 72 | err = nil 73 | } 74 | 75 | return &events, err 76 | } 77 | 78 | func persistEvents(gitWrapper GitWrapper, notesRef string, events *[]event.Event) error { 79 | noteText, err := event.Marshal(events) 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | log.WithField("noteText", noteText).Debug("Persisting new note text...") 84 | 85 | { 86 | out, err := gitWrapper.NotesAdd(notesRef, noteText) 87 | if err != nil { 88 | return convertGitOutputToError(out, err) 89 | } 90 | } 91 | 92 | return nil 93 | } 94 | 95 | func calculateKeyValues(gitWrapper GitWrapper, notesRef string) (values *Values, err error) { 96 | notes, err := getRelevantNotes(gitWrapper, notesRef) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | if len(notes) == 0 { 102 | log.WithField("ref", notesRef).Warning("No prior notes found") 103 | } 104 | 105 | events, err := getEventsFromNotes(gitWrapper, notesRef, notes) 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | return calculateKeyValuesFromEvents(events) 111 | } 112 | 113 | func getRelevantNotes(gitWrapper GitWrapper, notesRef string) (notes []string, err error) { 114 | allNotes, err := getNotesHashes(gitWrapper, notesRef) 115 | if err != nil { 116 | return nil, err 117 | } 118 | log.WithFields(log.Fields{ 119 | "first 10 notes": util.LimitStringSlice(allNotes, 10), 120 | "Total # of notes": len(allNotes), 121 | }).Debug("All notes in notes ref") 122 | 123 | // Try to get one more commit so we can detect if commits were exhausted in case no note was found 124 | commits, err := getCommitHashes(gitWrapper) 125 | if err != nil { 126 | return nil, err 127 | } 128 | log.WithFields(log.Fields{ 129 | "first 10 commits": util.LimitStringSlice(commits, 10), 130 | "Total # of commits": len(commits), 131 | }).Debug() 132 | 133 | // Get all notes for commits 134 | notes = util.GetSlicesIntersect(commits, allNotes) 135 | log.WithFields(log.Fields{ 136 | "first 10 notes": util.LimitStringSlice(notes, 10), 137 | "Total # of notes": len(notes), 138 | }).Debug("Notes intersecting with branch history") 139 | 140 | return notes, nil 141 | } 142 | 143 | func getEventsFromNotes(gitWrapper GitWrapper, notesRef string, notes []string) (events []event.Event, err error) { 144 | for _, n := range notes { // Iterate from new to old (newest note in front) 145 | log.WithField("hash", n).Debug("Get events from note") 146 | e, err := getEventsFromNote(gitWrapper, notesRef, n) 147 | if _, ok := err.(*event.NoEventsInNote); ok { 148 | log.Debug("Ignoring events from here on since 'events' key was missing") 149 | break 150 | } else if err != nil { 151 | return nil, err 152 | } 153 | events = append(events, e...) 154 | } 155 | return events, nil 156 | } 157 | 158 | func getEventsFromNote(gitWrapper GitWrapper, notesRef string, note string) (events []event.Event, err error) { 159 | events = []event.Event{} 160 | 161 | var noteText string 162 | { 163 | out, err := gitWrapper.NotesShow(notesRef, note) 164 | if err != nil { 165 | return nil, convertGitOutputToError(out, err) 166 | } 167 | noteText = out 168 | } 169 | 170 | if noteText != "" { 171 | log.WithField("rawText", noteText).Debug("Unmarshalling...") 172 | err = event.Unmarshal(noteText, &events) 173 | 174 | if err != nil { 175 | return nil, err 176 | } 177 | } 178 | 179 | return events, nil 180 | } 181 | 182 | func calculateKeyValuesFromEvents(events []event.Event) (values *Values, err error) { 183 | v := NewValues() 184 | keysUnset := []string{} 185 | 186 | for _, e := range events { // Iterate from new to old (newest event in front) 187 | switch e.EventType { 188 | case event.Set: 189 | if !v.HasKey(e.Key) && !util.Contains(keysUnset, e.Key) { 190 | v.Add(e.Key, Value(*e.Value)) 191 | } 192 | case event.Unset: 193 | keysUnset = append(keysUnset, e.Key) 194 | default: 195 | log.Fatal("Fatal: Unknown event type encountered") 196 | } 197 | } 198 | 199 | return v, nil 200 | } 201 | 202 | func fetchNotes(gitWrapper GitWrapper) (err error) { 203 | err = fetchNotesWithForce(gitWrapper, globalFlags.NotesRef, false) 204 | 205 | if _, ok := err.(*UpstreamChanged); ok { 206 | log.Warning("Unpushed local changes are now discarded") 207 | err = fetchNotesWithForce(gitWrapper, globalFlags.NotesRef, true) 208 | } 209 | 210 | if _, ok := err.(*NoRemoteRef); ok { 211 | log.WithField("notesRef", globalFlags.NotesRef).Debug("Couldn't find remote ref. Nothing fetched") 212 | err = nil 213 | } 214 | 215 | if err != nil { 216 | log.Error(err.Error()) 217 | } 218 | 219 | return err 220 | } 221 | 222 | func fetchNotesWithForce(gitWrapper GitWrapper, notesRef string, force bool) error { 223 | log.WithField("force", force).Debug("Fetching notes...") 224 | defer log.Debug("Done.") 225 | 226 | out, errorCode := gitWrapper.FetchNotes(globalFlags.NotesRef, force) 227 | return convertGitOutputToError(out, errorCode) 228 | } 229 | 230 | func pruneNotes(gitWrapper GitWrapper, notesRef string) error { 231 | log.Debug("Pruning notes...") 232 | defer log.Debug("Done.") 233 | 234 | out, errorCode := gitWrapper.NotesPrune(globalFlags.NotesRef) 235 | return convertGitOutputToError(out, errorCode) 236 | } 237 | 238 | func pushNotes(gitWrapper GitWrapper, notesRef string) error { 239 | log.Debug("Pushing notes...") 240 | defer log.Debug("Done.") 241 | 242 | out, errorCode := gitWrapper.PushNotes(globalFlags.NotesRef) 243 | err := convertGitOutputToError(out, errorCode) 244 | 245 | if _, ok := err.(*UpstreamChanged); ok { 246 | return err 247 | } 248 | 249 | if err != nil { 250 | log.Error(out) 251 | } 252 | 253 | return err 254 | } 255 | 256 | var getCommitHashes = func(gitWrapper GitWrapper) (hashList []string, err error) { 257 | out, err := gitWrapper.LogCommits() 258 | if err != nil { 259 | return nil, convertGitOutputToError(out, err) 260 | } 261 | 262 | if out == "" { 263 | hashList = []string{} 264 | } else { 265 | out := strings.TrimSuffix(out, "\n") 266 | hashList = strings.Split(out, "\n") 267 | } 268 | 269 | return hashList, nil 270 | } 271 | 272 | var getNotesHashes = func(gitWrapper GitWrapper, notesRef string) (hashList []string, err error) { 273 | out, err := gitWrapper.NotesList(notesRef) 274 | if err != nil { 275 | return nil, convertGitOutputToError(out, err) 276 | } 277 | 278 | if out == "" { 279 | hashList = []string{} 280 | } else { 281 | out := strings.TrimSuffix(out, "\n") 282 | lines := strings.Split(out, "\n") 283 | for _, line := range lines { 284 | hashes := strings.Split(line, " ") 285 | hashList = append(hashList, hashes[1]) 286 | } 287 | } 288 | return hashList, nil 289 | } 290 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 21 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 22 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 23 | cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= 24 | cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= 25 | cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= 26 | cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= 27 | cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= 28 | cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= 29 | cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= 30 | cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= 31 | cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= 32 | cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= 33 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 34 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 35 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 36 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 37 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 38 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 39 | cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= 40 | cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= 41 | cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= 42 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 43 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 44 | cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= 45 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 46 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 47 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 48 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 49 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 50 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 51 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 52 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 53 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 54 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 55 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 56 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 57 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 58 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 59 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 60 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 61 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 62 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 63 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 64 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 65 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 66 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 67 | github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= 68 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 69 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 70 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 71 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 72 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 73 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 74 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 75 | github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 76 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 77 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 78 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 79 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 80 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 81 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 82 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= 83 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 84 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 85 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 86 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 87 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 88 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 89 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 90 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 91 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 92 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 93 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 94 | github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 95 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 96 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 97 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 98 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 99 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 100 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 101 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 102 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 103 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 104 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 105 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 106 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 107 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 108 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 109 | github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= 110 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 111 | github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= 112 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 113 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 114 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 115 | github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= 116 | github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= 117 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 118 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 119 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 120 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 121 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 122 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 123 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 124 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 125 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 126 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 127 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 128 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 129 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 130 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 131 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 132 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 133 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 134 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 135 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 136 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 137 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 138 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 139 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 140 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 141 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 142 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 143 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 144 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 145 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 146 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 147 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 148 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 149 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 150 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 151 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 152 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 153 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 154 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 155 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 156 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 157 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 158 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 159 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 160 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 161 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 162 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 163 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 164 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 165 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 166 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 167 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 168 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 169 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 170 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 171 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 172 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 173 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 174 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 175 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 176 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 177 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 178 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 179 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 180 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 181 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 182 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 183 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 184 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 185 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 186 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 187 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 188 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 189 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 190 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 191 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 192 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 193 | github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 194 | github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 195 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 196 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 197 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 198 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 199 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 200 | github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= 201 | github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= 202 | github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= 203 | github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= 204 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 205 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 206 | github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= 207 | github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= 208 | github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= 209 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 210 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 211 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 212 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 213 | github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 214 | github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 215 | github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 216 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 217 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 218 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 219 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 220 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 221 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 222 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 223 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 224 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 225 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 226 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 227 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 228 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 229 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 230 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 231 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 232 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 233 | github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= 234 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 235 | github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 236 | github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 237 | github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= 238 | github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 239 | github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 240 | github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 241 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 242 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 243 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 244 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 245 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 246 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 247 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 248 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 249 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 250 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 251 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 252 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 253 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 254 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 255 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 256 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 257 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 258 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 259 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 260 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 261 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 262 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 263 | github.com/ldez/go-git-cmd-wrapper/v2 v2.3.0 h1:DFa0UMjHXPnPWsyca1Dj+C4w/zXCh1P59PEBN5/XJuo= 264 | github.com/ldez/go-git-cmd-wrapper/v2 v2.3.0/go.mod h1:657ooZ9WLGqP16DRl4kBbEXQYV7sdZBAPP9UCaVGYIU= 265 | github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= 266 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 267 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 268 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 269 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 270 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 271 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 272 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 273 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 274 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 275 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 276 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 277 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 278 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 279 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 280 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 281 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 282 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= 283 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= 284 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 285 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 286 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 287 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 288 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 289 | github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= 290 | github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 291 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 292 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 293 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 294 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 295 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 296 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 297 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 298 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 299 | github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= 300 | github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 301 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= 302 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 303 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 304 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 305 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 306 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 307 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 308 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 309 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 310 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 311 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 312 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 313 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 314 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 315 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 316 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 317 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 318 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 319 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 320 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 321 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 322 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 323 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 324 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 325 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 326 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 327 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 328 | github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= 329 | github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= 330 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 331 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 332 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 333 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 334 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 335 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 336 | github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= 337 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 338 | github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= 339 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= 340 | github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= 341 | github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 342 | github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= 343 | github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= 344 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 345 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 346 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 347 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 348 | github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= 349 | github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= 350 | github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= 351 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 352 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 353 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 354 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 355 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 356 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 357 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 358 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 359 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 360 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 361 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 362 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 363 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 364 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 365 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 366 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 367 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 368 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 369 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 370 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 371 | go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 372 | go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= 373 | go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 374 | go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 375 | go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= 376 | go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI= 377 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 378 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 379 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 380 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 381 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 382 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 383 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 384 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 385 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 386 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 387 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 388 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 389 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 390 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 391 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 392 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 393 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 394 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 395 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 396 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 397 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 398 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 399 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 400 | golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 401 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 402 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 403 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 404 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 405 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 406 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 407 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 408 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 409 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 410 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 411 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 412 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 413 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 414 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 415 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 416 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 417 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 418 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 419 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 420 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 421 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 422 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 423 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 424 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 425 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 426 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 427 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 428 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 429 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 430 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 431 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 432 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 433 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 434 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 435 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 436 | golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= 437 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 438 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 439 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 440 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 441 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 442 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 443 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 444 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 445 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 446 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 447 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 448 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 449 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 450 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 451 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 452 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 453 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 454 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 455 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 456 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 457 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 458 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 459 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 460 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 461 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 462 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 463 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 464 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 465 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 466 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 467 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 468 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 469 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 470 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 471 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 472 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 473 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 474 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 475 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 476 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= 477 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 478 | golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 479 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 480 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 481 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 482 | golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 483 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 484 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 485 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 486 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 487 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 488 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 489 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 490 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 491 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 492 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 493 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 494 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 495 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 496 | golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 497 | golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 498 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 499 | golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 500 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 501 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 502 | golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 503 | golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 504 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 505 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 506 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 507 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 508 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 509 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 510 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 511 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 512 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 513 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 514 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 515 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 516 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 517 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 518 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 519 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 520 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 521 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 522 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 537 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 538 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 539 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 540 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 541 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 542 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 543 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 544 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 545 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 546 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 547 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 548 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 549 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 550 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 551 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 552 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 553 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 554 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 555 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 556 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 557 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 558 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 559 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 560 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 561 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 562 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 563 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 564 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 565 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 566 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 567 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 568 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 569 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 570 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 571 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 572 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 573 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 574 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 575 | golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 576 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 577 | golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 578 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 579 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 580 | golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 581 | golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 582 | golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 583 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 584 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 585 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 586 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 587 | golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 588 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 589 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= 590 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 591 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 592 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 593 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 594 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 595 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 596 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 597 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 598 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 599 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 600 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 601 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 602 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 603 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 604 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 605 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 606 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 607 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 608 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 609 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 610 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 611 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 612 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 613 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 614 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 615 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 616 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 617 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 618 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 619 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 620 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 621 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 622 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 623 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 624 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 625 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 626 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 627 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 628 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 629 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 630 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 631 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 632 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 633 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 634 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 635 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 636 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 637 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 638 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 639 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 640 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 641 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 642 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 643 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 644 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 645 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 646 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 647 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 648 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 649 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 650 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 651 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 652 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 653 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 654 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 655 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 656 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 657 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 658 | golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 659 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 660 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 661 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 662 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 663 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 664 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 665 | golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 666 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 667 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 668 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 669 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 670 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 671 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 672 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 673 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 674 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 675 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 676 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 677 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 678 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 679 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 680 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 681 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 682 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 683 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 684 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 685 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 686 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 687 | google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= 688 | google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= 689 | google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= 690 | google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= 691 | google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= 692 | google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 693 | google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 694 | google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= 695 | google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= 696 | google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= 697 | google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= 698 | google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= 699 | google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= 700 | google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= 701 | google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= 702 | google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= 703 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 704 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 705 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 706 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 707 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 708 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 709 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 710 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 711 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 712 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 713 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 714 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 715 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 716 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 717 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 718 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 719 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 720 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 721 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 722 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 723 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 724 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 725 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 726 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 727 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 728 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 729 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 730 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 731 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 732 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 733 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 734 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 735 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 736 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 737 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 738 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 739 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 740 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 741 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 742 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 743 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 744 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 745 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 746 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 747 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 748 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 749 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 750 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 751 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 752 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= 753 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 754 | google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 755 | google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 756 | google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= 757 | google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 758 | google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 759 | google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 760 | google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 761 | google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= 762 | google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 763 | google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 764 | google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 765 | google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 766 | google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 767 | google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 768 | google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 769 | google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 770 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 771 | google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 772 | google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 773 | google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 774 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 775 | google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 776 | google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 777 | google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 778 | google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 779 | google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 780 | google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 781 | google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 782 | google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= 783 | google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 784 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 785 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 786 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 787 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 788 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 789 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 790 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 791 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 792 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 793 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 794 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 795 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 796 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 797 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 798 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 799 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 800 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 801 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 802 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 803 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 804 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 805 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 806 | google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 807 | google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 808 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 809 | google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 810 | google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 811 | google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 812 | google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= 813 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 814 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 815 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 816 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 817 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 818 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 819 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 820 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 821 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 822 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 823 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 824 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 825 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 826 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 827 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 828 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 829 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 830 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 831 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 832 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 833 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 834 | gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 835 | gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= 836 | gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 837 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 838 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 839 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 840 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 841 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 842 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 843 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 844 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 845 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 846 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 847 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 848 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 849 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 850 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 851 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 852 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 853 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 854 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 855 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 856 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 857 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 858 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 859 | --------------------------------------------------------------------------------