Initial implementation of go-git client (#2720)

Signed-off-by: Azeem Shaikh <azeemshaikh38@gmail.com>
This commit is contained in:
Azeem Shaikh 2023-03-16 02:58:09 +05:30 committed by GitHub
parent 603263cd64
commit 8966abdceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 425 additions and 9 deletions

209
clients/git/client.go Normal file
View File

@ -0,0 +1,209 @@
// Copyright 2021 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package git defines helper functions for clients.RepoClient interface.
package git
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
cp "github.com/otiai10/copy"
"github.com/ossf/scorecard/v4/clients"
)
const repoDir = "repo*"
var (
errNilCommitFound = errors.New("nil commit found")
errEmptyQuery = errors.New("query is empty")
)
type Client struct {
gitRepo *git.Repository
worktree *git.Worktree
listCommits *sync.Once
tempDir string
errListCommits error
commits []clients.Commit
commitDepth int
}
func (c *Client) InitRepo(uri, commitSHA string, commitDepth int) error {
// cleanup previous state, if any.
c.Close()
c.listCommits = new(sync.Once)
c.commits = nil
// init
c.commitDepth = commitDepth
tempDir, err := os.MkdirTemp("", repoDir)
if err != nil {
return fmt.Errorf("os.MkdirTemp: %w", err)
}
// git clone
const filePrefix = "file://"
if strings.HasPrefix(uri, filePrefix) {
if err := cp.Copy(strings.TrimPrefix(uri, filePrefix), tempDir); err != nil {
return fmt.Errorf("cp.Copy: %w", err)
}
c.gitRepo, err = git.PlainOpen(tempDir)
if err != nil {
return fmt.Errorf("git.PlainOpen: %w", err)
}
} else {
c.gitRepo, err = git.PlainClone(tempDir, false /*isBare*/, &git.CloneOptions{
URL: uri,
Progress: os.Stdout,
})
if err != nil {
return fmt.Errorf("git.PlainClone: %w", err)
}
}
c.tempDir = tempDir
c.worktree, err = c.gitRepo.Worktree()
if err != nil {
return fmt.Errorf("git.Worktree: %w", err)
}
// git checkout
if commitSHA != clients.HeadSHA {
if err := c.worktree.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(commitSHA),
Force: true, // throw away any unsaved changes.
}); err != nil {
return fmt.Errorf("git.Worktree: %w", err)
}
}
return nil
}
func (c *Client) ListCommits() ([]clients.Commit, error) {
c.listCommits.Do(func() {
commitIter, err := c.gitRepo.Log(&git.LogOptions{
Order: git.LogOrderCommitterTime,
})
if err != nil {
c.errListCommits = fmt.Errorf("git.CommitObjects: %w", err)
return
}
c.commits = make([]clients.Commit, 0, c.commitDepth)
for i := 0; i < c.commitDepth; i++ {
commit, err := commitIter.Next()
if err != nil && !errors.Is(err, io.EOF) {
c.errListCommits = fmt.Errorf("commitIter.Next: %w", err)
return
}
// No more commits.
if errors.Is(err, io.EOF) {
break
}
if commit == nil {
// Not sure in what case a nil commit is returned. Fail explicitly.
c.errListCommits = fmt.Errorf("%w", errNilCommitFound)
return
}
c.commits = append(c.commits, clients.Commit{
SHA: commit.Hash.String(),
Message: commit.Message,
CommittedDate: commit.Committer.When,
Committer: clients.User{
Login: commit.Committer.Email,
},
})
}
})
return c.commits, c.errListCommits
}
func (c *Client) Search(request clients.SearchRequest) (clients.SearchResponse, error) {
// Pattern
if request.Query == "" {
return clients.SearchResponse{}, errEmptyQuery
}
queryRegexp, err := regexp.Compile(request.Query)
if err != nil {
return clients.SearchResponse{}, fmt.Errorf("regexp.Compile: %w", err)
}
grepOpts := &git.GrepOptions{
Patterns: []*regexp.Regexp{queryRegexp},
}
// path/filename
var pathExpr string
switch {
case request.Path != "" && request.Filename != "":
pathExpr = filepath.Join(fmt.Sprintf("^%s", request.Path),
fmt.Sprintf(".*%s$", request.Filename))
case request.Path != "":
pathExpr = fmt.Sprintf("^%s", request.Path)
case request.Filename != "":
pathExpr = filepath.Join(".*", fmt.Sprintf("%s$", request.Filename))
}
if pathExpr != "" {
pathRegexp, err := regexp.Compile(pathExpr)
if err != nil {
return clients.SearchResponse{}, fmt.Errorf("regexp.Compile: %w", err)
}
grepOpts.PathSpecs = append(grepOpts.PathSpecs, pathRegexp)
}
// Grep
grepResults, err := c.worktree.Grep(grepOpts)
if err != nil {
return clients.SearchResponse{}, fmt.Errorf("git.Grep: %w", err)
}
ret := clients.SearchResponse{}
for _, grepResult := range grepResults {
ret.Results = append(ret.Results, clients.SearchResult{
Path: grepResult.FileName,
})
}
ret.Hits = len(grepResults)
return ret, nil
}
// TODO(#1709): Implement below fns using go-git.
func (c *Client) SearchCommits(request clients.SearchCommitsOptions) ([]clients.Commit, error) {
return nil, nil
}
func (c *Client) ListFiles(predicate func(string) (bool, error)) ([]string, error) {
return nil, nil
}
func (c *Client) GetFileContent(filename string) ([]byte, error) {
return nil, nil
}
func (c *Client) Close() error {
if err := os.RemoveAll(c.tempDir); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("os.RemoveAll: %w", err)
}
return nil
}

155
clients/git/e2e_test.go Normal file
View File

@ -0,0 +1,155 @@
// Copyright 2021 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package git
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/ossf/scorecard/v4/clients"
)
var _ = DescribeTable("Test ListCommits commit-depth for HEAD",
func(uri string) {
const commitSHA = clients.HeadSHA
const commitDepth = 1
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
commits, err := client.ListCommits()
Expect(err).To(BeNil())
Expect(len(commits)).Should(BeEquivalentTo(commitDepth))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
Entry("GitLab", "https://gitlab.haskell.org/haskell/filepath"),
)
var _ = DescribeTable("Test ListCommits commit-depth and latest commit at [0]",
func(uri, commitSHA string) {
const commitDepth = 10
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
commits, err := client.ListCommits()
Expect(err).To(BeNil())
Expect(len(commits)).Should(BeEquivalentTo(commitDepth))
Expect(commits[0].SHA).Should(BeEquivalentTo(commitSHA))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard", "c06ac740cc49fea404c54c036000731d5ea6ebe3"),
Entry("Local", "file://../../", "c06ac740cc49fea404c54c036000731d5ea6ebe3"),
Entry("GitLab", "https://gitlab.haskell.org/haskell/filepath", "98f8bba9eac8c7183143d290d319be7df76c258b"),
)
var _ = DescribeTable("Test ListCommits without enough commits",
func(uri string) {
const commitSHA = "dc1835b7ffe526969d65436b621e171e3386771e"
const commitDepth = 10
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
commits, err := client.ListCommits()
Expect(err).To(BeNil())
Expect(len(commits)).Should(BeEquivalentTo(3))
Expect(commits[0].SHA).Should(BeEquivalentTo(commitSHA))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
// TODO(#1709): Add equivalent test for GitLab.
)
var _ = DescribeTable("Test Search across a repo",
func(uri string) {
const (
commitSHA = "c06ac740cc49fea404c54c036000731d5ea6ebe3"
commitDepth = 10
)
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
resp, err := client.Search(clients.SearchRequest{
Query: "github/codeql-action/analyze",
})
Expect(err).To(BeNil())
Expect(resp.Hits).Should(BeNumerically(">=", 1))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
// TODO(#1709): Add equivalent test for GitLab.
)
var _ = DescribeTable("Test Search within a path",
func(uri string) {
const (
commitSHA = "c06ac740cc49fea404c54c036000731d5ea6ebe3"
commitDepth = 10
)
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
resp, err := client.Search(clients.SearchRequest{
Query: "github/codeql-action/analyze",
Path: ".github/workflows",
})
Expect(err).To(BeNil())
Expect(resp.Hits).Should(BeEquivalentTo(1))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
// TODO(#1709): Add equivalent test for GitLab.
)
var _ = DescribeTable("Test Search within a filename",
func(uri string) {
const (
commitSHA = "c06ac740cc49fea404c54c036000731d5ea6ebe3"
commitDepth = 10
)
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
resp, err := client.Search(clients.SearchRequest{
Query: "github/codeql-action/analyze",
Filename: "codeql-analysis.yml",
})
Expect(err).To(BeNil())
Expect(resp.Hits).Should(BeEquivalentTo(1))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
// TODO(#1709): Add equivalent test for GitLab.
)
var _ = DescribeTable("Test Search within path and filename",
func(uri string) {
const (
commitSHA = "c06ac740cc49fea404c54c036000731d5ea6ebe3"
commitDepth = 10
)
client := &Client{}
Expect(client.InitRepo(uri, commitSHA, commitDepth)).To(BeNil())
resp, err := client.Search(clients.SearchRequest{
Query: "github/codeql-action/analyze",
Path: ".github/workflows",
Filename: "codeql-analysis.yml",
})
Expect(err).To(BeNil())
Expect(resp.Hits).Should(BeEquivalentTo(1))
Expect(client.Close()).To(BeNil())
},
Entry("GitHub", "https://github.com/ossf/scorecard"),
Entry("Local", "file://../../"),
// TODO(#1709): Add equivalent test for GitLab.
)

View File

@ -0,0 +1,39 @@
// Copyright 2021 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package git
import (
"os"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestGitRepo(t *testing.T) {
// TODO(#1709): GitHub tests are taking >10m to run slowing down CI/CD.
// Need to fix that before re-enabling.
// TODO(#1709): Local tests require fake Git repo to be available in CI/CD
// environment.
if val, exists := os.LookupEnv("RUN_GIT_E2E"); !exists || val == "0" {
t.Skip()
}
if val, exists := os.LookupEnv("SKIP_GINKGO"); exists && val == "1" {
t.Skip()
}
t.Parallel()
RegisterFailHandler(Fail)
RunSpecs(t, "GitRepo Suite")
}

9
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/jszwec/csvutil v1.8.0
github.com/moby/buildkit v0.11.4
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/gomega v1.24.2
github.com/onsi/gomega v1.27.0
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
github.com/sirupsen/logrus v1.9.0
@ -35,7 +35,7 @@ require (
go.opencensus.io v0.24.0
gocloud.dev v0.26.0
golang.org/x/text v0.7.0
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2
golang.org/x/tools v0.6.0
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v2 v2.4.0
@ -49,7 +49,8 @@ require (
github.com/gobwas/glob v0.2.3
github.com/google/osv-scanner v1.2.1-0.20230302232134-592acbc2539b
github.com/mcuadros/go-jsonschema-generator v0.0.0-20200330054847-ba7a369d4303
github.com/onsi/ginkgo/v2 v2.7.0
github.com/onsi/ginkgo/v2 v2.8.3
github.com/otiai10/copy v1.9.0
sigs.k8s.io/release-utils v0.6.0
)
@ -67,8 +68,10 @@ require (
github.com/CycloneDX/cyclonedx-go v0.7.0 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/googleapis/gnostic v0.4.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect

22
go.sum
View File

@ -406,6 +406,8 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
@ -525,6 +527,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@ -779,8 +782,8 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.8.3 h1:RpbK1G8nWPNaCVFBWsOGnEQQGgASi6b8fxcWBvDYjxQ=
github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -788,13 +791,20 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE=
github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk=
github.com/onsi/gomega v1.27.0 h1:QLidEla4bXUuZVFa4KX6JHCsuGgbi85LC/pCHrt/O08=
github.com/onsi/gomega v1.27.0/go.mod h1:i189pavgK95OSIipFBa74gC2V4qrQuvjuyGEr3GmbXA=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4=
github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI=
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 h1:DiLBVp4DAcZlBVBEtJpNWZpZVq0AEeCY7Hqk8URVs4o=
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@ -1347,8 +1357,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 h1:v0FhRDmSCNH/0EurAT6T8KRY4aNuUhz6/WwBMxG+gvQ=
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6 h1:XZD8apnMaMVuqE3ZEzf5JJncKMlOsMnnov7U+JRT/d4=
golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=