├── .gitattributes ├── examples ├── list-flag.jpg ├── download-flag.jpg └── skipextraction-flag.jpg ├── .gitignore ├── utils ├── osinfo.go ├── scanstdin.go ├── regex.go └── extractor.go ├── .goreleaser.yaml ├── moveBinaries.go.NOT_NOW ├── cleanup.go ├── go.mod ├── main.go ├── options └── options.go ├── README.md ├── github └── github_releases.go └── go.sum /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /examples/list-flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kavishgr/ghrelease/HEAD/examples/list-flag.jpg -------------------------------------------------------------------------------- /examples/download-flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kavishgr/ghrelease/HEAD/examples/download-flag.jpg -------------------------------------------------------------------------------- /examples/skipextraction-flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kavishgr/ghrelease/HEAD/examples/skipextraction-flag.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | dist/ 3 | 4 | # System Files 5 | .DS_Store 6 | Thumbs.db 7 | 8 | # Ignore files 9 | *.txt 10 | -------------------------------------------------------------------------------- /utils/osinfo.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | func OsInfo() (string, string){ 8 | os := runtime.GOOS 9 | arch := runtime.GOARCH 10 | return os, arch 11 | } 12 | -------------------------------------------------------------------------------- /utils/scanstdin.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | ) 7 | 8 | // scan StdIn and send each line to the apiUrl channel 9 | 10 | func ScanStdIn(apiUrl chan string) { 11 | scanner := bufio.NewScanner(os.Stdin) 12 | for scanner.Scan() { 13 | apiUrl <- scanner.Text() 14 | } 15 | close(apiUrl) 16 | } 17 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | builds: 5 | - 6 | ldflags: 7 | - -s -w 8 | env: 9 | - CGO_ENABLED=0 10 | goos: 11 | - linux 12 | - darwin 13 | goarch: 14 | - amd64 15 | - arm64 16 | archives: 17 | - format: tar.gz 18 | snapshot: 19 | name_template: "{{ incpatch .Version }}" 20 | changelog: 21 | sort: asc 22 | filters: 23 | exclude: 24 | - '^docs:' 25 | - '^test:' 26 | 27 | -------------------------------------------------------------------------------- /moveBinaries.go.NOT_NOW: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // tempdir and opts.Dir 10 | // remove tempdir afterwards 11 | func moveBinaries(tempdir string, destinationDir string) error { 12 | if _, err := os.Stat(destinationDir); os.IsNotExist(err) { 13 | if err = os.MkdirAll(destinationDir, os.ModePerm); err != nil { 14 | return err 15 | } 16 | } 17 | files, err := os.ReadDir(tempdir) 18 | if err != nil { 19 | return err 20 | } 21 | for _, file := range files { 22 | if !file.IsDir() { 23 | if err = os.Rename(filepath.Join(tempdir, file.Name()), filepath.Join(destinationDir, file.Name())); err != nil { 24 | return err 25 | } 26 | } 27 | } 28 | 29 | err = os.RemoveAll(tempdir) 30 | if err != nil{ 31 | fmt.Printf("%s: not deleted...\n", tempdir) 32 | } 33 | return nil 34 | } 35 | 36 | -------------------------------------------------------------------------------- /cleanup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "debug/elf" 5 | "debug/macho" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "runtime" 10 | "io/fs" 11 | ) 12 | 13 | func cleanup(tempdir string) error { 14 | var verifyFile func(file *os.File) error 15 | 16 | switch runtime.GOOS { 17 | case "linux": 18 | verifyFile = func(file *os.File) error { 19 | _, err := elf.NewFile(file) 20 | return err 21 | } 22 | case "darwin": 23 | verifyFile = func(file *os.File) error { 24 | _, err := macho.NewFile(file) 25 | return err 26 | } 27 | } 28 | 29 | err := filepath.WalkDir(tempdir, func(binpath string, d fs.DirEntry, err error) error { 30 | if err != nil { 31 | return err 32 | } 33 | 34 | if binpath == tempdir { 35 | return nil 36 | } 37 | 38 | // If a directory - skip 39 | if d.IsDir() { 40 | // fmt.Println("Not regular: ", binpath) // comment 41 | return nil 42 | } 43 | 44 | // Open the file 45 | f, err := os.Open(binpath) 46 | defer f.Close() 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // Verify if the open file is either an ELF or Mach-O 52 | if err := verifyFile(f); err == nil { 53 | err = os.Chmod(binpath, 0755) 54 | // fmt.Println("Binary: ", binpath) //comment 55 | if err != nil { 56 | return err 57 | } 58 | } else { 59 | // fmt.Println("Removing: ", binpath) // comment 60 | os.Remove(binpath) 61 | } 62 | return nil 63 | }) 64 | 65 | if err != nil { 66 | fmt.Println(err) 67 | } 68 | 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kavishgr/getghrel 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/dlclark/regexp2 v1.11.0 7 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 8 | github.com/mholt/archiver/v4 v4.0.0-alpha.8 9 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db 10 | github.com/schollz/progressbar/v3 v3.14.2 11 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc 12 | github.com/tidwall/gjson v1.17.1 13 | golang.org/x/oauth2 v0.17.0 14 | ) 15 | 16 | require ( 17 | github.com/andybalholm/brotli v1.1.0 // indirect 18 | github.com/bodgit/plumbing v1.3.0 // indirect 19 | github.com/bodgit/sevenzip v1.5.0 // indirect 20 | github.com/bodgit/windows v1.0.1 // indirect 21 | github.com/dsnet/compress v0.0.1 // indirect 22 | github.com/golang/protobuf v1.5.3 // indirect 23 | github.com/golang/snappy v0.0.4 // indirect 24 | github.com/hashicorp/errwrap v1.1.0 // indirect 25 | github.com/hashicorp/go-multierror v1.1.1 // indirect 26 | github.com/klauspost/compress v1.17.7 // indirect 27 | github.com/klauspost/pgzip v1.2.6 // indirect 28 | github.com/mattn/go-isatty v0.0.20 // indirect 29 | github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect 30 | github.com/pierrec/lz4/v4 v4.1.21 // indirect 31 | github.com/rivo/uniseg v0.4.7 // indirect 32 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect 33 | github.com/therootcompany/xz v1.0.1 // indirect 34 | github.com/tidwall/match v1.1.1 // indirect 35 | github.com/tidwall/pretty v1.2.1 // indirect 36 | github.com/ulikunitz/xz v0.5.11 // indirect 37 | go4.org v0.0.0-20230225012048-214862532bf5 // indirect 38 | golang.org/x/sys v0.17.0 // indirect 39 | golang.org/x/term v0.17.0 // indirect 40 | golang.org/x/text v0.14.0 // indirect 41 | google.golang.org/appengine v1.6.8 // indirect 42 | google.golang.org/protobuf v1.32.0 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /utils/regex.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import( 4 | "log" 5 | ) 6 | 7 | func SetRegex(ost, arch string) string{ 8 | var regex string 9 | 10 | // perl regex 11 | // works with github.com/dlclark/regexp2 12 | switch { 13 | // darwin amd64 14 | case (ost == "darwin" && arch == "amd64"): 15 | regex = `(?i)(?=.*(?:apple|darwin|macos|mac))(?=.*(?:amd64|x86_64|x64))(?!.*(?:freebsd|netbsd|openbsd|linux|windows|win64|.sha256sum|.sha256|.sbom|checksums|.txt))(?:.*(?:apple|darwin|macos|mac).*?(?:amd64|x86_64|x64)|(?:amd64|x86_64|x64).*?(?:apple|darwin|macos|mac))(?:[^a-z]|$)` 16 | 17 | // linux amd64 18 | case (ost == "linux" && arch == "amd64"): 19 | //good 20 | regex = `(?i)(?=.*(?:linux))(?=.*(?:amd64|x86_64|x64))(?!.*(?:freebsd|netbsd|openbsd|windows|win64|apple|darwin|macos|mac|.sha256sum|.sha256|.sbom|checksums|.txt|.rpm|.deb))(?:.*(?:linux).*?(?:amd64|x86_64|x64)|(?:amd64|x86_64|x64).*?(?:linux))(?:[^a-z]|$)` 21 | 22 | // darwin arm64 23 | case (ost == "darwin" && arch == "arm64"): 24 | regex = `(?i)(?=.*(?:apple|darwin|macos|mac))(?=.*(?:arm64|aarch64))(?!.*(?:freebsd|netbsd|openbsd|linux|windows|win64|.sha256sum|.sha256|.sbom|checksums|.txt))(?:.*(?:apple|darwin|macos|mac).*?(?:arm64|aarch64)|(?:arm64|aarch64).*?(?:apple|darwin|macos|mac))(?:[^a-z]|$)` 25 | 26 | // linux arm64 27 | case (ost == "linux" && arch == "aarch64"): 28 | regex = `(?i)(?=.*(?:linux))(?=.*(?:arm64|aarch64))(?!.*(?:freebsd|netbsd|openbsd|windows|win64|apple|darwin|macos|mac|.sha256sum|.sha256|.sbom|checksums|.txt|.rpm|.deb))(?:.*(?:linux).*?(?:arm64|aarch64)|(?:arm64|aarch64).*?(?:linux))(?:[^a-z]|$)` 29 | 30 | default: 31 | msg1 := "OS or Architecture is not supported or not found in the regex pattern" 32 | msg2 := "File an issue or make a pull request for your OS and Arch" 33 | msg3 := "Will only list/download for macOS and Linux for the following architecture: " 34 | msg4 := "x86_64/amd64 and arm64" 35 | log.Fatalf("%v\n%v\n%v\n%v", msg1, msg2, msg3, msg4) 36 | } 37 | return regex 38 | } 39 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | 8 | "github.com/kavishgr/getghrel/github" 9 | "github.com/kavishgr/getghrel/options" 10 | "github.com/kavishgr/getghrel/utils" 11 | ) 12 | 13 | func main() { 14 | 15 | var ( 16 | opts = options.ParseFlags() 17 | skipextraction = opts.SkipExtraction 18 | token = opts.GHToken 19 | tempdir = opts.TempDir 20 | ost, arch = utils.OsInfo() 21 | regex = utils.SetRegex(ost, arch) 22 | stdInUrls = make(chan string) 23 | jobs sync.WaitGroup 24 | version = "0.1.2" 25 | ) 26 | 27 | if opts.Version { 28 | fmt.Println("getghrel version: ", version) 29 | os.Exit(1) 30 | } 31 | 32 | if token == "" { 33 | fmt.Println("GITHUB_TOKEN environment variable is not found.") 34 | fmt.Println("Nor is -ghtoken provided on the command line.") 35 | fmt.Println("") 36 | fmt.Println("Run 'getghrel -h'") 37 | fmt.Println("Or browse to: 'https://github.com/kavishgr/getghrel'") 38 | os.Exit(1) 39 | } 40 | 41 | if len(os.Args) == 1 { 42 | fmt.Println("No arguments were provided.") 43 | fmt.Println("Run: 'getghrel -h'") 44 | os.Exit(1) 45 | } 46 | 47 | go utils.ScanStdIn(stdInUrls) 48 | 49 | if opts.List { 50 | for c := 0; c < opts.Concurrency; c++ { 51 | jobs.Add(1) 52 | go github.FetchGithubReleaseUrl(stdInUrls, &jobs, regex, token) 53 | } 54 | } 55 | 56 | if opts.Download { 57 | _, err := os.Stat(tempdir) 58 | 59 | if os.IsNotExist(err) { 60 | err = os.Mkdir(tempdir, 0755) 61 | if err != nil { 62 | panic(err) 63 | } 64 | } 65 | 66 | for c := 0; c < opts.Concurrency; c++ { 67 | jobs.Add(1) 68 | go github.DownloadRelease(stdInUrls, &jobs, token, tempdir, skipextraction) 69 | } 70 | } 71 | 72 | jobs.Wait() // wait for above jobs to finish 73 | 74 | switch { 75 | 76 | case opts.List: 77 | return 78 | 79 | case skipextraction: 80 | fmt.Println("Archives are inside: ", tempdir) 81 | 82 | default: 83 | cleanup(tempdir) 84 | fmt.Println("") 85 | fmt.Println("All Binaries are inside: ", tempdir) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /utils/extractor.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/mholt/archiver/v4" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | func Extractor(src, tempdir string) error { 14 | 15 | supportFormat := []string{ 16 | "rar", 17 | "zip", 18 | "tar", 19 | "gz", 20 | "br", 21 | "sz", 22 | "zz", 23 | "zst", 24 | "bz2", 25 | "7z", 26 | "xz", 27 | "tar", 28 | "tbz", 29 | "tar.xz", 30 | "tar.gz", 31 | "gzip", 32 | } 33 | 34 | var isSupported = false 35 | 36 | for _, format := range supportFormat { 37 | if strings.HasSuffix(src, format) { 38 | isSupported = true 39 | break // stop the loop and jump to the next instruction 40 | } 41 | 42 | } 43 | 44 | if !isSupported { 45 | // check if the file has no suffix at all 46 | if strings.IndexByte(src, '.') == -1 { 47 | // return fmt.Errorf("%s has no supported suffix", src) 48 | // just a binary, not an archive, or compressed archive 49 | return nil 50 | // do something else because it'a a regular file 51 | } 52 | return fmt.Errorf("%s is not supported", src) 53 | } 54 | 55 | reader, err := os.Open(src) 56 | // fullpath, _ := filepath.Abs(src) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | format, input, err := archiver.Identify(src, reader) 62 | if err != nil { 63 | return err 64 | } else { 65 | if ex, ok := format.(archiver.Extractor); ok { 66 | // fmt.Println("Extracting ", src) 67 | ex.Extract(context.Background(), input, nil, func(ctx context.Context, f archiver.File) error { 68 | stat, _ := f.Stat() 69 | // fmt.Println(stat.Name()) 70 | 71 | // create a new file with the same name as the extracted file 72 | // f.Open() returns an io.ReadCloser 73 | content, _ := f.Open() 74 | defer content.Close() 75 | 76 | newFilePath := filepath.Join(tempdir, stat.Name()) 77 | newFile, err := os.Create(newFilePath) 78 | if err != nil { 79 | return err 80 | } 81 | defer newFile.Close() 82 | 83 | // copy the contents of the extracted file to the new file 84 | 85 | _, err = io.Copy(newFile, content) 86 | if err != nil { 87 | return err 88 | } 89 | return nil 90 | }) 91 | 92 | } 93 | } 94 | return nil 95 | } 96 | -------------------------------------------------------------------------------- /options/options.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | import ( 4 | "flag" 5 | // "fmt" 6 | "github.com/mitchellh/colorstring" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | type options struct { 12 | List bool 13 | Download bool 14 | SkipExtraction bool 15 | Concurrency int 16 | GHToken string 17 | TempDir string 18 | Version bool 19 | } 20 | 21 | func ParseFlags() options { 22 | 23 | flag.Usage = func() { 24 | h := []string{ 25 | "", 26 | "Download releases from github and retain only the binaries", 27 | "", 28 | "[light_cyan]Usage:[reset]", 29 | "", 30 | " echo 'https://github.com/sharkdp/bat' | getghrel -list | sort", 31 | " cat urls.txt | getghrel -list | sort | tee releases.txt", 32 | " cat releases.txt | getghrel -download", 33 | " cat releases.txt | getghrel -download -tempdir '/tmp/bin'", 34 | " cat urls.txt | getghrel -list | grep -vi '^n/a'", 35 | " ", 36 | "[light_cyan]The url format for -list[reset]: \n", 37 | " A github url -> 'https://github.com/owner/repo'", 38 | " For e.g 'https://github.com/sharkdp/bat'\n", 39 | " Or only owner and repository -> 'owner/repo'", 40 | " For e.g -> 'sharkdp/bat'\n", 41 | "For more examples, browse to: https://github.com/kavishgr/getghrel", 42 | "", 43 | "Options:", 44 | " [light_cyan]-list[reset]", 45 | "", 46 | "\tWill list all the release/releases found for your OS and Architecture.\n", 47 | "\tExample: cat urls.txt | getghrel -list | sort", 48 | "\tExample: echo 'https://github.com/sharkdp/bat' | getghrel -list | sort", 49 | "\tExample: echo 'sharkdp/bat' | getghrel -list | sort", 50 | "", 51 | " [light_cyan]-con[reset]", 52 | "", 53 | "\t Set the concurrency level (default: 2)\n", 54 | "\t Example: cat urls.txt | getghrel -list -con 3 | tee releases.txt", 55 | "\t Example: cat releases.txt | getghrel -download -con 3", 56 | "", 57 | " [light_cyan]-ghtoken[reset]", 58 | "", 59 | "\t Specify your GITHUB TOKEN", 60 | "\t Default is the GIHUB_TOKEN environment variable.\n", 61 | "\t Example: cat urls.txt | getghrel -list -ghtoken 'YOUR TOKEN'", 62 | "", 63 | " [light_cyan]-download[reset]", 64 | "", 65 | "\t Download the releases", 66 | "\t Default directory in which the release will be downloaded is '/tmp/getghrel'", 67 | "\t If the release is compressed or in an archive format, the tool will automatically", 68 | "\t extract and unpack it no matter how it's compressed or archived", 69 | "\t and keep only the binary.\n", 70 | "\t Example: cat releases.txt | getghrel -download", 71 | "\t Example: cat releases.txt | getghrel -download -tempdir '/tmp/test'", 72 | "", 73 | " [light_cyan]-skipextraction[reset]", 74 | "", 75 | "\t Skip the extraction/unpack process\n", 76 | "\t Example: echo \"neovim/neovim\" | getghrel -list | getghrel -download -skipextraction", 77 | "", 78 | " [light_cyan]-tempdir[reset] ", 79 | "", 80 | "\t Specify a temporary directory to download/extract the binaries\n", 81 | "\t Example: cat releases.txt | getghrel -download -tempdir '/tmp/test'", 82 | "", 83 | " [light_cyan]-version[reset]", 84 | "\t Print version\n", 85 | "", 86 | } 87 | help := strings.Join(h, "\n") 88 | 89 | // fmt.Fprint(os.Stderr, strings.Join(h, "\n")) 90 | colorstring.Println(help) 91 | } 92 | 93 | opts := options{} 94 | flag.BoolVar(&opts.Download, "download", false, "") 95 | flag.BoolVar(&opts.List, "list", false, "") 96 | flag.BoolVar(&opts.SkipExtraction, "skipextraction", false, "") 97 | flag.IntVar(&opts.Concurrency, "con", 2, "") 98 | default_ghtoken := os.Getenv("GITHUB_TOKEN") 99 | flag.StringVar(&opts.GHToken, "ghtoken", default_ghtoken, "") 100 | flag.StringVar(&opts.TempDir, "tempdir", "/tmp/getghrel", "") 101 | flag.BoolVar(&opts.Version, "version", false, "") 102 | 103 | flag.Parse() 104 | 105 | return opts 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getghrel 2 | 3 | getghrel is a command-line interface (CLI) tool that locates and downloads the most recent release assets from Github for MacOS and Linux architectures, specifically "amd64" and "arm64". The tool automatically identifies your operating system and architecture, and downloads the binary. If the release is compressed or in an archive format, it will automatically extract and unpack it, no matter how it's compressed, and keep only the binary. 4 | 5 | You can also choose to skip the extraction and keep the archive itself if the release is a binary or script that needs dependencies available in the archive. 6 | 7 | ## Installation 8 | 9 | Download the latest binary from the [releases](https://github.com/kavishgr/getghrel/releases) section and place it in your `$PATH`. 10 | 11 | ### Dependencies 12 | 13 | - A GitHub token. By default, it will look for the `GITHUB_TOKEN` environment variable. You can also provide one directly on the command line. 14 | 15 | ## Usage 16 | 17 | ```sh 18 | getghrel -h 19 | ``` 20 | 21 | All the supported flags: 22 | 23 | ```sh 24 | -list list all the releases found 25 | Will print the latest release for your OS and Architecture. 26 | 27 | -con set the concurrency level (default: 2) 28 | 29 | -ghtoken provide a GITHUB TOKEN 30 | Default is the GITHUB_TOKEN environment variable. 31 | Example: cat urls.txt | getghrel -list -ghtoken 'YOUR TOKEN' | sort 32 | 33 | -download will download and extract the binary inside `/tmp/getghrel` 34 | Example: cat urls_from_list_results.txt | getghrel -download 35 | 36 | -skipextraction skip the extraction process 37 | Example: echo "helix-editor/helix" | getghrel -list | getghrel -download -skipextraction 38 | 39 | -tempdir specify a a temporary directory to download/extract the binaries 40 | Default is `/tmp/getghrel` 41 | Example: cat urls_from_list_results.txt | getghrel -download -tempdir /tmp/test 42 | 43 | -version display version 44 | ``` 45 | 46 | ### List Found Releases 47 | 48 | To list the found releases, create a text file with a **complete URL** or **owner/repo** per line, and run: 49 | 50 | ```sh 51 | # List of URLs 52 | # e.g "sharkdp/bat" or https://github.com/sharkdp/bat 53 | 54 | cat urls.txt | getghrel -list -con 3 | tee releases.txt 55 | 56 | # Single one 57 | echo "sharkdp/bat" | getghrel -list | sort 58 | ``` 59 | 60 | #### Demo 61 | 62 | ![-list](examples/list-flag.jpg) 63 | 64 | 65 | This will display a list of URLs representing the latest release assets found for each repository. 66 | 67 | In rare cases, you may come across additional files like checksums and SBOMs that are specific to your operating system and architecture. I have taken care to exclude them in the regular expression. However, if any such files exist, you can simply filter them out before using the `-download` flag to ensure a clean download. But don't worry, even if you don't filter the output, the tool will automatically keep only the binaries and remove any unnecessary files. Filtering them out can help save bandwidth. 68 | 69 | In the case of `N/A`(not available), it means that the repository doesn't have any release assets available. For Linux releases, there might be separate versions for both Gnu and Musl. You can choose to filter them out based on your preferences. 70 | 71 | > **Note**: In the example above, the first line shows that the 'neovim' package is unavailable. But neovim does have a latest release. The reason it's not listed is because my regex always checks for assets containing both the OS and architecture, while neovim's assets only specify the OS. There are ways to resolve this issue, but it involves dealing with regex, which can be a bit complex. Nevertheless, you can be confident that every release will be discoverable, except for this particular case. 72 | 73 | >> **update**: On macOS, Neovim is now being listed because the release includes the OS and arch. I haven't tried it on Linux yet. 74 | 75 | ``` 76 | ➜ ~ echo "neovim/neovim" | getghrel -list 77 | https://github.com/neovim/neovim/releases/download/v0.10.0/nvim-macos-arm64.tar.gz 78 | ``` 79 | Duplicates are unlikely, but if they do occur, you can easily filter them out using tools like `sort` and `uniq`. That should do the trick. 80 | 81 | In case a repository lacks a latest release tag, the tool will search for the most recent release tag instead. In rare cases this can be an unstable/nightly release. 82 | 83 | > If all urls is being listed as `N/A`, maybe your github token has expired: 84 | 85 | ``` 86 | N/A: https://github.com/neovim/neovim 87 | N/A: https://github.com/BurntSushi/ripgrep 88 | N/A: https://github.com/Byron/dua-cli 89 | N/A: https://github.com/ClementTsang/bottom 90 | N/A: https://github.com/Macchina-CLI/macchina 91 | N/A: https://github.com/Elkowar/pipr 92 | N/A: https://github.com/Orange-OpenSource/hurl 93 | N/A: https://github.com/PaulJuliusMartinez/jless 94 | ... 95 | ... 96 | ... 97 | ``` 98 | 99 | ### Download Found Assets 100 | 101 | To download the found assets and keep the binaries in a temporary folder (which is `/tmp/getghrel` by default), simply use the `-download` flag: 102 | 103 | ```sh 104 | # List of URLS found with -list 105 | cat releases.txt | getghrel -download 106 | cat releases.txt | getghrel -download -con 3 107 | 108 | # Single one 109 | echo "https://github.com/sharkdp/bat" | getghrel -list | getghrel -download 110 | ``` 111 | 112 | Before using `-download`, remove any lines starting with 'N/A' from the list of found assets, like shown below. 113 | 114 | > `CTRL+C`(signal interupt) during download, will leave all files in `/tmp/getghrel`. You'll have to remove it manually. 115 | 116 | #### Demo 117 | 118 | ![-download](examples/download-flag.jpg) 119 | 120 | 121 | In the example above, you can observe that the `ClementTsang/bottom` package had two releases due to different versions of GNU. However, the tool only retained one version. You can filter out these additional releases. I included them here for the purpose of this example. 122 | 123 | To download to a different location, use the `-tempdir` flag : 124 | 125 | ```sh 126 | # List of URLS 127 | cat releases.txt | getghrel -download -tempdir '/tmp/tempbin' 128 | 129 | # Single one 130 | echo "https://github.com/sharkdp/bat" | getghrel -list | getghrel -download -tempdir '/tmp/tempbin' 131 | ``` 132 | 133 | ### Skip Extraction 134 | 135 | To keep the file unarchived or uncompressed, you can simply use the `-skipextraction` option: 136 | 137 | ```sh 138 | echo "helix-editor/helix" | getghrel -list | getghrel -skipextraction -download 139 | ``` 140 | 141 | It is useful for releases that require dependencies bundled together in separate files or folders, rather than just a single binary. 142 | 143 | #### Demo 144 | 145 | ![-skipextraction](examples/skipextraction-flag.jpg) 146 | 147 | ## TODO 148 | 149 | - **Optional**: update the regex or add some sort of backup/rescue regex to include releases that contain only the operating system and not the architecture. Most releases do include both the OS and architecture, I'm mentioning it here because of the neovim(solved now) issue discussed earlier. 150 | -------------------------------------------------------------------------------- /github/github_releases.go: -------------------------------------------------------------------------------- 1 | package github 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/dlclark/regexp2" 7 | "github.com/k0kubun/go-ansi" 8 | "github.com/kavishgr/getghrel/utils" 9 | "github.com/schollz/progressbar/v3" 10 | "github.com/shurcooL/githubv4" 11 | "github.com/tidwall/gjson" 12 | "golang.org/x/oauth2" 13 | "io" 14 | "log" 15 | "net/http" 16 | "net/url" 17 | "os" 18 | "path" 19 | "path/filepath" 20 | "regexp" 21 | "strings" 22 | "sync" 23 | ) 24 | 25 | /* 26 | - Check if a given string is a valid URL 27 | got the regex from chatgpt 28 | */ 29 | func isValidURL(url string) bool { 30 | urlRegex := regexp.MustCompile(`^(https?|ftp)://[^\s/$.?#].[^\s]*$`) 31 | return urlRegex.MatchString(url) 32 | } 33 | 34 | /* 35 | Takes a GitHub URL as input and returns two strings. 36 | It aims to standardize the URL format 37 | to match the API endpoint for fetching the latest release 38 | of a GitHub repository. 39 | 40 | - It initializes apiDomain and apiDomainSuffix variables with fixed parts 41 | of the API URL. 42 | 43 | - It parses the input githubUrl using the url.Parse function 44 | to extract path information. 45 | 46 | - If the input URL is valid (using the isValidURL function), 47 | it constructs the API URL by combining apiDomain, the parsed path, 48 | and apiDomainSuffix. 49 | 50 | - If the input URL is not valid, 51 | it assumes the input is a GitHub repository name 52 | and constructs the API URL accordingly. 53 | 54 | - It then returns the standardized API URL 55 | and the extracted repository path. 56 | */ 57 | func fixUrl(githubUrl string) (string, string) { 58 | apiDomain := "https://api.github.com/repos" 59 | apiDomainSuffix := "/releases/latest" 60 | u, _ := url.Parse(githubUrl) 61 | fortag := fmt.Sprintf("%s", u.Path) 62 | 63 | if isValidURL(githubUrl) { 64 | result := fmt.Sprintf("%s%s%s", apiDomain, u.Path, apiDomainSuffix) 65 | return result, fortag 66 | } 67 | 68 | result := fmt.Sprintf("%s/%s%s", apiDomain, githubUrl, apiDomainSuffix) 69 | return result, fortag 70 | } 71 | 72 | /* 73 | - Creates an authenticated HTTP GET request for the GitHub API 74 | by setting the required headers, 75 | including the GitHub token and user agent 76 | */ 77 | func craftGithubReq(ghtoken, url string) *http.Request { 78 | req, err := http.NewRequest("GET", url, nil) 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | req.Header.Add("Authorization", fmt.Sprintf("token %s", ghtoken)) 83 | // req.Header.Add("X-GitHub-Api-Version", "2022-11-28") 84 | req.Header.Add("User-Agent", "getghrel-cli") 85 | return req 86 | } 87 | 88 | /* 89 | - Downloads and processes files 90 | concurrently from a list of URLs provided through the urlsChan. 91 | 92 | - It uses a GitHub token for authentication 93 | saves the downloaded files to a temporary directory 94 | and optionally extracts the files if specified. 95 | */ 96 | func DownloadRelease(urlsChan chan string, job *sync.WaitGroup, ghtoken, tempdir string, skipextraction bool) { 97 | 98 | defer job.Done() 99 | 100 | // anonymous func() to handle file download and processing 101 | // so that defer() gets called upon each iteration 102 | // instead of waiting for DownloadRelease() to return 103 | downloadAndProcessFile := func(u string) { 104 | // get the assetname of each url -> e.g bat.tar.gz 105 | file := path.Base(u) 106 | src := filepath.Join(tempdir, file) 107 | 108 | req := craftGithubReq(ghtoken, u) 109 | client := http.Client{} 110 | resp, err := client.Do(req) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | defer resp.Body.Close() 115 | 116 | f, _ := os.OpenFile(src, os.O_CREATE|os.O_WRONLY, 0644) 117 | defer f.Close() 118 | 119 | // bar := progressbar.DefaultBytes( 120 | // resp.ContentLength, 121 | // file, 122 | // ) 123 | 124 | // io.Copy(io.MultiWriter(f, bar), resp.Body) 125 | 126 | bar := progressbar.NewOptions64(resp.ContentLength, 127 | progressbar.OptionSetWriter(ansi.NewAnsiStdout()), 128 | progressbar.OptionEnableColorCodes(true), 129 | progressbar.OptionClearOnFinish(), 130 | progressbar.OptionSetElapsedTime(true), 131 | progressbar.OptionShowBytes(true), 132 | progressbar.OptionSetWidth(15), 133 | progressbar.OptionSetDescription(fmt.Sprintf("%s", file)), 134 | progressbar.OptionSetTheme(progressbar.Theme{ 135 | Saucer: "[green]=[reset]", 136 | SaucerHead: "[green]>[reset]", 137 | SaucerPadding: " ", 138 | BarStart: "[", 139 | BarEnd: "]", 140 | })) 141 | 142 | io.Copy(io.MultiWriter(f, bar), resp.Body) 143 | bar.Reset() 144 | bar.Finish() 145 | 146 | if skipextraction { 147 | fmt.Printf("Downloaded: %s\n", file) 148 | bar.Close() 149 | return 150 | } 151 | 152 | fmt.Printf("Downloaded and Extracted: %s\n", file) 153 | bar.Close() 154 | utils.Extractor(src, tempdir) 155 | } 156 | 157 | // iterate over urls sent by stdin 158 | for u := range urlsChan { 159 | downloadAndProcessFile(u) 160 | } 161 | } 162 | 163 | /* 164 | - Takes a string in the format "owner/repo" as input 165 | and returns two strings. 166 | 167 | - It extracts the owner and repository names 168 | from the input string by splitting it at the '/' character. 169 | 170 | - If the input starts with a '/', 171 | it removes it before performing the split. 172 | 173 | - The function then returns the extracted owner 174 | and repository names as separate strings. 175 | */ 176 | func split(ownerNrepo string) (string, string) { 177 | var str string 178 | if strings.HasPrefix(ownerNrepo, "/") { 179 | str = strings.TrimPrefix(ownerNrepo, "/") 180 | } 181 | parts := strings.Split(str, "/") 182 | return parts[0], parts[1] 183 | } 184 | 185 | /* 186 | - Retrieves information about a GitHub repository's latest release tag 187 | using the GitHub GraphQL API. 188 | It takes a GitHub API token (`ghtoken`) 189 | and a string in the format "owner/repo" (`ownerNrepo`) 190 | representing the repository's owner and name. 191 | 192 | - The function uses the `split` function to separate 193 | the owner and repository names from the input string. 194 | It then sets up an OAuth2 token source 195 | and an HTTP client to create a GitHub GraphQL client. 196 | 197 | - Next, the function defines a GraphQL query to fetch 198 | the latest release tag for the given repository. 199 | The query specifies the required fields, 200 | such as the name of the tag and sorting based on commit date. 201 | 202 | - After executing the GraphQL query, 203 | the function extracts the latest tag name 204 | from the query result. 205 | It constructs the URL for the GitHub API 206 | endpoint related to the retrieved tag. 207 | 208 | - Using the `craftGithubReq` function, 209 | it creates an HTTP GET request 210 | with the provided GitHub token and tag URL. 211 | The function then sends the request, 212 | reads the response body, 213 | and returns it as a byte slice containing 214 | the information about the latest release tag. 215 | */ 216 | func getTagByName(ghtoken, ownerNrepo string) []byte { 217 | // GitHub API token 218 | 219 | var tagname string 220 | owner, name := split(ownerNrepo) // owner and name of the repo 221 | 222 | // Create an OAuth2 token source 223 | src := oauth2.StaticTokenSource( 224 | &oauth2.Token{AccessToken: ghtoken}, 225 | ) 226 | 227 | // Create an HTTP client with the token source 228 | oauthClient := oauth2.NewClient(context.Background(), src) 229 | 230 | // Create a new GitHub GraphQL client 231 | gqlClient := githubv4.NewClient(oauthClient) 232 | 233 | // Define the GraphQL query 234 | var query struct { 235 | Repository struct { 236 | Refs struct { 237 | Edges []struct { 238 | Node struct { 239 | Name string 240 | } 241 | } 242 | } `graphql:"refs(refPrefix: $refPrefix, first: $first, orderBy: $orderBy)"` 243 | } `graphql:"repository(owner: $owner, name: $name)"` 244 | } 245 | 246 | // Set the query variables 247 | variables := map[string]interface{}{ 248 | "owner": githubv4.String(owner), 249 | "name": githubv4.String(name), 250 | "refPrefix": githubv4.String("refs/tags/"), 251 | "first": githubv4.Int(1), 252 | "orderBy": githubv4.RefOrder{ 253 | Field: githubv4.RefOrderFieldTagCommitDate, 254 | Direction: githubv4.OrderDirectionDesc, 255 | }, 256 | } 257 | 258 | // Execute the GraphQL query 259 | err := gqlClient.Query(context.Background(), &query, variables) 260 | if err != nil { 261 | log.Fatal(err) 262 | } 263 | 264 | // Access the query result 265 | for _, edge := range query.Repository.Refs.Edges { 266 | // fmt.Println("Tag Name:", edge.Node.Name) 267 | tagname = edge.Node.Name 268 | // fmt.Println(tagname) 269 | } 270 | 271 | tagUrl := fmt.Sprintf("https://api.github.com/repos%s/releases/tags/%s", ownerNrepo, tagname) 272 | // fmt.Println(tagUrl) 273 | // fmt.Println("TAGURL:", tagUrl) 274 | 275 | req := craftGithubReq(ghtoken, tagUrl) 276 | client := http.Client{} 277 | resp, err := client.Do(req) 278 | if err != nil { 279 | log.Fatal(err) 280 | } 281 | body, err := io.ReadAll(resp.Body) 282 | defer resp.Body.Close() 283 | if err != nil { 284 | log.Fatal(err) 285 | } 286 | return body 287 | } 288 | 289 | // verify if a map is empty or not 290 | func mapIsEmpty(m map[string]int) bool { 291 | return len(m) == 0 // returns true if map is empty 292 | } 293 | 294 | /* - fetch the asset urls from latest release for each url or username/repo (used by -list) 295 | - the regex is used to find the required asset url for your os/arch 296 | - return/print found urls for each asset that matched 297 | - repos that do not have a release for the os and arch will be printed like so: 298 | - N/A: https://github.com/user/repo 299 | */ 300 | 301 | /* 302 | - Fetches the download URLs for specific assets from GitHub releases. 303 | It uses a regular expression (regex) to filter URLs 304 | based on the target OS/architecture. 305 | The function takes URLs from the urlsChan channel 306 | and uses a provided GitHub API token (ghtoken) 307 | to make API requests to fetch release information. 308 | 309 | - The function starts by defining an inner function fetch responsible 310 | for handling the URL processing. 311 | Within this function, it prepares the API URL 312 | using fixUrl and constructs an HTTP GET request 313 | with the provided GitHub token using craftGithubReq. 314 | It then sends the request and reads the response body 315 | containing release information. 316 | 317 | - The function checks if the response contains a "Not Found" message, 318 | indicating that the repository may be using tags 319 | instead of the latest release. In such cases, 320 | it fetches the assets for the most recent tag 321 | using the getTagByName function. 322 | 323 | - Next, the function uses gjson to parse the response body 324 | and extract the URLs of the assets. 325 | It applies the provided regular expression 326 | to filter the URLs based on the target OS/architecture. 327 | URLs that match the regex are stored in the github_release map. 328 | 329 | - If there are matching URLs, they are printed to the console. 330 | If there are no matching URLs, "N/A" is printed to 331 | indicate that no relevant assets were found. 332 | 333 | - The main loop of the function continuously receives URLs 334 | from urlsChan and processes them using the fetch function. 335 | */ 336 | func FetchGithubReleaseUrl(urlsChan chan string, job *sync.WaitGroup, regex, ghtoken string) { 337 | 338 | defer job.Done() 339 | // var github_release []string 340 | // github_release := make(map[string]int) 341 | 342 | fetch := func(u string) { 343 | github_release := make(map[string]int) 344 | // fmt.Println(u) 345 | // map to keep assets 346 | // sometimes there are multiple assets for same os/architecture 347 | // for e.g gnu and musl for linux 348 | re2 := regexp2.MustCompile(regex, 0) // regex for os/arch 349 | // fmt.Println("Regex: ", re) 350 | githubUrl, ownerNrepo := fixUrl(u) // fix url and return valid api url 351 | // fmt.Println(githubUrl) 352 | 353 | // HTTP client starts 354 | req := craftGithubReq(ghtoken, githubUrl) // craft request with token and valid api url 355 | client := http.Client{} 356 | resp, err := client.Do(req) 357 | if err != nil { 358 | log.Fatal(err) 359 | } 360 | body, err := io.ReadAll(resp.Body) 361 | defer resp.Body.Close() 362 | if err != nil { 363 | log.Fatal(err) 364 | } 365 | // HTTP client ends 366 | 367 | message := gjson.Get(fmt.Sprintf("%s", body), "message") 368 | // if the message is "Not Found" 369 | // release/asset section is EMPTY or is using tags instead of latest release 370 | if message.Str == "Not Found" { 371 | // fetch assets for most recent tag 372 | body = getTagByName(ghtoken, ownerNrepo) 373 | } 374 | 375 | // fetch all the browser_download_url keys which contains the asset urls 376 | // results := gjson.Get(fmt.Sprintf("%s", body), "assets.#.browser_download_url") 377 | results := gjson.Get(fmt.Sprintf("%s", body), "assets.#.browser_download_url") 378 | 379 | // fmt.Println("Matching values for URL:", u) 380 | results.ForEach(func(key, value gjson.Result) bool { 381 | // fmt.Println(key.String()) 382 | asset_url := value.String() 383 | // fmt.Println(asset_url) 384 | isMatch, _ := re2.MatchString(asset_url) 385 | 386 | if isMatch == true { 387 | github_release[asset_url] = 1 388 | } 389 | 390 | return true // keep iterating in case there are multiple urls that match 391 | }) 392 | 393 | if mapIsEmpty(github_release) { 394 | fmt.Println("N/A:", u) 395 | } else { 396 | for k, _ := range github_release { 397 | // fmt.Println("URL found:", k) 398 | fmt.Println(k) 399 | } 400 | } 401 | 402 | } 403 | 404 | for u := range urlsChan { 405 | fetch(u) 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 10 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 11 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 12 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 13 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 14 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 15 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 16 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 17 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 18 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 19 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 20 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 21 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 22 | github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= 23 | github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= 24 | github.com/bodgit/sevenzip v1.5.0 h1:QESwnPUnhqftOgbi6wIiWm1WEkrT4puHukt5a2psEcw= 25 | github.com/bodgit/sevenzip v1.5.0/go.mod h1:+E74G6pfBX8IMaVybsKMgGTTTBcbHU8ssPTJ9mLUr38= 26 | github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= 27 | github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= 28 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 29 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 30 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 31 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 32 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 33 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 34 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 35 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 36 | github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 37 | github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 38 | github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= 39 | github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= 40 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 41 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 42 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 43 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 44 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 45 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 46 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 47 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 48 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 49 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 50 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 51 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 52 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 53 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 54 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 55 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 56 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 57 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 58 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 59 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 60 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 61 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 62 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 63 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 64 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 65 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 66 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 67 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 68 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 69 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 70 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 71 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 72 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 73 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 74 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 75 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 76 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 77 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 78 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 79 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 80 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 81 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 82 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 83 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 84 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 85 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 86 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 87 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 88 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 89 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= 90 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 91 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 92 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 93 | github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= 94 | github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 95 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 96 | github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= 97 | github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 98 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 99 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 100 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 101 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 102 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 103 | github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= 104 | github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= 105 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 106 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 107 | github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= 108 | github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= 109 | github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= 110 | github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 111 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 112 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 113 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 114 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 115 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 116 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 117 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= 118 | github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks= 119 | github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4= 120 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJLvBliNGfcQgUmhlniWBDXC79oRxfZA0= 121 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= 122 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= 123 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= 124 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 125 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 126 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 127 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 128 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 129 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 130 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 131 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 132 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 133 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 134 | github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= 135 | github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= 136 | github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= 137 | github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 138 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 139 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 140 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 141 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 142 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 143 | github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 144 | github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= 145 | github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 146 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 147 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 148 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 149 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 150 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 151 | go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= 152 | go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 153 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 154 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 155 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 156 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 157 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 158 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 159 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 160 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 161 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 162 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 163 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 164 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 165 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 166 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 167 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 168 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 169 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 170 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 171 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 172 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 173 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 174 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 175 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 176 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 177 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 178 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 179 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 180 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 181 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 182 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 183 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 184 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 185 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 186 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 187 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 188 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 189 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 190 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 191 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 192 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 193 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 194 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 195 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 196 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 197 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 198 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 199 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 200 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 201 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 202 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 203 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 204 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 205 | golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= 206 | golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= 207 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 208 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 209 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 210 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 211 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 212 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 213 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 214 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 215 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 216 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 217 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 218 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 219 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 220 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 221 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 222 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 223 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 228 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 229 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 230 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 231 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 232 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 233 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 234 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 235 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 236 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 237 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 238 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 239 | golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 240 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 241 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 242 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 243 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 244 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 245 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 246 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 247 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 248 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 249 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 250 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 251 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 252 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 253 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 254 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 255 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 256 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 257 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 258 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 259 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 260 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 261 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 262 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 263 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 264 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 265 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 266 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 267 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 268 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 269 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 270 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 271 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 272 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 273 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 274 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 275 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 276 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 277 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 278 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 279 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 280 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 281 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 282 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 283 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 284 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 285 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 286 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 287 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 288 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 289 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 290 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 291 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 292 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 293 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 294 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 295 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 296 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 297 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 298 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 299 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 300 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 301 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 302 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 303 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 304 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 305 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 306 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 307 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 308 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 309 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 310 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 311 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 312 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 313 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 314 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 315 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 316 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 317 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 318 | google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= 319 | google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 320 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 321 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 322 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 323 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 324 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 325 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 326 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 327 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 328 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 329 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 330 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 331 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 332 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 333 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 334 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 335 | --------------------------------------------------------------------------------