diff --git a/extensions/internal/github/check_suite.go b/extensions/internal/github/check_suite.go new file mode 100644 index 00000000..ac3f3247 --- /dev/null +++ b/extensions/internal/github/check_suite.go @@ -0,0 +1,283 @@ +package github + +import ( + "context" + "io" + "time" + + "github.com/augmentable-dev/vtab" + "github.com/shurcooL/githubv4" + "go.riyazali.net/sqlite" + "go.uber.org/zap" +) + +// fetch checkRun from the checkSuiteConnection +type checkSuite struct { + App struct { + Name githubv4.String + LogoUrl githubv4.URI + } + Branch struct { + Name githubv4.String + } + Commit struct { + Oid githubv4.GitObjectID + } + Creator struct { + Login githubv4.String + } + Conclusion githubv4.CheckConclusionState + CreatedAt githubv4.DateTime + Repository struct { + NameWithOwner string + } + ResourcePath githubv4.URI + UpdatedAt githubv4.DateTime + Url githubv4.URI + WorkflowRun struct { + Workflow struct { + Name githubv4.String + } + } + CheckRuns struct { + Nodes []*checkRun + } `graphql:"checkRuns(first: 50)"` +} + +type checkRunRow struct { + checkRun + AppName string + AppLogoUrl string + BranchName string + CommitId string + User string + WorkflowName string + WorkflowConclusion string + Repository string +} +type checkRun struct { + Name githubv4.String + Title githubv4.String + Conclusion githubv4.CheckConclusionState + Summary githubv4.String + Status githubv4.String + StartedAt githubv4.DateTime + CompletedAt githubv4.DateTime + Url githubv4.URI +} + +type ref struct { + Name string + Target struct { + Commit struct { + CheckSuites struct { + Nodes []*checkSuite + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + } `graphql:"checkSuites(first: 50)"` + } `graphql:"... on Commit"` + } +} + +type fetchCheckSuiteResults struct { + Edges []*checkRunRow + HasNextPage bool + EndCursor *githubv4.String +} + +type iterCheckSuites struct { + *Options + owner string + name string + current int + results *fetchCheckSuiteResults +} + +func (i *iterCheckSuites) fetchCheckSuiteResults(ctx context.Context, startCursor *githubv4.String) (*fetchCheckSuiteResults, error) { + var CheckSuiteQuery struct { + Repository struct { + Owner struct { + Login string + } + Name string + Refs struct { + Nodes []*ref + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + } `graphql:"refs(first: $perpage, after: $checkCursor, refPrefix: \"refs/heads/\")"` + } `graphql:"repository(owner: $owner, name: $name)"` + } + + variables := map[string]interface{}{ + "owner": githubv4.String(i.owner), + "name": githubv4.String(i.name), + "perpage": githubv4.Int(i.PerPage), + "checkCursor": startCursor, + } + err := i.Client().Query(ctx, &CheckSuiteQuery, variables) + if err != nil { + return nil, err + } + rows := getCheckRowsFromRefs(CheckSuiteQuery.Repository.Refs.Nodes) + return &fetchCheckSuiteResults{rows, CheckSuiteQuery.Repository.Refs.PageInfo.HasNextPage, &CheckSuiteQuery.Repository.Refs.PageInfo.EndCursor}, nil +} + +func (i *iterCheckSuites) logger() *zap.SugaredLogger { + logger := i.Logger.Sugar().With("per-page", i.PerPage, "owner", i.owner, "name", i.name) + return logger +} + +func (i *iterCheckSuites) Next() (vtab.Row, error) { + i.current += 1 + + if i.results == nil || i.current >= len(i.results.Edges) { + if i.results == nil || i.results.HasNextPage { + err := i.RateLimiter.Wait(context.Background()) + if err != nil { + return nil, err + } + + var cursor *githubv4.String + if i.results != nil { + cursor = i.results.EndCursor + } + + i.logger().With("cursor", cursor).Infof("fetching page of repo_check_runs for %s/%s", i.owner, i.name) + results, err := i.fetchCheckSuiteResults(context.Background(), cursor) + if err != nil { + return nil, err + } + + i.results = results + i.current = 0 + + } else { + return nil, io.EOF + } + } + + return i, nil +} + +var checkCols = []vtab.Column{ + {Name: "owner", Type: "TEXT", NotNull: true, Hidden: true, Filters: []*vtab.ColumnFilter{{Op: sqlite.INDEX_CONSTRAINT_EQ, Required: true, OmitCheck: true}}}, + {Name: "reponame", Type: "TEXT", NotNull: true, Hidden: true, Filters: []*vtab.ColumnFilter{{Op: sqlite.INDEX_CONSTRAINT_EQ, OmitCheck: true}}}, + {Name: "name", Type: "TEXT"}, + {Name: "workflow_name", Type: "TEXT"}, + {Name: "repository", Type: "TEXT"}, + {Name: "branch", Type: "TEXT"}, + {Name: "commitId", Type: "TEXT"}, + {Name: "conclusion", Type: "TEXT"}, + {Name: "workflow_conclusion", Type: "TEXT"}, + {Name: "status", Type: "TEXT"}, + {Name: "summary", Type: "TEXT"}, + {Name: "user", Type: "TEXT"}, + {Name: "started_at", Type: "DATETIME"}, + {Name: "completed_at", Type: "DATETIME"}, + {Name: "url", Type: "TEXT"}, + {Name: "app_name", Type: "TEXT"}, + {Name: "app_logo_url", Type: "TEXT"}, +} + +func (i *iterCheckSuites) Column(ctx *sqlite.Context, c int) error { + current := i.results.Edges[i.current] + col := checkCols[c] + + switch col.Name { + case "branch": + ctx.ResultText(current.BranchName) + case "commitId": + ctx.ResultText(current.CommitId) + case "name": + ctx.ResultText(string(current.Name)) + case "repository": + ctx.ResultText(current.Repository) + case "workflow_name": + ctx.ResultText(current.WorkflowName) + case "conclusion": + ctx.ResultText(string(current.Conclusion)) + case "summary": + ctx.ResultText(string(current.Summary)) + case "status": + ctx.ResultText(string(current.Status)) + case "user": + ctx.ResultText(current.User) + case "url": + ctx.ResultText(current.Url.String()) + case "app_name": + ctx.ResultText(current.AppName) + case "app_logo_url": + ctx.ResultText(current.AppLogoUrl) + case "workflow_conclusion": + ctx.ResultText(current.WorkflowConclusion) + case "started_at": + t := current.StartedAt + if t.IsZero() { + ctx.ResultNull() + } else { + ctx.ResultText(t.Format(time.RFC3339Nano)) + } + case "completed_at": + t := current.CompletedAt + if t.IsZero() { + ctx.ResultNull() + } else { + ctx.ResultText(t.Format(time.RFC3339Nano)) + } + } + return nil +} + +func NewCheckModule(opts *Options) sqlite.Module { + return vtab.NewTableFunc("github_repo_checks", checkCols, func(constraints []*vtab.Constraint, orders []*sqlite.OrderBy) (vtab.Iterator, error) { + var fullNameOrOwner, name string + for _, constraint := range constraints { + if constraint.Op == sqlite.INDEX_CONSTRAINT_EQ { + switch constraint.ColIndex { + case 0: + fullNameOrOwner = constraint.Value.Text() + case 1: + name = constraint.Value.Text() + } + } + } + + owner, name, err := repoOwnerAndName(name, fullNameOrOwner) + if err != nil { + return nil, err + } + + iter := &iterCheckSuites{opts, owner, name, -1, nil} + iter.logger().Infof("starting GitHub check iterator for %s/%s", owner, name) + return iter, nil + }) +} + +func getCheckRowsFromRefs(refs []*ref) []*checkRunRow { + var rows []*checkRunRow + for _, r := range refs { + for _, suite := range r.Target.Commit.CheckSuites.Nodes { + for _, check := range suite.CheckRuns.Nodes { + if check.Name != "" { + var row = checkRunRow{ + checkRun: *check, + AppName: string(suite.App.Name), + AppLogoUrl: suite.App.LogoUrl.String(), + BranchName: string(suite.Branch.Name), + CommitId: string(suite.Commit.Oid), + User: string(suite.Creator.Login), + WorkflowName: string(suite.WorkflowRun.Workflow.Name), + WorkflowConclusion: string(suite.Conclusion), + Repository: suite.Repository.NameWithOwner, + } + rows = append(rows, &row) + } + } + } + } + return rows +} diff --git a/extensions/internal/github/github.go b/extensions/internal/github/github.go index 15a97f8f..5b1d6dfe 100644 --- a/extensions/internal/github/github.go +++ b/extensions/internal/github/github.go @@ -48,6 +48,7 @@ func Register(ext *sqlite.ExtensionApi, opt *options.Options) (_ sqlite.ErrorCod "github_org_repos": NewOrgReposModule(githubOpts), "github_repo_issues": NewIssuesModule(githubOpts), "github_repo_pull_requests": NewPRModule(githubOpts), + "github_repo_checks": NewCheckModule(githubOpts), } modules["github_issues"] = modules["github_repo_issues"] diff --git a/go.mod b/go.mod index 766f07ab..d737585e 100644 --- a/go.mod +++ b/go.mod @@ -32,12 +32,10 @@ require ( go.mongodb.org/mongo-driver v1.7.1 // indirect go.riyazali.net/sqlite v0.0.0-20210803070208-9d9dbac3dd38 go.uber.org/zap v1.17.0 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/mod v0.5.0 - golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f - golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b + golang.org/x/term v0.1.0 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac google.golang.org/protobuf v1.27.1 // indirect ) diff --git a/go.sum b/go.sum index e147dec6..45e16939 100644 --- a/go.sum +++ b/go.sum @@ -401,6 +401,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -438,8 +439,9 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -475,8 +477,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -517,8 +519,9 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -545,6 +548,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -604,12 +608,15 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4= -golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -617,8 +624,10 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -680,6 +689,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=