├── .gitattributes ├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── autoupdater └── autoupdate.go ├── cmd ├── bitbucket.go ├── local.go ├── root.go └── version.go ├── commit └── commit.go ├── emailsimilarity └── similarity.go ├── entities └── entities.go ├── extractor ├── extractor.go ├── extractor_suite_test.go ├── extractor_test.go └── uploader.go ├── go.mod ├── go.sum ├── languagedetection ├── analyzer.go ├── analyzer_test.go └── languagedetection_suite_test.go ├── librarydetection ├── analyzer.go └── languages │ ├── C.go │ ├── C_test.go │ ├── Cpp.go │ ├── Cpp_test.go │ ├── Dart.go │ ├── Dart_test.go │ ├── Go.go │ ├── Go_test.go │ ├── Java.go │ ├── JavaScript.go │ ├── JavaScript_test.go │ ├── Java_test.go │ ├── Perl.go │ ├── Perl_test.go │ ├── Python.go │ ├── Python_test.go │ ├── R.go │ ├── R_test.go │ ├── Ruby.go │ ├── Ruby_test.go │ ├── Rust.go │ ├── Rust_test.go │ ├── Swift.go │ ├── Swift_test.go │ ├── TypeScript.go │ ├── TypeScript_test.go │ ├── cSharp.go │ ├── cSharp_test.go │ ├── common.go │ ├── fixtures │ ├── c.fixture │ ├── cpp.fixture │ ├── csharp.fixture │ ├── dart.fixture │ ├── go.fixture │ ├── html.fixture │ ├── java.fixture │ ├── javascript.fixture │ ├── kotlin.fixture │ ├── perl.fixture │ ├── php.fixture │ ├── python.fixture │ ├── r.fixture │ ├── ruby.fixture │ ├── rust.fixture │ ├── swift.fixture │ └── typescript.fixture │ ├── html.go │ ├── html_test.go │ ├── kotlin.go │ ├── kotlin_test.go │ ├── language_suite_test.go │ ├── php.go │ └── php_test.go ├── main.go ├── obfuscation └── obfuscator.go ├── repoSources ├── bitbucket.go ├── bitbucket_test.go ├── local.go ├── local_test.go ├── repoSource.go ├── repoSources_suite_test.go └── upload.go ├── test_fixtures └── repoSources │ └── bitbucket │ └── public.json └── ui ├── confirm.go ├── progressBar.go └── selectEmail.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # These files should have specific line endings 5 | *.sh text eol=lf 6 | *.bat text eol=crlf 7 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.15 20 | 21 | - name: Test 22 | run: go test -v ./... 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | *.pyc 3 | repo_data.json.zip 4 | .DS_Store 5 | .idea/ 6 | repo_data.json 7 | .pyenv/ 8 | *.json.zip 9 | *.log 10 | repo_data_v2.json 11 | repo_data_v2.json.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 codersrankOrg 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | generated: build 2 | 3 | test: 4 | @go test ./... 5 | 6 | build: test 7 | export CGO_ENABLED=0 export GOOS=linux && export GOARCH=amd64 && go build -a -tags netgo -ldflags '-w -X main.version=v2.4.2' -o repo_info_extractor_linux 8 | export CGO_ENABLED=0 export GOOS=darwin && export GOARCH=amd64 && go build -a -tags netgo -ldflags '-w -X main.version=v2.4.2' -o repo_info_extractor_osx 9 | export CGO_ENABLED=0 export GOOS=windows && export GOARCH=amd64 && go build -a -tags netgo -ldflags '-w -X main.version=v2.4.2' -o repo_info_extractor_windows.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is it? 2 | This script is used to extract data from your private repo. The data is used to calculate your score on https://codersrank.io 3 | 4 | CodersRank by default only considers public repositories, however, most developers have their code in private repositories. We want to give the chance to these developers to improve their scores too by adding their private repositories. 5 | 6 | We can understand private repos are private because of a reason. This script extracts only the most important information from the repos: 7 | - Number of inserted lines in each commit 8 | - Number of deleted lines in each commit 9 | 10 | Other information such as remote URLs, file names, emails, names are hashed. So we can know if two commits belong to the same file but we won't know the file name. 11 | 12 | Moreover, the output is saved to your machine and you can check what data is extracted and you can decide whether you want to share it with us or not. 13 | 14 | ## How does it work? 15 | When a repository is analyzed two tools are used: this and [libraries](https://github.com/codersrank-org/libraries) repository. 16 | This repository is responsible to recognize the languages and export the imported libraries. 17 | The [libraries](https://github.com/codersrank-org/libraries) contains a list of supported libraries, imports and technologies they belong to. 18 | 19 | ### In short 20 | - Language recognition: [repo_info_extractor](https://github.com/codersrank-org/repo_info_extractor/). 21 | - Library recognition: [libraries](https://github.com/codersrank-org/libraries) 22 | 23 | ## How to use it 24 | The repo_info_extractor is written in Go, so you can either clone the repo and compile the program or just download the binary and start using it. 25 | ``` 26 | git clone --depth 1 https://github.com/codersrank-org/repo_info_extractor.git 27 | cd repo_info_extractor 28 | go run . local --repo_path ./path/to/repo 29 | ``` 30 | 31 | ### Binary approach (easiest) 32 | If using this approach, download the binary from [releases](https://github.com/codersrank-org/repo_info_extractor/releases) and run it. 33 | 34 | ``` 35 | wget https://github.com/codersrank-org/repo_info_extractor/releases/download/vx.x.x/repo_info_extractor_osx # replace with the latest version 36 | chmod +x repo_info_extractor_osx # in case of Linux, OSX first make it executable 37 | ./repo_info_extractor_osx local --repo_path ./path_to_repo 38 | ``` 39 | You can find a short video about the usage 40 | 41 | [![How to use repo_info_extractor](https://img.youtube.com/vi/9IqgmYl8l2Y/0.jpg)](https://www.youtube.com/watch?v=9IqgmYl8l2Y) 42 | 43 | ### Available commands 44 | You can see the available commands and flags with the `--help` flag. For example: 45 | ``` 46 | ./repo_info_extractor_osx --help 47 | ... 48 | ./repo_info_extractor_osx bitbucket --help 49 | ``` 50 | Commands: 51 | - `bitbucket` Extract repository from BitBucket 52 | - `help` Help about any command 53 | - `local` Extract local repository by path 54 | - `version` Print the version number 55 | 56 | The commands might have flags. For example `local` has: 57 | `--repo_name` You can overwrite the default repo name. This name will be shown on the profile page. 58 | `--repo_path` Path of the repo 59 | 60 | ## BitBucket 61 | Right now only BitBucket Cloud is supported. For authentication your have to use your username 62 | and create an app password. You can create it here: https://bitbucket.org/account/settings/app-passwords/. 63 | The app password and username must be set via the `--password` and `--username` flags. Example usage: 64 | ``` 65 | ./repo_info_extractor_osx bitbucket --username="peti2001" --password=xxxxxx --visibility=private --emails=karakas.peter@gmail.com 66 | ``` 67 | When you create the a new `app password` make sure you select all the necessary scopes. 68 | ![repo_scope](https://raw.githubusercontent.com/peti2001/multi_repo_extractor/master/docs/bitbucket-scope.png) 69 | The safest way if you create an `app password` and use it instead of your user's password. 70 | 71 | ## Run UnitTests 72 | In the root directory of the repo, run the following command: 73 | 74 | ``` 75 | go test ./... 76 | ``` 77 | -------------------------------------------------------------------------------- /autoupdater/autoupdate.go: -------------------------------------------------------------------------------- 1 | package autoupdater 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "path/filepath" 12 | "regexp" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | type autoUpdater struct { 20 | version string 21 | appName string 22 | apiURL string 23 | osPostFix string 24 | } 25 | 26 | func NewAutoUpdater(version string) autoUpdater { 27 | var osPostFix string 28 | switch runtime.GOOS { 29 | case "darwin": 30 | osPostFix = "_osx" 31 | case "linux": 32 | osPostFix = "_linux" 33 | case "windows": 34 | osPostFix = "_windows.exe" 35 | } 36 | 37 | return autoUpdater{ 38 | version: version, 39 | appName: "repo_info_extractor" + osPostFix, 40 | apiURL: "https://api.github.com/repos/codersrank-org/repo_info_extractor/releases/latest", 41 | osPostFix: osPostFix, 42 | } 43 | } 44 | 45 | // CheckUpdates checks github to see if there is a new version and if there is one, downloads it. 46 | func (au autoUpdater) CheckUpdates() { 47 | fmt.Println("Checking for new versions. Current version: " + au.version) 48 | release, err := au.getRelease() 49 | if err != nil { 50 | fmt.Printf("Couldn't get latest release from Github, skipping update. Error: %s\n", err.Error()) 51 | return 52 | } 53 | latestVersion, err := au.getLatestVersion(release) 54 | if err != nil { 55 | fmt.Printf("Couldn't find the latest version, skipping update. Error: %s\n", err.Error()) 56 | return 57 | } 58 | if au.shouldUpdate(latestVersion) { 59 | fmt.Printf("Found new version %s, updating...\n", latestVersion) 60 | err := au.update(release) 61 | if err != nil { 62 | fmt.Printf("Couldn't download latest release. Error: %s\n", err.Error()) 63 | } else { 64 | fmt.Println("New version downloaded. Please run the program again.") 65 | os.Exit(0) 66 | } 67 | } else { 68 | fmt.Printf("You already have latest version, skipping update\n") 69 | } 70 | } 71 | 72 | func (au autoUpdater) update(r *release) error { 73 | for _, asset := range r.Assets { 74 | // Found the correct binary 75 | if strings.Contains(asset.Name, au.osPostFix) { 76 | fmt.Printf("Downloading %s\n", asset.BrowserDownloadURL) 77 | return au.download(asset.BrowserDownloadURL) 78 | } 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func (au autoUpdater) download(downloadURL string) error { 85 | resp, err := http.Get(downloadURL) 86 | if err != nil { 87 | return err 88 | } 89 | defer resp.Body.Close() 90 | 91 | appPath, err := os.Getwd() 92 | if err != nil { 93 | return err 94 | } 95 | oldName := filepath.Join(appPath, au.appName) 96 | newName := filepath.Join(appPath, au.appName) + "_old" 97 | err = os.Rename(oldName, newName) 98 | if err != nil { 99 | fmt.Printf("Couldn't rename file from %s to %s\n", oldName, newName) 100 | } 101 | filePath := appPath + "/" + au.appName 102 | 103 | out, err := os.Create(filePath) 104 | if err != nil { 105 | return err 106 | } 107 | defer out.Close() 108 | 109 | _, err = io.Copy(out, resp.Body) 110 | if err != nil { 111 | fmt.Printf("New binary saved to %s\n", filePath) 112 | } else { 113 | chmodErr := os.Chmod(filePath, 0755) 114 | if chmodErr != nil { 115 | fmt.Printf("Couldn't set execute permissions for %s\n", filePath) 116 | } 117 | } 118 | return err 119 | } 120 | 121 | func (au autoUpdater) getRelease() (*release, error) { 122 | request, err := http.NewRequest(http.MethodGet, au.apiURL, nil) 123 | if err != nil { 124 | return nil, err 125 | } 126 | 127 | client := &http.Client{} 128 | response, err := client.Do(request) 129 | if err != nil { 130 | return nil, err 131 | } 132 | defer response.Body.Close() 133 | body, err := ioutil.ReadAll(response.Body) 134 | 135 | var r *release 136 | err = json.Unmarshal([]byte(body), &r) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | return r, nil 142 | } 143 | 144 | func (au autoUpdater) getLatestVersion(r *release) (string, error) { 145 | 146 | // "v" is not part of semantic versioning 147 | r.Name = strings.TrimLeft(r.Name, "v") 148 | 149 | // Regex for finding Major, Minor and Patch versions 150 | // Taken from here: https://semver.org/ 151 | regex := regexp.MustCompile(`^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) 152 | matches := regex.FindAllString(r.Name, -1) 153 | if len(matches) == 0 { 154 | return "", errors.New("Couldn't parse current version") 155 | } 156 | 157 | // Split as major, minor and patch 158 | matches = strings.Split(matches[0], ".") 159 | if len(matches) != 3 { 160 | return "", errors.New("Couldn't parse current version") 161 | } 162 | 163 | var err error 164 | major, err := strconv.Atoi(matches[0]) 165 | if err != nil { 166 | return "", err 167 | } 168 | minor, err := strconv.Atoi(matches[1]) 169 | if err != nil { 170 | return "", err 171 | } 172 | patch, err := strconv.Atoi(matches[2]) 173 | 174 | return fmt.Sprintf("v%d.%d.%d", major, minor, patch), err 175 | } 176 | 177 | func (au autoUpdater) shouldUpdate(version string) bool { 178 | return version > au.version 179 | } 180 | 181 | type release struct { 182 | URL string `json:"url"` 183 | AssetsURL string `json:"assets_url"` 184 | UploadURL string `json:"upload_url"` 185 | HTMLURL string `json:"html_url"` 186 | ID int `json:"id"` 187 | NodeID string `json:"node_id"` 188 | TagName string `json:"tag_name"` 189 | TargetCommitish string `json:"target_commitish"` 190 | Name string `json:"name"` 191 | Draft bool `json:"draft"` 192 | Author struct { 193 | Login string `json:"login"` 194 | ID int `json:"id"` 195 | NodeID string `json:"node_id"` 196 | AvatarURL string `json:"avatar_url"` 197 | GravatarID string `json:"gravatar_id"` 198 | URL string `json:"url"` 199 | HTMLURL string `json:"html_url"` 200 | FollowersURL string `json:"followers_url"` 201 | FollowingURL string `json:"following_url"` 202 | GistsURL string `json:"gists_url"` 203 | StarredURL string `json:"starred_url"` 204 | SubscriptionsURL string `json:"subscriptions_url"` 205 | OrganizationsURL string `json:"organizations_url"` 206 | ReposURL string `json:"repos_url"` 207 | EventsURL string `json:"events_url"` 208 | ReceivedEventsURL string `json:"received_events_url"` 209 | Type string `json:"type"` 210 | SiteAdmin bool `json:"site_admin"` 211 | } `json:"author"` 212 | Prerelease bool `json:"prerelease"` 213 | CreatedAt time.Time `json:"created_at"` 214 | PublishedAt time.Time `json:"published_at"` 215 | Assets []struct { 216 | URL string `json:"url"` 217 | ID int `json:"id"` 218 | NodeID string `json:"node_id"` 219 | Name string `json:"name"` 220 | Label interface{} `json:"label"` 221 | Uploader struct { 222 | Login string `json:"login"` 223 | ID int `json:"id"` 224 | NodeID string `json:"node_id"` 225 | AvatarURL string `json:"avatar_url"` 226 | GravatarID string `json:"gravatar_id"` 227 | URL string `json:"url"` 228 | HTMLURL string `json:"html_url"` 229 | FollowersURL string `json:"followers_url"` 230 | FollowingURL string `json:"following_url"` 231 | GistsURL string `json:"gists_url"` 232 | StarredURL string `json:"starred_url"` 233 | SubscriptionsURL string `json:"subscriptions_url"` 234 | OrganizationsURL string `json:"organizations_url"` 235 | ReposURL string `json:"repos_url"` 236 | EventsURL string `json:"events_url"` 237 | ReceivedEventsURL string `json:"received_events_url"` 238 | Type string `json:"type"` 239 | SiteAdmin bool `json:"site_admin"` 240 | } `json:"uploader"` 241 | ContentType string `json:"content_type"` 242 | State string `json:"state"` 243 | Size int `json:"size"` 244 | DownloadCount int `json:"download_count"` 245 | CreatedAt time.Time `json:"created_at"` 246 | UpdatedAt time.Time `json:"updated_at"` 247 | BrowserDownloadURL string `json:"browser_download_url"` 248 | } `json:"assets"` 249 | TarballURL string `json:"tarball_url"` 250 | ZipballURL string `json:"zipball_url"` 251 | Body string `json:"body"` 252 | } 253 | -------------------------------------------------------------------------------- /cmd/bitbucket.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | repoSource "github.com/codersrank-org/repo_info_extractor/v2/repoSources" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | type bitbucketConfig struct { 11 | Username string 12 | Password string 13 | Visibility string 14 | } 15 | 16 | var ( 17 | bitbucketCmd = &cobra.Command{ 18 | Use: "bitbucket", 19 | Short: "Extract repository from BitBucket", 20 | Long: `Provide the username and password and it will extract the repositories from BitBucket`, 21 | Run: func(cmd *cobra.Command, args []string) { 22 | source := repoSource.NewBitbucketProvider( 23 | BitbucketConfig.Username, 24 | BitbucketConfig.Password, 25 | BitbucketConfig.Visibility, 26 | *RootConfig.GitPath, 27 | ) 28 | config := repoSource.ExtractConfig{ 29 | OutputPath: *RootConfig.OutPutPath, 30 | GitPath: *RootConfig.GitPath, 31 | Headless: *RootConfig.Headless, 32 | Obfuscate: *RootConfig.Obfuscate, 33 | UserEmails: *RootConfig.Emails, 34 | Seeds: *RootConfig.Seeds, 35 | ShowProgressBar: !*RootConfig.Headless, 36 | SkipLibraries: *RootConfig.SkipLibraries, 37 | SkipUpload: *RootConfig.SkipUpload, 38 | } 39 | err := repoSource.ExtractFromSource(source, config) 40 | 41 | if err != nil { 42 | fmt.Println("Couldn't locally extract repo. Error:", err.Error()) 43 | } 44 | }, 45 | } 46 | 47 | BitbucketConfig bitbucketConfig 48 | ) 49 | 50 | func init() { 51 | rootCmd.AddCommand(bitbucketCmd) 52 | bitbucketCmd.Flags().StringVar(&BitbucketConfig.Username, "username", "", "Username to authenticate to BitBucket") 53 | bitbucketCmd.Flags().StringVar(&BitbucketConfig.Password, "password", "", "Password to authenticate to BitBucket") 54 | bitbucketCmd.Flags().StringVar(&BitbucketConfig.Visibility, "visibility", "", "Filter extracted repos by visibility. Possible values: public, private, all") 55 | bitbucketCmd.MarkFlagRequired("username") 56 | bitbucketCmd.MarkFlagRequired("password") 57 | bitbucketCmd.MarkFlagRequired("visibility") 58 | } 59 | -------------------------------------------------------------------------------- /cmd/local.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | repoSource "github.com/codersrank-org/repo_info_extractor/v2/repoSources" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | type extractConfig struct { 11 | RepoPath string 12 | RepoName string 13 | } 14 | 15 | var ( 16 | localCmd = &cobra.Command{ 17 | Use: "local", 18 | Short: "Extract local repository by path", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | source := repoSource.NewDirectoryPath(ExtractConfig.RepoPath, ExtractConfig.RepoName) 21 | config := repoSource.ExtractConfig{ 22 | OutputPath: *RootConfig.OutPutPath, 23 | GitPath: *RootConfig.GitPath, 24 | Headless: *RootConfig.Headless, 25 | Obfuscate: *RootConfig.Obfuscate, 26 | UserEmails: *RootConfig.Emails, 27 | Seeds: *RootConfig.Seeds, 28 | ShowProgressBar: !*RootConfig.Headless, 29 | SkipLibraries: *RootConfig.SkipLibraries, 30 | SkipUpload: *RootConfig.SkipUpload, 31 | } 32 | err := repoSource.ExtractFromSource(source, config) 33 | 34 | if err != nil { 35 | fmt.Println("Couldn't locally extract repo. Error:", err.Error()) 36 | } 37 | }, 38 | } 39 | 40 | ExtractConfig extractConfig 41 | ) 42 | 43 | func init() { 44 | rootCmd.AddCommand(localCmd) 45 | localCmd.Flags().StringVar(&ExtractConfig.RepoPath, "repo_path", "", "Path of the repo") 46 | localCmd.MarkFlagRequired("repo_path") 47 | localCmd.Flags().StringVar(&ExtractConfig.RepoName, "repo_name", "", "You can overwrite the default repo name. This name will be shown on the profile page.") 48 | } 49 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | type rootConfig struct { 13 | SkipLibraries *bool 14 | SkipUpdate *bool 15 | Seeds *[]string 16 | Emails *[]string 17 | GitPath *string 18 | OutPutPath *string 19 | Obfuscate *bool 20 | Headless *bool 21 | SkipUpload *bool 22 | } 23 | 24 | var ( 25 | rootCmd = &cobra.Command{ 26 | Use: "repo_info_extractor", 27 | Short: "Extract data from a Git repository", 28 | Long: `Use this command to extract and upload repo data your CodersRank profile. 29 | Example usage: repo_info_extractor path --repo_path /path/to/repo`, 30 | } 31 | 32 | RootConfig rootConfig 33 | 34 | emailString *string 35 | seedsString *string 36 | Version string 37 | ) 38 | 39 | func Execute() { 40 | if err := rootCmd.Execute(); err != nil { 41 | fmt.Fprintln(os.Stderr, err) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func init() { 47 | cobra.OnInitialize(initConfig) 48 | 49 | RootConfig.SkipLibraries = rootCmd.PersistentFlags().Bool("skip_libraries", false, "Turns off the library detection in order to reduce the execution time") 50 | RootConfig.SkipUpdate = rootCmd.PersistentFlags().Bool("skip_update", false, "If set the auto-update is skipped") 51 | emailString = rootCmd.PersistentFlags().String("emails", "", "Predefined emails. Example: \"alim.giray@codersrank.io,alimgiray@gmail.com\"") 52 | seedsString = rootCmd.PersistentFlags().String("seeds", "", "The seed is used to find similar emails. Example: \"alimgiray, alimgiray@codersrank.io\"") 53 | RootConfig.GitPath = rootCmd.PersistentFlags().String("git_path", "", "where the Git binary is") 54 | RootConfig.OutPutPath = rootCmd.PersistentFlags().String("output_path", "./artifacts", "Where to put output file. Existing artifacts will be overwritten.") 55 | RootConfig.Obfuscate = rootCmd.PersistentFlags().Bool("obfuscate", true, "File names and emails won't be hashed. Set it to true for debug purposes.") 56 | RootConfig.Headless = rootCmd.PersistentFlags().Bool("headless", false, "Headless mode is used on CodersRank's backend system.") 57 | RootConfig.SkipUpload = rootCmd.PersistentFlags().Bool("skip_upload", false, "Artifacts won't be uploaded. Don't even ask whether to upload the artifacts.") 58 | } 59 | 60 | func initConfig() { 61 | emails := make([]string, 0) 62 | if len(*emailString) > 0 { 63 | emails = strings.Split(*emailString, ",") 64 | } 65 | RootConfig.Emails = &emails 66 | 67 | seeds := make([]string, 0) 68 | if len(*seedsString) > 0 { 69 | seeds = strings.Split(*seedsString, ",") 70 | } 71 | 72 | RootConfig.Seeds = &seeds 73 | 74 | // Find git executable if it is not provided 75 | if *RootConfig.GitPath == "" { 76 | gitPath, err := exec.LookPath("git") 77 | if err != nil { 78 | defaultGitPath := "/usr/bin/git" 79 | fmt.Printf("Couldn't find git path. Fall back to default (%s). Error: %s.\n", defaultGitPath, err.Error()) 80 | // Try default git path 81 | *RootConfig.GitPath = defaultGitPath 82 | return 83 | } 84 | gitPath = strings.TrimRight(gitPath, "\r\n") 85 | gitPath = strings.TrimRight(gitPath, "\n") 86 | 87 | *RootConfig.GitPath = gitPath 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | rootCmd.AddCommand(versionCmd) 11 | } 12 | 13 | var versionCmd = &cobra.Command{ 14 | Use: "version", 15 | Short: "Print the version number", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | fmt.Printf("repo_info_extractor %s by CodersRank\n", Version) 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /commit/commit.go: -------------------------------------------------------------------------------- 1 | package commit 2 | 3 | type Commit struct { 4 | Hash string `json:"commitHash"` 5 | AuthorName string `json:"authorName"` 6 | AuthorEmail string `json:"authorEmail"` 7 | Date string `json:"createdAt"` 8 | ChangedFiles []*ChangedFile `json:"changedFiles"` 9 | Libraries map[string][]string `json:"libraries"` 10 | } 11 | 12 | type ChangedFile struct { 13 | Path string `json:"fileName"` 14 | Insertions int `json:"insertions"` 15 | Deletions int `json:"deletions"` 16 | Language string `json:"language"` 17 | } 18 | -------------------------------------------------------------------------------- /emailsimilarity/similarity.go: -------------------------------------------------------------------------------- 1 | package emailsimilarity 2 | 3 | import ( 4 | "math" 5 | "strings" 6 | 7 | "github.com/iancoleman/orderedmap" 8 | ) 9 | 10 | var filterDomains = []string{"@gmail.com", ".local", "@users.noreply.github.com"} 11 | 12 | // FindSimilarEmails returns similar emails to seed, from a given email list 13 | func FindSimilarEmails(seeds, emails []string) []string { 14 | 15 | similarEmails := make([]string, 0, len(emails)) 16 | 17 | for _, seed := range seeds { 18 | for _, email := range emails { 19 | similarity := calculateSimilarity(seed, email) 20 | if similarity > 0.55 { 21 | similarEmails = append(similarEmails, email) 22 | } 23 | } 24 | } 25 | 26 | return similarEmails 27 | } 28 | 29 | func calculateSimilarity(seed, email string) float64 { 30 | 31 | seedNgrams := getNgrams(seed, 2) 32 | emailNgrams := getNgrams(email, 2) 33 | 34 | ngramList := getNgramFullList(seedNgrams, emailNgrams) 35 | 36 | seedVec := getNgramVector(seedNgrams, ngramList) 37 | emailVec := getNgramVector(emailNgrams, ngramList) 38 | 39 | similarity := dotProduct(seedVec, emailVec) / (math.Sqrt(sum(square(seedVec))) * math.Sqrt(sum(square(emailVec)))) 40 | return similarity 41 | } 42 | 43 | func getNgrams(word string, n int) *orderedmap.OrderedMap { 44 | ngrams := orderedmap.New() 45 | ngrams.SetEscapeHTML(false) // Default is true for serialization, but we don't need it 46 | 47 | for _, f := range filterDomains { 48 | word = strings.TrimRight(word, f) 49 | } 50 | 51 | for i := 0; i < len(word)-n+1; i++ { 52 | ngram := word[i : i+n] 53 | 54 | if val, ok := ngrams.Get(ngram); ok { 55 | ngrams.Set(ngram, val.(int)+1) 56 | } else { 57 | ngrams.Set(ngram, 1) 58 | } 59 | } 60 | 61 | return ngrams 62 | } 63 | 64 | // Just merging two maps, we won't use values here, only keys 65 | func getNgramFullList(ngram1, ngram2 *orderedmap.OrderedMap) *orderedmap.OrderedMap { 66 | fullList := orderedmap.New() 67 | fullList.SetEscapeHTML(false) 68 | 69 | for _, v := range ngram1.Keys() { 70 | fullList.Set(v, 0) 71 | } 72 | 73 | for _, v := range ngram2.Keys() { 74 | fullList.Set(v, 0) 75 | } 76 | 77 | return fullList 78 | } 79 | 80 | func getNgramVector(ngram, ngramList *orderedmap.OrderedMap) []int { 81 | vector := make([]int, 0, len(ngramList.Keys())) 82 | for _, n := range ngramList.Keys() { 83 | if val, ok := ngram.Get(n); ok { 84 | vector = append(vector, val.(int)) 85 | } else { 86 | vector = append(vector, 0) 87 | } 88 | } 89 | return vector 90 | } 91 | 92 | func dotProduct(x, y []int) float64 { 93 | total := 0.0 94 | for i := 0; i < len(x) && i < len(y); i++ { 95 | total += float64(x[i] * y[i]) 96 | } 97 | 98 | return total 99 | } 100 | 101 | func square(x []int) []int { 102 | result := make([]int, len(x)) 103 | for i := range x { 104 | result[i] = x[i] * x[i] 105 | } 106 | return result 107 | } 108 | 109 | func sum(x []int) float64 { 110 | sum := 0 111 | for i := range x { 112 | sum += x[i] 113 | } 114 | return float64(sum) 115 | } 116 | -------------------------------------------------------------------------------- /entities/entities.go: -------------------------------------------------------------------------------- 1 | package entities 2 | 3 | import "strings" 4 | 5 | // Repository is the internal representation of repository information 6 | type Repository struct { 7 | // ID of the external repository. e.g. GitHub ID 8 | ID string 9 | // FullName the full name of the repo including the vendor. e.g. microsoft/vscode 10 | FullName string 11 | // Name this name will be used to save the artifact e.g. vscode 12 | Name string 13 | // CloneURL this URL needs to be used to clone the repo 14 | CloneURL string 15 | } 16 | 17 | // GetSafeFullName returns with a string that can be used as file name. 18 | func (r *Repository) GetSafeFullName() string { 19 | return strings.Replace(r.FullName, "/", "_", -1) 20 | } 21 | -------------------------------------------------------------------------------- /extractor/extractor.go: -------------------------------------------------------------------------------- 1 | package extractor 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "runtime" 13 | "strconv" 14 | "strings" 15 | "time" 16 | 17 | "golang.org/x/net/context" 18 | "golang.org/x/text/language" 19 | "golang.org/x/text/search" 20 | 21 | "github.com/codersrank-org/repo_info_extractor/v2/commit" 22 | "github.com/codersrank-org/repo_info_extractor/v2/emailsimilarity" 23 | "github.com/codersrank-org/repo_info_extractor/v2/languagedetection" 24 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 25 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 26 | "github.com/codersrank-org/repo_info_extractor/v2/obfuscation" 27 | "github.com/codersrank-org/repo_info_extractor/v2/ui" 28 | "github.com/mholt/archiver" 29 | ) 30 | 31 | // RepoExtractor is responsible for all parts of repo extraction process 32 | // Including cloning the repo, processing the commits and uploading the results 33 | type RepoExtractor struct { 34 | RepoPath string 35 | OutputPath string 36 | GitPath string 37 | Headless bool 38 | Obfuscate bool 39 | ShowProgressBar bool // If it is false there is no progress bar. 40 | SkipLibraries bool // If it is false there is no library detection. 41 | UserEmails []string 42 | OverwrittenRepoName string // If set this will be used instead of the original repo name 43 | TimeLimit time.Duration // If set the extraction will be stopped after the given time limit and the partial result will be uploaded 44 | Seed []string 45 | repo *repo 46 | userCommits []*commit.Commit // Commits which are belong to user (from selected emails) 47 | commitPipeline chan commit.Commit 48 | libraryExtractionCompleted chan bool 49 | } 50 | 51 | // Extract a single repo in the path 52 | func (r *RepoExtractor) Extract() error { 53 | var ctx context.Context 54 | var cancel context.CancelFunc 55 | 56 | if r.TimeLimit.Seconds() != 0.0 { 57 | ctx, cancel = context.WithTimeout(context.Background(), r.TimeLimit) 58 | defer cancel() 59 | } else { 60 | ctx = context.Background() 61 | } 62 | 63 | err := r.initRepo() 64 | if err != nil { 65 | fmt.Println("Cannot init repo_info_extractor. Error: ", err.Error()) 66 | return err 67 | } 68 | 69 | // For library detection 70 | r.initAnalyzers() 71 | 72 | err = r.analyseCommits(ctx) 73 | if err != nil { 74 | return err 75 | } 76 | go r.analyseLibraries(ctx) 77 | 78 | err = r.export() 79 | if err != nil { 80 | fmt.Println("Couldn't export commits to artifact. Error:", err.Error()) 81 | return err 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // Creates Repo struct 88 | func (r *RepoExtractor) initRepo() error { 89 | fmt.Println("Initializing repository") 90 | 91 | r.commitPipeline = make(chan commit.Commit) 92 | r.libraryExtractionCompleted = make(chan bool) 93 | cmd := exec.Command(r.GitPath, 94 | "config", 95 | "--get", 96 | "remote.origin.url", 97 | ) 98 | cmd.Dir = r.RepoPath 99 | 100 | out, err := cmd.CombinedOutput() 101 | if err != nil { 102 | fmt.Println("Cannot get remote.origin.url. Use directory path to get repo name.") 103 | } 104 | 105 | repoName := "" 106 | remoteOrigin := string(out) 107 | remoteOrigin = strings.TrimRight(remoteOrigin, "\r\n") 108 | remoteOrigin = strings.TrimRight(remoteOrigin, "\n") 109 | 110 | repoName = r.GetRepoName(remoteOrigin) 111 | 112 | r.repo = &repo{ 113 | RepoName: repoName, 114 | Emails: []string{}, 115 | SuggestedEmails: []string{}, // TODO implement 116 | } 117 | return nil 118 | } 119 | 120 | // GetRepoName gets the repo name in the following format: 121 | // in case of headless: "owner_name/repo_name" 122 | // in case of interactive mode: "repo_name" 123 | func (r *RepoExtractor) GetRepoName(remoteOrigin string) string { 124 | // If remoteOrigin is empty fall back to the repos path. It can happen in interactive mode 125 | if remoteOrigin == "" { 126 | parts := strings.Split(r.RepoPath, "/") 127 | return parts[len(parts)-1] 128 | } 129 | repoName := "" 130 | remoteOrigin = strings.TrimSuffix(remoteOrigin, ".git") 131 | if strings.Contains(remoteOrigin, "http") { 132 | // Cloned using http 133 | parts := strings.Split(remoteOrigin, "/") 134 | if r.Headless { 135 | repoName = parts[len(parts)-2] + "/" + parts[len(parts)-1] 136 | } else { 137 | // If it's a private repo, we only need last part of the name 138 | repoName = parts[len(parts)-1] 139 | } 140 | } else { 141 | // Cloned using ssh 142 | parts := strings.Split(remoteOrigin, ":") 143 | repoName = parts[len(parts)-1] 144 | parts = strings.Split(repoName, "/") 145 | if r.Headless { 146 | repoName = parts[len(parts)-2] + "/" + parts[len(parts)-1] 147 | } else { 148 | repoName = parts[len(parts)-1] 149 | } 150 | } 151 | 152 | return repoName 153 | } 154 | 155 | func (r *RepoExtractor) initAnalyzers() { 156 | librarydetection.AddAnalyzer("Go", languages.NewGoAnalyzer()) 157 | librarydetection.AddAnalyzer("C", languages.NewCAnalyzer()) 158 | librarydetection.AddAnalyzer("C++", languages.NewCppAnalyzer()) 159 | librarydetection.AddAnalyzer("C#", languages.NewCSharpAnalyzer()) 160 | librarydetection.AddAnalyzer("Dart", languages.NewDartScriptAnalyzer()) 161 | librarydetection.AddAnalyzer("Java", languages.NewJavaAnalyzer()) 162 | librarydetection.AddAnalyzer("JavaScript", languages.NewJavaScriptAnalyzer()) 163 | librarydetection.AddAnalyzer("Kotlin", languages.NewKotlinAnalyzer()) 164 | librarydetection.AddAnalyzer("TypeScript", languages.NewTypeScriptAnalyzer()) 165 | librarydetection.AddAnalyzer("Perl", languages.NewPerlAnalyzer()) 166 | librarydetection.AddAnalyzer("PHP", languages.NewPHPAnalyzer()) 167 | librarydetection.AddAnalyzer("Python", languages.NewPythonScriptAnalyzer()) 168 | librarydetection.AddAnalyzer("R", languages.NewRAnalyzer()) 169 | librarydetection.AddAnalyzer("Ruby", languages.NewRubyScriptAnalyzer()) 170 | librarydetection.AddAnalyzer("Rust", languages.NewRustAnalyzer()) 171 | librarydetection.AddAnalyzer("Swift", languages.NewSwiftAnalyzer()) 172 | } 173 | 174 | // Creates commits 175 | func (r *RepoExtractor) analyseCommits(ctx context.Context) error { 176 | fmt.Println("Analysing commits") 177 | 178 | var commits []*commit.Commit 179 | commits, err := r.getCommits(ctx) 180 | userCommits := make([]*commit.Commit, 0, len(commits)) 181 | if len(commits) == 0 { 182 | return nil 183 | } 184 | if err != nil { 185 | return err 186 | } 187 | 188 | allEmails := getAllEmails(commits) 189 | selectedEmails := make(map[string]bool) 190 | 191 | // If seed is provided use it in headless mode 192 | if len(r.Seed) > 0 && r.Headless { 193 | similarEmails := emailsimilarity.FindSimilarEmails(r.Seed, allEmails) 194 | similarEmailsWithoutNames, selectedSimilarEmailsMap := getEmailsWithoutNames(similarEmails) 195 | r.repo.SuggestedEmails = similarEmailsWithoutNames 196 | for mail := range selectedSimilarEmailsMap { 197 | selectedEmails[mail] = true 198 | } 199 | } 200 | 201 | if len(r.UserEmails) == 0 && !r.Headless { 202 | selectedEmailsWithNames := ui.SelectEmail(allEmails) 203 | emails, emailsMap := getEmailsWithoutNames(selectedEmailsWithNames) 204 | r.repo.Emails = append(r.repo.Emails, emails...) 205 | for mail := range emailsMap { 206 | selectedEmails[mail] = true 207 | } 208 | } else { 209 | r.repo.Emails = append(r.repo.Emails, r.UserEmails...) 210 | for _, email := range r.UserEmails { 211 | selectedEmails[email] = true 212 | } 213 | } 214 | 215 | // Only consider commits for user 216 | for _, v := range commits { 217 | if _, ok := selectedEmails[v.AuthorEmail]; ok { 218 | userCommits = append(userCommits, v) 219 | } 220 | } 221 | 222 | r.userCommits = userCommits 223 | return nil 224 | } 225 | 226 | func (r *RepoExtractor) getCommits(ctx context.Context) ([]*commit.Commit, error) { 227 | jobs := make(chan *req) 228 | results := make(chan []*commit.Commit) 229 | noMoreChan := make(chan bool) 230 | for w := 0; w < runtime.NumCPU(); w++ { 231 | go func() { 232 | err := r.commitWorker(w, jobs, results, noMoreChan) 233 | if err != nil { 234 | fmt.Println("Error during getting commits. Error: " + err.Error()) 235 | } 236 | }() 237 | } 238 | 239 | // launch initial jobs 240 | lastOffset := 0 241 | step := 1000 242 | for x := 0; x < runtime.NumCPU(); x++ { 243 | jobs <- &req{ 244 | Limit: step, 245 | Offset: x * step, 246 | } 247 | lastOffset = step * x 248 | } 249 | 250 | var commits []*commit.Commit 251 | workersReturnedNoMore := 0 252 | 253 | var pb ui.ProgressBar 254 | numberOfCommits := r.getNumberOfCommits() 255 | if r.ShowProgressBar && numberOfCommits > 0 { 256 | pb = ui.NewProgressBar(numberOfCommits) 257 | } else { 258 | pb = ui.NilProgressBar() 259 | } 260 | 261 | func() { 262 | for { 263 | select { 264 | case res := <-results: 265 | lastOffset += step 266 | jobs <- &req{ 267 | Limit: step, 268 | Offset: lastOffset, 269 | } 270 | commits = append(commits, res...) 271 | pb.SetCurrent(len(commits)) 272 | case <-noMoreChan: 273 | workersReturnedNoMore++ 274 | if workersReturnedNoMore == runtime.NumCPU() { 275 | close(jobs) 276 | return 277 | } 278 | case <-ctx.Done(): 279 | fmt.Println("Time limit exceeded. Couldn't get all the commits.") 280 | close(jobs) 281 | return 282 | } 283 | } 284 | }() 285 | pb.Finish() 286 | 287 | return commits, nil 288 | } 289 | 290 | func getAllEmails(commits []*commit.Commit) []string { 291 | allEmails := make([]string, 0, len(commits)) 292 | emails := make(map[string]bool) // To prevent duplicates 293 | for _, v := range commits { 294 | if _, ok := emails[v.AuthorEmail]; !ok { 295 | emails[v.AuthorEmail] = true 296 | allEmails = append(allEmails, fmt.Sprintf("%s -> %s", v.AuthorName, v.AuthorEmail)) 297 | } 298 | } 299 | return allEmails 300 | } 301 | 302 | func getEmailsWithoutNames(emails []string) ([]string, map[string]bool) { 303 | emailsWithoutNames := make(map[string]bool, len(emails)) 304 | emailsWithoutNamesArray := make([]string, len(emails)) 305 | for i, selectedEmail := range emails { 306 | fields := strings.Split(selectedEmail, " -> ") 307 | // TODO handle authorName being empty 308 | if len(fields) > 0 { 309 | emailsWithoutNames[fields[1]] = true 310 | emailsWithoutNamesArray[i] = fields[1] 311 | } 312 | } 313 | return emailsWithoutNamesArray, emailsWithoutNames 314 | } 315 | 316 | func (r *RepoExtractor) getNumberOfCommits() int { 317 | cmd := exec.Command(r.GitPath, 318 | "--no-pager", 319 | "log", 320 | "--all", 321 | "--no-merges", 322 | "--pretty=oneline", 323 | ) 324 | cmd.Dir = r.RepoPath 325 | stdout, err := cmd.CombinedOutput() 326 | if err != nil { 327 | fmt.Println("Cannot get number of commits. Cannot show progress bar. Error: " + err.Error()) 328 | return 0 329 | } 330 | return strings.Count(string(stdout), "\n") 331 | } 332 | 333 | // commitWorker get commits from git 334 | func (r *RepoExtractor) commitWorker(w int, jobs <-chan *req, results chan<- []*commit.Commit, noMoreChan chan<- bool) error { 335 | for v := range jobs { 336 | var commits []*commit.Commit 337 | 338 | cmd := exec.Command(r.GitPath, 339 | "log", 340 | "--numstat", 341 | "--all", 342 | fmt.Sprintf("--skip=%d", v.Offset), 343 | fmt.Sprintf("--max-count=%d", v.Limit), 344 | "--pretty=format:|||BEGIN|||%H|||SEP|||%an|||SEP|||%ae|||SEP|||%ad", 345 | "--no-merges", 346 | ) 347 | cmd.Dir = r.RepoPath 348 | stdout, err := cmd.StdoutPipe() 349 | if nil != err { 350 | fmt.Println("Cannot create pipe.") 351 | return err 352 | } 353 | if err := cmd.Start(); err != nil { 354 | fmt.Println("Error during execution of Git command.") 355 | return err 356 | } 357 | defer func(c *exec.Cmd) { 358 | c.Wait() 359 | }(cmd) 360 | 361 | // parse the output into stats 362 | scanner := bufio.NewScanner(stdout) 363 | currentLine := 0 364 | var currectCommit *commit.Commit 365 | for scanner.Scan() { 366 | m := scanner.Text() 367 | currentLine++ 368 | if m == "" { 369 | continue 370 | } 371 | if strings.HasPrefix(m, "|||BEGIN|||") { 372 | // we reached a new commit 373 | // save the existing 374 | if currectCommit != nil { 375 | commits = append(commits, currectCommit) 376 | } 377 | 378 | // and add new one commit 379 | m = strings.Replace(m, "|||BEGIN|||", "", 1) 380 | bits := strings.Split(m, "|||SEP|||") 381 | changedFiles := []*commit.ChangedFile{} 382 | dateStr := "" 383 | t, err := time.Parse("Mon Jan 2 15:04:05 2006 -0700", bits[3]) 384 | if err == nil { 385 | dateStr = t.Format("2006-01-02 15:04:05 -0700") 386 | } else { 387 | fmt.Println("Cannot convert date. Expected date format: Mon Jan 2 15:04:05 2006 -0700. Got: " + bits[3]) 388 | } 389 | currectCommit = &commit.Commit{ 390 | Hash: bits[0], 391 | AuthorName: bits[1], 392 | AuthorEmail: bits[2], 393 | Date: dateStr, 394 | ChangedFiles: changedFiles, 395 | } 396 | continue 397 | } 398 | 399 | bits := strings.Fields(m) 400 | 401 | insertionsString := bits[0] 402 | if insertionsString == "-" { 403 | insertionsString = "0" 404 | } 405 | insertions, err := strconv.Atoi(insertionsString) 406 | if err != nil { 407 | fmt.Println("Cannot convert the following into integer: " + insertionsString) 408 | return err 409 | } 410 | 411 | deletionsString := bits[1] 412 | if deletionsString == "-" { 413 | deletionsString = "0" 414 | } 415 | deletions, err := strconv.Atoi(deletionsString) 416 | if err != nil { 417 | fmt.Println("Cannot convert the following into integer: " + deletionsString) 418 | return err 419 | } 420 | // If there is no filechange at all 421 | if len(bits) <= 2 { 422 | continue 423 | } 424 | fileName := bits[2] 425 | // it is a rename, skip 426 | if strings.Contains("=>", fileName) { 427 | continue 428 | } 429 | 430 | changedFile := &commit.ChangedFile{ 431 | Path: bits[2], 432 | Insertions: insertions, 433 | Deletions: deletions, 434 | } 435 | 436 | if currectCommit == nil { 437 | // TODO maybe skip? does this break anything? 438 | return errors.New("did not expect current commit to be null") 439 | } 440 | 441 | if currectCommit.ChangedFiles == nil { 442 | // TODO maybe skip? does this break anything? 443 | return errors.New("did not expect current commit changed files to be null") 444 | } 445 | 446 | currectCommit.ChangedFiles = append(currectCommit.ChangedFiles, changedFile) 447 | } 448 | 449 | // last commit will not get appended otherwise 450 | // because scanner is not returning anything 451 | if currectCommit != nil { 452 | commits = append(commits, currectCommit) 453 | } 454 | 455 | if len(commits) == 0 { 456 | noMoreChan <- true 457 | return nil 458 | } 459 | results <- commits 460 | } 461 | return nil 462 | } 463 | 464 | func (r *RepoExtractor) analyseLibraries(ctx context.Context) { 465 | fmt.Println("Analysing libraries") 466 | defer func() { 467 | r.libraryExtractionCompleted <- true 468 | }() 469 | 470 | jobs := make(chan *commit.Commit, len(r.userCommits)) 471 | results := make(chan bool, len(r.userCommits)) 472 | // Analyse libraries for every commit 473 | for w := 1; w <= runtime.NumCPU(); w++ { 474 | go r.libraryWorker(ctx, jobs, results) 475 | } 476 | for _, v := range r.userCommits { 477 | jobs <- v 478 | } 479 | close(jobs) 480 | var pb ui.ProgressBar 481 | if r.ShowProgressBar { 482 | pb = ui.NewProgressBar(len(r.userCommits)) 483 | } else { 484 | pb = ui.NilProgressBar() 485 | } 486 | for a := 1; a <= len(r.userCommits); a++ { 487 | <-results 488 | pb.Inc() 489 | } 490 | pb.Finish() 491 | } 492 | 493 | func (r *RepoExtractor) getFileContent(commitHash, filePath string) ([]byte, error) { 494 | cmd := exec.Command(r.GitPath, 495 | "--no-pager", 496 | "show", 497 | fmt.Sprintf("%s:%s", commitHash, filePath), 498 | ) 499 | cmd.Dir = r.RepoPath 500 | var err error 501 | fileContents, err := cmd.CombinedOutput() 502 | if err != nil { 503 | searchString1 := fmt.Sprintf("Path '%s' does not exist in '%s'", filePath, commitHash) 504 | searchString2 := fmt.Sprintf("Path '%s' exists on disk, but not in '%s'", filePath, commitHash) 505 | // Ignore case is needed because on windows error message starts with lowercase letter, in other systems it starts with uppercase letter 506 | stringSearcher := search.New(language.English, search.IgnoreCase) 507 | // means the file was deleted, skip 508 | start, end := stringSearcher.IndexString(string(fileContents), searchString1) 509 | if start != -1 && end != -1 { 510 | return []byte{}, nil 511 | } 512 | start, end = stringSearcher.IndexString(string(fileContents), searchString2) 513 | if start != -1 && end != -1 { 514 | return []byte{}, nil 515 | } 516 | return nil, err 517 | } 518 | 519 | return fileContents, nil 520 | } 521 | 522 | func (r *RepoExtractor) libraryWorker(ctx context.Context, commits <-chan *commit.Commit, results chan<- bool) error { 523 | languageAnalyzer := languagedetection.NewLanguageAnalyzer() 524 | hasTimeout := false 525 | for commitToAnalyse := range commits { 526 | c := commit.Commit{ 527 | ChangedFiles: commitToAnalyse.ChangedFiles, 528 | Libraries: make(map[string][]string), 529 | } 530 | c.Hash = commitToAnalyse.Hash 531 | c.AuthorEmail = commitToAnalyse.AuthorEmail 532 | c.AuthorName = commitToAnalyse.AuthorName 533 | c.Date = commitToAnalyse.Date 534 | libraries := map[string][]string{} 535 | for n, fileChange := range commitToAnalyse.ChangedFiles { 536 | select { 537 | case <-ctx.Done(): 538 | if !hasTimeout { 539 | hasTimeout = true 540 | fmt.Println("Time limit exceeded. Couldn't analyze all the commits.") 541 | } 542 | c.Libraries = libraries 543 | r.commitPipeline <- c 544 | results <- true 545 | continue 546 | default: 547 | } 548 | 549 | lang := "" 550 | var fileContents []byte 551 | fileContents = nil 552 | 553 | extension := filepath.Ext(fileChange.Path) 554 | if extension == "" { 555 | continue 556 | } 557 | // remove the trailing dot 558 | extension = extension[1:] 559 | 560 | if languageAnalyzer.ShouldUseFile(extension) { 561 | var err error 562 | if fileContents == nil { 563 | fileContents, err = r.getFileContent(commitToAnalyse.Hash, fileChange.Path) 564 | if err != nil { 565 | continue 566 | } 567 | } 568 | lang = languageAnalyzer.DetectLanguageFromFile(fileChange.Path, fileContents) 569 | } else { 570 | lang = languageAnalyzer.DetectLanguageFromExtension(extension) 571 | } 572 | 573 | // We don't know extension, nothing to do 574 | if lang == "" { 575 | continue 576 | } 577 | c.ChangedFiles[n].Language = lang 578 | if !r.SkipLibraries { 579 | analyzer, err := librarydetection.GetAnalyzer(lang) 580 | if err != nil { 581 | continue 582 | } 583 | if fileContents == nil { 584 | fileContents, err = r.getFileContent(commitToAnalyse.Hash, fileChange.Path) 585 | if err != nil { 586 | continue 587 | } 588 | } 589 | fileLibraries, err := analyzer.ExtractLibraries(string(fileContents)) 590 | if err != nil { 591 | fmt.Printf("error extracting libraries for %s: %s \n", lang, err.Error()) 592 | } 593 | if libraries[lang] == nil { 594 | libraries[lang] = make([]string, 0) 595 | } 596 | libraries[lang] = append(libraries[lang], fileLibraries...) 597 | } 598 | } 599 | c.Libraries = libraries 600 | r.commitPipeline <- c 601 | results <- true 602 | } 603 | return nil 604 | } 605 | 606 | // Writes result to the file 607 | func (r *RepoExtractor) export() error { 608 | fmt.Println("Creating artifact at: " + r.OutputPath) 609 | 610 | repoDataPath := r.OutputPath + "_v2.json" 611 | zipPath := r.OutputPath + "_v2.json.zip" 612 | // Remove old files 613 | os.Remove(repoDataPath) 614 | os.Remove(zipPath) 615 | 616 | // Create directory 617 | directories := strings.Split(r.OutputPath, string(os.PathSeparator)) 618 | err := os.MkdirAll(strings.Join(directories[:len(directories)-1], string(os.PathSeparator)), 0755) 619 | if err != nil { 620 | log.Println("Cannot create directory. Error:", err.Error()) 621 | } 622 | 623 | file, err := os.Create(repoDataPath) 624 | if err != nil { 625 | return err 626 | } 627 | 628 | w := bufio.NewWriter(file) 629 | if r.OverwrittenRepoName != "" { 630 | r.repo.RepoName = r.OverwrittenRepoName 631 | } 632 | repoMetaData, err := json.Marshal(r.repo) 633 | if err != nil { 634 | return err 635 | } 636 | fmt.Fprintln(w, string(repoMetaData)) 637 | 638 | loop: 639 | for { 640 | select { 641 | case commit := <-r.commitPipeline: 642 | if r.Obfuscate { 643 | obfuscation.Obfuscate(&commit) 644 | } 645 | commitData, err := json.Marshal(commit) 646 | if err != nil { 647 | fmt.Printf("Couldn't write commit to file. CommitHash: %s Error: %s", commit.Hash, err.Error()) 648 | continue 649 | } 650 | fmt.Fprintln(w, string(commitData)) 651 | case <-r.libraryExtractionCompleted: 652 | break loop 653 | } 654 | } 655 | w.Flush() // important 656 | file.Close() 657 | 658 | err = archiver.Archive([]string{repoDataPath}, zipPath) 659 | if err != nil { 660 | fmt.Println("Couldn't make zip archive of the artifact. Error:", err.Error()) 661 | return err 662 | } 663 | 664 | // We don't need this because we already have zip file 665 | os.Remove(repoDataPath) 666 | return nil 667 | } 668 | 669 | // This is for repo_info_extractor used locally and for user to 670 | // upload his/her results automatically to the codersrank 671 | func (r *RepoExtractor) upload() error { 672 | fmt.Println("Uploading result to CodersRank") 673 | url, err := Upload(r.OutputPath+"_v2.json.zip", r.repo.RepoName) 674 | if err != nil { 675 | return err 676 | } 677 | fmt.Println("Go to this link in the browser =>", url) 678 | return nil 679 | } 680 | 681 | type repo struct { 682 | RepoName string `json:"repo"` 683 | Emails []string `json:"emails"` 684 | SuggestedEmails []string `json:"suggestedEmails"` 685 | } 686 | 687 | type req struct { 688 | Limit int 689 | Offset int 690 | } 691 | -------------------------------------------------------------------------------- /extractor/extractor_suite_test.go: -------------------------------------------------------------------------------- 1 | package extractor_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestExtractor(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Extractor Suite") 13 | } 14 | -------------------------------------------------------------------------------- /extractor/extractor_test.go: -------------------------------------------------------------------------------- 1 | package extractor_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/codersrank-org/repo_info_extractor/v2/extractor" 8 | ) 9 | 10 | var _ = Describe("GetRepoName", func() { 11 | 12 | Context("RepoExtractor headless", func() { 13 | It("should get the repo name with the owner name", func() { 14 | re := extractor.RepoExtractor{ 15 | Headless: true, 16 | } 17 | Expect(re.GetRepoName("git@github.com:alimgiray/repo_info_extractor.git")).To(Equal("alimgiray/repo_info_extractor")) 18 | Expect(re.GetRepoName("https://github.com/alimgiray/repo_info_extractor.git")).To(Equal("alimgiray/repo_info_extractor")) 19 | Expect(re.GetRepoName("https://github.com/peti2001-test/second-project.git")).To(Equal("peti2001-test/second-project")) 20 | Expect(re.GetRepoName("ssh://user@host:port/group/repoName.git")).To(Equal("group/repoName")) 21 | }) 22 | }) 23 | 24 | Context("RepoExtractor interactive", func() { 25 | It("should get the repo name without the owner name", func() { 26 | re := extractor.RepoExtractor{ 27 | Headless: false, 28 | RepoPath: "/some/path/alimgiray/repo_info_extractor", 29 | } 30 | Expect(re.GetRepoName("git@github.com:alimgiray/repo_info_extractor.git")).To(Equal("repo_info_extractor")) 31 | Expect(re.GetRepoName("https://github.com/alimgiray/repo_info_extractor.git")).To(Equal("repo_info_extractor")) 32 | Expect(re.GetRepoName("")).To(Equal("repo_info_extractor")) 33 | Expect(re.GetRepoName("ssh://user@host:port/group/repoName.git")).To(Equal("repoName")) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /extractor/uploader.go: -------------------------------------------------------------------------------- 1 | package extractor 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "mime/multipart" 11 | "net/http" 12 | "os" 13 | "path/filepath" 14 | ) 15 | 16 | // Upload result to CodersRAnk 17 | func Upload(path, repoName string) (string, error) { 18 | 19 | url := "https://grpcgateway.codersrank.io/candidate/privaterepo/Upload" 20 | 21 | // Read file 22 | file, err := os.Open(path) 23 | if err != nil { 24 | return "", err 25 | } 26 | defer file.Close() 27 | 28 | // Add file as multipart/form-data 29 | body := &bytes.Buffer{} 30 | writer := multipart.NewWriter(body) 31 | part, err := writer.CreateFormFile("file", filepath.Base(file.Name())) 32 | if err != nil { 33 | return "", err 34 | } 35 | io.Copy(part, file) 36 | writer.Close() 37 | 38 | // Create and make the request 39 | request, err := http.NewRequest("POST", url, body) 40 | if err != nil { 41 | return "", err 42 | } 43 | request.Header.Add("Content-Type", writer.FormDataContentType()) 44 | 45 | client := &http.Client{} 46 | response, err := client.Do(request) 47 | if err != nil { 48 | return "", err 49 | } 50 | if response.StatusCode != http.StatusOK { 51 | return "", errors.New(fmt.Sprintf("Server returned non 200 response. Error code: %d", response.StatusCode)) 52 | } 53 | defer response.Body.Close() 54 | 55 | content, err := ioutil.ReadAll(response.Body) 56 | if err != nil { 57 | return "", err 58 | } 59 | 60 | // Get response and return resulting token 61 | var result uploadResponse 62 | err = json.Unmarshal(content, &result) 63 | if err != nil { 64 | return "", err 65 | } 66 | 67 | processURL := fmt.Sprintf("https://profile.codersrank.io/repo?token=%s&reponame=%s", result.Token, repoName) 68 | return processURL, nil 69 | } 70 | 71 | type uploadResponse struct { 72 | Token string `json:"token"` 73 | } 74 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/codersrank-org/repo_info_extractor/v2 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.2.8 7 | github.com/cheggaaa/pb/v3 v3.0.8 8 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect 9 | github.com/frankban/quicktest v1.14.0 // indirect 10 | github.com/golang/snappy v0.0.2 // indirect 11 | github.com/iancoleman/orderedmap v0.2.0 12 | github.com/jarcoal/httpmock v1.0.8 13 | github.com/mholt/archiver v3.1.1+incompatible 14 | github.com/nwaples/rardecode v1.1.0 // indirect 15 | github.com/onsi/ginkgo v1.15.1 16 | github.com/onsi/gomega v1.11.0 17 | github.com/pierrec/lz4 v2.6.0+incompatible // indirect 18 | github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 19 | github.com/pkg/errors v0.8.1 20 | github.com/spf13/cobra v1.1.3 21 | github.com/src-d/enry/v2 v2.1.0 22 | github.com/ulikunitz/xz v0.5.9 // indirect 23 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 24 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb 25 | golang.org/x/text v0.3.3 26 | ) 27 | -------------------------------------------------------------------------------- /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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/AlecAivazis/survey/v2 v2.2.8 h1:TgxCwybKdBckmC+/P9/5h49rw/nAHe/itZL0dgHs+Q0= 15 | github.com/AlecAivazis/survey/v2 v2.2.8/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= 16 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 17 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 18 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 19 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= 20 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= 21 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 22 | github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= 23 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= 24 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 25 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 26 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 27 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 28 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 29 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 30 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 31 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 32 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 33 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 34 | github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= 35 | github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= 36 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 37 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 38 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 39 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 40 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 41 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 42 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 43 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 44 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 46 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 47 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 48 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 49 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 50 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 51 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 52 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 53 | github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= 54 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 55 | github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= 56 | github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 57 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 58 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 59 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 60 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 61 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 62 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 63 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 64 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 65 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 66 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 67 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 68 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 69 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 70 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 71 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 72 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 73 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 74 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 75 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 76 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 77 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 78 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 79 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 80 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 81 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 82 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 83 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 84 | github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= 85 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 86 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 87 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 88 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 89 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 90 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 91 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 92 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 93 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 94 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 95 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 96 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 97 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 98 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 99 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 100 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 101 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 102 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 103 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 104 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 105 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 106 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 107 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 108 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 109 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 110 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 111 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 112 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 113 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 114 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 115 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 116 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 117 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 118 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 119 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 120 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 121 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 122 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 123 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 124 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 125 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 126 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 127 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= 128 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= 129 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 130 | github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= 131 | github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= 132 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 133 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 134 | github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= 135 | github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= 136 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 137 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 138 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 139 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 140 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 141 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 142 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 143 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 144 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 145 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 146 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 147 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 148 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 149 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 150 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 151 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 152 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 153 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 154 | github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= 155 | github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 156 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 157 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 158 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 159 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 160 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 161 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 162 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 163 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 164 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 165 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 166 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 167 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 168 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 169 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 170 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 171 | github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= 172 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 173 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 174 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= 175 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 176 | github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= 177 | github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= 178 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 179 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 180 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 181 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 182 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 183 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 184 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 185 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 186 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 187 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 188 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 189 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 190 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 191 | github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= 192 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 193 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 194 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 195 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 196 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 197 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 198 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 199 | github.com/onsi/ginkgo v1.15.1 h1:DsXNrKujDlkMS9Rsxmd+Fg7S6Kc5lhE+qX8tY6laOxc= 200 | github.com/onsi/ginkgo v1.15.1/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o= 201 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 202 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 203 | github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= 204 | github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= 205 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 206 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 207 | github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A= 208 | github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 209 | github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= 210 | github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= 211 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 212 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 213 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 214 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 215 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 216 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 217 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 218 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 219 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 220 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 221 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 222 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 223 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 224 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 225 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 226 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 227 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 228 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 229 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 230 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 231 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 232 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 233 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 234 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 235 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 236 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 237 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 238 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 239 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 240 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 241 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 242 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 243 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 244 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 245 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 246 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 247 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 248 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 249 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 250 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 251 | github.com/src-d/enry/v2 v2.1.0 h1:z1L8t+B8bh3mmjPkJrgOTnVRpFGmTPJsplHX9wAn6BI= 252 | github.com/src-d/enry/v2 v2.1.0/go.mod h1:qQeCMRwzMF3ckeGr+h0tJLdxXnq+NVZsIDMELj0t028= 253 | github.com/src-d/go-oniguruma v1.1.0 h1:EG+Nm5n2JqWUaCjtM0NtutPxU7ZN5Tp50GWrrV8bTww= 254 | github.com/src-d/go-oniguruma v1.1.0/go.mod h1:chVbff8kcVtmrhxtZ3yBVLLquXbzCS6DrxQaAK/CeqM= 255 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 256 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 257 | github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 258 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 259 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 260 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 261 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 262 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 263 | github.com/toqueteos/trie v1.0.0 h1:8i6pXxNUXNRAqP246iibb7w/pSFquNTQ+uNfriG7vlk= 264 | github.com/toqueteos/trie v1.0.0/go.mod h1:Ywk48QhEqhU1+DwhMkJ2x7eeGxDHiGkAdc9+0DYcbsM= 265 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 266 | github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= 267 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 268 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 269 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 270 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 271 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 272 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 273 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 274 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 275 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 276 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 277 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 278 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 279 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 280 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 281 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 282 | golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= 283 | golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 284 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 285 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 286 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 287 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 288 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 289 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 290 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 291 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 292 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 293 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 294 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 295 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 296 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 297 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 298 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 299 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 300 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 301 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 302 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 303 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 304 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 305 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 306 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 307 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 308 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 309 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 310 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 311 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 312 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 313 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 314 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 315 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 316 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 317 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 318 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 319 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 320 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 321 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 322 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 323 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 324 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= 325 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 326 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 327 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 328 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 329 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 330 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 331 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 332 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 333 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 334 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 335 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 336 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 337 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 338 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 339 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 340 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 341 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 342 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 343 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 344 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 h1:R4dVlxdmKenVdMRS/tTspEpSTRWINYrHD8ySIU9yCIU= 349 | golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 359 | golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= 360 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= 362 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 363 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 364 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 365 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 366 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 367 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 368 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 369 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 370 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 371 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 372 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 373 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 374 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 375 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 376 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 377 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 378 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 379 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 380 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 381 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 382 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 383 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 384 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 385 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 386 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 387 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 388 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 389 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= 390 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 391 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 392 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 393 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 394 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 395 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 396 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 397 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 398 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 399 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 400 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 401 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 402 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 403 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 404 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 405 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 406 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 407 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 408 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 409 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 410 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 411 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 412 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 413 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 414 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 415 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 416 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 417 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 418 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 419 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 420 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 421 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 422 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 423 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 424 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 425 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 426 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 427 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 428 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 429 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 430 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 431 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 432 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 433 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 434 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 435 | gopkg.in/toqueteos/substring.v1 v1.0.2 h1:urLqCeMm6x/eTuQa1oZerNw8N1KNOIp5hD5kGL7lFsE= 436 | gopkg.in/toqueteos/substring.v1 v1.0.2/go.mod h1:Eb2Z1UYehlVK8LYW2WBVR2rwbujsz3aX8XDrM1vbNew= 437 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 438 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 439 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 440 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 441 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 442 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 443 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 444 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 445 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 446 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 447 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 448 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 449 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 450 | -------------------------------------------------------------------------------- /languagedetection/analyzer.go: -------------------------------------------------------------------------------- 1 | package languagedetection 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/src-d/enry/v2" 8 | ) 9 | 10 | // LanguageAnalyzer used for detecting programming language of a file 11 | type LanguageAnalyzer struct { 12 | FileNameMap map[string]string 13 | FileExtensionMap map[string]string 14 | } 15 | 16 | // NewLanguageAnalyzer constructor 17 | func NewLanguageAnalyzer() *LanguageAnalyzer { 18 | return &LanguageAnalyzer{ 19 | FileNameMap: reverseLanguageMap(fileNameMap), 20 | FileExtensionMap: reverseLanguageMap(fileExtensionMap), 21 | } 22 | } 23 | 24 | // Detect returns with the detected language 25 | // If no language could be detected it will return with an empty string. 26 | // The filePath is the path to the file. 27 | // For some file types it reads the content of the file. 28 | func (l *LanguageAnalyzer) Detect(filePath string, fileContent []byte) string { 29 | fileName := filepath.Base(filePath) 30 | 31 | val := l.DetectLanguageFromFileName(fileName) 32 | if val != "" { 33 | return val 34 | } 35 | 36 | extension := filepath.Ext(filePath) 37 | if extension == "" { 38 | return "" 39 | } 40 | 41 | // remove the trailing dot 42 | extension = extension[1:] 43 | if l.ShouldUseFile(extension) { 44 | return l.DetectLanguageFromFile(filePath, fileContent) 45 | } else { 46 | return l.DetectLanguageFromExtension(extension) 47 | } 48 | } 49 | 50 | // DetectLanguageFromFileName returns programming language based on files name 51 | func (l *LanguageAnalyzer) DetectLanguageFromFileName(fileName string) string { 52 | fileName = strings.ToLower(fileName) 53 | if val, ok := l.FileNameMap[fileName]; ok { 54 | return val 55 | } 56 | return "" 57 | } 58 | 59 | // DetectLanguageFromExtension returns programming language based on files extension 60 | // Works for most cases, but for some cases we have to use DetectLanguageFromFile 61 | func (l *LanguageAnalyzer) DetectLanguageFromExtension(extension string) string { 62 | extension = strings.ToLower(extension) 63 | if val, ok := l.FileExtensionMap[extension]; ok { 64 | return val 65 | } 66 | return "" 67 | } 68 | 69 | // DetectLanguageFromFile returns programming language based on file itself 70 | // It also needs filename to increase accuracy 71 | func (l *LanguageAnalyzer) DetectLanguageFromFile(filePath string, fileContents []byte) string { 72 | lang, _ := enry.GetLanguageByContent(filePath, fileContents) 73 | // For some reason enry is too bad at detecting Perl files 74 | // However it can successfully detect Prolog files 75 | // So, if the extension is "pl" but enry couldn't detect the language, it is probably Perl 76 | if filepath.Ext(filePath) == ".pl" && lang == "" { 77 | return "Perl" 78 | } 79 | return lang 80 | } 81 | 82 | // ShouldUseFile determines if it is enough to use extension, or we should try to read the file 83 | // to determine the language 84 | func (l *LanguageAnalyzer) ShouldUseFile(extension string) bool { 85 | _, ok := extensionsWithMultipleLanguages[extension] 86 | return ok 87 | } 88 | 89 | func reverseLanguageMap(input map[string][]string) map[string]string { 90 | extensionMap := map[string]string{} 91 | for lang, extensions := range input { 92 | for _, extension := range extensions { 93 | extensionMap[extension] = lang 94 | } 95 | } 96 | return extensionMap 97 | } 98 | 99 | var fileNameMap = map[string][]string{ 100 | "CMake": {"cmakelists.txt"}, 101 | "Dockerfile": {"dockerfile"}, 102 | "Go": {"go.mod"}, 103 | "Jenkins": {"jenkinsfile"}, 104 | "Makefile": {"gnumakefile", "makefile"}, 105 | "Ruby": {"gemfile", "rakefile"}, 106 | } 107 | 108 | var extensionsWithMultipleLanguages = map[string]bool{ 109 | "m": true, // Objective-C, Matlab 110 | "pl": true, // Perl, Prolog 111 | "sql": true, // Dialects of SQL 112 | } 113 | 114 | var fileExtensionMap = map[string][]string{ 115 | "1C Enterprise": {"bsl", "os"}, 116 | "Apex": {"cls"}, 117 | "Assembly": {"asm"}, 118 | "Ballerina": {"bal"}, 119 | "Batchfile": {"bat", "cmd", "btm"}, 120 | "Blazor": {"razor"}, 121 | "C": {"c", "h"}, 122 | "C++": {"cpp", "cxx", "hpp", "cc", "hh", "hxx"}, 123 | "C#": {"cs"}, 124 | "CSS": {"css"}, 125 | "Clojure": {"clj"}, 126 | "COBOL": {"cbl", "cob", "cpy"}, 127 | "CoffeeScript": {"coffee"}, 128 | "Crystal": {"cr"}, 129 | "D": {"d"}, 130 | "Dart": {"dart"}, 131 | "Groovy": {"groovy", "gvy", "gy", "gsh"}, 132 | "HTML+Razor": {"cshtml"}, 133 | "Ebuild": {"ebuild", "eclass"}, 134 | "EJS": {"ejs"}, 135 | "Elixir": {"ex", "exs"}, 136 | "Elm": {"elm"}, 137 | "EPP": {"epp"}, 138 | "ERB": {"erb"}, 139 | "Erlang": {"erl", "hrl"}, 140 | "F#": {"fs", "fsi", "fsx", "fsscript"}, 141 | "Fortran": {"f90", "f95", "f03", "f08", "for"}, 142 | "Go": {"go"}, 143 | "Haskell": {"hs", "lhs"}, 144 | "HCL": {"hcl", "tf", "tfvars"}, 145 | "HTML": {"html", "htm", "xhtml"}, 146 | "JSON": {"json"}, 147 | "Java": {"java"}, 148 | "JavaScript": {"js", "jsx", "mjs", "cjs"}, 149 | "Julia": {"jl"}, 150 | "Jupyter Notebook": {"ipynb"}, 151 | "Kivy": {"kv"}, 152 | "Kotlin": {"kt", "kts"}, 153 | "LabVIEW": {"vi", "lvproj", "lvclass", "ctl", "ctt", "llb", "lvbit", "lvbitx", "lvlad", "lvlib", "lvmodel", "lvsc", "lvtest", "vidb"}, 154 | "Less": {"less"}, 155 | "Lex": {"l"}, 156 | "Liquid": {"liquid"}, 157 | "Lua": {"lua"}, 158 | "MATLAB": {"m"}, 159 | "Nix": {"nix"}, 160 | "Objective-C": {"mm"}, 161 | "OpenEdge ABL": {"p", "ab", "w", "i", "x"}, 162 | "Perl": {"pl", "pm", "pod", "t"}, 163 | "PHP": {"php"}, 164 | "PLSQL": {"pks", "pkb"}, 165 | "Protocol Buffer": {"proto"}, 166 | "Puppet": {"pp"}, 167 | "Python": {"py"}, 168 | "PureScript": {"pursx", "purs"}, 169 | "QML": {"qml"}, 170 | "R": {"r"}, 171 | "Raku": {"p6", "pl6", "pm6", "rk", "raku", "pod6", "rakumod", "rakudoc"}, 172 | "Reason": {"re", "rei"}, 173 | "Robot": {"robot"}, 174 | "Ruby": {"gemspec", "ra", "rake", "rb"}, 175 | "Rust": {"rs"}, 176 | "Scala": {"scala"}, 177 | "SASS": {"sass"}, 178 | "SCSS": {"scss"}, 179 | "Shell": {"sh"}, 180 | "Smalltalk": {"st"}, 181 | "Solidity": {"sol"}, 182 | "Stylus": {"styl"}, 183 | "Svelte": {"svelte"}, 184 | "Swift": {"swift"}, 185 | "TypeScript": {"ts", "tsx"}, 186 | "Vue": {"vue"}, 187 | "Xtend": {"xtend"}, 188 | "Xtext": {"xtext"}, 189 | "Yacc": {"y"}, 190 | "Zig": {"zig"}, 191 | } 192 | -------------------------------------------------------------------------------- /languagedetection/analyzer_test.go: -------------------------------------------------------------------------------- 1 | package languagedetection 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("Analyzer", func() { 9 | a := NewLanguageAnalyzer() 10 | Context("Detect language by extension", func() { 11 | It("should detect PHP ", func() { 12 | // Act 13 | l1 := a.Detect("/home/something/index.php", []byte{}) 14 | l2 := a.Detect("/home/something/index.Php", []byte{}) 15 | l3 := a.Detect("/home/something/index.razor", []byte{}) 16 | 17 | // Assert 18 | Expect(l1).To(Equal("PHP")) 19 | Expect(l2).To(Equal("PHP")) 20 | Expect(l3).To(Equal("Blazor")) 21 | }) 22 | }) 23 | 24 | Context("Detect language by file name", func() { 25 | It("should detect build files ", func() { 26 | // Act 27 | l1 := a.Detect("/home/something/Makefile", []byte{}) 28 | l2 := a.Detect("/home/something/Dockerfile", []byte{}) 29 | l3 := a.Detect("/home/something/Jenkinsfile", []byte{}) 30 | l4 := a.Detect("/home/something/Rakefile", []byte{}) 31 | l5 := a.Detect("/home/something/CMakeLists.txt", []byte{}) 32 | 33 | // Assert 34 | Expect(l1).To(Equal("Makefile")) 35 | Expect(l2).To(Equal("Dockerfile")) 36 | Expect(l3).To(Equal("Jenkins")) 37 | Expect(l4).To(Equal("Ruby")) 38 | Expect(l5).To(Equal("CMake")) 39 | }) 40 | }) 41 | 42 | Context("Detect language by file name", func() { 43 | It("should detect SQL and PLpgSQL ", func() { 44 | // Act 45 | l1 := a.Detect("/home/something/get_pg_users.sql", []byte(`SELECT usename FROM pg_catalog.pg_user;`)) 46 | l2 := a.Detect("/home/something/create_pg_user.sql", []byte(`-- 47 | CREATE OR REPLACE FUNCTION __tmp_create_user() 48 | RETURNS VOID 49 | LANGUAGE plpgsql 50 | AS 51 | $$ 52 | BEGIN 53 | IF NOT EXISTS ( 54 | SELECT 55 | FROM pg_catalog.pg_user 56 | WHERE usename = 'new_user') THEN 57 | CREATE USER 'new_user'; 58 | END IF; 59 | END; 60 | $$;`)) 61 | 62 | // Assert 63 | Expect(l1).To(Equal("SQL")) 64 | Expect(l2).To(Equal("PLpgSQL")) 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /languagedetection/languagedetection_suite_test.go: -------------------------------------------------------------------------------- 1 | package languagedetection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestLanguagedetection(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Languagedetection Suite") 13 | } 14 | -------------------------------------------------------------------------------- /librarydetection/analyzer.go: -------------------------------------------------------------------------------- 1 | package librarydetection 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Analyzer is an interface for extracting various features from files 8 | // Language specific implementations are at ./languages folder 9 | type Analyzer interface { 10 | ExtractLibraries(contents string) ([]string, error) 11 | } 12 | 13 | // Analyzers is the map for all analyzers 14 | // like "Go" has "GoAnalyzer", "Python" has "PythonAnalyzer" and so on. 15 | type Analyzers map[string]Analyzer 16 | 17 | var analyzers = Analyzers{} 18 | 19 | // GetAnalyzer returns given analyzer for that language 20 | func GetAnalyzer(language string) (Analyzer, error) { 21 | analyzer := analyzers[language] 22 | if analyzer == nil { 23 | return nil, fmt.Errorf("no analyzer for %s exists", language) 24 | } 25 | return analyzer, nil 26 | } 27 | 28 | // AddAnalyzer allows users to add new analyzers 29 | func AddAnalyzer(language string, analyzer Analyzer) { 30 | analyzers[language] = analyzer 31 | } 32 | -------------------------------------------------------------------------------- /librarydetection/languages/C.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewCAnalyzer constructor 10 | func NewCAnalyzer() librarydetection.Analyzer { 11 | return &cAnalyzer{} 12 | } 13 | 14 | type cAnalyzer struct{} 15 | 16 | func (a *cAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | regex, err := regexp.Compile(`(?i)#include\s?[<"]([/a-zA-Z0-9.-]+)[">]`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return executeRegexes(contents, []*regexp.Regexp{regex}), nil 23 | } 24 | -------------------------------------------------------------------------------- /librarydetection/languages/C_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("CLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/c.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "assert.h", 19 | "complex.h", 20 | "ctype.h", 21 | "stdio.h", 22 | "float.h", 23 | "string.h", 24 | "hey/sup/iomanip.h", 25 | "Hey/ssup/math.h", 26 | "hello/how/stdlib.h", 27 | "great/wchar.h", 28 | "stdbool.h", 29 | "stdint.h", 30 | "hello12", 31 | "WsSup34", 32 | "heyYo3-lol", 33 | } 34 | 35 | analyzer := languages.NewCAnalyzer() 36 | 37 | Describe("Extract C Libraries", func() { 38 | It("Should be able to extract libraries", func() { 39 | libs, err := analyzer.ExtractLibraries(string(fixture)) 40 | if err != nil { 41 | panic(err) 42 | } 43 | assertSameUnordered(libs, expectedLibraries) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /librarydetection/languages/Cpp.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewCAnalyzer constructor 10 | func NewCppAnalyzer() librarydetection.Analyzer { 11 | return &cppAnalyzer{} 12 | } 13 | 14 | type cppAnalyzer struct{} 15 | 16 | func (a *cppAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | regex, err := regexp.Compile(`(?i)#include\s?[<"]([/a-zA-Z0-9.-]+)[">]`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return executeRegexes(contents, []*regexp.Regexp{regex}), nil 23 | } 24 | -------------------------------------------------------------------------------- /librarydetection/languages/Cpp_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("CLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/cpp.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "common.h", 19 | "Accident.h", 20 | "Ped.h", 21 | "Pools.h", 22 | "World.h", 23 | } 24 | 25 | analyzer := languages.NewCppAnalyzer() 26 | 27 | Describe("Extract C Libraries", func() { 28 | It("Should be able to extract libraries", func() { 29 | libs, err := analyzer.ExtractLibraries(string(fixture)) 30 | if err != nil { 31 | panic(err) 32 | } 33 | assertSameUnordered(libs, expectedLibraries) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /librarydetection/languages/Dart.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewDartScriptAnalyzer constructor 10 | func NewDartScriptAnalyzer() librarydetection.Analyzer { 11 | return &dartScriptAnalyzer{} 12 | } 13 | 14 | type dartScriptAnalyzer struct{} 15 | 16 | func (a *dartScriptAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // for imports like this: import 'dart:developer' as dev; OR import 'dart:developer'; 18 | importRegex, err := regexp.Compile(`import '([:a-zA-Z0-9_-]+)'(?:|as)`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | // for imports like this: import 'package:flutter/material.dart'; 23 | importRegex2, err := regexp.Compile(`import '([:a-zA-Z0-9_-]+)/[\.a-zA-Z0-9_-]+'`) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return executeRegexes(contents, []*regexp.Regexp{importRegex, importRegex2}), nil 29 | } 30 | -------------------------------------------------------------------------------- /librarydetection/languages/Dart_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("DartLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/dart.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "dart:async", 19 | "dart:developer", 20 | "package:flutter", 21 | } 22 | 23 | analyzer := languages.NewDartScriptAnalyzer() 24 | 25 | Describe("Extract Dart Libraries", func() { 26 | It("Should be able to extract libraries", func() { 27 | libs, err := analyzer.ExtractLibraries(string(fixture)) 28 | if err != nil { 29 | panic(err) 30 | } 31 | assertSameUnordered(libs, expectedLibraries) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /librarydetection/languages/Go.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 5 | "regexp" 6 | ) 7 | 8 | // NewGoAnalyzer constructor 9 | func NewGoAnalyzer() librarydetection.Analyzer { 10 | return &goAnalyzer{} 11 | } 12 | 13 | type goAnalyzer struct { 14 | } 15 | 16 | func (a *goAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // regex for multiline imports 18 | regex1, err := regexp.Compile(`(?msi)import\s*\(\s*(.*?)\s*\)`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | // Find libraries in a multi line import 24 | regex2, err := regexp.Compile(`"(.*?)"`) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | allLibs := []string{} 30 | 31 | matches := regex1.FindAllStringSubmatch(contents, -1) 32 | for _, match := range matches { 33 | if len(match) > 1 { 34 | subgroup := match[1] 35 | for _, subgroupMatch := range regex2.FindAllStringSubmatch(subgroup, -1) { 36 | if len(subgroupMatch) > 1 { 37 | allLibs = append(allLibs, subgroupMatch[1:]...) 38 | } 39 | } 40 | } 41 | } 42 | 43 | // regex for imports like this: import _ "github.com/user/repo/..." 44 | regex3, err := regexp.Compile(`(?i)import[\t ]*(?:[_.].*)?[\t ]?\(?"(.+)"\)?;?\s`) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | //// regex for imports with alias. Like this: import alias1 "github.com/user/repo/..." 50 | regex4, err := regexp.Compile(`(?i)import[\t ]*[a-z].+[\t ]?\(?"(.+)"\)?;?\s`) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | regexes := []*regexp.Regexp{ 56 | regex3, 57 | regex4, 58 | } 59 | 60 | allLibs = append(allLibs, executeRegexes(contents, regexes)...) 61 | 62 | return allLibs, nil 63 | } 64 | -------------------------------------------------------------------------------- /librarydetection/languages/Go_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | "io/ioutil" 6 | 7 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 8 | ) 9 | 10 | var _ = Describe("GoLibraryDetection", func() { 11 | fixture, err := ioutil.ReadFile("./fixtures/go.fixture") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | expectedLibraries := []string{ 17 | "fmt", 18 | "library1", 19 | "gitlab.com/username/reponame/library2", 20 | "gitlab.com/username/library3", 21 | "gitlab.com/username/reponame/library4", 22 | "gitlab.com/username/library5", 23 | "gitlab.com/username/reponame/library6", 24 | "gitlab.com/username/library7", 25 | "gitlab.com/username/reponame/library8", 26 | "gitlab.com/username/library9", 27 | "library10", 28 | "gitlab.com/username/reponame/library11", 29 | "gitlab.com/username/library12", 30 | "gitlab.com/username/reponame/library13", 31 | "gitlab.com/username/library14", 32 | "gitlab.com/username/reponame/library15", 33 | "gitlab.com/username/library16", 34 | "gitlab.com/username/reponame/library17", 35 | "gitlab.com/username/library18", 36 | } 37 | 38 | analyzer := languages.NewGoAnalyzer() 39 | 40 | Describe("Extract Go sLibraries", func() { 41 | It("Should be able to extract libraries", func() { 42 | libs, err := analyzer.ExtractLibraries(string(fixture)) 43 | if err != nil { 44 | panic(err) 45 | } 46 | assertSameUnordered(libs, expectedLibraries) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /librarydetection/languages/Java.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewJavaAnalyzer constructor 10 | func NewJavaAnalyzer() librarydetection.Analyzer { 11 | return &javaAnalyzer{} 12 | } 13 | 14 | type javaAnalyzer struct{} 15 | 16 | func (a *javaAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // regex to find imports like org.springframework (excluding standart java libraries) 18 | regex1, err := regexp.Compile(`import ([^java][a-zA-Z0-9]*\.[a-zA-Z0-9]*)`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | // regex to find imports like org.springframework.boot 23 | regex2, err := regexp.Compile(`import ([^java][a-zA-Z0-9]*\.[a-zA-Z0-9]*\.[a-zA-Z0-9]*)`) 24 | if err != nil { 25 | return nil, err 26 | } 27 | // regex to find static imports like org.springframework (excluding standart java libraries) 28 | regex3, err := regexp.Compile(`import static ([^java][a-zA-Z0-9]*\.[a-zA-Z0-9]*)`) 29 | if err != nil { 30 | return nil, err 31 | } 32 | // regex to find static imports like org.springframework.boot 33 | regex4, err := regexp.Compile(`import static ([^java][a-zA-Z0-9]*\.[a-zA-Z0-9]*\.[a-zA-Z0-9]*)`) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return executeRegexes(contents, []*regexp.Regexp{regex1, regex2, regex3, regex4}), nil 39 | } 40 | -------------------------------------------------------------------------------- /librarydetection/languages/JavaScript.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewJavaScriptAnalyzer constructor 10 | func NewJavaScriptAnalyzer() librarydetection.Analyzer { 11 | return &javaScriptAnalyzer{} 12 | } 13 | 14 | type javaScriptAnalyzer struct{} 15 | 16 | func (a *javaScriptAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | 18 | require, err := regexp.Compile(`require\(["\'](.+)["\']\);?\s`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | importRegex, err := regexp.Compile(`import\s*(?:.+ from)?\s?\(?[\'"](.+)[\'"]\)?;?\s`) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return executeRegexes(contents, []*regexp.Regexp{require, importRegex}), nil 29 | } 30 | -------------------------------------------------------------------------------- /librarydetection/languages/JavaScript_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("JavaScriptLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/javascript.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "lib1", 19 | "lib2", 20 | "lib3", 21 | "lib4", 22 | } 23 | 24 | analyzer := languages.NewJavaScriptAnalyzer() 25 | 26 | Describe("Extract JavaScript Libraries", func() { 27 | It("Should be able to extract libraries", func() { 28 | libs, err := analyzer.ExtractLibraries(string(fixture)) 29 | if err != nil { 30 | panic(err) 31 | } 32 | assertSameUnordered(libs, expectedLibraries) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /librarydetection/languages/Java_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("JavaLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/java.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "org.bytedeco", 19 | "org.bytedeco.javacv", 20 | "org.bytedeco", 21 | "org.bytedeco.opencv", 22 | } 23 | 24 | analyzer := languages.NewJavaAnalyzer() 25 | 26 | Describe("Extract Java Libraries", func() { 27 | It("Should be able to extract libraries", func() { 28 | libs, err := analyzer.ExtractLibraries(string(fixture)) 29 | if err != nil { 30 | panic(err) 31 | } 32 | assertSameUnordered(libs, expectedLibraries) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /librarydetection/languages/Perl.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewPerlAnalyzer constructor 10 | func NewPerlAnalyzer() librarydetection.Analyzer { 11 | return &perlAnalyzer{} 12 | } 13 | 14 | type perlAnalyzer struct{} 15 | 16 | func (a *perlAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | regex, err := regexp.Compile(`(?:use|require)[^\S\n]+(?:if.*,\s+)?[\"']?([a-zA-Z][a-zA-Z0-9:]*)[\"']?(?:\s+.*)?;`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return executeRegexes(contents, []*regexp.Regexp{regex}), nil 23 | } 24 | -------------------------------------------------------------------------------- /librarydetection/languages/Perl_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("PerlLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/perl.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "strict", 19 | "Benchmark", 20 | "Carp", 21 | "sigtrap", 22 | "Sub::Module", 23 | "Import::This", 24 | "utf8", 25 | "warnings", 26 | "Foo::Bar", 27 | "Module", 28 | } 29 | 30 | analyzer := languages.NewPerlAnalyzer() 31 | 32 | Describe("Extract Perl Libraries", func() { 33 | It("Should be able to extract libraries", func() { 34 | libs, err := analyzer.ExtractLibraries(string(fixture)) 35 | if err != nil { 36 | panic(err) 37 | } 38 | assertSameUnordered(libs, expectedLibraries) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /librarydetection/languages/Python.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewPythonScriptAnalyzer constructor 10 | func NewPythonScriptAnalyzer() librarydetection.Analyzer { 11 | return &pythonScriptAnalyzer{} 12 | } 13 | 14 | type pythonScriptAnalyzer struct{} 15 | 16 | func (a *pythonScriptAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | fromRegex, err := regexp.Compile(`from (.+) import`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | importRegex, err := regexp.Compile(`import ([a-zA-Z0-9_-]+)(?:\s| as)`) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return executeRegexes(contents, []*regexp.Regexp{fromRegex, importRegex}), nil 27 | } 28 | -------------------------------------------------------------------------------- /librarydetection/languages/Python_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("PythonLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/python.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "lib1.lib2", 19 | "lib3", 20 | "lib4", 21 | } 22 | 23 | analyzer := languages.NewPythonScriptAnalyzer() 24 | 25 | Describe("Extract Python Libraries", func() { 26 | It("Should be able to extract libraries", func() { 27 | libs, err := analyzer.ExtractLibraries(string(fixture)) 28 | if err != nil { 29 | panic(err) 30 | } 31 | assertSameUnordered(libs, expectedLibraries) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /librarydetection/languages/R.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewRubyScriptAnalyzer constructor 10 | func NewRAnalyzer() librarydetection.Analyzer { 11 | return &rAnalyzer{} 12 | } 13 | 14 | type rAnalyzer struct{} 15 | 16 | func (a *rAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // According to https://community.rstudio.com/t/analysis-package-naming-or-can-package-name-differ-from-rproj-name/9421 18 | // Package name can contain letters, numbers and coma 19 | 20 | // for this format library(shiny) 21 | requireRegex, err := regexp.Compile(`(?m)^\s*(?:require|library|package)\s*\(\s*["]*([\w\d,]+)"*\s*\)`) 22 | if err != nil { 23 | return nil, err 24 | } 25 | // for this format BiocManager123::repositories() 26 | requireRegex2, err := regexp.Compile(`(?m)^\s*([\w\d,]+)::`) 27 | if err != nil { 28 | return nil, err 29 | } 30 | // for this format library(package=quantmod) 31 | requireRegex3, err := regexp.Compile(`(?m)^\s*(?:require|library|package)\s*\(\s*package\s*=\s*([\w\d,]+)\s*\)`) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return executeRegexes(contents, []*regexp.Regexp{requireRegex, requireRegex2, requireRegex3}), nil 37 | } 38 | -------------------------------------------------------------------------------- /librarydetection/languages/R_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("RLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/r.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "ggplot", 19 | "quantmod", 20 | "tibble", 21 | "tidyr", 22 | "shiny", 23 | "bslib", 24 | "DT", 25 | "WGCNA", 26 | "impute", 27 | "BiocManager123", 28 | } 29 | 30 | analyzer := languages.NewRAnalyzer() 31 | 32 | Describe("Extract R Libraries", func() { 33 | It("Should be able to extract libraries", func() { 34 | libs, err := analyzer.ExtractLibraries(string(fixture)) 35 | if err != nil { 36 | panic(err) 37 | } 38 | assertSameUnordered(libs, expectedLibraries) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /librarydetection/languages/Ruby.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewRubyScriptAnalyzer constructor 10 | func NewRubyScriptAnalyzer() librarydetection.Analyzer { 11 | return &RubyScriptAnalyzer{} 12 | } 13 | 14 | type RubyScriptAnalyzer struct{} 15 | 16 | func (a *RubyScriptAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | 18 | requireRegex, err := regexp.Compile(`(?:require|gem)[( ]{1}['"]([a-zA-Z0-9_\-/]+)["'][)]?`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return executeRegexes(contents, []*regexp.Regexp{requireRegex}), nil 24 | } 25 | -------------------------------------------------------------------------------- /librarydetection/languages/Ruby_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("RubyLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/ruby.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "lib1", 19 | "lib2", 20 | "lib1/lib2", 21 | "lib-2/lib_1", 22 | "lib3", 23 | "lib4", 24 | "lib3/lib4", 25 | "lib4/lib3", 26 | "lib-13", 27 | "lib14", 28 | "lib15", 29 | "lib_16", 30 | "lib17", 31 | "lib18", 32 | "lib19", 33 | } 34 | 35 | analyzer := languages.NewRubyScriptAnalyzer() 36 | 37 | Describe("Extract Ruby Libraries", func() { 38 | It("Should be able to extract libraries", func() { 39 | libs, err := analyzer.ExtractLibraries(string(fixture)) 40 | if err != nil { 41 | panic(err) 42 | } 43 | assertSameUnordered(libs, expectedLibraries) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /librarydetection/languages/Rust.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 5 | "regexp" 6 | ) 7 | 8 | // NewRustAnalyzer constructor 9 | func NewRustAnalyzer() librarydetection.Analyzer { 10 | return &rustAnalyzer{} 11 | } 12 | 13 | type rustAnalyzer struct { 14 | } 15 | 16 | func (a *rustAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // regex to find "uses", braces are not supported as their tree syntax is too complicated for Go's regexes 18 | useRegex, err := regexp.Compile(`use\s+(?:::)?(?:r#)?([^;:\s]+)`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | // regex for "extern crate" statements 24 | externCrateRegex, err := regexp.Compile(`extern\s+crate\s+(?:r#)?(\S+)\s*;`) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return executeRegexes(contents, []*regexp.Regexp{useRegex, externCrateRegex}), nil 30 | } 31 | -------------------------------------------------------------------------------- /librarydetection/languages/Rust_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | "io/ioutil" 6 | 7 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 8 | ) 9 | 10 | var _ = Describe("RustLibraryDetection", func() { 11 | fixture, err := ioutil.ReadFile("./fixtures/rust.fixture") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | expectedLibraries := []string{ 17 | "lib1", 18 | "lib2", 19 | "lib3", 20 | "lib4", 21 | "lib5", 22 | "lib6", 23 | } 24 | 25 | analyzer := languages.NewRustAnalyzer() 26 | 27 | Describe("Extract Rust Libraries", func() { 28 | It("Should be able to extract libraries", func() { 29 | libs, err := analyzer.ExtractLibraries(string(fixture)) 30 | if err != nil { 31 | panic(err) 32 | } 33 | assertSameUnordered(libs, expectedLibraries) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /librarydetection/languages/Swift.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewSwiftAnalyzer constructor 10 | func NewSwiftAnalyzer() librarydetection.Analyzer { 11 | return &swiftAnalyzer{} 12 | } 13 | 14 | type swiftAnalyzer struct{} 15 | 16 | func (a *swiftAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | // regex to find imports like import Stuff 18 | regexImport, err := regexp.Compile(`import ([a-zA-Z\.]*)[\n|\r\n]`) 19 | if err != nil { 20 | return nil, err 21 | } 22 | // regex to find imports like import kind module.symbol 23 | // here kind can be func, var, let, typealias, protocol, enum, class, struct 24 | // this regex will return a list of (kind, module.symbol) 25 | regexDeclarations, err := regexp.Compile(`import [func|var|let|typealias|protocol|enum|class|struct]+ ([a-zA-Z|\.]*)`) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | return executeRegexes(contents, []*regexp.Regexp{regexImport, regexDeclarations}), nil 31 | } 32 | -------------------------------------------------------------------------------- /librarydetection/languages/Swift_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("SwiftLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/swift.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "Cocoa", 19 | "os.log", 20 | "StatsKit", 21 | "ModuleKit", 22 | "CPU", 23 | "Memory", 24 | "Disk", 25 | "Net", 26 | "Battery", 27 | "Sensors", 28 | "GPU", 29 | "Fans", 30 | "Pentathlon.swim", 31 | "test.test", 32 | "CoreServices.DictionaryServices", 33 | } 34 | 35 | analyzer := languages.NewSwiftAnalyzer() 36 | 37 | Describe("Extract Swift Libraries", func() { 38 | It("Should be able to extract libraries", func() { 39 | libs, err := analyzer.ExtractLibraries(string(fixture)) 40 | if err != nil { 41 | panic(err) 42 | } 43 | assertSameUnordered(libs, expectedLibraries) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /librarydetection/languages/TypeScript.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewTypeScriptAnalyzer constructor 10 | func NewTypeScriptAnalyzer() librarydetection.Analyzer { 11 | return &typeScriptAnalyzer{} 12 | } 13 | 14 | type typeScriptAnalyzer struct{} 15 | 16 | func (a *typeScriptAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | require, err := regexp.Compile(`require\(["\'](.+)["\']\);?\s`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | importRegex, err := regexp.Compile(`import\s*(?:.+ from)?\s?\(?[\'"](.+)[\'"]\)?;?\s`) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return executeRegexes(contents, []*regexp.Regexp{require, importRegex}), nil 27 | } 28 | -------------------------------------------------------------------------------- /librarydetection/languages/TypeScript_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | . "github.com/onsi/ginkgo" 7 | 8 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 9 | ) 10 | 11 | var _ = Describe("TypeScriptLibraryDetection", func() { 12 | fixture, err := ioutil.ReadFile("./fixtures/typescript.fixture") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | expectedLibraries := []string{ 18 | "lib1", 19 | "lib2", 20 | "lib3", 21 | "lib4", 22 | } 23 | 24 | analyzer := languages.NewTypeScriptAnalyzer() 25 | 26 | Describe("Extract TypeScript Libraries", func() { 27 | It("Should be able to extract libraries", func() { 28 | libs, err := analyzer.ExtractLibraries(string(fixture)) 29 | if err != nil { 30 | panic(err) 31 | } 32 | assertSameUnordered(libs, expectedLibraries) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /librarydetection/languages/cSharp.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 7 | ) 8 | 9 | // NewCAnalyzer constructor 10 | func NewCSharpAnalyzer() librarydetection.Analyzer { 11 | return &cSparpAnalyzer{} 12 | } 13 | 14 | type cSparpAnalyzer struct{} 15 | 16 | func (a *cSparpAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | regex1, err := regexp.Compile(`(?i)using\s?([/a-zA-z0-9.]+);`) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | regex2, err := regexp.Compile(`(?i)using [/a-zA-z0-9.]+ = ([/a-zA-z0-9.]+);`) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | regexes := []*regexp.Regexp{ 28 | regex1, 29 | regex2, 30 | } 31 | 32 | return executeRegexes(contents, regexes), nil 33 | } 34 | -------------------------------------------------------------------------------- /librarydetection/languages/cSharp_test.go: -------------------------------------------------------------------------------- 1 | package languages_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | "io/ioutil" 6 | 7 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection/languages" 8 | ) 9 | 10 | var _ = Describe("GoLibraryDetection", func() { 11 | fixture, err := ioutil.ReadFile("./fixtures/csharp.fixture") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | expectedLibraries := []string{ 17 | "System", 18 | "System.Collections.Generic", 19 | "System.IO", 20 | "System.IO.Compression", 21 | "System.Linq", 22 | "System.Net", 23 | "System.Net.Http", 24 | "System.Net.Http.Headers", 25 | "System.Text", 26 | "System.Threading", 27 | "System.Threading.Tasks", 28 | "GitHub.DistributedTask.ObjectTemplating.Tokens", 29 | "GitHub.Runner.Common", 30 | "GitHub.Runner.Sdk", 31 | "GitHub.Runner.Worker.Container", 32 | "GitHub.Services.Common", 33 | "GitHub.DistributedTask.WebApi", 34 | "GitHub.DistributedTask.Pipelines", 35 | "GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants", 36 | } 37 | 38 | analyzer := languages.NewCSharpAnalyzer() 39 | 40 | Describe("Extract C# Libraries", func() { 41 | It("Should be able to extract libraries", func() { 42 | libs, err := analyzer.ExtractLibraries(string(fixture)) 43 | if err != nil { 44 | panic(err) 45 | } 46 | assertSameUnordered(libs, expectedLibraries) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /librarydetection/languages/common.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import "regexp" 4 | 5 | // executeRegexes is a helper function that executes a number of regexes and assumes each of them returns 1 group only 6 | func executeRegexes(contents string, regexes []*regexp.Regexp) []string { 7 | var res []string 8 | for _, r := range regexes { 9 | matches := r.FindAllStringSubmatch(contents, -1) 10 | for _, match := range matches { 11 | if len(match) > 1 { 12 | libs := match[1:] 13 | res = append(res, libs...) 14 | } 15 | } 16 | } 17 | return res 18 | } 19 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/c.fixture: -------------------------------------------------------------------------------- 1 | #include 2 | #include "complex.h" 3 | #include 4 | #include "stdio.h" 5 | #include 6 | #include"string.h" 7 | #include "hey/sup/iomanip.h" 8 | #include 9 | #include"hello/how/stdlib.h" 10 | #include 11 | #include"stdbool.h" 12 | #include 13 | #include "hello12" 14 | #include 15 | #include 16 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/cpp.fixture: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include "Accident.h" 4 | 5 | #include "Ped.h" 6 | #include "Pools.h" 7 | #include "World.h" 8 | 9 | CAccidentManager gAccidentManager; 10 | 11 | CAccident* 12 | CAccidentManager::GetNextFreeAccident() 13 | { 14 | for (int i = 0; i < NUM_ACCIDENTS; i++) { 15 | if (m_aAccidents[i].m_pVictim == nil) 16 | return &m_aAccidents[i]; 17 | } 18 | 19 | return nil; 20 | } 21 | 22 | void 23 | CAccidentManager::ReportAccident(CPed *ped) 24 | { 25 | if (!ped->IsPlayer() && ped->CharCreatedBy != MISSION_CHAR && !ped->bRenderScorched && !ped->bBodyPartJustCameOff && ped->bAllowMedicsToReviveMe && !ped->bIsInWater) { 26 | for (int i = 0; i < NUM_ACCIDENTS; i++) { 27 | if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_pVictim == ped) 28 | return; 29 | } 30 | 31 | if (ped->m_pCurrentPhysSurface == nil) { 32 | CVector point = ped->GetPosition(); 33 | point.z -= 2.0f; 34 | 35 | CColPoint colPoint; 36 | CEntity *pEntity; 37 | 38 | if (!CWorld::ProcessVerticalLine(point, -100.0f, colPoint, pEntity, true, false, false, false, false, false, nil)) { 39 | CAccident *accident = GetNextFreeAccident(); 40 | if (accident != nil) { 41 | accident->m_pVictim = ped; 42 | ped->RegisterReference((CEntity**)&accident->m_pVictim); 43 | accident->m_nMedicsPerformingCPR = 0; 44 | accident->m_nMedicsAttending = 0; 45 | ped->m_lastAccident = accident; 46 | WorkToDoForMedics(); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | void 54 | CAccidentManager::Update() 55 | { 56 | #ifdef SQUEEZE_PERFORMANCE 57 | // Handled after injury registered. 58 | return; 59 | #endif 60 | int32 e; 61 | if (CEventList::GetEvent(EVENT_INJURED_PED, &e)) { 62 | CPed *ped = CPools::GetPed(gaEvent[e].entityRef); 63 | if (ped) { 64 | ReportAccident(ped); 65 | CEventList::ClearEvent(e); 66 | } 67 | } 68 | } 69 | 70 | CAccident* 71 | CAccidentManager::FindNearestAccident(CVector vecPos, float *pDistance) 72 | { 73 | for (int i = 0; i < MAX_MEDICS_TO_ATTEND_ACCIDENT; i++){ 74 | int accidentId = -1; 75 | float minDistance = 999999; 76 | for (int j = 0; j < NUM_ACCIDENTS; j++){ 77 | CPed* pVictim = m_aAccidents[j].m_pVictim; 78 | if (!pVictim) 79 | continue; 80 | if (pVictim->CharCreatedBy == MISSION_CHAR) 81 | continue; 82 | if (pVictim->m_fHealth != 0.0f) 83 | continue; 84 | if (m_aAccidents[j].m_nMedicsPerformingCPR != i) 85 | continue; 86 | float distance = (pVictim->GetPosition() - vecPos).Magnitude2D(); 87 | if (distance / 2 > pVictim->GetPosition().z - vecPos.z && distance < minDistance){ 88 | minDistance = distance; 89 | accidentId = j; 90 | } 91 | } 92 | *pDistance = minDistance; 93 | if (accidentId != -1) 94 | return &m_aAccidents[accidentId]; 95 | } 96 | return nil; 97 | } 98 | 99 | uint16 100 | CAccidentManager::CountActiveAccidents() 101 | { 102 | uint16 accidents = 0; 103 | for (int i = 0; i < NUM_ACCIDENTS; i++) { 104 | if (m_aAccidents[i].m_pVictim) 105 | accidents++; 106 | } 107 | return accidents; 108 | } 109 | 110 | bool 111 | CAccidentManager::WorkToDoForMedics() 112 | { 113 | for (int i = 0; i < NUM_ACCIDENTS; i++) { 114 | if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending < MAX_MEDICS_TO_ATTEND_ACCIDENT) 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | bool 121 | CAccidentManager::UnattendedAccidents() 122 | { 123 | for (int i = 0; i < NUM_ACCIDENTS; i++) { 124 | if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending == 0) 125 | return true; 126 | } 127 | return false; 128 | } 129 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/csharp.fixture: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Net.Http.Headers; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using GitHub.DistributedTask.ObjectTemplating.Tokens; 13 | using GitHub.Runner.Common; 14 | using GitHub.Runner.Sdk; 15 | using GitHub.Runner.Worker.Container; 16 | using GitHub.Services.Common; 17 | using WebApi = GitHub.DistributedTask.WebApi; 18 | using Pipelines = GitHub.DistributedTask.Pipelines; 19 | using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants; 20 | 21 | namespace GitHub.Runner.Worker 22 | { 23 | public class PrepareResult 24 | { 25 | public PrepareResult(List containerSetupSteps, Dictionary preStepTracker) 26 | { 27 | this.ContainerSetupSteps = containerSetupSteps; 28 | this.PreStepTracker = preStepTracker; 29 | } 30 | 31 | public List ContainerSetupSteps { get; set; } 32 | 33 | public Dictionary PreStepTracker { get; set; } 34 | } 35 | 36 | [ServiceLocator(Default = typeof(ActionManager))] 37 | public interface IActionManager : IRunnerService 38 | { 39 | Dictionary CachedActionContainers { get; } 40 | Task PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps); 41 | Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action); 42 | } 43 | 44 | public sealed class ActionManager : RunnerService, IActionManager 45 | { 46 | private const int _defaultFileStreamBufferSize = 4096; 47 | 48 | //81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k). 49 | private const int _defaultCopyBufferSize = 81920; 50 | private const string _dotcomApiUrl = "https://api.github.com"; 51 | private readonly Dictionary _cachedActionContainers = new Dictionary(); 52 | 53 | public Dictionary CachedActionContainers => _cachedActionContainers; 54 | public async Task PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps) 55 | { 56 | ArgUtil.NotNull(executionContext, nameof(executionContext)); 57 | ArgUtil.NotNull(steps, nameof(steps)); 58 | 59 | executionContext.Output("Prepare all required actions"); 60 | Dictionary> imagesToPull = new Dictionary>(StringComparer.OrdinalIgnoreCase); 61 | Dictionary> imagesToBuild = new Dictionary>(StringComparer.OrdinalIgnoreCase); 62 | Dictionary imagesToBuildInfo = new Dictionary(StringComparer.OrdinalIgnoreCase); 63 | List containerSetupSteps = new List(); 64 | Dictionary preStepTracker = new Dictionary(); 65 | IEnumerable actions = steps.OfType(); 66 | 67 | // TODO: Deprecate the PREVIEW_ACTION_TOKEN 68 | // Log even if we aren't using it to ensure users know. 69 | if (!string.IsNullOrEmpty(executionContext.Global.Variables.Get("PREVIEW_ACTION_TOKEN"))) 70 | { 71 | executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is deprecated. Please remove it from the repository's secrets"); 72 | } 73 | 74 | // Clear the cache (for self-hosted runners) 75 | IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken); 76 | 77 | // todo: Remove when feature flag DistributedTask.NewActionMetadata is removed 78 | var newActionMetadata = executionContext.Global.Variables.GetBoolean("DistributedTask.NewActionMetadata") ?? false; 79 | 80 | var repositoryActions = new List(); 81 | 82 | foreach (var action in actions) 83 | { 84 | if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry) 85 | { 86 | ArgUtil.NotNull(action, nameof(action)); 87 | var containerReference = action.Reference as Pipelines.ContainerRegistryReference; 88 | ArgUtil.NotNull(containerReference, nameof(containerReference)); 89 | ArgUtil.NotNullOrEmpty(containerReference.Image, nameof(containerReference.Image)); 90 | 91 | if (!imagesToPull.ContainsKey(containerReference.Image)) 92 | { 93 | imagesToPull[containerReference.Image] = new List(); 94 | } 95 | 96 | Trace.Info($"Action {action.Name} ({action.Id}) needs to pull image '{containerReference.Image}'"); 97 | imagesToPull[containerReference.Image].Add(action.Id); 98 | } 99 | // todo: Remove when feature flag DistributedTask.NewActionMetadata is removed 100 | else if (action.Reference.Type == Pipelines.ActionSourceType.Repository && !newActionMetadata) 101 | { 102 | // only download the repository archive 103 | await DownloadRepositoryActionAsync(executionContext, action); 104 | 105 | // more preparation base on content in the repository (action.yml) 106 | var setupInfo = PrepareRepositoryActionAsync(executionContext, action); 107 | if (setupInfo != null) 108 | { 109 | if (!string.IsNullOrEmpty(setupInfo.Image)) 110 | { 111 | if (!imagesToPull.ContainsKey(setupInfo.Image)) 112 | { 113 | imagesToPull[setupInfo.Image] = new List(); 114 | } 115 | 116 | Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to pull image '{setupInfo.Image}'"); 117 | imagesToPull[setupInfo.Image].Add(action.Id); 118 | } 119 | else 120 | { 121 | ArgUtil.NotNullOrEmpty(setupInfo.ActionRepository, nameof(setupInfo.ActionRepository)); 122 | 123 | if (!imagesToBuild.ContainsKey(setupInfo.ActionRepository)) 124 | { 125 | imagesToBuild[setupInfo.ActionRepository] = new List(); 126 | } 127 | 128 | Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to build image '{setupInfo.Dockerfile}'"); 129 | imagesToBuild[setupInfo.ActionRepository].Add(action.Id); 130 | imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/dart.fixture: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer' as dev; 3 | import 'package:flutter/material.dart'; -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/go.fixture: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | //import from standard library 6 | "library1" 7 | //import from git repo 8 | "gitlab.com/username/reponame/library2" 9 | "gitlab.com/username/library3" 10 | 11 | //import with alias 12 | lib4 "gitlab.com/username/reponame/library4" 13 | lib5 "gitlab.com/username/library5" 14 | 15 | //blank identifier import 16 | _ "gitlab.com/username/reponame/library6" 17 | _ "gitlab.com/username/library7" 18 | 19 | //dot import 20 | . "gitlab.com/username/reponame/library8" 21 | . "gitlab.com/username/library9" 22 | ) 23 | 24 | //import from standard library 25 | import "library10" 26 | //import from git repo 27 | import "gitlab.com/username/reponame/library11" 28 | import "gitlab.com/username/library12" 29 | 30 | //import with alias 31 | import lib4 "gitlab.com/username/reponame/library13" 32 | import lib5 "gitlab.com/username/library14" 33 | 34 | //blank identifier import 35 | import _ "gitlab.com/username/reponame/library15" 36 | import _ "gitlab.com/username/library16" 37 | 38 | //dot import 39 | import . "gitlab.com/username/reponame/library17" 40 | import . "gitlab.com/username/library18" 41 | 42 | //Don't find these libraries 43 | func main() { 44 | _ := "gitlab.com/username/library19" 45 | fmt.Println("import \"gitlab.com/username/library20\"") 46 | fmt.Println("import gitlab.com/username/library20) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/html.fixture: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title of the document 6 | 7 | 8 | 9 | 10 | 11 | 12 | body content 13 |

more content

14 | 15 | 16 | Some text 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/java.fixture: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 Samuel Audet 3 | * 4 | * Licensed either under the Apache License, Version 2.0, or (at your option) 5 | * under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation (subject to the "Classpath" exception), 7 | * either version 2, or any later version (collectively, the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * http://www.gnu.org/licenses/ 13 | * http://www.gnu.org/software/classpath/license.html 14 | * 15 | * or as provided in the LICENSE.txt file that accompanied this code. 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | package org.bytedeco.javacv; 24 | 25 | import java.awt.Color; 26 | 27 | import org.bytedeco.opencv.opencv_core.*; 28 | import static org.bytedeco.opencv.global.opencv_core.*; 29 | 30 | /** 31 | * 32 | * @author Samuel Audet 33 | */ 34 | public class ColorCalibrator { 35 | public ColorCalibrator(ProjectiveDevice device) { 36 | this.device = device; 37 | } 38 | 39 | private ProjectiveDevice device; 40 | 41 | public double calibrate(Color[] referenceColors, Color[] deviceColors) { 42 | assert(referenceColors.length == deviceColors.length); 43 | 44 | int[] order = device.getRGBColorOrder(); 45 | 46 | // solve for X and a in c = X p + a 47 | CvMat A = CvMat.create(referenceColors.length*3, 12); 48 | CvMat b = CvMat.create(referenceColors.length*3, 1); 49 | CvMat x = CvMat.create(12, 1); 50 | 51 | double gamma = device.getSettings().getResponseGamma(); 52 | 53 | for (int i = 0; i < referenceColors.length; i++) { 54 | float[] dc = deviceColors [i].getRGBColorComponents(null); 55 | float[] rc = referenceColors[i].getRGBColorComponents(null); 56 | 57 | double dc1 = Math.pow(dc[order[0]], gamma); 58 | double dc2 = Math.pow(dc[order[1]], gamma); 59 | double dc3 = Math.pow(dc[order[2]], gamma); 60 | for (int j = 0; j < 3; j++) { 61 | int k = i*36 + j*16; 62 | A.put(k , dc1); 63 | A.put(k+1, dc2); 64 | A.put(k+2, dc3); 65 | A.put(k+3, 1.0); 66 | if (j < 2) { 67 | for (int m = 0; m < 12; m++) { 68 | A.put(k+4+m, 0.0); 69 | } 70 | } 71 | } 72 | 73 | b.put(i*3 , rc[order[0]]); 74 | b.put(i*3+1, rc[order[1]]); 75 | b.put(i*3+2, rc[order[2]]); 76 | } 77 | 78 | //System.out.println("A =\n" + A); 79 | //System.out.println("b =\n" + b); 80 | 81 | // A.height = b.height = 18; 82 | if (cvSolve(A, b, x, CV_SVD) != 1.0) { 83 | System.out.println("Error solving."); 84 | } 85 | 86 | // compute RMSE and R^2 coefficient ... 87 | CvMat b2 = CvMat.create(b.rows(), 1); 88 | cvMatMul(A, x, b2); 89 | double MSE = cvNorm(b, b2)*cvNorm(b, b2)/b.rows(); 90 | double RMSE = Math.sqrt(MSE); 91 | CvScalar mean = new CvScalar(), stddev = new CvScalar(); 92 | cvAvgSdv(b, mean, stddev, null); 93 | double R2 = 1 - MSE/(stddev.val(0)*stddev.val(0)); 94 | //System.out.println("RMSE: " + RMSE + " R2: " + R2); 95 | //System.out.println("b2 =\n" + b2); 96 | 97 | device.colorMixingMatrix = CvMat.create(3, 3); 98 | device.additiveLight = CvMat.create(3, 1); 99 | for (int i = 0; i < 3; i++) { 100 | double x0 = x.get(i*4 ); 101 | double x1 = x.get(i*4+1); 102 | double x2 = x.get(i*4+2); 103 | double x3 = x.get(i*4+3); 104 | device.colorMixingMatrix.put(i*3 , x0); 105 | device.colorMixingMatrix.put(i*3+1, x1); 106 | device.colorMixingMatrix.put(i*3+2, x2); 107 | device.additiveLight .put(i, x3); 108 | } 109 | 110 | //System.out.println(device.colorMixingMatrix); 111 | //System.out.println(device.additiveLight); 112 | 113 | device.colorR2 = R2; 114 | return device.avgColorErr = RMSE; 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/javascript.fixture: -------------------------------------------------------------------------------- 1 | require('lib1') 2 | require("lib2"); 3 | import lib from "lib3"; 4 | import lib from 'lib4' 5 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/kotlin.fixture: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import syntax.lexer.Token 4 | import syntax.tree.* 5 | import java.tree.* # This should not be matched 6 | import kotlin.tree.* # This should not be matched 7 | 8 | typealias CommandNameId = Int 9 | typealias Address = Int 10 | 11 | class CodeGenerator(private val semantics: KarelSemantics) { 12 | 13 | private val program: MutableList = createInstructionBuffer() 14 | 15 | private val pc: Int 16 | get() = program.size 17 | 18 | private val lastInstruction: Instruction 19 | get() = program.last() 20 | 21 | private fun removeLastInstruction() { 22 | program.removeAt(program.lastIndex) 23 | } 24 | 25 | private fun generateInstruction(bytecode: Int, token: Token) { 26 | program.add(Instruction(bytecode, token.start)) 27 | } 28 | 29 | private val id = IdentityGenerator() 30 | // Forward calls cannot know their target address during code generation. 31 | // For simplicity, ALL call targets are therefore initially encoded as command name ids. 32 | // In a subsequent phase, the command name ids are then translated into addresses. 33 | private val addressOfCommandNameId = HashMap() 34 | 35 | private fun translateCallTargets() { 36 | program.forEachIndexed { index, instruction -> 37 | if (instruction.category == CALL) { 38 | program[index] = instruction.mapTarget { addressOfCommandNameId[it]!! } 39 | } 40 | } 41 | } 42 | 43 | fun generate(): List { 44 | semantics.reachableCommands.forEach { it.generate() } 45 | translateCallTargets() 46 | return program 47 | } 48 | 49 | private fun Command.generate() { 50 | addressOfCommandNameId[id(identifier.lexeme)] = pc 51 | body.generate() 52 | generateInstruction(RETURN, body.closingBrace) 53 | } 54 | 55 | private fun prepareForwardJump(token: Token): Int { 56 | if (lastInstruction.bytecode != NOT) { 57 | generateInstruction(ELSE, token) 58 | } else { 59 | removeLastInstruction() 60 | generateInstruction(THEN, token) 61 | } 62 | return pc - 1 63 | } 64 | 65 | private fun patchForwardJumpFrom(origin: Int) { 66 | program[origin] = program[origin].withTarget(pc) 67 | } 68 | 69 | private fun Statement.generate() { 70 | when (this) { 71 | is Block -> { 72 | statements.forEach { it.generate() } 73 | } 74 | is IfThenElse -> { 75 | if (e1se == null) { 76 | condition.generate() 77 | val over = prepareForwardJump(iF) 78 | th3n.generate() 79 | patchForwardJumpFrom(over) 80 | } else { 81 | condition.generate() 82 | val overThen = prepareForwardJump(iF) 83 | th3n.generate() 84 | val overElse = pc 85 | generateInstruction(JUMP, th3n.closingBrace) 86 | patchForwardJumpFrom(overThen) 87 | e1se.generate() 88 | patchForwardJumpFrom(overElse) 89 | } 90 | } 91 | is While -> { 92 | val back = pc 93 | condition.generate() 94 | val over = prepareForwardJump(whi1e) 95 | body.generate() 96 | generateInstruction(JUMP + back, body.closingBrace) 97 | patchForwardJumpFrom(over) 98 | } 99 | is Repeat -> { 100 | generateInstruction(PUSH + times, repeat) 101 | val back = pc 102 | body.generate() 103 | generateInstruction(LOOP + back, body.closingBrace) 104 | } 105 | is Call -> { 106 | val builtin = builtinCommands[target.lexeme] 107 | val bytecode = builtin ?: CALL + id(target.lexeme) 108 | generateInstruction(bytecode, target) 109 | } 110 | } 111 | } 112 | 113 | private fun Condition.generate() { 114 | when (this) { 115 | is False -> generateInstruction(FALSE, fa1se) 116 | is True -> generateInstruction(TRUE, tru3) 117 | 118 | is OnBeeper -> generateInstruction(ON_BEEPER, onBeeper) 119 | is BeeperAhead -> generateInstruction(BEEPER_AHEAD, beeperAhead) 120 | is LeftIsClear -> generateInstruction(LEFT_IS_CLEAR, leftIsClear) 121 | is FrontIsClear -> generateInstruction(FRONT_IS_CLEAR, frontIsClear) 122 | is RightIsClear -> generateInstruction(RIGHT_IS_CLEAR, rightIsClear) 123 | 124 | is Not -> { 125 | p.generate() 126 | if (lastInstruction.bytecode != NOT) { 127 | generateInstruction(NOT, not) 128 | } else { 129 | removeLastInstruction() 130 | } 131 | } 132 | 133 | is Conjunction -> { 134 | p.generate() 135 | q.generate() 136 | generateInstruction(AND, and) 137 | } 138 | 139 | is Disjunction -> { 140 | p.generate() 141 | q.generate() 142 | generateInstruction(OR, or) 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/perl.fixture: -------------------------------------------------------------------------------- 1 | package Perl::Sample; 2 | 3 | # import with use 4 | use strict; 5 | use Benchmark; 6 | use Carp 'croak'; 7 | use sigtrap qw(SEGV BUS); 8 | use Sub::Module; 9 | use Import::This 12.34; 10 | 11 | # conditional imports 12 | use if $] < 5.008, "utf8"; 13 | use if WANT_WARNINGS, warnings => qw(all); 14 | 15 | # include with require 16 | require Foo::Bar; 17 | BEGIN { require Module; Module->import(LIST); } 18 | 19 | # versions 20 | use v5.32.0; 21 | use 5.30.3; 22 | use 5.028_003; 23 | 24 | require v5.26.3; 25 | require 5.24.4; 26 | require 5.022_004; 27 | 28 | =pod 29 | Documentation lines about how to use this module should not match. 30 | =cut 31 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/php.fixture: -------------------------------------------------------------------------------- 1 | 2.0" 29 | gem 'lib_16', '>= 4.1.0' 30 | 31 | gem "lib17", github: "org/lib17" 32 | 33 | gem("lib18") 34 | gem('lib19') 35 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/rust.fixture: -------------------------------------------------------------------------------- 1 | extern crate lib1; 2 | #[macro_use] 3 | extern crate lib2; 4 | 5 | use lib3::io::test; 6 | use lib4 as something; 7 | // whitespace action 8 | use 9 | lib5::test as hi; 10 | // global path with a raw name 11 | use ::r#lib6; -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/swift.fixture: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import os.log 3 | import StatsKit 4 | import ModuleKit 5 | import CPU 6 | import Memory 7 | import Disk 8 | import Net 9 | import Battery 10 | import Sensors 11 | import GPU 12 | import Fans 13 | import func Pentathlon.swim 14 | import enum test.test 15 | import CoreServices.DictionaryServices 16 | -------------------------------------------------------------------------------- /librarydetection/languages/fixtures/typescript.fixture: -------------------------------------------------------------------------------- 1 | require('lib1') 2 | require("lib2"); 3 | import lib from "lib3"; 4 | import lib from 'lib4' 5 | -------------------------------------------------------------------------------- /librarydetection/languages/html.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | "github.com/codersrank-org/repo_info_extractor/v2/librarydetection" 5 | "golang.org/x/net/html" 6 | "strings" 7 | ) 8 | 9 | // NewCAnalyzer constructor 10 | func NewHTMLAnalyzer() librarydetection.Analyzer { 11 | return &htmlAnalyzer{} 12 | } 13 | 14 | type htmlAnalyzer struct{} 15 | 16 | func (a *htmlAnalyzer) ExtractLibraries(contents string) ([]string, error) { 17 | doc, err := html.Parse(strings.NewReader(contents)) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return a.traverseNodes(doc), nil 23 | } 24 | 25 | func (a *htmlAnalyzer) traverseNodes(node *html.Node) []string { 26 | var res []string 27 | // JS