├── .gitignore ├── go.work ├── cmd ├── testdata │ └── dingding.json ├── root_test.go ├── argoworkflow │ ├── README.md │ ├── Dockerfile │ ├── template │ │ ├── workflow-output.go │ │ ├── util.go │ │ ├── comment.go │ │ ├── util_test.go │ │ └── comment_test.go │ ├── plugin.yaml │ ├── gogit-executor-plugin-configmap.yaml │ ├── main_test.go │ ├── workflow_output_test.go │ ├── workflow_output.go │ ├── go.mod │ ├── main.go │ └── go.sum ├── root.go ├── comment.go ├── status_test.go ├── checkout_test.go ├── pull_request_test.go ├── msg.go ├── status.go ├── pull_request.go └── checkout.go ├── main.go ├── codecov.yml ├── .goreleaser.yaml ├── .github ├── dependabot.yml └── workflows │ ├── release.yaml │ └── pull-request.yaml ├── Dockerfile ├── pkg ├── oneapi │ └── types.go ├── error.go ├── type.go ├── git_test.go ├── type_test.go ├── error_test.go └── git.go ├── Makefile ├── LICENSE ├── go.mod ├── README.md └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | dist/ 3 | .idea/ 4 | coverage.out 5 | -------------------------------------------------------------------------------- /go.work: -------------------------------------------------------------------------------- 1 | go 1.22 2 | 3 | use . 4 | use ./cmd/argoworkflow 5 | -------------------------------------------------------------------------------- /cmd/testdata/dingding.json: -------------------------------------------------------------------------------- 1 | {"msgtype": "text", "text": {"content": "hello world"}} -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/linuxsuren/gogit/cmd" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | if err := cmd.NewRootCommand().Execute(); err != nil { 10 | os.Exit(1) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cmd/root_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestNewRootCommand(t *testing.T) { 9 | c := NewRootCommand() 10 | assert.NotNil(t, c) 11 | assert.Equal(t, "gogit", c.Use) 12 | } 13 | -------------------------------------------------------------------------------- /cmd/argoworkflow/README.md: -------------------------------------------------------------------------------- 1 | 2 | # gogit 3 | 4 | * Needs: 5 | * Image: ghcr.io/linuxsuren/workflow-executor-gogit:master 6 | 7 | 8 | 9 | Install: 10 | 11 | kubectl apply -f gogit-executor-plugin-configmap.yaml 12 | 13 | Uninstall: 14 | 15 | kubectl delete cm gogit-executor-plugin 16 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.com/docs/ignoring-paths 2 | 3 | # https://docs.codecov.com/docs/commit-status 4 | # it's hard to have a coverage for some code lines 5 | coverage: 6 | status: 7 | project: 8 | default: 9 | target: auto 10 | threshold: 0.1% 11 | patch: 12 | default: 13 | target: auto 14 | threshold: 0% 15 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | - CGO_ENABLED=0 4 | goos: 5 | - linux 6 | - windows 7 | - darwin 8 | archives: 9 | - name_template: "{{ .Binary }}-{{ .Os }}-{{ .Arch }}" 10 | format_overrides: 11 | - goos: windows 12 | format: zip 13 | files: 14 | - README.md 15 | snapshot: 16 | name_template: "{{ incpatch .Version }}-next" 17 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | // NewRootCommand returns the root command 6 | func NewRootCommand() (c *cobra.Command) { 7 | c = &cobra.Command{ 8 | Use: "gogit", 9 | Short: "Git client across Gitlab/GitHub", 10 | } 11 | 12 | c.AddCommand(newCheckoutCommand(), 13 | newStatusCmd(), newCommentCommand(), 14 | newPullRequestCmd(), newCommitCmd()) 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 as builder 2 | 3 | WORKDIR /workspace 4 | COPY cmd/ cmd 5 | COPY pkg/ pkg 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | COPY main.go main.go 9 | 10 | RUN go mod download 11 | RUN CGO_ENABLED=0 go build -ldflags "-w -s" -o gogit 12 | 13 | FROM alpine:3.10 14 | 15 | LABEL "repository"="https://github.com/linuxsuren/gogit" 16 | LABEL "homepage"="https://github.com/linuxsuren/gogit" 17 | LABEL "maintainer"="Rick" 18 | LABEL "Name"="A tool for sending build status to git providers" 19 | 20 | COPY --from=builder /workspace/gogit /usr/bin/gogit 21 | RUN apk add ca-certificates 22 | 23 | ENTRYPOINT ["gogit"] 24 | -------------------------------------------------------------------------------- /cmd/argoworkflow/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18 as builder 2 | ARG GOPROXY=direct 3 | 4 | WORKDIR /workspace 5 | COPY . . 6 | 7 | RUN go mod download 8 | RUN GOPROXY=${GOPROXY} CGO_ENABLED=0 go build -ldflags "-w -s" -o workflow-executor-gogit 9 | 10 | FROM alpine:3.10 11 | 12 | LABEL "repository"="https://github.com/linuxsuren/gogit" 13 | LABEL "homepage"="https://github.com/linuxsuren/gogit" 14 | LABEL "maintainer"="Rick" 15 | LABEL "Name"="A tool for sending build status to git providers" 16 | 17 | COPY --from=builder /workspace/workflow-executor-gogit /usr/bin/workflow-executor-gogit 18 | RUN apk add ca-certificates 19 | 20 | CMD ["workflow-executor-gogit"] 21 | -------------------------------------------------------------------------------- /cmd/argoworkflow/template/workflow-output.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // OutputObject represents a output object 4 | type OutputObject struct { 5 | Kind OutputObjectKind 6 | File string 7 | FileName string 8 | Value string 9 | } 10 | 11 | // OutputObjectKind represents the type of the outout object. 12 | // This is a type alias of string. 13 | type OutputObjectKind string 14 | 15 | const ( 16 | // FileOutput represetnts a file path 17 | FileOutput OutputObjectKind = "file" 18 | // ValueOutput represents a string value 19 | ValueOutput OutputObjectKind = "string" 20 | // MarkdownOutput represents a markdown format string value 21 | MarkdownOutput OutputObjectKind = "markdown" 22 | ) 23 | -------------------------------------------------------------------------------- /cmd/argoworkflow/plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ExecutorPlugin 3 | metadata: 4 | name: gogit 5 | spec: 6 | sidecar: 7 | container: 8 | image: ghcr.io/linuxsuren/workflow-executor-gogit:master 9 | args: 10 | - --provider 11 | - gitlab 12 | # - --server 13 | # - http://ip:port 14 | # - --username 15 | # - linuxsuren 16 | # - --token 17 | # - your-token 18 | name: gogit-executor-plugin 19 | ports: 20 | - containerPort: 4355 21 | securityContext: 22 | runAsNonRoot: true 23 | runAsUser: 65534 # nobody 24 | resources: 25 | requests: 26 | memory: "64Mi" 27 | cpu: "250m" 28 | limits: 29 | memory: "128Mi" 30 | cpu: "500m" 31 | -------------------------------------------------------------------------------- /cmd/argoworkflow/gogit-executor-plugin-configmap.yaml: -------------------------------------------------------------------------------- 1 | # This is an auto-generated file. DO NOT EDIT 2 | apiVersion: v1 3 | data: 4 | sidecar.automountServiceAccountToken: "true" 5 | sidecar.container: | 6 | args: 7 | - --provider 8 | - gitlab 9 | image: ghcr.io/linuxsuren/workflow-executor-gogit:master 10 | command: 11 | - workflow-executor-gogit 12 | name: gogit-executor-plugin 13 | ports: 14 | - containerPort: 3001 15 | resources: 16 | limits: 17 | cpu: 500m 18 | memory: 128Mi 19 | requests: 20 | cpu: 250m 21 | memory: 64Mi 22 | securityContext: 23 | allowPrivilegeEscalation: true 24 | runAsNonRoot: true 25 | runAsUser: 65534 26 | kind: ConfigMap 27 | metadata: 28 | creationTimestamp: null 29 | labels: 30 | workflows.argoproj.io/configmap-type: ExecutorPlugin 31 | name: gogit-executor-plugin 32 | namespace: argo 33 | -------------------------------------------------------------------------------- /pkg/oneapi/types.go: -------------------------------------------------------------------------------- 1 | package oneapi 2 | 3 | type ChatMessage struct { 4 | Content string `json:"content"` 5 | Role string `json:"role"` 6 | } 7 | 8 | type ChatPayload struct { 9 | Messages []ChatMessage `json:"messages"` 10 | Model string `json:"model"` 11 | } 12 | 13 | type ChatResponse struct { 14 | Created int64 `json:"created"` 15 | ID string `json:"id"` 16 | Object string `json:"object"` 17 | Usage map[string]int `json:"usage"` 18 | Choices []ChatResponseChoice `json:"choices"` 19 | } 20 | 21 | type ChatResponseChoice struct { 22 | FinishReason string `json:"finish_reason"` 23 | Index int `json:"index"` 24 | Message ChatMessage `json:"message"` 25 | } 26 | 27 | func NewChatPayload(message string, model string) ChatPayload { 28 | return ChatPayload{ 29 | Messages: []ChatMessage{{Content: message, Role: "user"}}, 30 | Model: model, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/gogit 3 | plugin-build: 4 | cd cmd/argoworkflow && CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/gogit-executor-plugin 5 | copy: build 6 | cp bin/gogit /usr/local/bin 7 | test: 8 | go test ./... -coverprofile coverage.out 9 | pre-commit: test build plugin-build 10 | goreleaser: 11 | goreleaser build --snapshot --rm-dist 12 | image: 13 | docker build . -t ghcr.io/linuxsuren/gogit 14 | build-workflow-executor-gogit: 15 | cd cmd/argoworkflow && CGO_ENABLED=0 go build -ldflags "-w -s" -o ../../bin/workflow-executor-gogit 16 | image-workflow-executor-gogit: 17 | cd cmd/argoworkflow && docker build . -t ghcr.io/linuxsuren/workflow-executor-gogit:dev --build-arg GOPROXY=https://goproxy.io,direct 18 | push-image-workflow-executor-gogit: image-workflow-executor-gogit 19 | docker push ghcr.io/linuxsuren/workflow-executor-gogit:dev 20 | test-workflow-executor-gogit: 21 | cd cmd/argoworkflow && go test ./... 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 Rick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmd/argoworkflow/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestEmptyThen(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | first string 14 | second string 15 | expect string 16 | }{{ 17 | name: "first is empty", 18 | first: "", 19 | second: "second", 20 | expect: "second", 21 | }, { 22 | name: "first is blank", 23 | first: " ", 24 | second: "second", 25 | expect: " ", 26 | }} 27 | for i, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | result := EmptyThen(tt.first, tt.second) 30 | assert.Equal(t, tt.expect, result, "not match with", i) 31 | }) 32 | } 33 | } 34 | 35 | func TestDeleteRetryNodes(t *testing.T) { 36 | wf := &wfv1.Workflow{ 37 | Status: wfv1.WorkflowStatus{ 38 | Nodes: map[string]wfv1.NodeStatus{}, 39 | }, 40 | } 41 | wf.Status.Nodes["test"] = wfv1.NodeStatus{DisplayName: "test"} 42 | wf.Status.Nodes["test-0"] = wfv1.NodeStatus{DisplayName: "test-(0)"} 43 | wf.Status.Nodes["test-10"] = wfv1.NodeStatus{DisplayName: "test-(10)"} 44 | wf.Status.Nodes["test-100"] = wfv1.NodeStatus{DisplayName: "test-(100)"} 45 | 46 | deleteRetryNodes(wf) 47 | assert.Equal(t, 1, len(wf.Status.Nodes)) 48 | } 49 | -------------------------------------------------------------------------------- /cmd/comment.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/linuxsuren/gogit/pkg" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func newCommentCommand() (c *cobra.Command) { 9 | opt := &commentOption{} 10 | c = &cobra.Command{ 11 | Use: "comment", 12 | Short: "Create a comment against the pull request", 13 | Example: `gogit comment --provider github --username linuxsuren --repo test --pr 45 --token $GITHUB_TOKEN -m LGTM`, 14 | Aliases: []string{"c"}, 15 | PreRunE: opt.preRunE, 16 | RunE: opt.runE, 17 | } 18 | 19 | opt.addFlags(c) 20 | flags := c.Flags() 21 | flags.StringVarP(&opt.message, "message", "m", "", "The comment body") 22 | flags.StringVarP(&opt.identity, "identity", "", pkg.CommentEndMarker, "The identity for matching exiting comment") 23 | return 24 | } 25 | 26 | func (o *commentOption) runE(c *cobra.Command, args []string) (err error) { 27 | err = pkg.CreateComment(c.Context(), pkg.RepoInformation{ 28 | Provider: o.provider, 29 | Server: o.server, 30 | Owner: o.owner, 31 | Repo: o.repo, 32 | PrNumber: o.pr, 33 | Username: o.username, 34 | Token: o.token, 35 | }, o.message, o.identity) 36 | return 37 | } 38 | 39 | func (o *commentOption) preRunE(c *cobra.Command, args []string) (err error) { 40 | o.preHandle() 41 | return 42 | } 43 | 44 | type commentOption struct { 45 | gitProviderOption 46 | message string 47 | identity string 48 | } 49 | -------------------------------------------------------------------------------- /cmd/argoworkflow/template/util.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | "time" 7 | 8 | "github.com/Masterminds/sprig" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // RenderTemplate renders the template, and returns the result text 13 | func RenderTemplate(text string, object interface{}) (result string, err error) { 14 | var tpl *template.Template 15 | if tpl, err = template.New("temp").Funcs(sprig.TxtFuncMap()).Funcs(map[string]any{ 16 | "durationStr": durationStr, 17 | "duration": Duration, 18 | "get": getValue, 19 | }).Parse(text); err != nil { 20 | return 21 | } 22 | 23 | data := new(bytes.Buffer) 24 | if err = tpl.Execute(data, object); err == nil { 25 | result = data.String() 26 | } 27 | return 28 | } 29 | 30 | func getValue(data map[string]string, key string) string { 31 | return data[key] 32 | } 33 | 34 | func durationStr(finishedAt, startedAt string) string { 35 | finishedTime, _ := time.Parse(time.RFC3339, finishedAt) 36 | startedTime, _ := time.Parse(time.RFC3339, startedAt) 37 | return Duration(v1.Time{Time: finishedTime}, v1.Time{Time: startedTime}) 38 | } 39 | 40 | // Duration returns the duration time string 41 | func Duration(finishedAt, startedAt v1.Time) string { 42 | if finishedAt.Before(&startedAt) { 43 | // have not finished 44 | finishedAt = v1.Now() 45 | } 46 | duration := finishedAt.Sub(startedAt.Time) 47 | return duration.String() 48 | } 49 | -------------------------------------------------------------------------------- /cmd/argoworkflow/template/comment.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // CommentTemplate is the default comment template 4 | const CommentTemplate = ` 5 | 6 | {{ $link := (get .Annotations "workflow.link") }} 7 | [{{.Spec.WorkflowTemplateRef.Name}}]({{get .Annotations "workflow.templatelink"}}) is {{.Status.Phase}}. It started from {{date "01-02 15:04" .Status.StartedAt.Time}}, and took {{duration .Status.FinishedAt .Status.StartedAt}}. Please check log output from [here]({{$link}}). 8 | 9 | | Stage | Status | Duration | 10 | |---|---|---| 11 | {{- range $_, $status := .Status.Nodes}} 12 | | [{{$status.DisplayName}}]({{$link}}) | {{if eq $status.Phase "Failed"}}:broken_heart:{{else if eq $status.Phase "Succeeded"}}:white_check_mark:{{else if eq $status.Phase "Running"}}:hourglass_flowing_sand:{{end}} {{$status.Phase}} | {{duration $status.FinishedAt $status.StartedAt}} | 13 | {{- end}} 14 | ` 15 | 16 | const OutputsTemplate = ` 17 | 18 | Please feel free to check the following outputs: 19 | 20 | | Output | 21 | |---| 22 | {{- range $name, $output := .}} 23 | {{- if eq "file" (toString $output.Kind)}} 24 | | [{{$output.FileName}}]({{$output.File}}) | 25 | {{- else if eq "string" (toString $output.Kind)}} 26 | | {{$name}}: {{$output.Value}} | 27 | {{- end}} 28 | {{- end}} 29 | 30 | {{ range $name, $output := .}} 31 | {{- if eq "markdown" (toString $output.Kind)}} 32 | {{$output.Value}} 33 | {{- end}} 34 | {{- end}} 35 | ` 36 | -------------------------------------------------------------------------------- /pkg/error.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package pkg 26 | 27 | import "fmt" 28 | 29 | func WrapError(err error, msg string, args ...any) error { 30 | if err != nil { 31 | err = fmt.Errorf(msg, append(args, err)...) 32 | } 33 | return err 34 | } 35 | 36 | func IgnoreError(err error, msg string) error { 37 | if err == nil || err.Error() == msg { 38 | return nil 39 | } else { 40 | return err 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pkg/type.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023-2024 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package pkg 26 | 27 | import "fmt" 28 | 29 | type RepoInformation struct { 30 | Provider string 31 | Server string 32 | Owner string 33 | Repo string 34 | PrNumber int 35 | 36 | Username, Token string 37 | 38 | Status string 39 | Target string 40 | Label string 41 | Description string 42 | } 43 | 44 | // String returns the human-readable string 45 | func (r RepoInformation) String() string { 46 | return r.GetRepoPath() 47 | } 48 | 49 | // GetRepoPath returns the repository path 50 | func (r RepoInformation) GetRepoPath() string { 51 | return fmt.Sprintf("%s/%s", r.Owner, r.Repo) 52 | } 53 | -------------------------------------------------------------------------------- /cmd/argoworkflow/workflow_output_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" 8 | "github.com/linuxsuren/gogit/argoworkflow/template" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGetOutputsWithTarget(t *testing.T) { 13 | emptyWf := &wfv1.Workflow{ 14 | Status: wfv1.WorkflowStatus{ 15 | Nodes: wfv1.Nodes{ 16 | "test": wfv1.NodeStatus{}, 17 | }, 18 | }, 19 | } 20 | assert.Equal(t, 0, len(GetOutputsWithTarget(emptyWf, ""))) 21 | 22 | wf := &wfv1.Workflow{ 23 | Status: wfv1.WorkflowStatus{ 24 | Nodes: wfv1.Nodes{ 25 | "test": wfv1.NodeStatus{ 26 | Name: "test", 27 | Outputs: &wfv1.Outputs{ 28 | Artifacts: wfv1.Artifacts{{ 29 | Path: "test/install.yaml", 30 | Name: "test", 31 | }}, 32 | }, 33 | }, 34 | "report_md": wfv1.NodeStatus{ 35 | Name: "report_md", 36 | Outputs: &wfv1.Outputs{ 37 | Parameters: []wfv1.Parameter{{ 38 | Name: "report_md", 39 | Value: wfv1.AnyStringPtr("## Report\n"), 40 | }}, 41 | }, 42 | }, 43 | }, 44 | }, 45 | } 46 | 47 | outputs := GetOutputsWithTarget(wf, "https://github.com") 48 | for _, output := range outputs { 49 | if output.Kind == "file" { 50 | assert.Truef(t, strings.HasPrefix(output.File, "https://github.com"), output.File) 51 | } else if output.Kind == "markdown" { 52 | assert.Equal(t, "## Report\n", output.Value) 53 | } 54 | } 55 | assert.Equal(t, map[string]template.OutputObject{ 56 | "test-test": { 57 | Kind: template.FileOutput, 58 | File: "https://github.com/artifact-files//workflows///outputs/test", 59 | FileName: "test/install.yaml", 60 | }, 61 | "report_md-report_md": { 62 | Kind: template.MarkdownOutput, 63 | Value: "## Report\n", 64 | }, 65 | }, outputs) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/git_test.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/jenkins-x/go-scm/scm" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestReconcile(t *testing.T) { 12 | type args struct { 13 | ctx context.Context 14 | repoInfo RepoInformation 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{{ 21 | name: "pr number is -1", 22 | args: args{ 23 | repoInfo: RepoInformation{ 24 | PrNumber: -1, 25 | }, 26 | }, 27 | wantErr: false, 28 | }} 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | if err := NewMaker(tt.args.ctx, tt.args.repoInfo); (err != nil) != tt.wantErr { 32 | t.Errorf("Reconcile() error = %v, wantErr %v", err, tt.wantErr) 33 | } 34 | }) 35 | } 36 | } 37 | 38 | func TestNewStatusMaker(t *testing.T) { 39 | maker := NewStatusMaker("", "") 40 | assert.NotNil(t, maker) 41 | assert.NotNil(t, maker.expirationCheck) 42 | assert.False(t, maker.expirationCheck(nil, nil)) 43 | assert.False(t, maker.expirationCheck(&scm.Status{State: scm.StateSuccess}, 44 | &scm.StatusInput{State: scm.StateError})) 45 | assert.True(t, maker.expirationCheck(&scm.Status{State: scm.StateSuccess}, 46 | &scm.StatusInput{State: scm.StateSuccess})) 47 | } 48 | 49 | func TestGetCommentIDs(t *testing.T) { 50 | tests := []struct { 51 | comments []*scm.Comment 52 | expect []int 53 | }{{ 54 | comments: []*scm.Comment{{ 55 | Body: "start", 56 | ID: 1, 57 | }, { 58 | Body: "start - end", 59 | ID: 2, 60 | }, { 61 | Body: "other - end", 62 | ID: 3, 63 | }, { 64 | Body: "other", 65 | ID: 4, 66 | }, { 67 | Body: "good - end", 68 | ID: 5, 69 | }}, 70 | expect: []int{2, 3, 5}, 71 | }} 72 | for _, tt := range tests { 73 | assert.Equal(t, tt.expect, getCommentIDs(tt.comments, "end")) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /cmd/argoworkflow/template/util_test.go: -------------------------------------------------------------------------------- 1 | package template_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | "time" 7 | 8 | "github.com/linuxsuren/gogit/argoworkflow/template" 9 | "github.com/stretchr/testify/assert" 10 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | func TestRenderTemplate(t *testing.T) { 14 | tests := []struct { 15 | name string 16 | template string 17 | object interface{} 18 | expectErr bool 19 | expectResult string 20 | }{{ 21 | name: "normal", 22 | template: `{{.}}`, 23 | object: "hello-template", 24 | expectErr: false, 25 | expectResult: "hello-template", 26 | }, { 27 | name: "template syntax error", 28 | template: `{{.`, 29 | object: "", 30 | expectErr: true, 31 | expectResult: "", 32 | }} 33 | for _, tt := range tests { 34 | t.Run(tt.name, func(t *testing.T) { 35 | result, err := template.RenderTemplate(tt.template, tt.object) 36 | if tt.expectErr { 37 | assert.NotNil(t, err) 38 | } else { 39 | assert.Nil(t, err) 40 | } 41 | assert.Equal(t, tt.expectResult, result) 42 | }) 43 | } 44 | } 45 | 46 | func TestDuration(t *testing.T) { 47 | now := v1.Now() 48 | 49 | tests := []struct { 50 | name string 51 | finishedAt v1.Time 52 | startedAt v1.Time 53 | verify func(t *testing.T, result string) 54 | }{{ 55 | name: "normal", 56 | finishedAt: now, 57 | startedAt: v1.Time{Time: now.Add(time.Second * -3)}, 58 | verify: func(t *testing.T, result string) { 59 | assert.Equal(t, "3s", result) 60 | }, 61 | }, { 62 | name: "have not finished", 63 | startedAt: v1.Time{ 64 | Time: now.Add(time.Millisecond * -1), 65 | }, 66 | verify: func(t *testing.T, result string) { 67 | assert.True(t, strings.HasSuffix(result, "ms"), result) 68 | }, 69 | }} 70 | for _, tt := range tests { 71 | result := template.Duration(tt.finishedAt, tt.startedAt) 72 | tt.verify(t, result) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/type_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023-2024 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package pkg 26 | 27 | import "testing" 28 | 29 | func TestRepoInformation_getRepoPath(t *testing.T) { 30 | type fields struct { 31 | Owner string 32 | Repo string 33 | } 34 | tests := []struct { 35 | name string 36 | fields fields 37 | want string 38 | }{{ 39 | name: "normal case", 40 | fields: fields{ 41 | Owner: "owner", 42 | Repo: "repo", 43 | }, 44 | want: "owner/repo", 45 | }} 46 | for _, tt := range tests { 47 | t.Run(tt.name, func(t *testing.T) { 48 | r := RepoInformation{ 49 | Owner: tt.fields.Owner, 50 | Repo: tt.fields.Repo, 51 | } 52 | if got := r.GetRepoPath(); got != tt.want { 53 | t.Errorf("GetRepoPath() = %v, want %v", got, tt.want) 54 | } 55 | if got := r.String(); got != tt.want { 56 | t.Errorf("String() = %v, want %v", got, tt.want) 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/error_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package pkg_test 26 | 27 | import ( 28 | "errors" 29 | "testing" 30 | 31 | "github.com/linuxsuren/gogit/pkg" 32 | "github.com/stretchr/testify/assert" 33 | ) 34 | 35 | func TestWrapError(t *testing.T) { 36 | tests := []struct { 37 | err error 38 | msg string 39 | args []any 40 | expect error 41 | }{{ 42 | err: nil, 43 | expect: nil, 44 | }, { 45 | err: defaultErr, 46 | msg: "wrap %v", 47 | expect: errors.New("wrap fake"), 48 | }} 49 | for _, tt := range tests { 50 | err := pkg.WrapError(tt.err, tt.msg, tt.args...) 51 | assert.Equal(t, tt.expect, err) 52 | } 53 | } 54 | 55 | func TestIgnoreError(t *testing.T) { 56 | assert.Equal(t, nil, pkg.IgnoreError(nil, "fake")) 57 | assert.Equal(t, nil, pkg.IgnoreError(defaultErr, "fake")) 58 | assert.Equal(t, defaultErr, pkg.IgnoreError(defaultErr, "other")) 59 | } 60 | 61 | var defaultErr = errors.New("fake") 62 | -------------------------------------------------------------------------------- /cmd/argoworkflow/workflow_output.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" 8 | "github.com/linuxsuren/gogit/argoworkflow/template" 9 | ) 10 | 11 | // GetOutputs returns the outputs in map format. 12 | // Key is the node name, value is the output link. 13 | // The output link address does not have the host part. 14 | func GetOutputs(wf *wfv1.Workflow) (outputs map[string]template.OutputObject) { 15 | outputs = map[string]template.OutputObject{} 16 | nodes := wf.Status.Nodes 17 | for _, node := range nodes { 18 | if node.Outputs == nil { 19 | continue 20 | } 21 | 22 | var outputObject *template.OutputObject 23 | for _, artifact := range node.Outputs.Artifacts { 24 | key := fmt.Sprintf("%s-%s", node.Name, artifact.Name) 25 | 26 | if artifact.Path != "" { 27 | // TODO assume this is a artifact file 28 | outputObject = &template.OutputObject{ 29 | Kind: template.FileOutput, 30 | FileName: artifact.Path, 31 | File: fmt.Sprintf("/artifact-files/%s/workflows/%s/%s/outputs/%s", wf.Namespace, wf.Name, node.ID, artifact.Name), 32 | } 33 | outputs[key] = *outputObject 34 | } 35 | } 36 | 37 | for _, param := range node.Outputs.Parameters { 38 | key := fmt.Sprintf("%s-%s", node.Name, param.Name) 39 | 40 | if param.Value != nil && param.Value.String() != "" { 41 | outputObject = &template.OutputObject{ 42 | Kind: template.ValueOutput, 43 | Value: param.Value.String(), 44 | } 45 | outputs[key] = *outputObject 46 | } 47 | } 48 | } 49 | return 50 | } 51 | 52 | // GetOutputsWithTarget returns the outputs which has the target server address 53 | func GetOutputsWithTarget(wf *wfv1.Workflow, target string) map[string]template.OutputObject { 54 | outputs := GetOutputs(wf) 55 | // add the server address 56 | if len(outputs) > 0 { 57 | for i, output := range outputs { 58 | if output.File != "" { 59 | output.File = target + output.File 60 | } else if strings.HasSuffix(i, "_md") { 61 | output.Kind = template.MarkdownOutput 62 | } 63 | outputs[i] = output 64 | } 65 | } 66 | return outputs 67 | } 68 | -------------------------------------------------------------------------------- /cmd/status_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestStatus(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | opt *statusOption 15 | status string 16 | expectStatus string 17 | }{{ 18 | name: "Capital letter", 19 | status: "Running", 20 | expectStatus: "running", 21 | }, { 22 | name: "Special token: Succeeded", 23 | status: "Succeeded", 24 | expectStatus: "success", 25 | }} 26 | for i := range tests { 27 | tt := tests[i] 28 | t.Run(tt.name, func(t *testing.T) { 29 | opt := &statusOption{status: tt.status} 30 | _ = opt.preRunE(nil, nil) 31 | assert.Equal(t, tt.expectStatus, opt.status) 32 | }) 33 | } 34 | } 35 | 36 | func TestTokenFromFile(t *testing.T) { 37 | tests := []struct { 38 | name string 39 | opt *statusOption 40 | token string 41 | prepare func() string 42 | expect string 43 | }{{ 44 | name: "plain token", 45 | token: "token", 46 | expect: "token", 47 | }, { 48 | name: "token from file", 49 | prepare: func() string { 50 | f, _ := os.CreateTemp(os.TempDir(), "token") 51 | _, _ = io.WriteString(f, "token") 52 | return f.Name() 53 | }, 54 | expect: "token", 55 | }} 56 | for i := range tests { 57 | tt := tests[i] 58 | t.Run(tt.name, func(t *testing.T) { 59 | opt := &statusOption{gitProviderOption: gitProviderOption{token: tt.token}} 60 | if tt.prepare != nil { 61 | token := tt.prepare() 62 | opt.token = "file://" + token 63 | defer func() { 64 | _ = os.RemoveAll(token) 65 | }() 66 | } 67 | _ = opt.preRunE(nil, nil) 68 | assert.Equal(t, tt.expect, opt.token) 69 | }) 70 | } 71 | } 72 | 73 | func TestFlags(t *testing.T) { 74 | cmd := newStatusCmd() 75 | assert.Equal(t, "status", cmd.Use) 76 | flags := cmd.Flags() 77 | assert.NotNil(t, flags.Lookup("provider")) 78 | assert.NotNil(t, flags.Lookup("server")) 79 | assert.NotNil(t, flags.Lookup("owner")) 80 | assert.NotNil(t, flags.Lookup("repo")) 81 | assert.NotNil(t, flags.Lookup("pr")) 82 | assert.NotNil(t, flags.Lookup("username")) 83 | assert.NotNil(t, flags.Lookup("token")) 84 | assert.NotNil(t, flags.Lookup("status")) 85 | assert.NotNil(t, flags.Lookup("label")) 86 | assert.NotNil(t, flags.Lookup("description")) 87 | } 88 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/linuxsuren/gogit 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/jenkins-x/go-scm v1.11.19 7 | github.com/linuxsuren/go-fake-runtime v0.0.4 8 | github.com/mitchellh/go-homedir v1.1.0 9 | github.com/spf13/cobra v1.6.1 10 | github.com/stretchr/testify v1.8.2 11 | ) 12 | 13 | require ( 14 | github.com/Microsoft/go-winio v0.5.2 // indirect 15 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect 16 | github.com/acomagu/bufpipe v1.0.3 // indirect 17 | github.com/emirpasic/gods v1.12.0 // indirect 18 | github.com/go-git/gcfg v1.5.0 // indirect 19 | github.com/go-git/go-billy/v5 v5.3.1 // indirect 20 | github.com/google/go-cmp v0.5.9 // indirect 21 | github.com/imdario/mergo v0.3.13 // indirect 22 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 23 | github.com/kevinburke/ssh_config v1.2.0 // indirect 24 | github.com/sergi/go-diff v1.1.0 // indirect 25 | github.com/xanzy/ssh-agent v0.3.1 // indirect 26 | gopkg.in/warnings.v0 v0.1.2 // indirect 27 | ) 28 | 29 | require ( 30 | code.gitea.io/sdk/gitea v0.14.0 // indirect 31 | github.com/bluekeyes/go-gitdiff v0.4.0 // indirect 32 | github.com/davecgh/go-spew v1.1.1 // indirect 33 | github.com/go-git/go-git-fixtures/v4 v4.3.1 // indirect 34 | github.com/go-git/go-git/v5 v5.4.2 35 | github.com/golang/protobuf v1.5.2 // indirect 36 | github.com/hashicorp/go-version v1.3.0 // indirect 37 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 38 | github.com/mitchellh/copystructure v1.2.0 // indirect 39 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 40 | github.com/pkg/errors v0.9.1 41 | github.com/pmezard/go-difflib v1.0.0 // indirect 42 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 // indirect 43 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect 44 | github.com/sirupsen/logrus v1.9.0 // indirect 45 | github.com/spf13/pflag v1.0.5 // indirect 46 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 47 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect 48 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect 49 | golang.org/x/sys v0.3.0 // indirect 50 | google.golang.org/appengine v1.6.7 // indirect 51 | google.golang.org/protobuf v1.28.1 // indirect 52 | gopkg.in/yaml.v3 v3.0.1 // indirect 53 | k8s.io/apimachinery v0.24.3 // indirect 54 | ) 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | env: 8 | REGISTRY: ghcr.io 9 | IMAGE_NAME: ${{ github.repository }} 10 | IMAGE_EXECUTOR_NAME: "linuxsuren/workflow-executor-gogit" 11 | 12 | jobs: 13 | goreleaser: 14 | runs-on: ubuntu-20.04 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3.0.0 18 | - name: Set up Go 19 | uses: actions/setup-go@v3 20 | with: 21 | go-version: 1.22 22 | - name: Run GoReleaser 23 | uses: goreleaser/goreleaser-action@v6 24 | with: 25 | version: '~> v2' 26 | args: release --clean 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_SECRETS }} 29 | image: 30 | runs-on: ubuntu-20.04 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3.0.0 34 | - name: Setup Docker buildx 35 | uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf 36 | - name: Log into registry ${{ env.REGISTRY }} 37 | if: github.event_name != 'pull_request' 38 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 39 | with: 40 | registry: ${{ env.REGISTRY }} 41 | username: ${{ github.actor }} 42 | password: ${{ secrets.GH_PUBLISH_SECRETS }} 43 | - name: Extract Docker metadata 44 | id: meta 45 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 46 | with: 47 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 48 | - name: Build and push Docker image 49 | id: build-and-push 50 | uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a 51 | with: 52 | context: . 53 | push: ${{ github.event_name != 'pull_request' }} 54 | tags: ${{ steps.meta.outputs.tags }} 55 | labels: ${{ steps.meta.outputs.labels }} 56 | cache-from: type=gha 57 | cache-to: type=gha,mode=max 58 | 59 | image-argo-workflow-executor: 60 | runs-on: ubuntu-20.04 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@v3.0.0 64 | - name: Setup Docker buildx 65 | uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf 66 | - name: Log into registry ${{ env.REGISTRY }} 67 | if: github.event_name != 'pull_request' 68 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 69 | with: 70 | registry: ${{ env.REGISTRY }} 71 | username: ${{ github.actor }} 72 | password: ${{ secrets.GH_PUBLISH_SECRETS }} 73 | - name: Extract Docker metadata 74 | id: meta 75 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 76 | with: 77 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_EXECUTOR_NAME }} 78 | - name: Build and push Docker image 79 | id: build-and-push 80 | uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a 81 | with: 82 | context: cmd/argoworkflow 83 | push: ${{ github.event_name != 'pull_request' }} 84 | tags: ${{ steps.meta.outputs.tags }} 85 | labels: ${{ steps.meta.outputs.labels }} 86 | cache-from: type=gha 87 | cache-to: type=gha,mode=max 88 | -------------------------------------------------------------------------------- /cmd/checkout_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_prRef(t *testing.T) { 10 | type args struct { 11 | pr int 12 | kind string 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | wantRef string 18 | }{{ 19 | name: "gitlab", 20 | args: args{ 21 | pr: 1, 22 | kind: "gitlab", 23 | }, 24 | wantRef: "refs/merge-requests/1/head:pr-1", 25 | }, { 26 | name: "unknown", 27 | args: args{ 28 | pr: 1, 29 | kind: "unknown", 30 | }, 31 | wantRef: "", 32 | }, { 33 | name: "github", 34 | args: args{ 35 | pr: 1, 36 | kind: "github", 37 | }, 38 | wantRef: "refs/pull/1/head:pr-1", 39 | }} 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | assert.Equalf(t, tt.wantRef, prRef(tt.args.pr, tt.args.kind), "prRef(%v, %v)", tt.args.pr, tt.args.kind) 43 | }) 44 | } 45 | } 46 | 47 | func Test_detectGitKind(t *testing.T) { 48 | type args struct { 49 | gitURL string 50 | } 51 | tests := []struct { 52 | name string 53 | args args 54 | wantKind string 55 | }{{ 56 | name: "github", 57 | args: args{ 58 | gitURL: "https://github.com/linuxsuren/gogit", 59 | }, 60 | wantKind: "github", 61 | }, { 62 | name: "gitlab", 63 | args: args{ 64 | gitURL: "git@10.121.218.82:demo/test.git", 65 | }, 66 | wantKind: "gitlab", 67 | }} 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | assert.Equalf(t, tt.wantKind, detectGitKind(tt.args.gitURL), "detectGitKind(%v)", tt.args.gitURL) 71 | }) 72 | } 73 | } 74 | 75 | func TestGetAuth(t *testing.T) { 76 | opt := &checkoutOption{ 77 | sshPrivateKey: "/tmp", 78 | } 79 | auth, err := opt.getAuth("git@fake.com") 80 | assert.Nil(t, auth) 81 | assert.NotNil(t, err) 82 | 83 | auth, err = opt.getAuth("fake.com") 84 | assert.Nil(t, auth) 85 | assert.Nil(t, err) 86 | } 87 | 88 | func TestPreRunE(t *testing.T) { 89 | const sampleGit = "https://github.com/linuxsuren/gogit" 90 | const anotherGit = "https://github.com/linuxsuren/gogit.git" 91 | 92 | tests := []struct { 93 | name string 94 | opt *checkoutOption 95 | args []string 96 | expectErr bool 97 | verify func(t *testing.T, opt *checkoutOption) 98 | }{{ 99 | name: "url is empty", 100 | opt: &checkoutOption{}, 101 | args: []string{sampleGit}, 102 | expectErr: false, 103 | verify: func(t *testing.T, opt *checkoutOption) { 104 | assert.Equal(t, sampleGit, opt.url) 105 | }, 106 | }, { 107 | name: "url is not empty", 108 | opt: &checkoutOption{url: anotherGit}, 109 | args: []string{sampleGit}, 110 | expectErr: false, 111 | verify: func(t *testing.T, opt *checkoutOption) { 112 | assert.Equal(t, anotherGit, opt.url) 113 | }, 114 | }, { 115 | name: "branch is fullname", 116 | opt: &checkoutOption{branch: "refs/heads/master"}, 117 | expectErr: false, 118 | verify: func(t *testing.T, opt *checkoutOption) { 119 | assert.Equal(t, "master", opt.branch) 120 | }, 121 | }} 122 | for _, tt := range tests { 123 | t.Run(tt.name, func(t *testing.T) { 124 | err := tt.opt.preRunE(nil, tt.args) 125 | if tt.expectErr { 126 | assert.NotNil(t, err) 127 | } else { 128 | assert.Nil(t, err) 129 | } 130 | tt.verify(t, tt.opt) 131 | }) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /cmd/pull_request_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package cmd 26 | 27 | import ( 28 | "io" 29 | "testing" 30 | 31 | "github.com/jenkins-x/go-scm/scm" 32 | "github.com/stretchr/testify/assert" 33 | ) 34 | 35 | func TestPullRequestCmd(t *testing.T) { 36 | t.Run("missing required flags", func(t *testing.T) { 37 | c := NewRootCommand() 38 | c.SetOut(io.Discard) 39 | 40 | c.SetArgs([]string{"pr", "--dingding-tokens", "a=b"}) 41 | err := c.Execute() 42 | assert.Error(t, err) 43 | assert.Contains(t, err.Error(), "required flag(s)") 44 | }) 45 | 46 | t.Run("invalid dingding token pairs", func(t *testing.T) { 47 | c := NewRootCommand() 48 | c.SetOut(io.Discard) 49 | 50 | c.SetArgs([]string{"pr", "--dingding-tokens", "a=b=c"}) 51 | err := c.Execute() 52 | assert.Error(t, err) 53 | assert.Contains(t, err.Error(), "invalid dingding token pair") 54 | }) 55 | 56 | t.Run("invalid git kind", func(t *testing.T) { 57 | c := NewRootCommand() 58 | c.SetOut(io.Discard) 59 | 60 | c.SetArgs([]string{"pr", "--provider", "invalid", "--pr=1", "--repo=xxx/xxx", "--token=token", "--username=xxx"}) 61 | err := c.Execute() 62 | assert.Error(t, err) 63 | assert.Contains(t, err.Error(), "Unsupported") 64 | }) 65 | 66 | t.Run("invalid pr number, do not skip", func(t *testing.T) { 67 | c := NewRootCommand() 68 | c.SetOut(io.Discard) 69 | 70 | c.SetArgs([]string{"pr", "--pr=-1", "--repo=xxx/xxx", "--token=token", "--username=xxx", "--skip-invalid-pr=false"}) 71 | err := c.Execute() 72 | assert.Error(t, err) 73 | assert.Contains(t, err.Error(), "invalid pr number") 74 | }) 75 | 76 | t.Run("skip invalid pr number", func(t *testing.T) { 77 | c := NewRootCommand() 78 | c.SetOut(io.Discard) 79 | 80 | c.SetArgs([]string{"pr", "--pr=-1", "--repo=xxx/xxx", "--token=token", "--username=xxx"}) 81 | err := c.Execute() 82 | assert.NoError(t, err) 83 | }) 84 | } 85 | 86 | func TestFormatMessage(t *testing.T) { 87 | tests := []struct { 88 | name string 89 | msg string 90 | pr *scm.PullRequest 91 | expect string 92 | }{{ 93 | name: "normal", 94 | msg: "hello {{.Title}}", 95 | pr: &scm.PullRequest{ 96 | Title: "world", 97 | }, 98 | expect: "hello world", 99 | }, { 100 | name: "invalid template syntax", 101 | msg: "hello {{.Title}", 102 | expect: "", 103 | }} 104 | for _, tt := range tests { 105 | t.Run(tt.name, func(t *testing.T) { 106 | result, _ := formatMessage(tt.msg, tt.pr) 107 | assert.Equal(t, tt.expect, result) 108 | }) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /cmd/msg.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "strings" 10 | "syscall" 11 | 12 | fakeruntime "github.com/linuxsuren/go-fake-runtime" 13 | "github.com/linuxsuren/gogit/pkg/oneapi" 14 | "github.com/spf13/cobra" 15 | "io" 16 | "net/http" 17 | ) 18 | 19 | type commitOption struct { 20 | provider string 21 | token string 22 | flags []string 23 | runtime fakeruntime.Execer 24 | } 25 | 26 | func newCommitCmd() *cobra.Command { 27 | opt := &commitOption{ 28 | runtime: fakeruntime.NewDefaultExecer(), 29 | } 30 | cmd := &cobra.Command{ 31 | Use: "commit", 32 | RunE: opt.runE, 33 | PreRunE: opt.preRunE, 34 | Short: "Commit the current changes with AI", 35 | Long: `Commit the current changes with AI. 36 | The AI provider is defined by the environment variable AI_PROVIDER, 37 | and the token is defined by the environment variable ONEAPI_TOKEN.`, 38 | } 39 | cmd.Flags().StringSliceVarP(&opt.flags, "flag", "", []string{}, "The flags of the git commit command") 40 | return cmd 41 | } 42 | 43 | func (o *commitOption) preRunE(cmd *cobra.Command, args []string) (err error) { 44 | o.provider = os.Getenv("AI_PROVIDER") 45 | if o.provider == "" { 46 | err = fmt.Errorf("AI_PROVIDER is not set") 47 | } 48 | o.token = os.Getenv("ONEAPI_TOKEN") 49 | if o.token == "" { 50 | err = errors.Join(err, fmt.Errorf("ONEAPI_TOKEN is not set")) 51 | } 52 | return 53 | } 54 | 55 | func (o *commitOption) runE(cmd *cobra.Command, args []string) (err error) { 56 | var gitdiff string 57 | gitdiff, err = o.getGitDiff() 58 | 59 | payload := oneapi.NewChatPayload(fmt.Sprintf("Please write a conventional git commit message for the following git diff:\n%s", gitdiff), "chatglm_std") 60 | 61 | var body []byte 62 | if body, err = json.Marshal(payload); err != nil { 63 | return 64 | } 65 | 66 | var req *http.Request 67 | req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/chat/completions", o.provider), io.NopCloser(bytes.NewReader(body))) 68 | if err != nil { 69 | return 70 | } 71 | req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", o.token)) 72 | req.Header.Set("Content-Type", "application/json") 73 | 74 | var resp *http.Response 75 | resp, err = http.DefaultClient.Do(req) 76 | if err != nil { 77 | return 78 | } 79 | 80 | if resp.StatusCode == http.StatusOK { 81 | // read the body and parse to oenapi.ChatResponse 82 | var chatResp oneapi.ChatResponse 83 | if err = json.NewDecoder(resp.Body).Decode(&chatResp); err != nil { 84 | return 85 | } 86 | 87 | var tempF *os.File 88 | if tempF, err = os.CreateTemp(os.TempDir(), "msg"); err != nil { 89 | return 90 | } 91 | 92 | content := chatResp.Choices[0].Message.Content 93 | // convert \n to new line 94 | content = strings.ReplaceAll(content, "\\n", "\n") 95 | if _, err = io.WriteString(tempF, content); err != nil { 96 | return 97 | } 98 | if err = tempF.Close(); err != nil { 99 | return 100 | } 101 | 102 | cmd.Println("start to commit with", tempF.Name()) 103 | 104 | var gitExe string 105 | if gitExe, err = o.runtime.LookPath("git"); err != nil { 106 | return 107 | } 108 | 109 | if err = o.runtime.RunCommand(gitExe, "add", "."); err != nil { 110 | return 111 | } 112 | 113 | opts := []string{"git", "commit", "--edit", "--file", tempF.Name()} 114 | for _, flag := range o.flags { 115 | opts = append(opts, flag) 116 | } 117 | 118 | err = syscall.Exec(gitExe, opts, append(os.Environ(), "GIT_EDITOR=vim")) 119 | } 120 | return 121 | } 122 | 123 | func (o *commitOption) getGitDiff() (diff string, err error) { 124 | // run command git diff and get the output 125 | diff, err = o.runtime.RunCommandAndReturn("git", ".", "diff") 126 | return 127 | } 128 | -------------------------------------------------------------------------------- /cmd/argoworkflow/template/comment_test.go: -------------------------------------------------------------------------------- 1 | package template_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/linuxsuren/gogit/argoworkflow/template" 8 | "github.com/stretchr/testify/assert" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | func TestCommentTemplate(t *testing.T) { 13 | var layout string = "2006-01-02 15:04:05" 14 | var timeStr string = "2019-12-12 15:22:12" 15 | targetTime, err := time.ParseInLocation(layout, timeStr, time.Local) 16 | assert.Nil(t, err) 17 | 18 | startTime := v1.Time{Time: targetTime} 19 | endTime := v1.Time{Time: startTime.Add(time.Second * 5)} 20 | 21 | node1 := map[string]interface{}{"Phase": "Succeeded", "DisplayName": "node-1", "StartedAt": startTime, "FinishedAt": endTime, "StartedTime": "2023-01-06T07:49:07Z", "EndedTime": "2023-01-06T07:54:26Z"} 22 | node2 := map[string]interface{}{"Phase": "Failed", "DisplayName": "node-2", "StartedAt": startTime, "FinishedAt": endTime, "StartedTime": "2023-01-06T07:49:07Z", "EndedTime": "2023-01-06T07:54:26Z"} 23 | node3 := map[string]interface{}{"Phase": "Running", "DisplayName": "node-3", "StartedAt": startTime, "FinishedAt": endTime, "StartedTime": "2023-01-06T07:49:07Z", "EndedTime": "2023-01-06T07:54:26Z"} 24 | 25 | nodes := map[string]interface{}{} 26 | nodes["node1"] = node1 27 | nodes["node2"] = node2 28 | nodes["node3"] = node3 29 | 30 | status := map[string]interface{}{} 31 | status["Nodes"] = nodes 32 | status["Phase"] = "Failed" 33 | status["StartedAt"] = startTime 34 | status["FinishedAt"] = endTime 35 | 36 | object := map[string]interface{}{} 37 | object["Status"] = status 38 | object["Annotations"] = map[string]string{ 39 | "workflow.link": "https://github.com/linxusuren/gogit", 40 | "workflow.templatelink": "https://github.com/linxusuren/gogit.git", 41 | } 42 | object["Spec"] = map[string]interface{}{ 43 | "WorkflowTemplateRef": map[string]string{ 44 | "Name": "Sample", 45 | }, 46 | } 47 | 48 | result, err := template.RenderTemplate(template.CommentTemplate, object) 49 | assert.Nil(t, err) 50 | assert.Equal(t, ` 51 | 52 | 53 | [Sample](https://github.com/linxusuren/gogit.git) is Failed. It started from 12-12 15:22, and took 5s. Please check log output from [here](https://github.com/linxusuren/gogit). 54 | 55 | | Stage | Status | Duration | 56 | |---|---|---| 57 | | [node-1](https://github.com/linxusuren/gogit) | :white_check_mark: Succeeded | 5s | 58 | | [node-2](https://github.com/linxusuren/gogit) | :broken_heart: Failed | 5s | 59 | | [node-3](https://github.com/linxusuren/gogit) | :hourglass_flowing_sand: Running | 5s | 60 | `, result) 61 | 62 | result, err = template.RenderTemplate(` 63 | 64 | | Stage | Status | Duration | 65 | |---|---|---| 66 | {{ range $node, $status := .Status.Nodes -}} 67 | | {{$status.DisplayName}} | {{$status.Phase}} | {{durationStr $status.EndedTime $status.StartedTime}} | 68 | {{end -}} 69 | `, object) 70 | assert.Nil(t, err) 71 | assert.Equal(t, ` 72 | 73 | | Stage | Status | Duration | 74 | |---|---|---| 75 | | node-1 | Succeeded | 5m19s | 76 | | node-2 | Failed | 5m19s | 77 | | node-3 | Running | 5m19s | 78 | `, result) 79 | } 80 | 81 | func TestOutput(t *testing.T) { 82 | objects := map[string]template.OutputObject{} 83 | objects["test"] = template.OutputObject{ 84 | Kind: template.FileOutput, 85 | File: "https://github.com", 86 | FileName: "install.yaml", 87 | } 88 | objects["string"] = template.OutputObject{ 89 | Kind: template.ValueOutput, 90 | Value: "ghcr.io/linuxsuren/gogit", 91 | } 92 | objects["report_md"] = template.OutputObject{ 93 | Kind: template.MarkdownOutput, 94 | Value: "## title", 95 | } 96 | 97 | result, err := template.RenderTemplate(template.OutputsTemplate, objects) 98 | assert.Nil(t, err) 99 | assert.Equalf(t, ` 100 | 101 | Please feel free to check the following outputs: 102 | 103 | | Output | 104 | |---| 105 | | string: ghcr.io/linuxsuren/gogit | 106 | | [install.yaml](https://github.com) | 107 | 108 | 109 | ## title 110 | `, result, result) 111 | } 112 | -------------------------------------------------------------------------------- /cmd/argoworkflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/linuxsuren/gogit/argoworkflow 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/argoproj/argo-workflows/v3 v3.4.4 7 | github.com/linuxsuren/gogit v0.0.5-0.20230108094346-b4c1d3962862 8 | github.com/spf13/cobra v1.6.1 9 | github.com/stretchr/testify v1.8.1 10 | k8s.io/apimachinery v0.26.0 11 | k8s.io/client-go v0.26.0 12 | ) 13 | 14 | require ( 15 | github.com/Masterminds/goutils v1.1.1 // indirect 16 | github.com/Masterminds/semver v1.5.0 // indirect 17 | github.com/Microsoft/go-winio v0.5.2 // indirect 18 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect 19 | github.com/acomagu/bufpipe v1.0.3 // indirect 20 | github.com/emirpasic/gods v1.12.0 // indirect 21 | github.com/go-git/gcfg v1.5.0 // indirect 22 | github.com/go-git/go-billy/v5 v5.3.1 // indirect 23 | github.com/go-git/go-git/v5 v5.4.2 // indirect 24 | github.com/google/uuid v1.3.0 // indirect 25 | github.com/huandu/xstrings v1.3.2 // indirect 26 | github.com/imdario/mergo v0.3.13 // indirect 27 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 28 | github.com/kevinburke/ssh_config v1.2.0 // indirect 29 | github.com/mitchellh/go-homedir v1.1.0 // indirect 30 | github.com/sergi/go-diff v1.1.0 // indirect 31 | github.com/xanzy/ssh-agent v0.3.1 // indirect 32 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 33 | gopkg.in/warnings.v0 v0.1.2 // indirect 34 | ) 35 | 36 | require ( 37 | code.gitea.io/sdk/gitea v0.14.0 // indirect 38 | github.com/Masterminds/sprig v2.22.0+incompatible 39 | github.com/bluekeyes/go-gitdiff v0.4.0 // indirect 40 | github.com/davecgh/go-spew v1.1.1 // indirect 41 | github.com/emicklei/go-restful/v3 v3.9.0 // indirect 42 | github.com/go-logr/logr v1.2.3 // indirect 43 | github.com/go-openapi/jsonpointer v0.19.5 // indirect 44 | github.com/go-openapi/jsonreference v0.20.0 // indirect 45 | github.com/go-openapi/swag v0.19.15 // indirect 46 | github.com/gogo/protobuf v1.3.2 // indirect 47 | github.com/golang/protobuf v1.5.2 // indirect 48 | github.com/google/gnostic v0.5.7-v3refs // indirect 49 | github.com/google/go-cmp v0.5.9 // indirect 50 | github.com/google/gofuzz v1.2.0 // indirect 51 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect 52 | github.com/hashicorp/go-version v1.3.0 // indirect 53 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 54 | github.com/jenkins-x/go-scm v1.11.19 // indirect 55 | github.com/josharian/intern v1.0.0 // indirect 56 | github.com/json-iterator/go v1.1.12 // indirect 57 | github.com/mailru/easyjson v0.7.7 // indirect 58 | github.com/mitchellh/copystructure v1.2.0 // indirect 59 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 60 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 61 | github.com/modern-go/reflect2 v1.0.2 // indirect 62 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 63 | github.com/pkg/errors v0.9.1 // indirect 64 | github.com/pmezard/go-difflib v1.0.0 // indirect 65 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 // indirect 66 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect 67 | github.com/sirupsen/logrus v1.9.0 // indirect 68 | github.com/spf13/pflag v1.0.5 // indirect 69 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect 70 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect 71 | golang.org/x/sys v0.3.0 // indirect 72 | golang.org/x/term v0.3.0 // indirect 73 | golang.org/x/text v0.5.0 // indirect 74 | golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect 75 | google.golang.org/appengine v1.6.7 // indirect 76 | google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect 77 | google.golang.org/grpc v1.50.1 // indirect 78 | google.golang.org/protobuf v1.28.1 // indirect 79 | gopkg.in/inf.v0 v0.9.1 // indirect 80 | gopkg.in/yaml.v2 v2.4.0 // indirect 81 | gopkg.in/yaml.v3 v3.0.1 // indirect 82 | k8s.io/api v0.26.0 // indirect 83 | k8s.io/klog/v2 v2.80.1 // indirect 84 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect 85 | k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect 86 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect 87 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect 88 | sigs.k8s.io/yaml v1.3.0 // indirect 89 | ) 90 | -------------------------------------------------------------------------------- /cmd/status.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/jenkins-x/go-scm/scm" 8 | "github.com/jenkins-x/go-scm/scm/factory" 9 | "github.com/linuxsuren/gogit/pkg" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func newStatusCmd() (cmd *cobra.Command) { 14 | opt := &statusOption{} 15 | cmd = &cobra.Command{ 16 | Use: "status", 17 | Short: "Send the build token to a PR of Gitlab/GitHub", 18 | PreRunE: opt.preRunE, 19 | RunE: opt.runE, 20 | } 21 | 22 | opt.addFlags(cmd) 23 | flags := cmd.Flags() 24 | flags.StringVarP(&opt.status, "status", "", "", 25 | "Build token, such as: pending, success, cancelled, error") 26 | flags.StringVarP(&opt.target, "target", "", "https://github.com/LinuxSuRen/gogit", "Address of the build server") 27 | flags.StringVarP(&opt.label, "label", "", "", 28 | "Identity of a build token") 29 | flags.StringVarP(&opt.description, "description", "", "", 30 | "The description of a build token") 31 | flags.BoolVarP(&opt.print, "print", "", false, "Print the status list then exit") 32 | return 33 | } 34 | 35 | func (o *statusOption) preRunE(cmd *cobra.Command, args []string) (err error) { 36 | o.preHandle() 37 | if o.label == "" { 38 | o.label = "gogit" 39 | } 40 | if o.description == "" { 41 | o.description = "" 42 | } 43 | 44 | // keep the token be compatible with different system 45 | switch o.status { 46 | case "Succeeded": 47 | // from Argo Workflow 48 | o.status = "success" 49 | } 50 | o.status = strings.ToLower(o.status) 51 | 52 | if strings.HasPrefix(o.token, "file://") { 53 | tokenFile := strings.TrimPrefix(o.token, "file://") 54 | var data []byte 55 | if data, err = os.ReadFile(tokenFile); err == nil { 56 | o.token = string(data) 57 | } 58 | } 59 | return 60 | } 61 | 62 | func (o *statusOption) runE(cmd *cobra.Command, args []string) (err error) { 63 | if o.print { 64 | maker := pkg.NewMaker(cmd.Context(), pkg.RepoInformation{ 65 | Provider: o.provider, 66 | Server: o.server, 67 | Owner: o.owner, 68 | Repo: o.repo, 69 | PrNumber: o.pr, 70 | Target: o.target, 71 | Username: o.username, 72 | Token: o.token, 73 | Status: o.status, 74 | Label: o.label, 75 | Description: o.description, 76 | }) 77 | if maker != nil { 78 | err = maker.ListStatus(cmd.Context(), o.label, o.description) 79 | } 80 | return 81 | } 82 | 83 | err = pkg.CreateStatus(cmd.Context(), pkg.RepoInformation{ 84 | Provider: o.provider, 85 | Server: o.server, 86 | Owner: o.owner, 87 | Repo: o.repo, 88 | PrNumber: o.pr, 89 | Target: o.target, 90 | Username: o.username, 91 | Token: o.token, 92 | Status: o.status, 93 | Label: o.label, 94 | Description: o.description, 95 | }) 96 | return 97 | } 98 | 99 | type gitProviderOption struct { 100 | provider string 101 | server string 102 | username string 103 | token string 104 | owner string 105 | repo string 106 | pr int 107 | } 108 | 109 | func (o *gitProviderOption) addFlags(c *cobra.Command) { 110 | flags := c.Flags() 111 | flags.StringVarP(&o.provider, "provider", "p", "github", "The provider of git, such as: gitlab, github") 112 | flags.StringVarP(&o.server, "server", "s", "", "The server address of target git provider, only need when it's a private provider") 113 | flags.StringVarP(&o.owner, "owner", "o", "", "Owner of a git repository") 114 | flags.StringVarP(&o.repo, "repo", "r", "", "Name of target git repository") 115 | flags.IntVarP(&o.pr, "pr", "", 1, "The pull request number") 116 | flags.StringVarP(&o.username, "username", "u", "", "Username of the git repository") 117 | flags.StringVarP(&o.token, "token", "t", "", 118 | "The access token of the git repository. Or you could provide a file path, such as: file:///var/token") 119 | 120 | _ = c.MarkFlagRequired("repo") 121 | _ = c.MarkFlagRequired("pr") 122 | _ = c.MarkFlagRequired("username") 123 | _ = c.MarkFlagRequired("token") 124 | } 125 | 126 | func (o *gitProviderOption) preHandle() { 127 | if o.owner == "" { 128 | o.owner = o.username 129 | } 130 | } 131 | 132 | func (o *gitProviderOption) getClient() (scmClient *scm.Client, err error) { 133 | scmClient, err = factory.NewClient(o.provider, o.server, o.token, func(c *scm.Client) { 134 | c.Username = o.username 135 | }) 136 | return 137 | } 138 | 139 | type statusOption struct { 140 | gitProviderOption 141 | status string 142 | target string 143 | label string 144 | description string 145 | print bool 146 | } 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![codecov](https://codecov.io/gh/LinuxSuRen/gogit/branch/master/graph/badge.svg?token=mnFyeD2IQ7)](https://codecov.io/gh/LinuxSuRen/gogit) 2 | 3 | `gogit` could send the build status to different git providers. Such as: 4 | 5 | * GitHub 6 | * Gitlab (public or private) 7 | 8 | ## Usage 9 | 10 | ### Generate commit message with AI 11 | 12 | ```shell 13 | export AI_PROVIDER=your-one-api-server-address 14 | export ONEAPI_TOKEN=your-one-api-token 15 | 16 | gogit commit 17 | ``` 18 | 19 | It supports [one-api](https://github.com/songquanpeng/one-api) only. 20 | 21 | ### Checkout to branch or PR 22 | Ideally, `gogit` could checkout to your branch or PR in any kind of git repository. 23 | 24 | You can run the following command in a git repository directory: 25 | 26 | ```shell 27 | gogit checkout --pr 1 28 | ``` 29 | 30 | ### Send status to Git Provider 31 | Below is an example of sending build status to a private Gitlab server: 32 | 33 | ```shell 34 | gogit status --provider gitlab \ 35 | --server http://10.121.218.82:6080 \ 36 | --repo yaml-readme \ 37 | --pr 1 \ 38 | --username linuxsuren \ 39 | --token h-zez9CWzyzykbLoS53s 40 | ``` 41 | 42 | Or in the following use cases: 43 | 44 | * [Tekton Task](https://hub.tekton.dev/tekton/task/gogit) 45 | 46 | ### Send a notification to DingDing 47 | Below is an example of sending a notification to DingDing based on the Gitlab/GitHub pull request author/reviewers/assignees: 48 | 49 | ```shell 50 | gogit pr --provider gitlab \ 51 | --server http://10.121.218.82:6080 \ 52 | --repo yaml-readme \ 53 | --pr 1 \ 54 | --username linuxsuren \ 55 | --token h-zez9CWzyzykbLoS53s \ 56 | --msg 'workflow done' \ 57 | --dingding-tokens linuxsuren=dingdingtoken 58 | ``` 59 | 60 | ## Argo workflow Executor 61 | Install as an Argo workflow executor plugin: 62 | 63 | ```shell 64 | cat < v2' 74 | args: release --clean --snapshot 75 | 76 | build-image: 77 | name: Build Image 78 | runs-on: ubuntu-20.04 79 | if: github.ref != 'refs/heads/master' 80 | steps: 81 | - name: Check out code 82 | uses: actions/checkout@v3.0.0 83 | - name: Build Image 84 | run: | 85 | make image 86 | 87 | build-image-argo-workflow-executor: 88 | runs-on: ubuntu-20.04 89 | if: github.ref != 'refs/heads/master' 90 | steps: 91 | - name: Check out code 92 | uses: actions/checkout@v3.0.0 93 | - name: Build Image 94 | run: | 95 | make image-workflow-executor-gogit 96 | 97 | image: 98 | name: Publish Image 99 | runs-on: ubuntu-20.04 100 | if: github.ref == 'refs/heads/master' 101 | steps: 102 | - name: Checkout 103 | uses: actions/checkout@v3.0.0 104 | - name: Setup Docker buildx 105 | uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf 106 | - name: Log into registry ${{ env.REGISTRY }} 107 | if: github.event_name != 'pull_request' 108 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 109 | with: 110 | registry: ${{ env.REGISTRY }} 111 | username: ${{ github.actor }} 112 | password: ${{ secrets.GH_PUBLISH_SECRETS }} 113 | - name: Extract Docker metadata 114 | id: meta 115 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 116 | with: 117 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 118 | - name: Build and push Docker image 119 | id: build-and-push 120 | uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a 121 | with: 122 | context: . 123 | push: ${{ github.event_name != 'pull_request' }} 124 | tags: ${{ steps.meta.outputs.tags }} 125 | labels: ${{ steps.meta.outputs.labels }} 126 | cache-from: type=gha 127 | cache-to: type=gha,mode=max 128 | 129 | image-argo-workflow-executor: 130 | runs-on: ubuntu-20.04 131 | if: github.ref == 'refs/heads/master' 132 | steps: 133 | - name: Checkout 134 | uses: actions/checkout@v3.0.0 135 | - name: Setup Docker buildx 136 | uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf 137 | - name: Log into registry ${{ env.REGISTRY }} 138 | if: github.event_name != 'pull_request' 139 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 140 | with: 141 | registry: ${{ env.REGISTRY }} 142 | username: ${{ github.actor }} 143 | password: ${{ secrets.GH_PUBLISH_SECRETS }} 144 | - name: Extract Docker metadata 145 | id: meta 146 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 147 | with: 148 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_EXECUTOR_NAME }} 149 | - name: Build and push Docker image 150 | id: build-and-push 151 | uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a 152 | with: 153 | context: cmd/argoworkflow 154 | push: ${{ github.event_name != 'pull_request' }} 155 | tags: ${{ steps.meta.outputs.tags }} 156 | labels: ${{ steps.meta.outputs.labels }} 157 | cache-from: type=gha 158 | cache-to: type=gha,mode=max 159 | 160 | UpdateReleaseDraft: 161 | runs-on: ubuntu-20.04 162 | if: github.ref == 'refs/heads/master' 163 | steps: 164 | - uses: release-drafter/release-drafter@v5 165 | env: 166 | GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_SECRETS }} 167 | -------------------------------------------------------------------------------- /cmd/checkout.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mitchellh/go-homedir" 6 | "github.com/pkg/errors" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "github.com/go-git/go-git/v5/plumbing/transport/http" 13 | 14 | "github.com/go-git/go-git/v5" 15 | "github.com/go-git/go-git/v5/config" 16 | "github.com/go-git/go-git/v5/plumbing" 17 | "github.com/go-git/go-git/v5/plumbing/transport" 18 | "github.com/go-git/go-git/v5/plumbing/transport/ssh" 19 | "github.com/spf13/cobra" 20 | ) 21 | 22 | func newCheckoutCommand() (c *cobra.Command) { 23 | opt := &checkoutOption{} 24 | 25 | c = &cobra.Command{ 26 | Use: "checkout", 27 | Aliases: []string{"co"}, 28 | Short: "Clone and checkout the git repository with branch, tag, or pull request", 29 | Example: "gogit checkout https://github.com/linuxsuren/gogit", 30 | PreRunE: opt.preRunE, 31 | RunE: opt.runE, 32 | } 33 | 34 | userHomeDir, err := homedir.Dir() 35 | if err != nil { 36 | panic(errors.Wrap(err, "cannot find the home directory")) 37 | } 38 | 39 | flags := c.Flags() 40 | flags.StringVarP(&opt.url, "url", "", "", "The git repository URL") 41 | flags.StringVarP(&opt.remote, "remote", "", "origin", "The remote name") 42 | flags.StringVarP(&opt.sshPrivateKey, "ssh-private-key", "", filepath.Join(userHomeDir, ".ssh/id_rsa"), 43 | "The SSH private key file path") 44 | flags.StringVarP(&opt.username, "username", "", "", "The username of the git repository") 45 | flags.StringVarP(&opt.password, "password", "", "", "The password of the git repository") 46 | flags.StringVarP(&opt.branch, "branch", "b", "master", "The branch want to checkout. It could be a short name or fullname. Such as master or refs/heads/master") 47 | flags.StringVarP(&opt.tag, "tag", "", "", "The tag want to checkout") 48 | flags.IntVarP(&opt.pr, "pr", "p", -1, "The pr number want to checkout, -1 means do nothing") 49 | flags.StringVarP(&opt.target, "target", "", ".", "Clone git repository to the target path") 50 | flags.StringVarP(&opt.versionOutput, "version-output", "", "", "Write the version to target file") 51 | flags.StringVarP(&opt.trimVersionPrefix, "version-trim-prefix", "", "", "Trim the prefix of the version") 52 | flags.StringVarP(&opt.timestampOutput, "timestamp-output", "", "", "Write the current time to the target file") 53 | flags.StringVarP(&opt.timestampFormat, "timestamp-format", "", "2006-01-02-150405", "The format of the time stamp") 54 | return 55 | } 56 | 57 | func (o *checkoutOption) preRunE(c *cobra.Command, args []string) (err error) { 58 | if o.url == "" && len(args) > 0 { 59 | o.url = args[0] 60 | } 61 | o.branch = strings.TrimPrefix(o.branch, "refs/heads/") 62 | return 63 | } 64 | 65 | func (o *checkoutOption) runE(c *cobra.Command, args []string) (err error) { 66 | var repoDir string 67 | if repoDir, err = filepath.Abs(o.target); err != nil { 68 | return 69 | } 70 | 71 | var gitAuth transport.AuthMethod 72 | if gitAuth, err = o.getAuth(o.url); err != nil { 73 | return 74 | } 75 | 76 | var repo *git.Repository 77 | if _, serr := os.Stat(filepath.Join(repoDir, ".git")); serr != nil { 78 | if repo, err = git.PlainCloneContext(c.Context(), repoDir, false, &git.CloneOptions{ 79 | RemoteName: o.remote, 80 | Auth: gitAuth, 81 | URL: o.url, 82 | ReferenceName: plumbing.NewBranchReferenceName(o.branch), 83 | Progress: c.OutOrStdout(), 84 | }); err != nil { 85 | err = fmt.Errorf("failed to clone git repository '%s' into '%s', error: %v", o.url, repoDir, err) 86 | return 87 | } 88 | } else if repo, err = git.PlainOpen(repoDir); err != nil { 89 | return 90 | } 91 | 92 | var wd *git.Worktree 93 | var remotes []*git.Remote 94 | var version string 95 | 96 | if remotes, err = repo.Remotes(); err != nil { 97 | return 98 | } 99 | 100 | remoteURL := remotes[0].Config().URLs[0] 101 | kind := detectGitKind(remoteURL) 102 | // need to get auth again if the repo was exist 103 | if gitAuth, err = o.getAuth(remoteURL); err != nil { 104 | return 105 | } 106 | 107 | if wd, err = repo.Worktree(); err == nil { 108 | if c.Flags().Changed("branch") { 109 | version = o.branch 110 | 111 | if err = wd.Checkout(&git.CheckoutOptions{ 112 | Branch: plumbing.NewBranchReferenceName(o.branch), 113 | }); err != nil { 114 | err = fmt.Errorf("unable to checkout git branch: %s, error: %v", o.branch, err) 115 | return 116 | } 117 | c.Printf("Switched to branch '%s'\n", o.branch) 118 | } 119 | 120 | if o.tag != "" { 121 | var tagRef plumbing.ReferenceName 122 | if strings.HasPrefix(o.tag, "refs/tags/") { 123 | tagRef = plumbing.ReferenceName(o.tag) 124 | } else { 125 | tagRef = plumbing.NewTagReferenceName(o.tag) 126 | } 127 | 128 | if err = wd.Checkout(&git.CheckoutOptions{ 129 | Branch: tagRef, 130 | }); err != nil { 131 | err = fmt.Errorf("unable to checkout git tag: %s, error: %v", o.tag, err) 132 | return 133 | } 134 | c.Printf("Switched to tag '%s'\n", o.tag) 135 | } 136 | 137 | if o.pr > 0 { 138 | if err = repo.Fetch(&git.FetchOptions{ 139 | RemoteName: o.remote, 140 | Auth: gitAuth, 141 | Progress: c.OutOrStdout(), 142 | RefSpecs: []config.RefSpec{config.RefSpec(prRef(o.pr, kind))}, 143 | }); err != nil && err != git.NoErrAlreadyUpToDate { 144 | err = fmt.Errorf("failed to fetch '%s', error: %v", o.remote, err) 145 | return 146 | } 147 | 148 | if err = wd.Checkout(&git.CheckoutOptions{ 149 | Branch: plumbing.ReferenceName(fmt.Sprintf("pr-%d", o.pr)), 150 | }); err != nil && !strings.Contains(err.Error(), "already exists") { 151 | err = fmt.Errorf("unable to checkout git branch: %s, error: %v", o.tag, err) 152 | return 153 | } 154 | c.Printf("Switched to pr-%d\n", o.pr) 155 | version = fmt.Sprintf("pr-%d", o.pr) 156 | } 157 | 158 | if o.versionOutput != "" { 159 | err = os.WriteFile(o.versionOutput, []byte(strings.TrimPrefix(version, o.trimVersionPrefix)), 0444) 160 | } 161 | 162 | switch o.timestampOutput { 163 | case "": 164 | case "-": 165 | _, err = fmt.Fprint(c.OutOrStdout(), time.Now().Format(o.timestampFormat)) 166 | default: 167 | err = os.WriteFile(o.timestampOutput, []byte(time.Now().Format(o.timestampFormat)), 0444) 168 | } 169 | } 170 | return 171 | } 172 | 173 | func (o *checkoutOption) getAuth(remote string) (auth transport.AuthMethod, err error) { 174 | if strings.HasPrefix(remote, "git@") { 175 | rsa := os.ExpandEnv(o.sshPrivateKey) 176 | auth, err = ssh.NewPublicKeysFromFile("git", rsa, "") 177 | } else if o.username != "" && o.password != "" { 178 | auth = &http.BasicAuth{ 179 | Username: o.username, 180 | Password: o.password, 181 | } 182 | } 183 | return 184 | } 185 | 186 | func detectGitKind(gitURL string) (kind string) { 187 | kind = "gitlab" 188 | if strings.Contains(gitURL, "github.com") { 189 | kind = "github" 190 | } 191 | return 192 | } 193 | 194 | // see also https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally?gt 195 | func prRef(pr int, kind string) (ref string) { 196 | switch kind { 197 | case "gitlab": 198 | ref = fmt.Sprintf("refs/merge-requests/%d/head:pr-%d", pr, pr) 199 | case "github": 200 | ref = fmt.Sprintf("refs/pull/%d/head:pr-%d", pr, pr) 201 | } 202 | return 203 | } 204 | 205 | type checkoutOption struct { 206 | url string 207 | remote string 208 | branch string 209 | tag string 210 | pr int 211 | target string 212 | sshPrivateKey string 213 | versionOutput string 214 | trimVersionPrefix string 215 | timestampOutput string 216 | timestampFormat string 217 | username string 218 | password string 219 | } 220 | -------------------------------------------------------------------------------- /pkg/git.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/jenkins-x/go-scm/scm" 9 | "github.com/jenkins-x/go-scm/scm/factory" 10 | ) 11 | 12 | // CreateStatus is the main entry of this reconciler 13 | func CreateStatus(ctx context.Context, repoInfo RepoInformation) (err error) { 14 | if maker := NewMaker(ctx, repoInfo); maker != nil { 15 | err = maker.CreateStatus(ctx, scm.ToState(repoInfo.Status), repoInfo.Label, repoInfo.Description) 16 | } 17 | return 18 | } 19 | 20 | // CreateComment creates a comment against the pull request 21 | // 22 | // It will update the comment there is a comment has the same ender 23 | func CreateComment(ctx context.Context, repoInfo RepoInformation, message, identity string) (err error) { 24 | if maker := NewMaker(ctx, repoInfo); maker != nil { 25 | err = maker.CreateComment(ctx, message, identity) 26 | } 27 | return 28 | } 29 | 30 | // NewMaker creates a maker 31 | func NewMaker(ctx context.Context, repoInfo RepoInformation) (maker *StatusMaker) { 32 | if repoInfo.PrNumber == -1 { 33 | fmt.Println("skip due to pr number is -1") 34 | return 35 | } 36 | 37 | repo := repoInfo.GetRepoPath() 38 | maker = NewStatusMaker(repo, repoInfo.Token) 39 | maker.WithTarget(repoInfo.Target).WithPR(repoInfo.PrNumber). 40 | WithServer(repoInfo.Server). 41 | WithProvider(repoInfo.Provider). 42 | WithUsername(repoInfo.Username). 43 | WithToken(repoInfo.Token) 44 | return 45 | } 46 | 47 | // StatusMaker responsible for Pull Requests status creating 48 | type StatusMaker struct { 49 | provider string 50 | server string 51 | repo string 52 | pr int 53 | token string 54 | username string 55 | target string 56 | 57 | // expirationCheck checks if the current status is expiration that compared to the previous one 58 | expirationCheck expirationCheckFunc 59 | } 60 | 61 | // NewStatusMaker creates an instance of statusMaker 62 | func NewStatusMaker(repo, token string) *StatusMaker { 63 | return &StatusMaker{ 64 | repo: repo, 65 | token: token, 66 | expirationCheck: func(previousStatus *scm.Status, currentStatus *scm.StatusInput) bool { 67 | return previousStatus != nil && previousStatus.State == currentStatus.State 68 | }, 69 | } 70 | } 71 | 72 | type expirationCheckFunc func(previousStatus *scm.Status, currentStatus *scm.StatusInput) bool 73 | 74 | // WithExpirationCheck set the expiration check function 75 | func (s *StatusMaker) WithExpirationCheck(check expirationCheckFunc) *StatusMaker { 76 | s.expirationCheck = check 77 | return s 78 | } 79 | 80 | // WithUsername sets the username 81 | func (s *StatusMaker) WithUsername(username string) *StatusMaker { 82 | s.username = username 83 | return s 84 | } 85 | 86 | // WithToken sets the token 87 | func (s *StatusMaker) WithToken(token string) *StatusMaker { 88 | s.token = token 89 | return s 90 | } 91 | 92 | // WithProvider sets the Provider 93 | func (s *StatusMaker) WithProvider(provider string) *StatusMaker { 94 | s.provider = provider 95 | return s 96 | } 97 | 98 | // WithServer sets the server 99 | func (s *StatusMaker) WithServer(server string) *StatusMaker { 100 | s.server = server 101 | return s 102 | } 103 | 104 | // WithTarget sets the Target URL 105 | func (s *StatusMaker) WithTarget(target string) *StatusMaker { 106 | s.target = target 107 | return s 108 | } 109 | 110 | // WithPR sets the pr number 111 | func (s *StatusMaker) WithPR(pr int) *StatusMaker { 112 | s.pr = pr 113 | return s 114 | } 115 | 116 | // CommentEndMarker is the identify for matching existing comment 117 | const CommentEndMarker = "Comment from [gogit](https://github.com/linuxsuren/gogit)." 118 | 119 | // CreateComment creates a comment 120 | func (s *StatusMaker) CreateComment(ctx context.Context, message, endMarker string) (err error) { 121 | var scmClient *scm.Client 122 | if scmClient, err = factory.NewClient(s.provider, s.server, s.token, func(c *scm.Client) { 123 | c.Username = s.username 124 | }); err != nil { 125 | return 126 | } 127 | 128 | var comments []*scm.Comment 129 | if comments, _, err = scmClient.PullRequests.ListComments(ctx, s.repo, s.pr, &scm.ListOptions{ 130 | Page: 1, 131 | Size: 100, 132 | }); err != nil { 133 | if err = IgnoreError(err, "Not Found"); err != nil { 134 | err = fmt.Errorf("cannot any comments %v", err) 135 | return 136 | } 137 | } 138 | 139 | commentIDs := getCommentIDs(comments, endMarker) 140 | commentInput := &scm.CommentInput{ 141 | Body: fmt.Sprintf("%s\n\n%s", message, endMarker), 142 | } 143 | 144 | if len(commentIDs) == 0 { 145 | // not found existing comment, create a new one 146 | _, _, err = scmClient.PullRequests.CreateComment(ctx, s.repo, s.pr, commentInput) 147 | err = WrapError(err, "failed to create comment, repo is %q, pr is %d: %v", s.repo, s.pr) 148 | } else { 149 | _, _, err = scmClient.PullRequests.EditComment(ctx, s.repo, s.pr, commentIDs[0], commentInput) 150 | err = WrapError(err, "failed to edit comment: %v") 151 | 152 | // remove the duplicated comments 153 | for i := 1; i < len(commentIDs); i++ { 154 | _, _ = scmClient.PullRequests.DeleteComment(ctx, s.repo, s.pr, commentIDs[i]) 155 | } 156 | } 157 | return 158 | } 159 | 160 | func getCommentIDs(comments []*scm.Comment, endMarker string) (commentIDs []int) { 161 | for i := range comments { 162 | comment := comments[i] 163 | if strings.HasSuffix(comment.Body, endMarker) { 164 | commentIDs = append(commentIDs, comment.ID) 165 | } 166 | } 167 | return 168 | } 169 | 170 | // CreateStatus creates a generic status 171 | func (s *StatusMaker) CreateStatus(ctx context.Context, status scm.State, label, desc string) (err error) { 172 | var scmClient *scm.Client 173 | if scmClient, err = factory.NewClient(s.provider, s.server, s.token, func(c *scm.Client) { 174 | c.Username = s.username 175 | }); err != nil { 176 | return 177 | } 178 | 179 | var pullRequest *scm.PullRequest 180 | if pullRequest, _, err = scmClient.PullRequests.Find(ctx, s.repo, s.pr); err == nil { 181 | var previousStatus *scm.Status 182 | if previousStatus, err = s.FindPreviousStatus(ctx, scmClient, pullRequest.Sha, label); err != nil { 183 | return 184 | } 185 | 186 | currentStatus := &scm.StatusInput{ 187 | Desc: desc, 188 | Label: label, 189 | State: status, 190 | Target: s.target, 191 | } 192 | // avoid the previous building status override newer one 193 | if !s.expirationCheck(previousStatus, currentStatus) { 194 | _, _, err = scmClient.Repositories.CreateStatus(ctx, s.repo, pullRequest.Sha, currentStatus) 195 | } 196 | } else { 197 | err = fmt.Errorf("failed to find pull requests %v", err) 198 | } 199 | return 200 | } 201 | 202 | // ListStatus list the status 203 | func (s *StatusMaker) ListStatus(ctx context.Context, label, desc string) (err error) { 204 | var scmClient *scm.Client 205 | if scmClient, err = factory.NewClient(s.provider, s.server, s.token, func(c *scm.Client) { 206 | c.Username = s.username 207 | }); err != nil { 208 | return 209 | } 210 | 211 | var pullRequest *scm.PullRequest 212 | if pullRequest, _, err = scmClient.PullRequests.Find(ctx, s.repo, s.pr); err == nil { 213 | var exists []*scm.Status 214 | if exists, _, err = scmClient.Repositories.ListStatus(ctx, s.repo, pullRequest.Sha, &scm.ListOptions{ 215 | Page: 1, 216 | Size: 100, // assume this list has not too many items 217 | }); err != nil { 218 | err = fmt.Errorf("failed to list the existing status, error: %v", err) 219 | return 220 | } 221 | 222 | for _, item := range exists { 223 | if item.Label == label { 224 | fmt.Println(item.State) 225 | } 226 | } 227 | } else { 228 | err = fmt.Errorf("failed to find pull requests [%d] from [%s] %v", s.pr, s.repo, err) 229 | } 230 | return 231 | } 232 | 233 | // FindPreviousStatus finds the existing status by sha and label 234 | func (s *StatusMaker) FindPreviousStatus(ctx context.Context, scmClient *scm.Client, sha, label string) (target *scm.Status, err error) { 235 | var exists []*scm.Status 236 | if exists, _, err = scmClient.Repositories.ListStatus(ctx, s.repo, sha, &scm.ListOptions{ 237 | Page: 1, 238 | Size: 100, // assume this list has not too many items 239 | }); err != nil { 240 | err = fmt.Errorf("failed to list the existing status, error: %v", err) 241 | return 242 | } 243 | 244 | for _, item := range exists { 245 | if item.Label == label { 246 | target = item 247 | break 248 | } 249 | } 250 | return 251 | } 252 | -------------------------------------------------------------------------------- /cmd/argoworkflow/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2023-2024 Rick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "context" 29 | "encoding/json" 30 | "errors" 31 | "fmt" 32 | "io" 33 | "net/http" 34 | "os" 35 | "regexp" 36 | "strconv" 37 | "strings" 38 | "time" 39 | 40 | wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" 41 | wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" 42 | "github.com/argoproj/argo-workflows/v3/pkg/plugins/executor" 43 | "github.com/linuxsuren/gogit/argoworkflow/template" 44 | "github.com/linuxsuren/gogit/pkg" 45 | "github.com/spf13/cobra" 46 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 47 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 48 | "k8s.io/apimachinery/pkg/util/wait" 49 | 50 | "k8s.io/client-go/rest" 51 | "k8s.io/client-go/tools/clientcmd" 52 | ) 53 | 54 | func main() { 55 | opt := &option{} 56 | cmd := &cobra.Command{ 57 | Use: "workflow-executor-gogit", 58 | RunE: opt.runE, 59 | } 60 | flags := cmd.Flags() 61 | flags.StringVarP(&opt.Provider, "provider", "", "", 62 | "Git provider, such as: gitlab/github") 63 | flags.StringVarP(&opt.Server, "server", "", "", 64 | "Git server address, only required when it's not a public service") 65 | flags.StringVarP(&opt.Username, "username", "", "", 66 | "Username of the git server") 67 | flags.StringVarP(&opt.Token, "token", "", "", 68 | "Personal access token of the git server") 69 | flags.StringVarP(&opt.Target, "target", "", "http://argo.argo-server.svc:2746", 70 | "The root URL of Argo Workflows UI") 71 | flags.IntVarP(&opt.Port, "port", "", 3001, 72 | "The port of the HTTP server") 73 | flags.StringVarP(&opt.KubeConfig, "kubeconfig", "", "", "The kubeconfig file path") 74 | flags.BoolVarP(&opt.CreateComment, "create-comment", "", false, "Indicate if want to create a status comment") 75 | flags.StringVarP(&opt.CommentTemplate, "comment-template", "", "", "The template of the comment") 76 | flags.StringVarP(&opt.CommentIdentity, "comment-identity", "", pkg.CommentEndMarker, "The identity for matching exiting comment") 77 | if err := cmd.Execute(); err != nil { 78 | os.Exit(1) 79 | } 80 | } 81 | 82 | func (o *option) runE(cmd *cobra.Command, args []string) (err error) { 83 | var config *rest.Config 84 | if config, err = clientcmd.BuildConfigFromFlags("", o.KubeConfig); err != nil { 85 | return 86 | } 87 | client := wfclientset.NewForConfigOrDie(config) 88 | 89 | http.HandleFunc("/api/v1/template.execute", plugin(&DefaultPluginExecutor{option: o}, client)) 90 | err = http.ListenAndServe(fmt.Sprintf(":%d", o.Port), nil) 91 | return 92 | } 93 | 94 | type option struct { 95 | Provider string 96 | Server string 97 | Username string 98 | Token string 99 | Port int 100 | KubeConfig string 101 | 102 | CreateComment bool 103 | CommentTemplate string 104 | CommentIdentity string 105 | 106 | Owner string 107 | Repo string 108 | PR string 109 | Status string 110 | Target string 111 | Label string 112 | Description string 113 | } 114 | 115 | type DefaultPluginExecutor struct { 116 | option *option 117 | } 118 | 119 | type pluginOption struct { 120 | Option *option `json:"gogit-executor-plugin"` 121 | } 122 | 123 | func (e *DefaultPluginExecutor) Execute(args executor.ExecuteTemplateArgs, wf *wfv1.Workflow) ( 124 | resp executor.ExecuteTemplateResponse, err error) { 125 | defer func() { 126 | resp = executor.ExecuteTemplateResponse{ 127 | Body: executor.ExecuteTemplateReply{}, 128 | } 129 | 130 | if err == nil { 131 | resp.Body.Node = &wfv1.NodeResult{ 132 | Phase: wfv1.NodeSucceeded, 133 | Message: "success", 134 | } 135 | } else { 136 | resp.Body.Node = &wfv1.NodeResult{ 137 | Phase: wfv1.NodeFailed, 138 | Message: err.Error(), 139 | } 140 | } 141 | }() 142 | 143 | ctx := context.Background() 144 | var name string 145 | if wf.Spec.WorkflowTemplateRef != nil { 146 | name = wf.Spec.WorkflowTemplateRef.Name 147 | } 148 | wf.Status.Phase = wfv1.WorkflowPhase(wf.Status.Nodes[wf.Name].Phase) 149 | status := wf.Status 150 | 151 | p := args.Template.Plugin.Value 152 | 153 | opt := &pluginOption{Option: e.option} 154 | if err = json.Unmarshal(p, opt); err != nil { 155 | return 156 | } 157 | fmt.Println("option is", *opt.Option) 158 | 159 | targetAddress := fmt.Sprintf("%s/workflows/%s/%s", 160 | opt.Option.Target, 161 | args.Workflow.ObjectMeta.Namespace, 162 | args.Workflow.ObjectMeta.Name) 163 | repo := pkg.RepoInformation{ 164 | Provider: opt.Option.Provider, 165 | Server: opt.Option.Server, 166 | Owner: opt.Option.Owner, 167 | Repo: opt.Option.Repo, 168 | Target: targetAddress, 169 | Username: opt.Option.Username, 170 | Token: opt.Option.Token, 171 | Status: opt.Option.Status, 172 | Label: EmptyThen(opt.Option.Label, name), 173 | Description: EmptyThen(opt.Option.Description, status.Message), 174 | } 175 | if repo.Status == "" { 176 | switch status.Phase { 177 | case wfv1.WorkflowSucceeded: 178 | // from Argo Workflow 179 | repo.Status = "success" 180 | case wfv1.WorkflowFailed: 181 | repo.Status = "failure" 182 | default: 183 | repo.Status = strings.ToLower(string(status.Phase)) 184 | } 185 | } else { 186 | switch repo.Status { 187 | case string(wfv1.WorkflowSucceeded): 188 | // from Argo Workflow 189 | repo.Status = "success" 190 | case string(wfv1.WorkflowFailed): 191 | repo.Status = "failure" 192 | default: 193 | repo.Status = strings.ToLower(string(status.Phase)) 194 | } 195 | } 196 | 197 | if pr, parseErr := strconv.Atoi(opt.Option.PR); parseErr != nil { 198 | fmt.Printf("wrong pull-request number %q, %v\n", opt.Option.PR, parseErr) 199 | return 200 | } else { 201 | repo.PrNumber = pr 202 | } 203 | 204 | fmt.Println("send status", repo) 205 | if err = wait.PollImmediate(time.Second*2, time.Second*20, func() (bool, error) { 206 | return pkg.CreateStatus(ctx, repo) == nil, nil 207 | }); err == nil { 208 | fmt.Println("send status success", repo) 209 | } else { 210 | fmt.Printf("failed to send the status to %v: %v\n", repo, err) 211 | return 212 | } 213 | 214 | if err == nil && opt.Option.CreateComment { 215 | fmt.Println("start to create comment") 216 | tplText := EmptyThen(opt.Option.CommentTemplate, template.CommentTemplate) 217 | 218 | // find useless nodes 219 | var toRemoves []string 220 | for key, val := range wf.Status.Nodes { 221 | // TODO add a filter to allow users to do this, and keep this as default 222 | if strings.HasSuffix(val.Name, ".onExit") || strings.Contains(val.Name, ".hooks.") { 223 | toRemoves = append(toRemoves, key) 224 | } 225 | 226 | if val.FinishedAt.IsZero() { 227 | val.FinishedAt = metav1.Now() 228 | wf.Status.Nodes[key] = val 229 | } 230 | } 231 | // remove useless nodes 232 | delete(wf.Status.Nodes, wf.Name) 233 | for _, key := range toRemoves { 234 | delete(wf.Status.Nodes, key) 235 | } 236 | 237 | // put the workflow link into annotations 238 | if wf.Annotations == nil { 239 | wf.Annotations = map[string]string{} 240 | } 241 | 242 | var templatePath string 243 | if wf.Spec.WorkflowTemplateRef.ClusterScope { 244 | templatePath = "cluster-workflow-templates" 245 | } else { 246 | templatePath = "workflow-templates" 247 | } 248 | targetTemplateAddress := fmt.Sprintf("%s/%s/%s/%s", 249 | opt.Option.Target, 250 | templatePath, 251 | args.Workflow.ObjectMeta.Namespace, 252 | wf.Spec.WorkflowTemplateRef.Name) 253 | wf.Annotations["workflow.link"] = targetAddress 254 | wf.Annotations["workflow.templatelink"] = targetTemplateAddress 255 | if wf.Status.FinishedAt.IsZero() { 256 | // make sure the duration is positive 257 | wf.Status.FinishedAt = metav1.Now() 258 | } 259 | 260 | deleteRetryNodes(wf) 261 | 262 | var message string 263 | message, err = template.RenderTemplate(tplText, wf) 264 | if err == nil { 265 | outputs := GetOutputsWithTarget(wf, opt.Option.Target) 266 | var outputsComment string 267 | if len(outputs) > 0 { 268 | outputsComment, err = template.RenderTemplate(template.OutputsTemplate, outputs) 269 | } 270 | 271 | if err == nil { 272 | if outputsComment != "" { 273 | message = message + "\n" + outputsComment 274 | } 275 | 276 | err = pkg.CreateComment(ctx, repo, message, opt.Option.CommentIdentity) 277 | } 278 | } else { 279 | err = fmt.Errorf("failed to render comment template: %v", err) 280 | } 281 | 282 | if err != nil { 283 | fmt.Println("failed to create comment", err) 284 | } 285 | } 286 | return 287 | } 288 | 289 | func deleteRetryNodes(wf *wfv1.Workflow) { 290 | var toDeletes []string 291 | reg, err := regexp.Compile(`\(\d*\)`) 292 | if err == nil { 293 | for key, node := range wf.Status.Nodes { 294 | if matched := reg.MatchString(node.DisplayName); matched { 295 | toDeletes = append(toDeletes, key) 296 | } 297 | } 298 | 299 | for _, key := range toDeletes { 300 | delete(wf.Status.Nodes, key) 301 | } 302 | } 303 | } 304 | 305 | type PluginExecutor interface { 306 | // Execute commands based on the args provided from the workflow 307 | Execute(args executor.ExecuteTemplateArgs, wf *wfv1.Workflow) (executor.ExecuteTemplateResponse, error) 308 | } 309 | 310 | var ( 311 | ErrWrongContentType = errors.New("Content-Type header is not set to 'appliaction/json'") 312 | ErrReadingBody = errors.New("Couldn't read request body") 313 | ErrMarshallingBody = errors.New("Couldn't unmrashal request body") 314 | ) 315 | 316 | func plugin(p PluginExecutor, client *wfclientset.Clientset) func(w http.ResponseWriter, req *http.Request) { 317 | return func(w http.ResponseWriter, req *http.Request) { 318 | if header := req.Header.Get("Content-Type"); header != "application/json" { 319 | http.Error(w, ErrWrongContentType.Error(), http.StatusBadRequest) 320 | return 321 | } 322 | 323 | body, err := io.ReadAll(req.Body) 324 | if err != nil { 325 | http.Error(w, ErrReadingBody.Error(), http.StatusBadRequest) 326 | return 327 | } 328 | 329 | fmt.Println(string(body)) 330 | args := executor.ExecuteTemplateArgs{} 331 | if err := json.Unmarshal(body, &args); err != nil || args.Workflow == nil || args.Template == nil { 332 | http.Error(w, ErrMarshallingBody.Error(), http.StatusBadRequest) 333 | return 334 | } 335 | 336 | wfName := args.Workflow.ObjectMeta.Name 337 | wfNamespace := args.Workflow.ObjectMeta.Namespace 338 | 339 | // find the Workflow 340 | var workflow *wfv1.Workflow 341 | if workflow, err = client.ArgoprojV1alpha1().Workflows(wfNamespace).Get( 342 | context.Background(), 343 | wfName, 344 | v1.GetOptions{}); err != nil { 345 | fmt.Println("failed to find workflow", wfName, wfNamespace, err) 346 | return 347 | } 348 | 349 | var executeTemplateResponse executor.ExecuteTemplateResponse 350 | executeTemplateResponse, _ = p.Execute(args, workflow) 351 | 352 | jsonResp, err := json.Marshal(executeTemplateResponse.Body) 353 | if err != nil { 354 | fmt.Println("something went wrong", err) 355 | http.Error(w, "something went wrong", http.StatusBadRequest) 356 | } 357 | w.WriteHeader(http.StatusOK) 358 | _, _ = w.Write(jsonResp) 359 | return 360 | } 361 | } 362 | 363 | // EmptyThen return second if the first is empty 364 | func EmptyThen(first, second string) string { 365 | if first == "" { 366 | return second 367 | } else { 368 | return first 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /cmd/argoworkflow/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 | code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0= 4 | code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 7 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 8 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 9 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 10 | github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= 11 | github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= 12 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 13 | github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 14 | github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 15 | github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= 16 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 17 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= 18 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= 19 | github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= 20 | github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 21 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 22 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 23 | github.com/argoproj/argo-workflows/v3 v3.4.4 h1:6ODs/SZNvbkUICG9pRHQPyHLV5L0ZSVhj7AwgUZA+jE= 24 | github.com/argoproj/argo-workflows/v3 v3.4.4/go.mod h1:wYGIbAMl6BYUz5YeKbcwbEtjrvLyTsmbs9vWp6n7FGU= 25 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 26 | github.com/bluekeyes/go-gitdiff v0.4.0 h1:Q3qUnQ5cv27vG6ywUTiSQUobRYRcQIBs8KVGKojLg9I= 27 | github.com/bluekeyes/go-gitdiff v0.4.0/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM= 28 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 29 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 30 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 31 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 32 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 33 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 34 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 35 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 36 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 37 | github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= 38 | github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 39 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 40 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 41 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 42 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 43 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 44 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 45 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 46 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 47 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 48 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 49 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 50 | github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 51 | github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= 52 | github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 53 | github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= 54 | github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= 55 | github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 56 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 57 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 58 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 59 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 60 | github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 61 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 62 | github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= 63 | github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= 64 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 65 | github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= 66 | github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 67 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 68 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 69 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 70 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 71 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 72 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 73 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 74 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 75 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 76 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 77 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 78 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 79 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 80 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 81 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 82 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 83 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 84 | github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= 85 | github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 86 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 87 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 88 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 89 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 90 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 91 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 92 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 93 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 94 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 95 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 96 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 97 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 98 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 99 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 100 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 101 | github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= 102 | github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 103 | github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= 104 | github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 105 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= 106 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 107 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 108 | github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= 109 | github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 110 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 111 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 112 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 113 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 114 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 115 | github.com/jenkins-x/go-scm v1.11.19 h1:H4CzaM/C/0QcCVLDh603Q6Bv4hqU4G3De2yQntWubqg= 116 | github.com/jenkins-x/go-scm v1.11.19/go.mod h1:eIcty4+tf6E7ycGOg0cUqnaLP+1LH1Z8zncQFQqRa3E= 117 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 118 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 119 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 120 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 121 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 122 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 123 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 124 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 125 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 126 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 127 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 128 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 129 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 130 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 131 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 132 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 133 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 134 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 135 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 136 | github.com/linuxsuren/gogit v0.0.5-0.20230106063430-7d96e708cf7d h1:TDKAC8+UNSQt3YtQXkFdWxVQQUgcFHGEoWbOxmvDn5s= 137 | github.com/linuxsuren/gogit v0.0.5-0.20230106063430-7d96e708cf7d/go.mod h1:Qo/oN8JkdigUQqxwU7yPzQdbvmp8dyKmhQHle1YzWoc= 138 | github.com/linuxsuren/gogit v0.0.5-0.20230108073958-1962f90e6d0f h1:eQ7uEyknD+aMRN8iyQDReQasvWdAuK/Z09Od2VqCA4E= 139 | github.com/linuxsuren/gogit v0.0.5-0.20230108073958-1962f90e6d0f/go.mod h1:ncCff+SHeZUYEFCdGito+wuECgl/rXyyt8iV10aKKHU= 140 | github.com/linuxsuren/gogit v0.0.5-0.20230108094346-b4c1d3962862 h1:04Jk6OkM+HhA19Tg51Cg+bnlNvi4HzLh0ZaUaLIxbOQ= 141 | github.com/linuxsuren/gogit v0.0.5-0.20230108094346-b4c1d3962862/go.mod h1:ncCff+SHeZUYEFCdGito+wuECgl/rXyyt8iV10aKKHU= 142 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 143 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 144 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 145 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 146 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 147 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 148 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 149 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 150 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 151 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 152 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 153 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 154 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 155 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 156 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 157 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 158 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 159 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 160 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 161 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 162 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 163 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 164 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 165 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 166 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 167 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 168 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 169 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 170 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 171 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 172 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 h1:xKXiRdBUtMVp64NaxACcyX4kvfmHJ9KrLU+JvyB1mdM= 173 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= 174 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= 175 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 176 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 177 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 178 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 179 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 180 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 181 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 182 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 183 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 184 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 185 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 186 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 187 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 188 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 189 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 190 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 191 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 192 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 193 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 194 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 195 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 196 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 197 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 198 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 199 | github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= 200 | github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= 201 | github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= 202 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 203 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 204 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 205 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 206 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 207 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 208 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 209 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 210 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 211 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= 212 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 213 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 214 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 215 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 216 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 217 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 218 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 219 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 220 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 221 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 222 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 223 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 224 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 225 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 226 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 227 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 228 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 229 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 230 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 231 | golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= 232 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= 233 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 234 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 235 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 236 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= 237 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 238 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 239 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 240 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 241 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 242 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 243 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 244 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 245 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 246 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 247 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 252 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 253 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 254 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 255 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 260 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 261 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 263 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 264 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 265 | golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= 266 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 267 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 268 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 269 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 270 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 271 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 272 | golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= 273 | golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 274 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 275 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 276 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 277 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 278 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 279 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 280 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 281 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 282 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 283 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 284 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 285 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 286 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 287 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 288 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 289 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 290 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 291 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 292 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 293 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 294 | google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 295 | google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= 296 | google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= 297 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 298 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 299 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 300 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 301 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 302 | google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= 303 | google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 304 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 305 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 306 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 307 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 308 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 309 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 310 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 311 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 312 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 313 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 314 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 315 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 316 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 317 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 318 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 319 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 320 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 321 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 322 | gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8= 323 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 324 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 325 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 326 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 327 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 328 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 329 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 330 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 331 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 332 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 333 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 334 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 335 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 336 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 337 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 338 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 339 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 340 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 341 | k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= 342 | k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= 343 | k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= 344 | k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= 345 | k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= 346 | k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= 347 | k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= 348 | k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 349 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= 350 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= 351 | k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= 352 | k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 353 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= 354 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 355 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= 356 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= 357 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 358 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 359 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0= 3 | code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= 4 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 5 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 6 | github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 7 | github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 8 | github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= 9 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 10 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 11 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= 12 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= 13 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 14 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 15 | github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= 16 | github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 17 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 18 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 19 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 20 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 21 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= 22 | github.com/bluekeyes/go-gitdiff v0.4.0 h1:Q3qUnQ5cv27vG6ywUTiSQUobRYRcQIBs8KVGKojLg9I= 23 | github.com/bluekeyes/go-gitdiff v0.4.0/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM= 24 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 25 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 26 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 27 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 28 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 29 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 30 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 31 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 32 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 33 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 34 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 35 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 36 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 37 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 38 | github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 39 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 40 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 41 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 42 | github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= 43 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 44 | github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 45 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 46 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 47 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 48 | github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 49 | github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= 50 | github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 51 | github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= 52 | github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= 53 | github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= 54 | github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= 55 | github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 56 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 57 | github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 58 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 59 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 60 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 61 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 62 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 63 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 64 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 65 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 66 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 67 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 68 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 69 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 70 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 71 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 72 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 73 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 74 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 75 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 76 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 77 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 78 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 79 | github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 80 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 81 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 82 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 83 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 84 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 85 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 86 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 87 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 88 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 89 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 90 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 91 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 92 | github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= 93 | github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= 94 | github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 95 | github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= 96 | github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 97 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 98 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 99 | github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= 100 | github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 101 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 102 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 103 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 104 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 105 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 106 | github.com/jenkins-x/go-scm v1.11.19 h1:H4CzaM/C/0QcCVLDh603Q6Bv4hqU4G3De2yQntWubqg= 107 | github.com/jenkins-x/go-scm v1.11.19/go.mod h1:eIcty4+tf6E7ycGOg0cUqnaLP+1LH1Z8zncQFQqRa3E= 108 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 109 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 110 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 111 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 112 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 113 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 114 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 115 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 116 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 117 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 118 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 119 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 120 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 121 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 122 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 123 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 124 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 125 | github.com/linuxsuren/go-fake-runtime v0.0.4 h1:y+tvBuw6MKTCav8Bo5HWwaXhBx1Z//VAvqI3gpOWqvw= 126 | github.com/linuxsuren/go-fake-runtime v0.0.4/go.mod h1:zmh6J78hSnWZo68faMA2eKOdaEp8eFbERHi3ZB9xHCQ= 127 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 128 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 129 | github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= 130 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 131 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 132 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 133 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 134 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 135 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 136 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 137 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 138 | github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 139 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 140 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 141 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 142 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 143 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 144 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 145 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 146 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 147 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 148 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 149 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 150 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 151 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 152 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 153 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 154 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 155 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 156 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 157 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 158 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 159 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 160 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 161 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 162 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 163 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 h1:xKXiRdBUtMVp64NaxACcyX4kvfmHJ9KrLU+JvyB1mdM= 164 | github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= 165 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= 166 | github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 167 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 168 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 169 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 170 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 171 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 172 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 173 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 174 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 175 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 176 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 177 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 178 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 179 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 180 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 181 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 182 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 183 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 184 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 185 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 186 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 187 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 188 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 189 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 190 | github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= 191 | github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= 192 | github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= 193 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 194 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 195 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 196 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 197 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 198 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 199 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 200 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 201 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 202 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 203 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= 204 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 205 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 206 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 207 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 208 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 209 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 210 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 211 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 212 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 213 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 214 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 215 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 216 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 217 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 218 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 219 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 220 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 221 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 222 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 223 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 224 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 225 | golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= 226 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 227 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 228 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= 229 | golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 230 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 231 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= 232 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 233 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 234 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 235 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 236 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 237 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 238 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 239 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 240 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 241 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 242 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 243 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 244 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 245 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 246 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 252 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 253 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 254 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 255 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 260 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 261 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 263 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 264 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 265 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 266 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 267 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 268 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 269 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 270 | golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= 271 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 272 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 273 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 274 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 275 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 276 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 277 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 278 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 279 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 280 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 281 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 282 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 283 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 284 | golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 285 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 286 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 287 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 288 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 289 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 290 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 291 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 292 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 293 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 294 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 295 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 296 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 297 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 298 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 299 | google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 300 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 301 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 302 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 303 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 304 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 305 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 306 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 307 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 308 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 309 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 310 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 311 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 312 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 313 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 314 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 315 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 316 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 317 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 318 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 319 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 320 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 321 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 322 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 323 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 324 | gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8= 325 | gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8= 326 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 327 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 328 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 329 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 330 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 331 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 332 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 333 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 334 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 335 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 336 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 337 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 338 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 339 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 340 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 341 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 342 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 343 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 344 | k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= 345 | k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= 346 | k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= 347 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 348 | k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= 349 | k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 350 | k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= 351 | k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 352 | k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 353 | sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= 354 | sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 355 | sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= 356 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 357 | --------------------------------------------------------------------------------