├── .gitignore ├── CODEOWNERS ├── main.go ├── grafana ├── docker-compose.yml ├── grafana.ini ├── Dockerfile ├── dashboards │ └── dashboard.yml └── provisioning │ └── datasources │ └── datasource.yml ├── Makefile ├── .golangci.yml ├── cmd ├── export.go ├── stamp.go └── root.go ├── Dockerfile ├── step.sh ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── release.yml │ ├── ci.yml │ ├── nightly.yml │ └── metrics.yml ├── internal ├── metrics │ ├── last_run.go │ ├── issues_agg.go │ ├── pull_requests_agg.go │ ├── releases.go │ ├── issues.go │ └── pull_requests.go ├── githubclient │ ├── issue_count.go │ ├── pull_request_count.go │ ├── releases.go │ ├── client.go │ ├── issues.go │ └── pull_requests.go ├── bigqueryclient │ └── client.go ├── stamp │ └── stamp.go ├── configuration │ ├── configuration_test.go │ └── configuration.go └── export │ └── export.go ├── .goreleaser.yml ├── LICENSE ├── step.yml ├── CHANGELOG.md ├── README.md ├── go.mod └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | workflow.yml 3 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Setting ownership to the tooling team 2 | * @puppetlabs/devx 3 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/puppetlabs/cat-team-github-metrics/cmd" 7 | ) 8 | 9 | func main() { 10 | os.Exit(cmd.Execute()) 11 | } 12 | -------------------------------------------------------------------------------- /grafana/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | grafana: 4 | build: 5 | context: . 6 | container_name: grafana 7 | ports: 8 | - 9000:3000 9 | volumes: 10 | - grafana-data:/var/lib/grafana 11 | # Explicitly define the persistent volume for your data storage 12 | volumes: 13 | grafana-data: 14 | external: true 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | tag: 2 | @git tag -a $(version) -m "Release $(version)" 3 | @git push --follow-tags 4 | 5 | lint: 6 | @golangci-lint run ./... 7 | 8 | build: lint 9 | @WORKINGDIR=$(pwd) goreleaser build --snapshot --clean 10 | 11 | snapshot: 12 | @WORKINGDIR=$(pwd) goreleaser release --snapshot --clean 13 | 14 | release: lint snapshot 15 | @docker push ghcr.io/puppetlabs/cat-team-github-metrics:dev 16 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - dogsled 4 | - dupl 5 | - gofmt 6 | - goimports 7 | - gosec 8 | - misspell 9 | - nakedret 10 | - stylecheck 11 | - unconvert 12 | - unparam 13 | - whitespace 14 | - errcheck 15 | - gosimple 16 | - staticcheck 17 | - ineffassign 18 | - unused 19 | - staticcheck 20 | issues: 21 | exclude-use-default: false 22 | -------------------------------------------------------------------------------- /cmd/export.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/puppetlabs/cat-team-github-metrics/internal/export" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var exportCmd = &cobra.Command{ 9 | Use: "export", 10 | Short: "Exports GitHub metrics to BigQuery", 11 | Long: "Exports GitHub metrics to BigQuery", 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return export.Run() 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM ubuntu:jammy 3 | 4 | RUN apt-get update && apt-get install -y pcre2-utils ca-certificates && update-ca-certificates 5 | 6 | WORKDIR /app 7 | 8 | COPY linux-amd64/collector . 9 | COPY step.sh . 10 | CMD ["sh", "-c", "./step.sh"] 11 | 12 | LABEL "org.opencontainers.image.title"="cat-team-github-metrics" 13 | LABEL "org.opencontainers.image.description"="A step to collect metrics from GitHub and publish them to BigQuery." 14 | -------------------------------------------------------------------------------- /cmd/stamp.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/puppetlabs/cat-team-github-metrics/internal/stamp" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var stampCmd = &cobra.Command{ 9 | Use: "stamp", 10 | Short: "Updates the watermark table with the current timestamp", 11 | Long: "Updates the watermark table with the current timestamp", 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return stamp.Run() 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /step.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export GOOGLE_APPLICATION_CREDENTIALS="$GOOGLE_APPLICATION_CREDENTIALS" 4 | export GITHUB_TOKEN="$GITHUB_TOKEN" 5 | export BIG_QUERY_PROJECT_ID="$BQ_PROJECT_ID" 6 | export REPO_OWNER="$REPO_OWNER" 7 | export REPO_NAME="$REPO_NAME" 8 | 9 | COMMAND="$COBRA_COMMAND" 10 | 11 | case "$COMMAND" in 12 | export|stamp) ./collector "$COMMAND";; 13 | *) echo "Invalid command! Should be one of [export, stamp]." && exit 1;; 14 | esac 15 | -------------------------------------------------------------------------------- /.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/github/administering-a-repository/configuration-options-for-dependency-updates 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: "daily" 12 | -------------------------------------------------------------------------------- /grafana/grafana.ini: -------------------------------------------------------------------------------- 1 | [paths] 2 | provisioning = /etc/grafana/provisioning 3 | 4 | [server] 5 | enable_gzip = true 6 | # To add HTTPS support: 7 | #protocol = https 8 | #;http_addr = 9 | #http_port = 3000 10 | #domain = localhost 11 | #enforce_domain = false 12 | #root_url = https://localhost:3000 13 | #router_logging = false 14 | #static_root_path = public 15 | #cert_file = /etc/certs/cert.pem 16 | #cert_key = /etc/certs/cert-key.pem 17 | 18 | [security] 19 | # If you want to embed grafana into an iframe for example 20 | allow_embedding = true 21 | 22 | [users] 23 | default_theme = dark 24 | -------------------------------------------------------------------------------- /grafana/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM grafana/grafana-oss:8.5.3 2 | 3 | # Disable Login form or not 4 | ENV GF_AUTH_DISABLE_LOGIN_FORM "true" 5 | # Allow anonymous authentication or not 6 | ENV GF_AUTH_ANONYMOUS_ENABLED "true" 7 | # Role of anonymous user 8 | ENV GF_AUTH_ANONYMOUS_ORG_ROLE "Admin" 9 | # Install plugins here our in your own config file 10 | ENV GF_INSTALL_PLUGINS="grafana-bigquery-datasource" 11 | 12 | # Add provisioning 13 | ADD ./provisioning /etc/grafana/provisioning 14 | # Add configuration file 15 | ADD ./grafana.ini /etc/grafana/grafana.ini 16 | # Add dashboard json files 17 | ADD ./dashboards /etc/grafana/dashboards 18 | -------------------------------------------------------------------------------- /internal/metrics/last_run.go: -------------------------------------------------------------------------------- 1 | // Package metrics contains methods that are responsible for mapping responses to metrics 2 | // that can be sent to BigQuery. 3 | package metrics 4 | 5 | import ( 6 | "time" 7 | 8 | "cloud.google.com/go/bigquery" 9 | ) 10 | 11 | // LastRunMetric is a struct that implements the ValueSaver interface for saving to BigQuery 12 | type LastRunMetric struct { 13 | LastRunTime time.Time 14 | CollectionTime time.Time 15 | } 16 | 17 | func (i *LastRunMetric) Save() (map[string]bigquery.Value, string, error) { 18 | return map[string]bigquery.Value{ 19 | "LastRunTime": i.LastRunTime.Unix(), 20 | "CollectionTime": i.CollectionTime.Unix(), 21 | }, "", nil 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | pull_request: 5 | branches: [ "main" ] 6 | schedule: 7 | - cron: '29 22 * * 0' 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | language: [ 'go' ] 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - name: Initialize CodeQL 28 | uses: github/codeql-action/init@v2 29 | with: 30 | languages: ${{ matrix.language }} 31 | 32 | - name: Autobuild 33 | uses: github/codeql-action/autobuild@v2 34 | 35 | - name: Perform CodeQL Analysis 36 | uses: github/codeql-action/analyze@v2 37 | -------------------------------------------------------------------------------- /grafana/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | # config file version 2 | apiVersion: 1 3 | 4 | providers: 5 | # an unique provider name 6 | - name: My Dashboard 7 | # org id. will default to orgId 1 if not specified 8 | org_id: 1 9 | # name of the dashboard folder. Required 10 | folder: '' 11 | # provider type. Required 12 | type: 'file' 13 | # disable dashboard deletion 14 | disableDeletion: false 15 | # enable dashboard editing 16 | editable: true 17 | # how often Grafana will scan for changed dashboards 18 | updateIntervalSeconds: 5 19 | # allow updating provisioned dashboards from the UI 20 | allowUiUpdates: true 21 | options: 22 | # path to dashboard files on disk. Required 23 | path: /etc/grafana/dashboards 24 | # use folder names from filesystem to create folders in Grafana 25 | foldersFromFilesStructure: true 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: release 3 | 4 | on: 5 | push: 6 | tags: 7 | - v* 8 | workflow_dispatch: 9 | 10 | env: 11 | GO_VERSION: 1.18 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | env: 17 | WORKINGDIR: ${{ github.workspace }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - uses: actions/setup-go@v4 25 | with: 26 | go-version: ${{ env.GO_VERSION }} 27 | 28 | - name: Log into registry 🐳 29 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 30 | 31 | - name: Run GoReleaser 32 | uses: goreleaser/goreleaser-action@v4 33 | with: 34 | version: latest 35 | args: release --clean 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Package cmd holds all of the entry points for the cobra app. 2 | package cmd 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var errSilent = errors.New("ErrSilent") 13 | 14 | var rootCmd = &cobra.Command{ 15 | Use: "collector", 16 | Short: "Collects GitHub metrics", 17 | Long: "Collects GitHub metrics and exports them to BigQuery", 18 | SilenceUsage: true, 19 | SilenceErrors: true, 20 | Run: nil, 21 | } 22 | 23 | func init() { 24 | rootCmd.AddCommand(exportCmd) 25 | rootCmd.AddCommand(stampCmd) 26 | } 27 | 28 | // Execute is called from main and is responsible for processing 29 | // requests to the application and handling exit codes appropriately 30 | func Execute() int { 31 | if err := rootCmd.Execute(); err != nil { 32 | if err != errSilent { 33 | fmt.Fprintln(os.Stderr, fmt.Errorf("❌ %s", err)) 34 | } 35 | return 1 36 | } 37 | return 0 38 | } 39 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: cat-team-github-metrics 2 | 3 | release: 4 | name_template: "cat-team-github-metrics {{.Version}}" 5 | prerelease: auto 6 | 7 | before: 8 | hooks: 9 | - go mod tidy 10 | - go fmt ./... 11 | 12 | builds: 13 | - binary: "{{ tolower .Os }}-{{ .Arch }}/collector" 14 | env: 15 | - CGO_ENABLED=0 16 | goos: 17 | - linux 18 | goarch: 19 | - amd64 20 | asmflags: 21 | - all=-trimpath={{.Env.WORKINGDIR}} 22 | gcflags: 23 | - all=-trimpath={{.Env.WORKINGDIR}} 24 | mod_timestamp: '{{ .CommitTimestamp }}' 25 | no_unique_dist_dir: true 26 | 27 | dockers: 28 | - image_templates: 29 | - "ghcr.io/puppetlabs/{{ .ProjectName }}:latest" 30 | - "ghcr.io/puppetlabs/{{ .ProjectName }}:{{ .Tag }}" 31 | extra_files: 32 | - step.sh 33 | 34 | checksum: 35 | name_template: 'checksums.txt' 36 | 37 | changelog: 38 | sort: asc 39 | filters: 40 | exclude: 41 | - '^docs:' 42 | - '^test:' 43 | -------------------------------------------------------------------------------- /internal/githubclient/issue_count.go: -------------------------------------------------------------------------------- 1 | package githubclient 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/shurcooL/githubv4" 7 | ) 8 | 9 | type IssueCount struct { 10 | Repository string `json:"repository"` 11 | Count int `json:"count"` 12 | } 13 | 14 | var OpenIssueCountQuery struct { 15 | Repository struct { 16 | Issues struct { 17 | TotalCount int 18 | } `graphql:"issues(states: OPEN)"` 19 | } `graphql:"repository(owner: $owner, name: $name)"` 20 | } 21 | 22 | func (client *githubClient) GetOpenIssueCount(ctx context.Context, owner string, name string) (IssueCount, error) { 23 | variables := map[string]interface{}{ 24 | "owner": githubv4.String(owner), 25 | "name": githubv4.String(name), 26 | } 27 | 28 | err := client.v4.Query(ctx, &OpenIssueCountQuery, variables) 29 | if err != nil { 30 | return IssueCount{}, err 31 | } 32 | 33 | openIssues := IssueCount{ 34 | Repository: name, 35 | Count: OpenIssueCountQuery.Repository.Issues.TotalCount, 36 | } 37 | 38 | return openIssues, nil 39 | } 40 | -------------------------------------------------------------------------------- /internal/githubclient/pull_request_count.go: -------------------------------------------------------------------------------- 1 | package githubclient 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/shurcooL/githubv4" 7 | ) 8 | 9 | type PullRequestCount struct { 10 | Repository string `json:"repository"` 11 | Count int `json:"count"` 12 | } 13 | 14 | var OpenPullRequestCountQuery struct { 15 | Repository struct { 16 | PullRequests struct { 17 | TotalCount int 18 | } `graphql:"pullRequests(states: OPEN)"` 19 | } `graphql:"repository(owner: $owner, name: $name)"` 20 | } 21 | 22 | func (client *githubClient) GetOpenPullRequestCount(ctx context.Context, owner string, name string) (PullRequestCount, error) { 23 | variables := map[string]interface{}{ 24 | "owner": githubv4.String(owner), 25 | "name": githubv4.String(name), 26 | } 27 | 28 | err := client.v4.Query(ctx, &OpenPullRequestCountQuery, variables) 29 | if err != nil { 30 | return PullRequestCount{}, err 31 | } 32 | 33 | openPullRequests := PullRequestCount{ 34 | Repository: name, 35 | Count: OpenPullRequestCountQuery.Repository.PullRequests.TotalCount, 36 | } 37 | 38 | return openPullRequests, nil 39 | } 40 | -------------------------------------------------------------------------------- /internal/githubclient/releases.go: -------------------------------------------------------------------------------- 1 | package githubclient 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/shurcooL/githubv4" 8 | ) 9 | 10 | type Release struct { 11 | Repository string 12 | Name string 13 | PublishedAt time.Time 14 | } 15 | 16 | var ReleaseQuery struct { 17 | Repository struct { 18 | LatestRelease struct { 19 | TagName string 20 | PublishedAt time.Time 21 | } 22 | } `graphql:"repository(owner: $owner, name: $name)"` 23 | } 24 | 25 | func (client *githubClient) GetLatestRelease(ctx context.Context, owner string, name string) ([]Release, error) { 26 | variables := map[string]interface{}{ 27 | "owner": githubv4.String(owner), 28 | "name": githubv4.String(name), 29 | } 30 | 31 | err := client.v4.Query(ctx, &ReleaseQuery, variables) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | var metric []Release 37 | 38 | metric = append(metric, Release{ 39 | Repository: name, 40 | Name: ReleaseQuery.Repository.LatestRelease.TagName, 41 | PublishedAt: ReleaseQuery.Repository.LatestRelease.PublishedAt, 42 | }) 43 | 44 | return metric, nil 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Craig Gumbley 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 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: ci 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | workflow_dispatch: 9 | 10 | env: 11 | GO_VERSION: 1.18 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Set up Go 23 | uses: actions/setup-go@v4 24 | with: 25 | go-version: ${{ env.GO_VERSION }} 26 | 27 | - name: lint 28 | uses: golangci/golangci-lint-action@v3 29 | with: 30 | version: latest 31 | 32 | - name: Run GoReleaser 33 | uses: goreleaser/goreleaser-action@v4 34 | with: 35 | distribution: goreleaser 36 | version: latest 37 | args: --snapshot --clean 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | WORKINGDIR: ${{ env.GITHUB_WORKSPACE }} 41 | 42 | - name: Trivy Scan 43 | uses: docker://docker.io/aquasec/trivy:0.45.0 44 | with: 45 | args: 'i --vuln-type os --ignore-unfixed -s CRITICAL ghcr.io/puppetlabs/cat-team-github-metrics:latest' 46 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: nightly 3 | 4 | on: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | workflow_dispatch: 8 | 9 | env: 10 | GO_VERSION: 1.18 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@v4 23 | with: 24 | go-version: ${{ env.GO_VERSION }} 25 | 26 | - name: lint 27 | uses: golangci/golangci-lint-action@v3 28 | with: 29 | version: latest 30 | 31 | - name: Run GoReleaser 32 | uses: goreleaser/goreleaser-action@v4 33 | with: 34 | distribution: goreleaser 35 | version: latest 36 | args: --snapshot --clean 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | WORKINGDIR: ${{ env.GITHUB_WORKSPACE }} 40 | 41 | - name: Trivy Scan 42 | uses: docker://docker.io/aquasec/trivy:0.45.0 43 | with: 44 | args: 'i --vuln-type os --ignore-unfixed -s CRITICAL ghcr.io/puppetlabs/cat-team-github-metrics:latest' 45 | -------------------------------------------------------------------------------- /internal/metrics/issues_agg.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "cloud.google.com/go/bigquery" 8 | "github.com/puppetlabs/cat-team-github-metrics/internal/githubclient" 9 | ) 10 | 11 | type IssueAggregatedMetric struct { 12 | Repository string 13 | Count int 14 | CollectionTime time.Time 15 | } 16 | 17 | func (i *IssueAggregatedMetric) Save() (map[string]bigquery.Value, string, error) { 18 | return map[string]bigquery.Value{ 19 | "Repository": i.Repository, 20 | "Count": i.Count, 21 | "CollectionTime": i.CollectionTime.Unix(), 22 | }, "", nil 23 | } 24 | 25 | func GetIssueAggregatedMetrics(org string, repo string) (IssueAggregatedMetric, error) { 26 | client, err := githubclient.NewGitHubClient() 27 | if err != nil { 28 | return IssueAggregatedMetric{}, err 29 | } 30 | 31 | metrics, err := client.GetOpenIssueCount(context.Background(), org, repo) 32 | if err != nil { 33 | return IssueAggregatedMetric{}, err 34 | } 35 | 36 | issueMetric := IssueAggregatedMetric{ 37 | Repository: metrics.Repository, 38 | Count: metrics.Count, 39 | CollectionTime: time.Now(), 40 | } 41 | 42 | return issueMetric, nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/metrics/pull_requests_agg.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "cloud.google.com/go/bigquery" 8 | "github.com/puppetlabs/cat-team-github-metrics/internal/githubclient" 9 | ) 10 | 11 | type PullRequestAggregatedMetric struct { 12 | Repository string 13 | Count int 14 | CollectionTime time.Time 15 | } 16 | 17 | func (i *PullRequestAggregatedMetric) Save() (map[string]bigquery.Value, string, error) { 18 | return map[string]bigquery.Value{ 19 | "Repository": i.Repository, 20 | "Count": i.Count, 21 | "CollectionTime": i.CollectionTime.Unix(), 22 | }, "", nil 23 | } 24 | 25 | func GetPullRequestAggregatedMetrics(org string, repo string) (PullRequestAggregatedMetric, error) { 26 | client, err := githubclient.NewGitHubClient() 27 | if err != nil { 28 | return PullRequestAggregatedMetric{}, err 29 | } 30 | 31 | metrics, err := client.GetOpenPullRequestCount(context.Background(), org, repo) 32 | if err != nil { 33 | return PullRequestAggregatedMetric{}, err 34 | } 35 | 36 | pullRequestMetric := PullRequestAggregatedMetric{ 37 | Repository: metrics.Repository, 38 | Count: metrics.Count, 39 | CollectionTime: time.Now(), 40 | } 41 | 42 | return pullRequestMetric, nil 43 | } 44 | -------------------------------------------------------------------------------- /step.yml: -------------------------------------------------------------------------------- 1 | apiVersion: integration/v1 2 | kind: Step 3 | name: cat-team-github-metrics 4 | version: 1 5 | summary: A relay step to collect metrics from GitHub and publish them to BigQuery. 6 | 7 | description: | 8 | This step will invoke the metric collector app which will scrape the provided repository 9 | for metrics and upload them to Big Query. 10 | 11 | build: 12 | apiVersion: build/v1 13 | kind: Docker 14 | 15 | publish: 16 | repository: puppetlabs/cat-team-github-metrics 17 | 18 | schemas: 19 | spec: 20 | $schema: http://json-schema.org/draft-07/schema# 21 | type: object 22 | properties: 23 | github_token: 24 | type: string 25 | description: A GitHub PAT token. 26 | writeOnly: true 27 | big_query_project_id: 28 | type: string 29 | description: The GCP project id that contains the Big Query instance. 30 | writeOnly: true 31 | repo_owner: 32 | type: string 33 | description: The owner of the GitHub repository (e.g. puppetlabs) 34 | repo_name: 35 | type: string 36 | description: The name of the GitHub repository (e.g. puppetlabs-docker) 37 | required: 38 | - github_token 39 | - big_query_project_id 40 | - repo_owner 41 | - repo_name 42 | -------------------------------------------------------------------------------- /grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | # config file version 2 | apiVersion: 1 3 | 4 | # list of datasources to insert/update 5 | datasources: 6 | # name of the datasource. Required 7 | - name: My datasource 8 | # datasource type. Required 9 | type: postgres 10 | # access mode. proxy or direct (Server or Browser in the UI). Required 11 | access: proxy 12 | # org id. will default to orgId 1 if not specified 13 | orgId: 1 14 | # custom UID which can be used to reference this datasource in other parts of the configuration, if not specified will be generated automatically 15 | # uid: my_unique_uid 16 | # url 17 | url: 18 | user: 19 | # database name 20 | database: postgres 21 | # allow updating provisioned dashboards from the UI 22 | allowUiUpdates: true 23 | # mark as default datasource. Max one per org 24 | isDefault: true 25 | # fields that will be converted to json and stored in jsonData 26 | jsonData: 27 | sslmode: 'disable' # disable/require/verify-ca/verify-full 28 | postgresVersion: 1200 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10 29 | timescaledb: false 30 | secureJsonData: 31 | # database password, if used 32 | password: "" 33 | version: 1 34 | # allow users to edit datasources from the UI. 35 | editable: true 36 | -------------------------------------------------------------------------------- /internal/bigqueryclient/client.go: -------------------------------------------------------------------------------- 1 | // Package bigqueryclient is responsible for interacting with the BigQuery API. 2 | package bigqueryclient 3 | 4 | import ( 5 | "context" 6 | 7 | "cloud.google.com/go/bigquery" 8 | ) 9 | 10 | type BigQueryClient interface { 11 | CreateTable(table string, schema interface{}) error 12 | Insert(table string, rows interface{}) error 13 | } 14 | 15 | type bigqueryClient struct { 16 | client *bigquery.Client 17 | dataset string 18 | context context.Context 19 | } 20 | 21 | func (bq *bigqueryClient) CreateTable(table string, schema interface{}) error { 22 | s, err := bigquery.InferSchema(schema) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | t := bq.client.Dataset(bq.dataset).Table(table) 28 | 29 | if err := t.Create(bq.context, &bigquery.TableMetadata{Schema: s}); err != nil { 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | 36 | func (bq *bigqueryClient) Insert(table string, rows interface{}) error { 37 | inserter := bq.client.Dataset(bq.dataset).Table(table).Inserter() 38 | if err := inserter.Put(bq.context, rows); err != nil { 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func NewBigQueryClient(ctx context.Context, projectID string, dataset string) (BigQueryClient, error) { 46 | client, err := bigquery.NewClient(ctx, projectID) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return &bigqueryClient{client: client, dataset: dataset, context: ctx}, nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/githubclient/client.go: -------------------------------------------------------------------------------- 1 | // Package githubclient is responsible for interacting with the GitHub API 2 | package githubclient 3 | 4 | import ( 5 | "context" 6 | "errors" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/shurcooL/githubv4" 11 | "golang.org/x/oauth2" 12 | ) 13 | 14 | type GitHubClient interface { 15 | GetIssues(ctx context.Context, owner string, repo string) ([]Issue, error) 16 | GetOpenIssueCount(ctx context.Context, owner string, repo string) (IssueCount, error) 17 | GetPullRequests(ctx context.Context, owner string, repo string) ([]PullRequest, error) 18 | GetOpenPullRequestCount(ctx context.Context, owner string, repo string) (PullRequestCount, error) 19 | GetLatestRelease(ctx context.Context, owner string, repo string) ([]Release, error) 20 | } 21 | 22 | type githubClient struct { 23 | v4 *githubv4.Client 24 | } 25 | 26 | func NewGitHubClient() (GitHubClient, error) { 27 | httpClient, err := newOauthHTTPClient() 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | v4Client := githubv4.NewClient(httpClient) 33 | 34 | client := &githubClient{ 35 | v4: v4Client, 36 | } 37 | 38 | return client, nil 39 | } 40 | 41 | func newOauthHTTPClient() (*http.Client, error) { 42 | token := os.Getenv("GITHUB_TOKEN") 43 | if token == "" { 44 | return nil, errors.New("GITHUB_TOKEN is not set") 45 | } 46 | 47 | tokenSource := oauth2.StaticTokenSource( 48 | &oauth2.Token{AccessToken: token}, 49 | ) 50 | 51 | httpClient := oauth2.NewClient(context.Background(), tokenSource) 52 | 53 | return httpClient, nil 54 | } 55 | -------------------------------------------------------------------------------- /internal/stamp/stamp.go: -------------------------------------------------------------------------------- 1 | // Package stamp is respnsible for stamping the last run time to BigQuery. 2 | package stamp 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | "github.com/puppetlabs/cat-team-github-metrics/internal/bigqueryclient" 11 | "github.com/puppetlabs/cat-team-github-metrics/internal/configuration" 12 | "github.com/puppetlabs/cat-team-github-metrics/internal/metrics" 13 | "github.com/rs/zerolog" 14 | "github.com/rs/zerolog/log" 15 | ) 16 | 17 | var bq bigqueryclient.BigQueryClient 18 | 19 | func Run() error { 20 | var err error 21 | 22 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) 23 | 24 | err = configuration.InitConfig() 25 | if err != nil { 26 | return fmt.Errorf("config error: %w", err) 27 | } 28 | 29 | config := configuration.Config 30 | 31 | log.Info().Msg("Initializing BigQuery client") 32 | ctx := context.Background() 33 | bq, err = bigqueryclient.NewBigQueryClient(ctx, config.BigQueryProjectID, config.BigQueryDatasetName) 34 | if err != nil { 35 | return fmt.Errorf("failed to create BigQuery client: %w", err) 36 | } 37 | 38 | lastRunTime := time.Now() 39 | log.Info().Msgf("Last run time: %s", lastRunTime.Format(time.RFC3339)) 40 | 41 | log.Info().Msg("Uploading last run time.") 42 | err = bq.Insert(config.LastRunTable, metrics.LastRunMetric{ 43 | LastRunTime: lastRunTime, 44 | CollectionTime: time.Now(), 45 | }) 46 | 47 | if err != nil { 48 | return fmt.Errorf("failed to insert issue metrics: %w", err) 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org). 6 | ## [v0.3.0](https://github.com/puppetlabs/cat-team-github-metrics/tree/v0.3.0) - 2022-07-12 7 | 8 | [Full Changelog](https://github.com/puppetlabs/cat-team-github-metrics/compare/v0.2.0...v0.3.0) 9 | 10 | ### Added 11 | 12 | - Add aggregations for issues and PRs [#22](https://github.com/puppetlabs/cat-team-github-metrics/pull/22) ([chelnak](https://github.com/chelnak)) 13 | 14 | ## [v0.2.0](https://github.com/puppetlabs/cat-team-github-metrics/tree/v0.2.0) - 2022-07-12 15 | 16 | [Full Changelog](https://github.com/puppetlabs/cat-team-github-metrics/compare/v0.1.1...v0.2.0) 17 | 18 | ### Added 19 | 20 | - Add cobra to the mix [#20](https://github.com/puppetlabs/cat-team-github-metrics/pull/20) ([chelnak](https://github.com/chelnak)) 21 | 22 | ## [v0.1.1](https://github.com/puppetlabs/cat-team-github-metrics/tree/v0.1.1) - 2022-07-12 23 | 24 | [Full Changelog](https://github.com/puppetlabs/cat-team-github-metrics/compare/v0.1.0...v0.1.1) 25 | 26 | ### Fixed 27 | 28 | - (SECURITY) Update prce2 [#12](https://github.com/puppetlabs/cat-team-github-metrics/pull/12) ([chelnak](https://github.com/chelnak)) 29 | 30 | ## [v0.1.0](https://github.com/puppetlabs/cat-team-github-metrics/tree/v0.1.0) - 2022-07-12 31 | 32 | [Full Changelog](https://github.com/puppetlabs/cat-team-github-metrics/compare/e3365c1b9cecfe543bec1349c31c36ad7584295e...v0.1.0) 33 | -------------------------------------------------------------------------------- /internal/metrics/releases.go: -------------------------------------------------------------------------------- 1 | // Package metrics contains methods that are responsible for mapping responses to metrics 2 | // that can be sent to BigQuery. 3 | package metrics 4 | 5 | import ( 6 | "context" 7 | "time" 8 | 9 | "cloud.google.com/go/bigquery" 10 | "github.com/puppetlabs/cat-team-github-metrics/internal/githubclient" 11 | ) 12 | 13 | // ReleaseMetric is a struct that implements the ValueSaver interface for saving to BigQuery 14 | type ReleaseMetric struct { 15 | Repository string 16 | Name string 17 | PublishedAt time.Time 18 | CollectionTime time.Time 19 | } 20 | 21 | func (i *ReleaseMetric) Save() (map[string]bigquery.Value, string, error) { 22 | return map[string]bigquery.Value{ 23 | "Repository": i.Repository, 24 | "Name": i.Name, 25 | "PublishedAt": i.PublishedAt.Unix(), 26 | "CollectionTime": i.CollectionTime.Unix(), 27 | }, "", nil 28 | } 29 | 30 | func GetReleaseMetrics(org string, repo string) ([]ReleaseMetric, error) { 31 | client, err := githubclient.NewGitHubClient() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | metrics, err := client.GetLatestRelease(context.Background(), org, repo) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return mapReleaseMetrics(metrics), nil 42 | } 43 | 44 | func mapReleaseMetrics(metrics []githubclient.Release) []ReleaseMetric { 45 | var mapped []ReleaseMetric 46 | for _, m := range metrics { 47 | mapped = append(mapped, ReleaseMetric{ 48 | Repository: m.Repository, 49 | Name: m.Name, 50 | PublishedAt: m.PublishedAt, 51 | CollectionTime: time.Now(), 52 | }) 53 | } 54 | return mapped 55 | } 56 | -------------------------------------------------------------------------------- /internal/metrics/issues.go: -------------------------------------------------------------------------------- 1 | // Package metrics contains methods that are responsible for mapping responses to metrics 2 | // that can be sent to BigQuery. 3 | package metrics 4 | 5 | import ( 6 | "context" 7 | "time" 8 | 9 | "cloud.google.com/go/bigquery" 10 | "github.com/puppetlabs/cat-team-github-metrics/internal/githubclient" 11 | ) 12 | 13 | // IssueMetric is a struct that implements the ValueSaver interface for saving to BigQuery 14 | type IssueMetric struct { 15 | Repository string 16 | Number int 17 | Title string 18 | CreatedAt time.Time 19 | UpdatedAt time.Time 20 | Labels []string 21 | Author string 22 | State string 23 | CollectionTime time.Time 24 | } 25 | 26 | func (i *IssueMetric) Save() (map[string]bigquery.Value, string, error) { 27 | return map[string]bigquery.Value{ 28 | "Repository": i.Repository, 29 | "Number": i.Number, 30 | "Title": i.Title, 31 | "CreatedAt": i.CreatedAt.Unix(), 32 | "UpdatedAt": i.UpdatedAt.Unix(), 33 | "Labels": i.Labels, 34 | "Author": i.Author, 35 | "State": i.State, 36 | "CollectionTime": i.CollectionTime.Unix(), 37 | }, "", nil 38 | } 39 | 40 | func GetIssueMetrics(org string, repo string) ([]IssueMetric, error) { 41 | client, err := githubclient.NewGitHubClient() 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | metrics, err := client.GetIssues(context.Background(), org, repo) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | return mapIssueMetrics(metrics), nil 52 | } 53 | 54 | func mapIssueMetrics(metrics []githubclient.Issue) []IssueMetric { 55 | var mapped []IssueMetric 56 | for _, m := range metrics { 57 | mapped = append(mapped, IssueMetric{ 58 | Repository: m.Repository, 59 | Number: m.Number, 60 | Title: m.Title, 61 | CreatedAt: m.CreatedAt, 62 | UpdatedAt: m.UpdatedAt, 63 | Labels: m.Labels, 64 | Author: m.Author, 65 | State: m.State, 66 | CollectionTime: time.Now(), 67 | }) 68 | } 69 | return mapped 70 | } 71 | -------------------------------------------------------------------------------- /internal/metrics/pull_requests.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "cloud.google.com/go/bigquery" 8 | "github.com/puppetlabs/cat-team-github-metrics/internal/githubclient" 9 | ) 10 | 11 | // PullRequestMetric is a struct that implements the ValueSaver interface for saving to BigQuery 12 | type PullRequestMetric struct { 13 | Repository string 14 | Number int 15 | Title string 16 | CreatedAt time.Time 17 | UpdatedAt time.Time 18 | Labels []string 19 | Author string 20 | State string 21 | Merged bool 22 | CollectionTime time.Time 23 | } 24 | 25 | func (i *PullRequestMetric) Save() (map[string]bigquery.Value, string, error) { 26 | return map[string]bigquery.Value{ 27 | "Repository": i.Repository, 28 | "Number": i.Number, 29 | "Title": i.Title, 30 | "CreatedAt": i.CreatedAt.Unix(), 31 | "UpdatedAt": i.UpdatedAt.Unix(), 32 | "Labels": i.Labels, 33 | "Author": i.Author, 34 | "State": i.State, 35 | "Merged": i.Merged, 36 | "CollectionTime": i.CollectionTime.Unix(), 37 | }, "", nil 38 | } 39 | 40 | func GetPullRequestMetrics(org string, repo string) ([]PullRequestMetric, error) { 41 | client, err := githubclient.NewGitHubClient() 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | metrics, err := client.GetPullRequests(context.Background(), org, repo) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | return mapPullRequestMetrics(metrics), nil 52 | } 53 | 54 | func mapPullRequestMetrics(metrics []githubclient.PullRequest) []PullRequestMetric { 55 | var mapped []PullRequestMetric 56 | for _, m := range metrics { 57 | mapped = append(mapped, PullRequestMetric{ 58 | Repository: m.Repository, 59 | Number: m.Number, 60 | Title: m.Title, 61 | CreatedAt: m.CreatedAt, 62 | UpdatedAt: m.UpdatedAt, 63 | Labels: m.Labels, 64 | Author: m.Author, 65 | State: m.State, 66 | Merged: m.Merged, 67 | CollectionTime: time.Now(), 68 | }) 69 | } 70 | return mapped 71 | } 72 | -------------------------------------------------------------------------------- /internal/configuration/configuration_test.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func ExpectEqual(t *testing.T, check, expected interface{}) bool { 8 | if check != expected { 9 | t.Helper() 10 | t.Errorf("Expected %s, actual %s", expected, check) 11 | return false 12 | } 13 | return true 14 | } 15 | 16 | func Test_Validate_Function(t *testing.T) { 17 | t.Log("When all the attributes for Config struct is passed to validate method") 18 | { 19 | t.Run("it should append no element in the array", func(t *testing.T) { 20 | var config = configuration{ 21 | GitHubToken: "test-githubTokenKey", 22 | BigQueryProjectID: "test-bigQueryProjectIDKey", 23 | BigQueryDatasetName: "test-bigQueryDatasetNameKey", 24 | IssuesTable: "test-issuesTableKey", 25 | IssuesAggTable: "test-issuesAggTableKey", 26 | PullRequestsTable: "test-pullRequestsTableKey", 27 | PullRequestsAggTable: "test-pullRequestsAggTableKey", 28 | ReleasesTable: "test-releasesTableKey", 29 | LastRunTable: "test-lastRunTableKey", 30 | RepoName: "test-repoNameKey", 31 | RepoOwner: "test-repoOwner", 32 | } 33 | testValidate := validate(config) 34 | ExpectEqual(t, testValidate, nil) 35 | }) 36 | } 37 | t.Log("When RepoOwner for Config struct is passed to validate method") 38 | { 39 | t.Run("it should return error", func(t *testing.T) { 40 | var config = configuration{ 41 | GitHubToken: "test-githubTokenKey", 42 | BigQueryProjectID: "test-bigQueryProjectIDKey", 43 | BigQueryDatasetName: "test-bigQueryDatasetNameKey", 44 | IssuesTable: "test-issuesTableKey", 45 | IssuesAggTable: "test-issuesAggTableKey", 46 | PullRequestsTable: "test-pullRequestsTableKey", 47 | PullRequestsAggTable: "test-pullRequestsAggTableKey", 48 | ReleasesTable: "test-releasesTableKey", 49 | LastRunTable: "test-lastRunTableKey", 50 | RepoName: "", 51 | } 52 | testValidate := validate(config) 53 | ExpectEqual(t, testValidate.Error(), "required environment variables are missing: REPO_NAME, REPO_OWNER") 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cat-team-github-metrics 2 | 3 | This project is a responsible for collecting various metrics from GitHub and injecting them in to BigQuery. 4 | 5 | The mertrics are surfaced in our Grafana instance. 6 | For more information, reach out to the Content and Tooling team. 7 | 8 | ## Build and release 9 | 10 | Builds and releases are handled by goreleaser. 11 | For convenience when working locally use the provided Makefile. 12 | 13 | ### Release steps 14 | * Ensure that you are on the HEAD of the main branch. 15 | * Create a new release prep branch (e.g maint_release_prep) 16 | * Create a tag `make tag version=v.0.0.1`. This will also push the tag to the remote. 17 | * The release workflow is triggered by new tags. It will publish a binary and a Docker image. 18 | 19 | ## Working locally 20 | 21 | ### Exporter 22 | 23 | To use the exporter you'll need to be logged in to GCP with the gcloud cli and have the following environment variables exported in your current session. 24 | 25 | ```bash 26 | export MODULE_OWNER="" 27 | export MODULE_NAME="" 28 | export GITHUB_TOKEN=xxxxxxxxxx 29 | export BIG_QUERY_DATASET_ID="my_dataset" 30 | ``` 31 | 32 | #### Running the exporter in docker 33 | 34 | You can run the exporter in a docker container. 35 | The BigQuery SDK will use the local credential file that is presented to the container. 36 | 37 | ```bash 38 | make release 39 | 40 | docker run \ 41 | -e MODULE_OWNER=$MODULE_OWNER \ 42 | -e MODULE_NAME=$MODULE_NAME \ 43 | -e GITHUB_TOKEN=$GITHUB_TOKEN \ 44 | -e BIG_QUERY_PROJECT_ID=$BIG_QUERY_PROJECT_ID \ 45 | -e GOOGLE_APPLICATION_CREDENTIALS=/tmp/keys/creds.json \ 46 | -v ~/.config/gcloud/application_default_credentials.json:/tmp/keys/creds.json \ 47 | ghcr.io/puppetlabs/cat-team-github-metrics 48 | ``` 49 | 50 | ### Grafana 51 | 52 | This repository also comtains a local Grafana stack. 53 | It will be deployed with the BigQuery datasource which will need to be configured. 54 | 55 | First create a new volume where we will store data: 56 | 57 | ```bash 58 | docker volume create --name=grafana-data 59 | ``` 60 | 61 | Then move to the `grafana` directory and start the stack as follows: 62 | 63 | ```bash 64 | cd grafana 65 | docker-compose up 66 | ``` 67 | Your local stack will be accessible at 68 | -------------------------------------------------------------------------------- /internal/githubclient/issues.go: -------------------------------------------------------------------------------- 1 | package githubclient 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/shurcooL/githubv4" 8 | ) 9 | 10 | type Label string 11 | 12 | type Issue struct { 13 | Repository string `json:"repository"` 14 | Number int `json:"number"` 15 | Title string `json:"title"` 16 | CreatedAt time.Time `json:"created_at"` 17 | UpdatedAt time.Time `json:"updated_at"` 18 | Labels []string `json:"labels"` 19 | Author string `json:"author"` 20 | State string `json:"state"` 21 | } 22 | 23 | var IssueQuery struct { 24 | Repository struct { 25 | Issues struct { 26 | Nodes []struct { 27 | Number int 28 | Title string 29 | State string 30 | CreatedAt time.Time 31 | UpdatedAt time.Time 32 | Labels struct { 33 | Nodes []struct { 34 | Name string 35 | } 36 | } `graphql:"labels(first: 100)"` 37 | Author struct { 38 | Login string 39 | } 40 | } 41 | PageInfo struct { 42 | HasNextPage bool 43 | EndCursor githubv4.String 44 | } 45 | } `graphql:"issues(first: 100, after: $cursor)"` 46 | } `graphql:"repository(owner: $owner, name: $name)"` 47 | } 48 | 49 | func (client *githubClient) GetIssues(ctx context.Context, owner string, name string) ([]Issue, error) { 50 | variables := map[string]interface{}{ 51 | "owner": githubv4.String(owner), 52 | "name": githubv4.String(name), 53 | "cursor": (*githubv4.String)(nil), 54 | } 55 | 56 | var metrics []Issue 57 | for { 58 | err := client.v4.Query(ctx, &IssueQuery, variables) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | for _, node := range IssueQuery.Repository.Issues.Nodes { 64 | var labels []string 65 | for _, label := range node.Labels.Nodes { 66 | labels = append(labels, label.Name) 67 | } 68 | 69 | metrics = append(metrics, Issue{ 70 | Repository: name, 71 | Number: node.Number, 72 | Title: node.Title, 73 | CreatedAt: node.CreatedAt, 74 | UpdatedAt: node.UpdatedAt, 75 | Labels: labels, 76 | Author: node.Author.Login, 77 | State: node.State, 78 | }) 79 | } 80 | 81 | if !IssueQuery.Repository.Issues.PageInfo.HasNextPage { 82 | break 83 | } 84 | 85 | variables["cursor"] = IssueQuery.Repository.Issues.PageInfo.EndCursor 86 | } 87 | 88 | return metrics, nil 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/metrics.yml: -------------------------------------------------------------------------------- 1 | name: metrics 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | setup_matrix: 10 | name: Setup Matrix 11 | runs-on: ubuntu-latest 12 | outputs: 13 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 14 | steps: 15 | - id: generate-matrix 16 | name: Retrieve Tools 17 | run: | 18 | export TOOL_LIST=$(curl https://puppetlabs.github.io/content-and-tooling-team/tools/list.json) 19 | echo "matrix=$TOOL_LIST" >> $GITHUB_OUTPUT 20 | 21 | run-collector: 22 | name: ${{matrix.tool.name}} 23 | needs: setup_matrix 24 | runs-on: ubuntu-latest 25 | strategy: 26 | matrix: 27 | tool: ${{fromJson(needs.setup_matrix.outputs.matrix)}} 28 | steps: 29 | - name: Login to GitHub Container Registry 30 | uses: docker/login-action@v2 31 | with: 32 | registry: ghcr.io 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | - name: Collect metrics 36 | run: | 37 | echo '${{ secrets.GCP_CONNECTION }}' > $(pwd)/creds.json 38 | docker run \ 39 | -e BQ_PROJECT_ID=${{ secrets.BQ_PROJECT_ID }} \ 40 | -e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ 41 | -e COBRA_COMMAND=export \ 42 | -e GOOGLE_APPLICATION_CREDENTIALS=/tmp/keys/creds.json \ 43 | -e REPO_NAME=${{matrix.tool.name}} \ 44 | -e REPO_OWNER=${{matrix.tool.owner}} \ 45 | -v $(pwd)/creds.json:/tmp/keys/creds.json \ 46 | ghcr.io/puppetlabs/cat-team-github-metrics:latest 47 | 48 | successful_timestamp: 49 | name: Sucessful Timestamp 50 | needs: run-collector 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Login to GitHub Container Registry 54 | uses: docker/login-action@v2 55 | with: 56 | registry: ghcr.io 57 | username: ${{ github.actor }} 58 | password: ${{ secrets.GITHUB_TOKEN }} 59 | - name: Timestamp 60 | run: | 61 | echo '${{ secrets.GCP_CONNECTION }}' > $(pwd)/creds.json 62 | docker run \ 63 | -e BQ_PROJECT_ID=${{ secrets.BQ_PROJECT_ID }} \ 64 | -e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ 65 | -e COBRA_COMMAND=stamp \ 66 | -e GOOGLE_APPLICATION_CREDENTIALS=/tmp/keys/creds.json \ 67 | -e REPO_NAME=not_used \ 68 | -e REPO_OWNER=not_used \ 69 | -v $(pwd)/creds.json:/tmp/keys/creds.json \ 70 | ghcr.io/puppetlabs/cat-team-github-metrics:latest 71 | -------------------------------------------------------------------------------- /internal/githubclient/pull_requests.go: -------------------------------------------------------------------------------- 1 | package githubclient 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/rs/zerolog" 8 | "github.com/rs/zerolog/log" 9 | "github.com/shurcooL/githubv4" 10 | "golang.org/x/net/context" 11 | ) 12 | 13 | type PullRequest struct { 14 | Repository string `json:"repository"` 15 | Number int `json:"number"` 16 | Title string `json:"title"` 17 | CreatedAt time.Time `json:"created_at"` 18 | UpdatedAt time.Time `json:"updated_at"` 19 | State string `json:"state"` 20 | Merged bool `json:"merged"` 21 | Labels []string `json:"labels"` 22 | Author string `json:"author"` 23 | } 24 | 25 | var PRQuery struct { 26 | Repository struct { 27 | PullRequests struct { 28 | Nodes []struct { 29 | Number int 30 | Title string 31 | State string 32 | CreatedAt time.Time 33 | UpdatedAt time.Time 34 | IsDraft bool 35 | Labels struct { 36 | Nodes []struct { 37 | Name string 38 | } 39 | } `graphql:"labels(first: 100)"` 40 | Author struct { 41 | Login string 42 | } 43 | Merged bool 44 | } 45 | PageInfo struct { 46 | HasNextPage bool 47 | EndCursor githubv4.String 48 | } 49 | } `graphql:"pullRequests(first: 100, after: $cursor)"` 50 | } `graphql:"repository(owner: $owner, name: $name)"` 51 | } 52 | 53 | func (client *githubClient) GetPullRequests(ctx context.Context, owner string, name string) ([]PullRequest, error) { 54 | variables := map[string]interface{}{ 55 | "owner": githubv4.String(owner), 56 | "name": githubv4.String(name), 57 | "cursor": (*githubv4.String)(nil), 58 | } 59 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) 60 | var metrics []PullRequest 61 | for { 62 | err := client.v4.Query(ctx, &PRQuery, variables) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | for _, node := range PRQuery.Repository.PullRequests.Nodes { 68 | var labels []string 69 | for _, label := range node.Labels.Nodes { 70 | labels = append(labels, label.Name) 71 | } 72 | 73 | if node.IsDraft && node.State == "OPEN" { 74 | log.Info().Msgf("Skipping draft PR %s/%d", name, node.Number) 75 | continue 76 | } 77 | 78 | metrics = append(metrics, PullRequest{ 79 | Repository: name, 80 | Number: node.Number, 81 | Title: node.Title, 82 | CreatedAt: node.CreatedAt, 83 | UpdatedAt: node.UpdatedAt, 84 | Labels: labels, 85 | Author: node.Author.Login, 86 | State: node.State, 87 | Merged: node.Merged, 88 | }) 89 | } 90 | 91 | if !PRQuery.Repository.PullRequests.PageInfo.HasNextPage { 92 | break 93 | } 94 | 95 | variables["cursor"] = PRQuery.Repository.PullRequests.PageInfo.EndCursor 96 | } 97 | 98 | return metrics, nil 99 | } 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/puppetlabs/cat-team-github-metrics 2 | 3 | go 1.18 4 | 5 | require ( 6 | cloud.google.com/go/bigquery v1.50.0 7 | github.com/rs/zerolog v1.33.0 8 | github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00 9 | github.com/spf13/cobra v1.8.1 10 | github.com/spf13/viper v1.12.0 11 | golang.org/x/net v0.27.0 12 | golang.org/x/oauth2 v0.7.0 13 | ) 14 | 15 | require ( 16 | cloud.google.com/go v0.110.0 // indirect 17 | cloud.google.com/go/compute v1.19.1 // indirect 18 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 19 | cloud.google.com/go/iam v0.13.0 // indirect 20 | github.com/andybalholm/brotli v1.0.4 // indirect 21 | github.com/apache/arrow/go/v11 v11.0.0 // indirect 22 | github.com/apache/thrift v0.16.0 // indirect 23 | github.com/fsnotify/fsnotify v1.5.4 // indirect 24 | github.com/goccy/go-json v0.9.11 // indirect 25 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 26 | github.com/golang/protobuf v1.5.3 // indirect 27 | github.com/golang/snappy v0.0.4 // indirect 28 | github.com/google/flatbuffers v2.0.8+incompatible // indirect 29 | github.com/google/go-cmp v0.6.0 // indirect 30 | github.com/google/uuid v1.3.0 // indirect 31 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect 32 | github.com/googleapis/gax-go/v2 v2.7.1 // indirect 33 | github.com/hashicorp/hcl v1.0.0 // indirect 34 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 35 | github.com/klauspost/asmfmt v1.3.2 // indirect 36 | github.com/klauspost/compress v1.15.9 // indirect 37 | github.com/klauspost/cpuid/v2 v2.0.9 // indirect 38 | github.com/magiconair/properties v1.8.6 // indirect 39 | github.com/mattn/go-colorable v0.1.13 // indirect 40 | github.com/mattn/go-isatty v0.0.19 // indirect 41 | github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect 42 | github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect 43 | github.com/mitchellh/mapstructure v1.5.0 // indirect 44 | github.com/pelletier/go-toml v1.9.5 // indirect 45 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect 46 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 47 | github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e // indirect 48 | github.com/spf13/afero v1.8.2 // indirect 49 | github.com/spf13/cast v1.5.0 // indirect 50 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 51 | github.com/spf13/pflag v1.0.5 // indirect 52 | github.com/subosito/gotenv v1.3.0 // indirect 53 | github.com/zeebo/xxh3 v1.0.2 // indirect 54 | go.opencensus.io v0.24.0 // indirect 55 | golang.org/x/mod v0.17.0 // indirect 56 | golang.org/x/sync v0.7.0 // indirect 57 | golang.org/x/sys v0.22.0 // indirect 58 | golang.org/x/text v0.16.0 // indirect 59 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 60 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 61 | google.golang.org/api v0.114.0 // indirect 62 | google.golang.org/appengine v1.6.7 // indirect 63 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 64 | google.golang.org/grpc v1.56.3 // indirect 65 | google.golang.org/protobuf v1.33.0 // indirect 66 | gopkg.in/ini.v1 v1.66.4 // indirect 67 | gopkg.in/yaml.v2 v2.4.0 // indirect 68 | gopkg.in/yaml.v3 v3.0.1 // indirect 69 | ) 70 | -------------------------------------------------------------------------------- /internal/configuration/configuration.go: -------------------------------------------------------------------------------- 1 | // Package configuration contains a number of methods that are used 2 | // to provide configuration to the wider application. It uses viper 3 | // to pull config from either the environment or a config file then 4 | // unmarhsals the config into the configuration struct. The configuration struct 5 | // is made available to the application via a package level variable 6 | // called Config. 7 | package configuration 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | "strings" 13 | 14 | "github.com/spf13/viper" 15 | ) 16 | 17 | const ( 18 | githubTokenKey = "GITHUB_TOKEN" //nolint:gosec 19 | bigQueryProjectIDKey = "BIG_QUERY_PROJECT_ID" 20 | bigQueryDatasetNameKey = "BIG_QUERY_DATASET_NAME" 21 | issuesTableKey = "ISSUES_TABLE" 22 | issuesAggTableKey = "ISSUES_AGG_TABLE" 23 | pullRequestsTableKey = "PULL_REQUESTS_TABLE" 24 | pullRequestsAggTableKey = "PULL_REQUESTS_AGG_TABLE" 25 | releasesTableKey = "RELEASES_TABLE" 26 | lastRunTableKey = "LAST_RUN_TABLE" 27 | repoNameKey = "REPO_NAME" 28 | repoOwnerKey = "REPO_OWNER" 29 | ) 30 | 31 | var configMap = map[string]string{ 32 | "GitHubToken": "GITHUB_TOKEN", 33 | "BigQueryProjectID": "BIG_QUERY_PROJECT_ID", 34 | "BigQueryDatasetName": "BIG_QUERY_DATASET_NAME", 35 | "IssuesTable": "ISSUES_TABLE", 36 | "IssuesAggTable": "ISSUES_AGG_TABLE", 37 | "PullRequestsTable": "PULL_REQUESTS_TABLE", 38 | "PullRequestsAggTable": "PULL_REQUESTS_AGG_TABLE", 39 | "ReleasesTable": "RELEASES_TABLE", 40 | "LastRunTable": "LAST_RUN_TABLE", 41 | "RepoName": "REPO_NAME", 42 | "RepoOwner": "REPO_OWNER", 43 | } 44 | 45 | var Config configuration 46 | 47 | type configuration struct { 48 | GitHubToken string `mapstructure:"github_token"` 49 | BigQueryProjectID string `mapstructure:"big_query_project_id"` 50 | BigQueryDatasetName string `mapstructure:"big_query_dataset_name"` 51 | IssuesTable string `mapstructure:"issues_table"` 52 | IssuesAggTable string `mapstructure:"issues_agg_table"` 53 | PullRequestsTable string `mapstructure:"pull_requests_table"` 54 | PullRequestsAggTable string `mapstructure:"pull_requests_agg_table"` 55 | ReleasesTable string `mapstructure:"releases_table"` 56 | LastRunTable string `mapstructure:"last_run_table"` 57 | RepoName string `mapstructure:"repo_name"` 58 | RepoOwner string `mapstructure:"repo_owner"` 59 | } 60 | 61 | func InitConfig() error { 62 | viper.SetDefault(bigQueryDatasetNameKey, "github_metrics") 63 | viper.SetDefault(issuesTableKey, "issues") 64 | viper.SetDefault(issuesAggTableKey, "issues_agg") 65 | viper.SetDefault(pullRequestsTableKey, "pull_requests") 66 | viper.SetDefault(pullRequestsAggTableKey, "pull_requests_agg") 67 | viper.SetDefault(releasesTableKey, "releases") 68 | viper.SetDefault(lastRunTableKey, "last_run") 69 | 70 | _ = viper.BindEnv(githubTokenKey) 71 | _ = viper.BindEnv(bigQueryProjectIDKey) 72 | _ = viper.BindEnv(bigQueryDatasetNameKey) 73 | _ = viper.BindEnv(issuesTableKey) 74 | _ = viper.BindEnv(issuesAggTableKey) 75 | _ = viper.BindEnv(pullRequestsTableKey) 76 | _ = viper.BindEnv(pullRequestsAggTableKey) 77 | _ = viper.BindEnv(releasesTableKey) 78 | _ = viper.BindEnv(lastRunTableKey) 79 | _ = viper.BindEnv(repoNameKey) 80 | _ = viper.BindEnv(repoOwnerKey) 81 | 82 | err := viper.Unmarshal(&Config) 83 | if err != nil { 84 | return fmt.Errorf("failed to parse config: %s", err) 85 | } 86 | 87 | return validate(Config) 88 | } 89 | 90 | // Needs to be way better than this.. 91 | func validate(config configuration) error { 92 | var missingConfig []string 93 | 94 | v := reflect.ValueOf(config) 95 | for i := 0; i < v.NumField(); i++ { 96 | if v.Field(i).String() == "" { 97 | missingConfig = append(missingConfig, configMap[v.Type().Field(i).Name]) 98 | } 99 | } 100 | 101 | if len(missingConfig) > 0 { 102 | return fmt.Errorf("required environment variables are missing: %s", strings.Join(missingConfig, ", ")) 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /internal/export/export.go: -------------------------------------------------------------------------------- 1 | // Package export contains the logic for exporting GitHub metrics to BigQuery. 2 | // This is a port from the previous main.go and should be rationalized in the future. 3 | package export 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | "os" 10 | "time" 11 | 12 | "github.com/puppetlabs/cat-team-github-metrics/internal/bigqueryclient" 13 | "github.com/puppetlabs/cat-team-github-metrics/internal/configuration" 14 | "github.com/puppetlabs/cat-team-github-metrics/internal/metrics" 15 | "github.com/rs/zerolog" 16 | "github.com/rs/zerolog/log" 17 | ) 18 | 19 | var bq bigqueryclient.BigQueryClient 20 | 21 | func Run() error { 22 | var err error 23 | 24 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) 25 | 26 | err = configuration.InitConfig() 27 | if err != nil { 28 | return fmt.Errorf("config error: %w", err) 29 | } 30 | 31 | config := configuration.Config 32 | 33 | repoName := config.RepoName 34 | repoOwner := config.RepoOwner 35 | 36 | log.Info().Msg("Initializing BigQuery client") 37 | ctx := context.Background() 38 | bq, err = bigqueryclient.NewBigQueryClient(ctx, config.BigQueryProjectID, config.BigQueryDatasetName) 39 | if err != nil { 40 | return fmt.Errorf("failed to create BigQuery client: %w", err) 41 | } 42 | 43 | var hasErrors bool 44 | 45 | log.Info().Msg("starting to collect metrics") 46 | log.Info().Msgf("%s: fetching issue metrics", repoName) 47 | 48 | issues, err := metrics.GetIssueMetrics(repoOwner, repoName) 49 | if err != nil { 50 | log.Error().Err(err).Msgf("%s: failed to fetch issue metrics", repoName) 51 | hasErrors = true 52 | } 53 | 54 | log.Info().Msgf("%s: uploading %d issue metrics", repoName, len(issues)) 55 | err = bq.Insert(config.IssuesTable, issues) 56 | if err != nil { 57 | log.Error().Err(err).Msgf("%s: failed to insert issue metrics", repoName) 58 | hasErrors = true 59 | } 60 | 61 | log.Info().Msgf("%s: fetching issue aggregated metrics", repoName) 62 | issueAggregatedMetrics, err := metrics.GetIssueAggregatedMetrics(repoOwner, repoName) 63 | if err != nil { 64 | log.Error().Err(err).Msgf("%s: failed to fetch issue aggregated metrics", repoName) 65 | hasErrors = true 66 | } 67 | 68 | log.Info().Msgf("%s: uploading issue aggregated metrics", repoName) 69 | err = bq.Insert(config.IssuesAggTable, issueAggregatedMetrics) 70 | if err != nil { 71 | log.Error().Err(err).Msgf("%s: failed to insert issue aggregated metrics", repoName) 72 | hasErrors = true 73 | } 74 | 75 | log.Info().Msgf("%s: fetching pull request metrics", repoName) 76 | pullRequests, err := metrics.GetPullRequestMetrics(repoOwner, repoName) 77 | if err != nil { 78 | log.Error().Err(err).Msgf("%s: failed to fetch pull request metrics", repoName) 79 | hasErrors = true 80 | } 81 | 82 | log.Info().Msgf("%s: uploading %d pull request metrics", repoName, len(pullRequests)) 83 | err = bq.Insert(config.PullRequestsTable, pullRequests) 84 | if err != nil { 85 | log.Error().Err(err).Msgf("%s: failed to insert pull request metrics", repoName) 86 | hasErrors = true 87 | } 88 | 89 | log.Info().Msgf("%s: fetching pull request aggregated metrics", repoName) 90 | pullRequestAggregatedMetrics, err := metrics.GetPullRequestAggregatedMetrics(repoOwner, repoName) 91 | if err != nil { 92 | log.Error().Err(err).Msgf("%s: failed to fetch pull request aggregated metrics", repoName) 93 | hasErrors = true 94 | } 95 | 96 | log.Info().Msgf("%s: uploading pull request aggregated metrics", repoName) 97 | err = bq.Insert(config.PullRequestsAggTable, pullRequestAggregatedMetrics) 98 | if err != nil { 99 | log.Error().Err(err).Msgf("%s: failed to insert pull request aggregated metrics", repoName) 100 | hasErrors = true 101 | } 102 | 103 | log.Info().Msgf("%s: fetching release metrics", repoName) 104 | releases, err := metrics.GetReleaseMetrics(repoOwner, repoName) 105 | if err != nil { 106 | log.Error().Err(err).Msgf("%s: failed to fetch release metrics", repoName) 107 | hasErrors = true 108 | } 109 | 110 | log.Info().Msgf("%s: uploading %d release metrics", repoName, len(releases)) 111 | err = bq.Insert(config.ReleasesTable, releases) 112 | if err != nil { 113 | log.Error().Err(err).Msgf("%s: failed to insert release metrics", repoName) 114 | hasErrors = true 115 | } 116 | 117 | if hasErrors { 118 | return errors.New("errors occurred while processing metrics you should check the previous logs for more details") 119 | } else { 120 | log.Info().Msg("successfully processed metrics") 121 | } 122 | 123 | return nil 124 | } 125 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= 21 | cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= 22 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 23 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 24 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 25 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 26 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 27 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 28 | cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= 29 | cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= 30 | cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= 31 | cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= 32 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 33 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 34 | cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= 35 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 36 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 37 | cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= 38 | cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= 39 | cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= 40 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 41 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 42 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 43 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 44 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 45 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 46 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 47 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 48 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 49 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 50 | cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= 51 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 52 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 53 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 54 | github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= 55 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 56 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 57 | github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= 58 | github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= 59 | github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= 60 | github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= 61 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 62 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 63 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 64 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 65 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 66 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 67 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 68 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 69 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 70 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 71 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 72 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 73 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 74 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 75 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 76 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 77 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 78 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 79 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 80 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 81 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= 82 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 83 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 84 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 85 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 86 | github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= 87 | github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 88 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 89 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 90 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 91 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 92 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 93 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 94 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 95 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 96 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 97 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 98 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 99 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 100 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 101 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 102 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 103 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 104 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 105 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 106 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 107 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 108 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 109 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 110 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 111 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 112 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 113 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 114 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 115 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 116 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 117 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 118 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 119 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 120 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 121 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 122 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 123 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 124 | github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= 125 | github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 126 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 127 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 128 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 129 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 130 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 131 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 132 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 133 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 134 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 135 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 136 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 137 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 138 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 139 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 140 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 141 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 142 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 143 | github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= 144 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 145 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 146 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 147 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 148 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 149 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 150 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 151 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 152 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 153 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 154 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 155 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 156 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 157 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 158 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= 159 | github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= 160 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 161 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 162 | github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= 163 | github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= 164 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 165 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 166 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 167 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 168 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 169 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 170 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 171 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 172 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 173 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 174 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 175 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 176 | github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= 177 | github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= 178 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= 179 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 180 | github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= 181 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 182 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 183 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 184 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 185 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 186 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 187 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 188 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 189 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 190 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 191 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 192 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 193 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 194 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 195 | github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= 196 | github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= 197 | github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= 198 | github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= 199 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 200 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 201 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 202 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 203 | github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= 204 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 205 | github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= 206 | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 207 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 208 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 209 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 210 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 211 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 212 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 213 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 214 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 215 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 216 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 217 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 218 | github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00 h1:fiFvD4lT0aWjuuAb64LlZ/67v87m+Kc9Qsu5cMFNK0w= 219 | github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= 220 | github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e h1:dmM59/+RIH6bO/gjmUgaJwdyDhAvZkHgA5OJUcoUyGU= 221 | github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 222 | github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= 223 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= 224 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 225 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 226 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 227 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 228 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 229 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 230 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 231 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 232 | github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= 233 | github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= 234 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 235 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 236 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 237 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 238 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 239 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 240 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 241 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 242 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 243 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 244 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 245 | github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= 246 | github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= 247 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 248 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 249 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 250 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 251 | github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= 252 | github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= 253 | github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= 254 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 255 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 256 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 257 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 258 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 259 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 260 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 261 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 262 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 263 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 264 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 265 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 266 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 267 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 268 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 269 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 270 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 271 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 272 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 273 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 274 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 275 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 276 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 277 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 278 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 279 | golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= 280 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 281 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 282 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 283 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 284 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 285 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 286 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 287 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 288 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 289 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 290 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 291 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 292 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 293 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 294 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 295 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 296 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 297 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 298 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 299 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 300 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 301 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 302 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 303 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 304 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 305 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 306 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 307 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 308 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 309 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 310 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 311 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 312 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 313 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 314 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 315 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 316 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 317 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 318 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 319 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 320 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 321 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 322 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 323 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 324 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 325 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 326 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 327 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 328 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 329 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 330 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 331 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 332 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 333 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 334 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 335 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 336 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 337 | golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= 338 | golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 339 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 340 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 341 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 342 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 343 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 344 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 345 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 346 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 347 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 348 | golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= 349 | golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= 350 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 351 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 352 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 353 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 354 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 355 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 357 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 358 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 359 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 361 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 362 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 363 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 364 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 365 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 366 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 396 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 397 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 398 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 399 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 400 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 401 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 402 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 403 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 404 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 405 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 406 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 407 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 408 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 409 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 410 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 411 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 412 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 413 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 414 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 415 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 416 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 417 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 418 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 419 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 420 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 421 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 422 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 423 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 424 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 425 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 426 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 427 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 428 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 429 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 430 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 431 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 432 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 433 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 434 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 435 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 436 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 437 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 438 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 439 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 440 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 441 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 442 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 443 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 444 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 445 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 446 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 447 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 448 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 449 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 450 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 451 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 452 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 453 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 454 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 455 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 456 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 457 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 458 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 459 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 460 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 461 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 462 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 463 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 464 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 465 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 466 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 467 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 468 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 469 | gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= 470 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 471 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 472 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 473 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 474 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 475 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 476 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 477 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 478 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 479 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 480 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 481 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 482 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 483 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 484 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 485 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 486 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 487 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 488 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 489 | google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= 490 | google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= 491 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 492 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 493 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 494 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 495 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 496 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 497 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 498 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 499 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 500 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 501 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 502 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 503 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 504 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 505 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 506 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 507 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 508 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 509 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 510 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 511 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 512 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 513 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 514 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 515 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 516 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 517 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 518 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 519 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 520 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 521 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 522 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 523 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 524 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 525 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 526 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 527 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 528 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 529 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 530 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 531 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 532 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 533 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 534 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 535 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= 536 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= 537 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 538 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 539 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 540 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 541 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 542 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 543 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 544 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 545 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 546 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 547 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 548 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 549 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 550 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 551 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 552 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 553 | google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= 554 | google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= 555 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 556 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 557 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 558 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 559 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 560 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 561 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 562 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 563 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 564 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 565 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 566 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 567 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 568 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 569 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 570 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 571 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 572 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 573 | gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= 574 | gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 575 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 576 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 577 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 578 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 579 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 580 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 581 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 582 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 583 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 584 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 585 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 586 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 587 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 588 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 589 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 590 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 591 | --------------------------------------------------------------------------------