├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.MD ├── arduino └── arduino.go ├── cmd ├── add.go ├── init.go ├── install.go ├── remove.go └── root.go ├── go-downloader ├── LICENSE ├── README.md ├── downloader.go ├── downloader_config.go ├── downloader_test.go ├── go.mod ├── go.sum └── testdata │ ├── test.txt │ └── test.txt.part ├── go.mod ├── go.sum ├── install.sh ├── main.go ├── project ├── project.go └── structs.go ├── service └── library.go └── util ├── cli.go └── filesystem.go /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | 10 | build-linux-windows: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup Go 16 | uses: actions/setup-go@v2 17 | with: 18 | go-version: 1.15 19 | 20 | - name: Get Gox 21 | run: go get github.com/mitchellh/gox 22 | 23 | - name: Build for Linux/Windows 24 | run: gox -output="build/{{.Dir}}_{{.OS}}_{{.Arch}}" -os="linux" -os="windows" -arch="amd64" -cgo 25 | 26 | - name: Release 27 | uses: softprops/action-gh-release@v1 28 | if: startsWith(github.ref, 'refs/tags/') 29 | with: 30 | files: | 31 | build/* 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | build-macos: 36 | runs-on: macos-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | 40 | - name: Setup Go 41 | uses: actions/setup-go@v2 42 | with: 43 | go-version: 1.15 44 | 45 | - name: Get Gox 46 | run: go get github.com/mitchellh/gox 47 | 48 | - name: Build for MacOS 49 | run: gox -output="build/{{.Dir}}_{{.OS}}_{{.Arch}}" -os="darwin" -arch="amd64" -cgo 50 | 51 | - name: Release 52 | uses: softprops/action-gh-release@v1 53 | if: startsWith(github.ref, 'refs/tags/') 54 | with: 55 | files: | 56 | build/* 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./apm 2 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | apm (Arduino Package Manager) 2 | --- 3 | [![Go](https://github.com/ksrichard/apm/actions/workflows/release.yml/badge.svg)](https://github.com/ksrichard/apm/actions/workflows/release.yml) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/ksrichard/apm)](https://goreportcard.com/report/github.com/ksrichard/apm) 5 | [![Go Reference](https://pkg.go.dev/badge/github.com/ksrichard/apm.svg)](https://pkg.go.dev/github.com/ksrichard/apm) 6 | [![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/ksrichard/apm.svg)](https://github.com/ksrichard/apm) 7 | [![GitHub release](https://img.shields.io/github/release/ksrichard/apm.svg)](https://github.com/ksrichard/apm/releases/latest/) 8 | 9 | A package manager for Arduino projects to make them portable (no more manual download etc...). 10 | 11 | This tool fully includes the official `arduino-cli` (it won't be installed nor need to be installed) 12 | so it is automatically compatible with any kind of Arduino projects/sketches. 13 | 14 | ### Features 15 | ```bash 16 | A package manager for Arduino projects. 17 | The official arduino-cli packages are used to perform actions. 18 | 19 | Usage: 20 | apm [command] 21 | 22 | Available Commands: 23 | add Adding new libraries to the project 24 | help Help about any command 25 | init Init APM project 26 | install Install dependencies of project 27 | remove Remove library from the project 28 | 29 | Flags: 30 | -h, --help help for apm 31 | -p, --project-dir string Project directory to use (default "/Users/klavorar/Documents/Arduino/temp_sensor") 32 | 33 | Use "apm [command] --help" for more information about a command. 34 | ``` 35 | 36 | ### Demo 37 | https://asciinema.org/a/414014 38 | [![asciicast](https://asciinema.org/a/414014.png)](https://asciinema.org/a/414014) 39 | 40 | ### Installation 41 | #### Install on Linux/MacOS 42 | Run the following in your terminal to install the latest version of `apm` 43 | ```shell script 44 | curl -s https://raw.githubusercontent.com/ksrichard/apm/master/install.sh | sh 45 | ``` 46 | #### Install on Windows 47 | Download the latest version of `apm` from https://github.com/ksrichard/apm/releases/latest for Windows (`apm_windows_amd64.exe`), rename it to apm.exe and put it on path. 48 | 49 | ### How it works 50 | Every `apm` based project must have a file called `apm.json` in the project root (it can be create by running `apm init`) 51 | This configuration file is containing all the information that an Arduino project needs. 52 | 53 | `NOTE on versioning` - if you would like to use always the latest version, please use `latest` in any package version and always latest will be used! 54 | 55 | `apm.json` structure: 56 | - `board` - (Optional) you can select here the package/architecture of the board you will use, it will be automatically installed 57 | - `package` - Arduino core package name 58 | - `architecture` - Architecture of Arduino core package 59 | - `version` - Version of core package (`latest` for always latest version) 60 | - `board_manager_url` - (Optional) Additional Board Manager URL if needed for the board core package to be installed 61 | - `dependencies` - (Optional, if empty, no dependencies will be installed of course) 62 | contains all Arduino Library dependencies that the actual project needs (if any Version mismatch will be in place, process will be stopped) 63 | - `library` - Arduino Library name 64 | - `version` - Arduino Library version 65 | - `git` - (Optional - if it's set, do not set `library` and `version`) install library from git repository 66 | - `zip` - (Optional - if it's set, do not set `library` and `version`) install library from local zip file 67 | 68 | Example `apm.json`: 69 | ```json 70 | { 71 | "board": { 72 | "package": "esp8266", 73 | "architecture": "esp8266", 74 | "version": "latest", 75 | "board_manager_url": "https://arduino.esp8266.com/stable/package_esp8266com_index.json" 76 | }, 77 | "dependencies": [ 78 | { 79 | "library": "HomeKit-ESP8266", 80 | "version": "1.2.0" 81 | }, 82 | { 83 | "library": "OneWire", 84 | "version": "latest" 85 | }, 86 | { 87 | "library": "DallasTemperature", 88 | "version": "latest" 89 | }, 90 | { 91 | "git": "https://github.com/jandrassy/ArduinoOTA" 92 | }, 93 | { 94 | "zip": "ESP8266NetBIOS.zip" 95 | } 96 | ] 97 | } 98 | ``` 99 | 100 | -------------------------------------------------------------------------------- /arduino/arduino.go: -------------------------------------------------------------------------------- 1 | package arduino 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | acli "github.com/arduino/arduino-cli/cli" 8 | aconfig "github.com/arduino/arduino-cli/configuration" 9 | "github.com/arduino/arduino-cli/i18n" 10 | "github.com/ksrichard/apm/project" 11 | "github.com/phayes/freeport" 12 | "google.golang.org/grpc" 13 | "io" 14 | 15 | rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" 16 | //"github.com/arduino/arduino-cli/rpc/cc/arduino/cli/settings/v1" 17 | //"google.golang.org/grpc" 18 | "github.com/spf13/cobra" 19 | "log" 20 | "os" 21 | "strings" 22 | ) 23 | 24 | func RunCmdInteractive(cmd *cobra.Command, command []string) error { 25 | err := os.Setenv("ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL", "true") 26 | if err != nil { 27 | return err 28 | } 29 | cmd.SetArgs(command) 30 | err = cmd.Execute() 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | type ArduinoCli struct { 38 | grpcServerPort int 39 | cmd *cobra.Command 40 | client rpc.ArduinoCoreServiceClient 41 | grpcConn *grpc.ClientConn 42 | grpcInstance *rpc.Instance 43 | } 44 | 45 | func (c *ArduinoCli) Init() error { 46 | c.cmd = c.getArduinoCliCommand() 47 | grpcPort, err := c.startCliGrpcServer() 48 | if err != nil { 49 | return err 50 | } 51 | c.grpcServerPort = grpcPort 52 | 53 | // grpc connection 54 | conn, err := c.getGrpcConnection() 55 | if err != nil { 56 | return err 57 | } 58 | c.grpcConn = conn 59 | 60 | // client 61 | c.client = rpc.NewArduinoCoreServiceClient(conn) 62 | 63 | // init instance 64 | c.grpcInstance = c.initInstance(c.client) 65 | 66 | return nil 67 | } 68 | 69 | func (c *ArduinoCli) Destroy() { 70 | c.grpcConn.Close() 71 | } 72 | 73 | func (c *ArduinoCli) startCliGrpcServer() (int, error) { 74 | port, err := freeport.GetFreePort() 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | go func() { 79 | RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("daemon --daemonize --port %d", port), " ")) 80 | }() 81 | return port, nil 82 | } 83 | 84 | func (c *ArduinoCli) getArduinoCliCommand() *cobra.Command { 85 | aconfig.Settings = aconfig.Init(aconfig.FindConfigFileInArgsOrWorkingDirectory(os.Args)) 86 | i18n.Init() 87 | return acli.NewCommand() 88 | } 89 | 90 | func (c *ArduinoCli) SearchLibrary(query string) ([]*rpc.SearchedLibrary, error) { 91 | maxSizeOption := grpc.MaxCallRecvMsgSize(64 * 10e6) 92 | response, err := c.client.LibrarySearch(context.Background(), &rpc.LibrarySearchRequest{ 93 | Instance: c.grpcInstance, 94 | Query: query, 95 | }, maxSizeOption) 96 | if err != nil { 97 | return nil, err 98 | } 99 | return response.Libraries, nil 100 | } 101 | 102 | func (c *ArduinoCli) initInstance(client rpc.ArduinoCoreServiceClient) *rpc.Instance { 103 | initRespStream, err := client.Init(context.Background(), &rpc.InitRequest{}) 104 | if err != nil { 105 | log.Fatalf("Error creating server instance: %s", err) 106 | } 107 | 108 | var instance *rpc.Instance 109 | // Loop and consume the server stream until all the setup procedures are done. 110 | for { 111 | initResp, err := initRespStream.Recv() 112 | // The server is done. 113 | if err == io.EOF { 114 | break 115 | } 116 | 117 | // There was an error. 118 | if err != nil { 119 | log.Fatalf("Init error: %s", err) 120 | } 121 | 122 | // The server sent us a valid instance, let's print its ID. 123 | if initResp.GetInstance() != nil { 124 | instance = initResp.GetInstance() 125 | } 126 | } 127 | 128 | return instance 129 | } 130 | 131 | func (c *ArduinoCli) getGrpcConnection() (*grpc.ClientConn, error) { 132 | return grpc.Dial(fmt.Sprintf("localhost:%d", c.grpcServerPort), grpc.WithInsecure(), grpc.WithBlock()) 133 | } 134 | 135 | func (c *ArduinoCli) InstallBoardCore(details *project.ProjectDetails) error { 136 | log.Println("Installing board...") 137 | board := details.Board 138 | 139 | // update board core index 140 | additionalArgs := "" 141 | if board.BoardManagerUrl != "" { 142 | additionalArgs = fmt.Sprintf("--additional-urls %s", board.BoardManagerUrl) 143 | } 144 | err := RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("core update-index %s", additionalArgs), " ")) 145 | if err != nil { 146 | return err 147 | } 148 | 149 | if strings.ToLower(board.Version) == "latest" { 150 | return RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("core install %s:%s %s", board.Package, board.Architecture, additionalArgs), " ")) 151 | } 152 | 153 | return RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("core install %s:%s@%s %s", board.Package, board.Architecture, board.Version, additionalArgs), " ")) 154 | } 155 | 156 | func (c *ArduinoCli) CheckDependencyVersionMismatch(dep project.ProjectDependency, details *project.ProjectDetails) error { 157 | for _, projectLib := range details.Dependencies { 158 | if projectLib.Library != dep.Library || (projectLib.Library == dep.Library && projectLib.Version != dep.Version) { 159 | libs, err := c.SearchLibrary(projectLib.Library) 160 | if err != nil { 161 | return err 162 | } 163 | for _, lib := range libs { 164 | if lib.Name == projectLib.Library { 165 | if strings.ToLower(projectLib.Version) == "latest" { 166 | projectLib.Version = lib.Latest.Version 167 | } 168 | for _, release := range lib.Releases { 169 | if release.Version == projectLib.Version { 170 | // check for dep mismatch directly 171 | if lib.Name == dep.Library && release.Version != dep.Version { 172 | return errors.New( 173 | fmt.Sprintf( 174 | "Version mismatch: %s@%s ->|<- %s@%s", dep.Library, dep.Version, lib.Name, release.Version, 175 | ), 176 | ) 177 | } 178 | 179 | // check for dep mismatch in found lib dependencies 180 | for _, dependency := range release.Dependencies { 181 | if dependency.VersionConstraint == "" { 182 | dependency.VersionConstraint = "latest" 183 | } 184 | if dependency.Name == dep.Library && dependency.VersionConstraint != dep.Version { 185 | return errors.New( 186 | fmt.Sprintf( 187 | "Version mismatch: %s@%s ->|<- %s -> %s@%s", dep.Library, dep.Version, projectLib.Library, dependency.Name, dependency.VersionConstraint, 188 | ), 189 | ) 190 | } 191 | } 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | return nil 199 | } 200 | 201 | func (c *ArduinoCli) InstallDependencies(details *project.ProjectDetails) error { 202 | log.Println("Installing dependencies...") 203 | 204 | // update library index 205 | err := RunCmdInteractive(c.cmd, strings.Split("lib update-index", " ")) 206 | if err != nil { 207 | return err 208 | } 209 | 210 | // check if we have any dependency mismatch with current libs 211 | for _, dep := range details.Dependencies { 212 | err = c.CheckDependencyVersionMismatch(dep, details) 213 | if err != nil { 214 | return err 215 | } 216 | } 217 | 218 | // install all library dependencies 219 | for _, dep := range details.Dependencies { 220 | // we have a library specified 221 | if dep.Library != "" { 222 | // construct library install cmd 223 | if dep.Version == "" { 224 | return errors.New("please specify a version") 225 | } 226 | lib := fmt.Sprintf("%s@%s", dep.Library, dep.Version) 227 | if strings.ToLower(dep.Version) == "latest" { 228 | lib = fmt.Sprintf("%s", dep.Library) 229 | } 230 | 231 | // run lib install 232 | err = RunCmdInteractive(c.cmd, []string{"lib", "install", lib}) 233 | if err != nil { 234 | return err 235 | } 236 | } else { 237 | if dep.Git != "" && dep.Zip != "" { 238 | return errors.New("please specify git or zip, but NOT both") 239 | } 240 | 241 | // we have git specified 242 | if dep.Git != "" { 243 | log.Printf("Installing dependency from GIT repository: %s...\n", dep.Git) 244 | err = RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("lib install --git-url %s", dep.Git), " ")) 245 | if err != nil { 246 | return err 247 | } 248 | } 249 | 250 | // we have zip specified 251 | if dep.Zip != "" { 252 | log.Printf("Installing dependency from ZIP file: %s...\n", dep.Zip) 253 | err = RunCmdInteractive(c.cmd, strings.Split(fmt.Sprintf("lib install --zip-path %s", dep.Zip), " ")) 254 | if err != nil { 255 | return err 256 | } 257 | } 258 | } 259 | 260 | } 261 | return nil 262 | } 263 | 264 | func (c *ArduinoCli) UninstallDependency(dep *project.ProjectDependency) error { 265 | depName := "" 266 | if dep.Library != "" { 267 | depName = dep.Library 268 | } 269 | if depName == "" { 270 | log.Println("Dependency is a Git repository/Zip file based library, skipping uninstall...") 271 | return nil 272 | } 273 | log.Printf("Uninstalling dependency '%s'...\n", depName) 274 | return RunCmdInteractive(c.cmd, []string{"lib", "uninstall", depName}) 275 | } 276 | -------------------------------------------------------------------------------- /cmd/add.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "github.com/ksrichard/apm/arduino" 22 | "github.com/ksrichard/apm/project" 23 | "github.com/ksrichard/apm/service" 24 | "github.com/ksrichard/apm/util" 25 | "github.com/spf13/cobra" 26 | "strings" 27 | ) 28 | 29 | // addCmd represents the add command 30 | var addCmd = &cobra.Command{ 31 | Use: "add", 32 | Example: "apm add\napm add OneWire@2.3.5\napm add onewire\napm add onewire@latest", 33 | Short: "Adding new libraries to the project", 34 | Long: `Adding new libraries to the Arduino project`, 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | // project details 37 | details, err := project.GetProjectDetails(cmd) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | // init cli 43 | cli := &arduino.ArduinoCli{} 44 | err = cli.Init() 45 | if err != nil { 46 | return err 47 | } 48 | defer cli.Destroy() 49 | 50 | // add git repository 51 | gitRepo, err := cmd.Flags().GetString("git") 52 | if err != nil { 53 | return err 54 | } 55 | if strings.TrimSpace(gitRepo) != "" { 56 | return addGitRepoDep(cli, cmd, gitRepo, details) 57 | } 58 | 59 | // add zip library 60 | zipFile, err := cmd.Flags().GetString("zip") 61 | if err != nil { 62 | return err 63 | } 64 | if strings.TrimSpace(zipFile) != "" && util.FileExists(zipFile) { 65 | return addZipDep(cli, cmd, zipFile, details) 66 | } 67 | if strings.TrimSpace(zipFile) != "" && !util.FileExists(zipFile) { 68 | return errors.New(fmt.Sprintf("'%s' not found!", zipFile)) 69 | } 70 | 71 | // add library 72 | libName := "" 73 | libVersion := "" 74 | if len(args) < 1 { // we do not have any library set 75 | fmt.Println("No library provided...") 76 | libName, libVersion, err = service.SelectLibrary(cli) 77 | if err != nil { 78 | return err 79 | } 80 | } else { // we have library set 81 | libNameWithVersion := args[0] 82 | nameAndVer := strings.Split(libNameWithVersion, "@") 83 | 84 | // we have too many @ chars 85 | if len(nameAndVer) > 2 { 86 | return errors.New("please provide the library in the following form: LIBRARY_NAME or LIBRARY_NAME@VERSION") 87 | } 88 | 89 | // only library name provided 90 | if len(nameAndVer) == 1 { 91 | libName = nameAndVer[0] 92 | libVersion = "latest" 93 | } 94 | 95 | // library name and version provided 96 | if len(nameAndVer) == 2 { 97 | libName = nameAndVer[0] 98 | libVersion = nameAndVer[1] 99 | } 100 | 101 | // check validity and set library name to original one 102 | libName, err = service.CheckIfLibraryValid(cli, libName, libVersion, 5) 103 | if err != nil { 104 | return err 105 | } 106 | } 107 | 108 | // update changes 109 | fmt.Printf("Adding %s@%s...\n", libName, libVersion) 110 | hasDep := false 111 | for i, dep := range details.Dependencies { 112 | if dep.Library == libName { 113 | hasDep = true 114 | details.Dependencies[i].Version = libVersion 115 | } 116 | } 117 | if !hasDep { 118 | details.Dependencies = append(details.Dependencies, project.ProjectDependency{ 119 | Library: libName, 120 | Version: libVersion, 121 | }) 122 | } 123 | 124 | // check if we have any dependency mismatch with current libs 125 | for _, dep := range details.Dependencies { 126 | err = cli.CheckDependencyVersionMismatch(dep, details) 127 | if err != nil { 128 | return err 129 | } 130 | } 131 | 132 | // update project file 133 | err = project.UpdateProjectDetails(cmd, details) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | // install dependencies 139 | if details.Dependencies != nil && len(details.Dependencies) > 0 { 140 | err = cli.InstallDependencies(details) 141 | if err != nil { 142 | return err 143 | } 144 | } 145 | 146 | return nil 147 | }, 148 | } 149 | 150 | func init() { 151 | rootCmd.AddCommand(addCmd) 152 | 153 | addCmd.Flags().StringP("git", "g", "", "Library from Git repository") 154 | addCmd.Flags().StringP("zip", "z", "", "Library from ZIP file") 155 | } 156 | 157 | func addGitRepoDep(cli *arduino.ArduinoCli, cmd *cobra.Command, gitRepo string, details *project.ProjectDetails) error { 158 | fmt.Printf("Adding %s...\n", gitRepo) 159 | hasDep := false 160 | for i, dep := range details.Dependencies { 161 | if dep.Git == gitRepo { 162 | hasDep = true 163 | details.Dependencies[i].Git = gitRepo 164 | } 165 | } 166 | if !hasDep { 167 | details.Dependencies = append(details.Dependencies, project.ProjectDependency{ 168 | Git: gitRepo, 169 | }) 170 | } 171 | 172 | // check if we have any dependency mismatch with current libs 173 | for _, dep := range details.Dependencies { 174 | err := cli.CheckDependencyVersionMismatch(dep, details) 175 | if err != nil { 176 | return err 177 | } 178 | } 179 | 180 | // update project file 181 | err := project.UpdateProjectDetails(cmd, details) 182 | if err != nil { 183 | return err 184 | } 185 | 186 | // install dependencies 187 | if details.Dependencies != nil && len(details.Dependencies) > 0 { 188 | err = cli.InstallDependencies(details) 189 | if err != nil { 190 | return err 191 | } 192 | } 193 | return nil 194 | } 195 | 196 | func addZipDep(cli *arduino.ArduinoCli, cmd *cobra.Command, zipFile string, details *project.ProjectDetails) error { 197 | fmt.Printf("Adding %s...\n", zipFile) 198 | hasDep := false 199 | for i, dep := range details.Dependencies { 200 | if dep.Zip == zipFile { 201 | hasDep = true 202 | details.Dependencies[i].Zip = zipFile 203 | } 204 | } 205 | if !hasDep { 206 | details.Dependencies = append(details.Dependencies, project.ProjectDependency{ 207 | Zip: zipFile, 208 | }) 209 | } 210 | 211 | // check if we have any dependency mismatch with current libs 212 | for _, dep := range details.Dependencies { 213 | err := cli.CheckDependencyVersionMismatch(dep, details) 214 | if err != nil { 215 | return err 216 | } 217 | } 218 | 219 | // update project file 220 | err := project.UpdateProjectDetails(cmd, details) 221 | if err != nil { 222 | return err 223 | } 224 | 225 | // install dependencies 226 | if details.Dependencies != nil && len(details.Dependencies) > 0 { 227 | err = cli.InstallDependencies(details) 228 | if err != nil { 229 | return err 230 | } 231 | } 232 | return nil 233 | } 234 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "encoding/json" 20 | "errors" 21 | "fmt" 22 | "github.com/ksrichard/apm/project" 23 | "github.com/ksrichard/apm/util" 24 | "github.com/spf13/cobra" 25 | "io/ioutil" 26 | "os" 27 | ) 28 | 29 | // initCmd represents the init command 30 | var initCmd = &cobra.Command{ 31 | Use: "init", 32 | Short: "Init APM project", 33 | Long: `Init Arduino Package Manager project`, 34 | RunE: func(cmd *cobra.Command, args []string) error { 35 | projectDir, err := project.GetProjectDir(cmd) 36 | if err != nil { 37 | return err 38 | } 39 | jsonFilePath := fmt.Sprintf("%s/%s", projectDir, project.ProjectDetailsFileName) 40 | if util.FileExists(jsonFilePath) { 41 | return errors.New(fmt.Sprintf("'%s' is already initialized", projectDir)) 42 | } else { 43 | details := project.ProjectDetails{ 44 | Board: &project.ProjectBoard{}, 45 | Dependencies: []project.ProjectDependency{}, 46 | } 47 | fileData, err := json.MarshalIndent(details, "", " ") 48 | if err != nil { 49 | return err 50 | } 51 | err = ioutil.WriteFile(jsonFilePath, fileData, os.ModePerm) 52 | if err != nil { 53 | return err 54 | } 55 | return nil 56 | } 57 | }, 58 | } 59 | 60 | func init() { 61 | rootCmd.AddCommand(initCmd) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/install.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "github.com/ksrichard/apm/arduino" 20 | "github.com/ksrichard/apm/project" 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | // installCmd represents the install command 25 | var installCmd = &cobra.Command{ 26 | Use: "install", 27 | Short: "Install dependencies of project", 28 | Long: `Install dependencies of the Arduino project`, 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | // project details 31 | details, err := project.GetProjectDetails(cmd) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | cli := &arduino.ArduinoCli{} 37 | err = cli.Init() 38 | if err != nil { 39 | return err 40 | } 41 | defer cli.Destroy() 42 | 43 | // install board core package 44 | if details.Board != nil && details.Board.Package != "" { 45 | err = cli.InstallBoardCore(details) 46 | if err != nil { 47 | return err 48 | } 49 | } 50 | 51 | // install dependencies 52 | if details.Dependencies != nil && len(details.Dependencies) > 0 { 53 | err = cli.InstallDependencies(details) 54 | if err != nil { 55 | return err 56 | } 57 | } 58 | 59 | return nil 60 | }, 61 | } 62 | 63 | func init() { 64 | rootCmd.AddCommand(installCmd) 65 | } 66 | -------------------------------------------------------------------------------- /cmd/remove.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "github.com/ksrichard/apm/arduino" 22 | "github.com/ksrichard/apm/project" 23 | "github.com/ksrichard/apm/util" 24 | "log" 25 | "strings" 26 | 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | // removeCmd represents the remove command 31 | var removeCmd = &cobra.Command{ 32 | Use: "remove", 33 | Example: "apm remove\n" + 34 | "apm remove OneWire\n" + 35 | "apm remove onewire\n" + 36 | "apm remove \"robot control\"\n" + 37 | "apm remove \"Robot Control\"\n" + 38 | "apm remove \"https://github.com/jandrassy/ArduinoOTA\"\n" + 39 | "apm remove https://github.com/jandrassy/ArduinoOTA\n" + 40 | "apm remove ArduinoOTA.zip\n" + 41 | "apm remove \"ArduinoOTA.zip\"", 42 | Short: "Remove library from the project", 43 | Long: `Remove library from the Arduino project`, 44 | RunE: func(cmd *cobra.Command, args []string) error { 45 | // project details 46 | details, err := project.GetProjectDetails(cmd) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // init cli 52 | cli := &arduino.ArduinoCli{} 53 | err = cli.Init() 54 | if err != nil { 55 | return err 56 | } 57 | defer cli.Destroy() 58 | 59 | var libToRemove *project.ProjectDependency = nil 60 | // no library provided 61 | if len(args) < 1 { 62 | fmt.Println("No library provided...") 63 | items := make(map[string]interface{}) 64 | for _, dep := range details.Dependencies { 65 | libTitle := "" 66 | if dep.Library == "" && dep.Git != "" { 67 | libTitle = dep.Git 68 | } 69 | if dep.Library == "" && dep.Zip != "" { 70 | libTitle = dep.Zip 71 | } 72 | if dep.Library != "" && dep.Version != "" { 73 | libTitle = fmt.Sprintf("%s (%s)", dep.Library, dep.Version) 74 | } 75 | items[libTitle] = &dep 76 | } 77 | selectedLib, err := util.Select("Select library to remove", []string{"Cancel"}, items) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | // check if cancelled 83 | switch selectedLib.(type) { 84 | case string: 85 | if selectedLib.(string) == "Cancel" { 86 | return errors.New("cancelled") 87 | } 88 | break 89 | } 90 | 91 | libToRemove = selectedLib.(*project.ProjectDependency) 92 | } else { // library name provided 93 | libToRemoveArg := args[0] 94 | for _, dep := range details.Dependencies { 95 | if strings.ToLower(dep.Library) == strings.ToLower(libToRemoveArg) || 96 | dep.Git == libToRemoveArg || 97 | dep.Zip == libToRemoveArg { 98 | libToRemove = &dep 99 | } 100 | } 101 | if libToRemove == nil { 102 | return errors.New(fmt.Sprintf("failed to find '%s' library in the project", libToRemoveArg)) 103 | } 104 | } 105 | 106 | // log removal 107 | libName := "" 108 | if libToRemove.Library != "" { 109 | libName = libToRemove.Library 110 | } 111 | if libToRemove.Git != "" { 112 | libName = libToRemove.Git 113 | } 114 | if libToRemove.Zip != "" { 115 | libName = libToRemove.Zip 116 | } 117 | log.Printf("Removing '%s'...", libName) 118 | 119 | // remove from project file 120 | for i, dep := range details.Dependencies { 121 | if (dep.Library != "" && dep.Library == libToRemove.Library) || 122 | (dep.Git != "" && dep.Git == libToRemove.Git) || 123 | (dep.Zip != "" && dep.Zip == libToRemove.Zip) { 124 | details.Dependencies = removeFromDeps(details.Dependencies, i) 125 | break 126 | } 127 | } 128 | 129 | // update project file 130 | err = project.UpdateProjectDetails(cmd, details) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | // uninstall dependency 136 | err = cli.UninstallDependency(libToRemove) 137 | if err != nil { 138 | return err 139 | } 140 | 141 | // install dependencies 142 | if details.Dependencies != nil && len(details.Dependencies) > 0 { 143 | err = cli.InstallDependencies(details) 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | 149 | return nil 150 | }, 151 | } 152 | 153 | func init() { 154 | rootCmd.AddCommand(removeCmd) 155 | } 156 | 157 | func removeFromDeps(slice []project.ProjectDependency, i int) []project.ProjectDependency { 158 | return append(slice[:i], slice[i+1:]...) 159 | } 160 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "fmt" 20 | "github.com/spf13/cobra" 21 | "os" 22 | ) 23 | 24 | // rootCmd represents the base command when called without any subcommands 25 | var rootCmd = &cobra.Command{ 26 | Use: "apm", 27 | Short: "Arduino Package Manager", 28 | Long: `A package manager for Arduino projects. 29 | The official arduino-cli packages are used to perform actions.`, 30 | } 31 | 32 | // Execute adds all child commands to the root command and sets flags appropriately. 33 | // This is called by main.main(). It only needs to happen once to the rootCmd. 34 | func Execute() { 35 | if err := rootCmd.Execute(); err != nil { 36 | fmt.Println(err) 37 | os.Exit(1) 38 | } 39 | } 40 | 41 | func init() { 42 | currentDir, err := os.Getwd() 43 | if err != nil { 44 | fmt.Println(err) 45 | os.Exit(1) 46 | } 47 | rootCmd.PersistentFlags().StringP("project-dir", "p", currentDir, "Project directory to use") 48 | } 49 | -------------------------------------------------------------------------------- /go-downloader/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2018, Cristian Maglie. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | 34 | -------------------------------------------------------------------------------- /go-downloader/README.md: -------------------------------------------------------------------------------- 1 | # go.bug.st/downloader/v2 [![build status](https://api.travis-ci.org/bugst/go-downloader.svg?branch=master)](https://travis-ci.org/bugst/go-downloader) [![codecov](https://codecov.io/gh/bugst/go-downloader/branch/master/graph/badge.svg)](https://codecov.io/gh/bugst/go-downloader) 2 | 3 | A simple HTTP/S file downloader for golang. 4 | -------------------------------------------------------------------------------- /go-downloader/downloader.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018 Cristian Maglie. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | // 6 | 7 | package downloader 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | "net/http" 13 | "os" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | // Downloader is an asynchronous downloader 19 | type Downloader struct { 20 | URL string 21 | Done chan bool 22 | Resp *http.Response 23 | out *os.File 24 | completed int64 25 | completedLock sync.Mutex 26 | size int64 27 | err error 28 | } 29 | 30 | // DownloadOptions are optional flags that can be passed to Download function 31 | type DownloadOptions int 32 | 33 | const ( 34 | // NoResume will not try to resume a partial download 35 | NoResume DownloadOptions = iota 36 | ) 37 | 38 | // Close the download 39 | func (d *Downloader) Close() error { 40 | err1 := d.out.Close() 41 | err2 := d.Resp.Body.Close() 42 | if err1 != nil { 43 | return fmt.Errorf("closing output file: %s", err1) 44 | } 45 | if err2 != nil { 46 | return fmt.Errorf("closing input stream: %s", err2) 47 | } 48 | return nil 49 | } 50 | 51 | // Size return the size of the download 52 | func (d *Downloader) Size() int64 { 53 | return d.size 54 | } 55 | 56 | // RunAndPoll starts the downloader copy-loop and calls the poll function every 57 | // interval time to update progress. 58 | func (d *Downloader) RunAndPoll(poll func(current int64), interval time.Duration) error { 59 | t := time.NewTicker(interval) 60 | defer t.Stop() 61 | 62 | go d.AsyncRun() 63 | for { 64 | select { 65 | case <-t.C: 66 | poll(d.Completed()) 67 | case <-d.Done: 68 | poll(d.Completed()) 69 | return d.Error() 70 | } 71 | } 72 | } 73 | 74 | // AsyncRun starts the downloader copy-loop. This function is supposed to be run 75 | // on his own go routine because it sends a confirmation on the Done channel 76 | func (d *Downloader) AsyncRun() { 77 | in := d.Resp.Body 78 | buff := [4096]byte{} 79 | for { 80 | n, err := in.Read(buff[:]) 81 | if n > 0 { 82 | _, _ = d.out.Write(buff[:n]) 83 | d.completedLock.Lock() 84 | d.completed += int64(n) 85 | d.completedLock.Unlock() 86 | } 87 | if err == io.EOF { 88 | break 89 | } 90 | if err != nil { 91 | d.err = err 92 | break 93 | } 94 | } 95 | _ = d.Close() 96 | d.Done <- true 97 | } 98 | 99 | // Run starts the downloader and waits until it completes the download. 100 | func (d *Downloader) Run() error { 101 | go d.AsyncRun() 102 | <-d.Done 103 | return d.Error() 104 | } 105 | 106 | // Error returns the error during download or nil if no errors happened 107 | func (d *Downloader) Error() error { 108 | return d.err 109 | } 110 | 111 | // Completed returns the bytes read so far 112 | func (d *Downloader) Completed() int64 { 113 | d.completedLock.Lock() 114 | res := d.completed 115 | d.completedLock.Unlock() 116 | return res 117 | } 118 | 119 | // Download returns an asynchronous downloader that will download the specified url 120 | // in the specified file. A download resume is tried if a file shorter than the requested 121 | // url is already present. 122 | func Download(file string, reqURL string, options ...DownloadOptions) (*Downloader, error) { 123 | return DownloadWithConfig(file, reqURL, GetDefaultConfig(), options...) 124 | } 125 | 126 | // DownloadWithConfig applies an additional configuration to the http client and 127 | // returns an asynchronous downloader that will download the specified url 128 | // in the specified file. A download resume is tried if a file shorter than the requested 129 | // url is already present. 130 | func DownloadWithConfig(file string, reqURL string, config Config, options ...DownloadOptions) (*Downloader, error) { 131 | noResume := false 132 | for _, opt := range options { 133 | if opt == NoResume { 134 | noResume = true 135 | } 136 | } 137 | req, err := http.NewRequest("GET", reqURL, nil) 138 | 139 | if err != nil { 140 | return nil, fmt.Errorf("setting up HTTP request: %s", err) 141 | } 142 | 143 | var completed int64 144 | if !noResume { 145 | if info, err := os.Stat(file); err == nil { 146 | completed = info.Size() 147 | req.Header.Set("Range", fmt.Sprintf("bytes=%d-", completed)) 148 | } 149 | } 150 | 151 | resp, err := config.HttpClient.Do(req) 152 | if err != nil { 153 | return nil, err 154 | } 155 | 156 | // TODO: if file size == header size return nil, nil 157 | 158 | flags := os.O_WRONLY 159 | if completed == 0 { 160 | flags |= os.O_CREATE 161 | } else { 162 | flags |= os.O_APPEND 163 | } 164 | f, err := os.OpenFile(file, flags, 0644) 165 | if err != nil { 166 | _ = resp.Body.Close() 167 | return nil, fmt.Errorf("opening %s for writing: %s", file, err) 168 | } 169 | 170 | d := &Downloader{ 171 | URL: reqURL, 172 | Done: make(chan bool), 173 | Resp: resp, 174 | out: f, 175 | completed: completed, 176 | size: resp.ContentLength + completed, 177 | } 178 | return d, nil 179 | } 180 | -------------------------------------------------------------------------------- /go-downloader/downloader_config.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018 Cristian Maglie. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | // 6 | 7 | package downloader 8 | 9 | import ( 10 | "net/http" 11 | "sync" 12 | ) 13 | 14 | // Config contains the configuration for the downloader 15 | type Config struct { 16 | HttpClient http.Client 17 | } 18 | 19 | var defaultConfig Config = Config{} 20 | var defaultConfigLock sync.Mutex 21 | 22 | // SetDefaultConfig sets the configuration that will be used by the Download 23 | // function. 24 | func SetDefaultConfig(newConfig Config) { 25 | defaultConfigLock.Lock() 26 | defer defaultConfigLock.Unlock() 27 | defaultConfig = newConfig 28 | } 29 | 30 | // GetDefaultConfig returns a copy of the default configuration. The default 31 | // configuration can be changed using the SetDefaultConfig function. 32 | func GetDefaultConfig() Config { 33 | defaultConfigLock.Lock() 34 | defer defaultConfigLock.Unlock() 35 | 36 | // deep copy struct 37 | return defaultConfig 38 | } 39 | -------------------------------------------------------------------------------- /go-downloader/downloader_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018 Cristian Maglie. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | // 6 | 7 | package downloader 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | "testing" 16 | "time" 17 | 18 | "github.com/stretchr/testify/require" 19 | ) 20 | 21 | func makeTmpFile(t *testing.T) string { 22 | tmp, err := ioutil.TempFile("", "") 23 | require.NoError(t, err) 24 | require.NoError(t, tmp.Close()) 25 | tmpFile := tmp.Name() 26 | require.NoError(t, os.Remove(tmpFile)) 27 | return tmpFile 28 | } 29 | 30 | func TestDownload(t *testing.T) { 31 | tmpFile := makeTmpFile(t) 32 | defer os.Remove(tmpFile) 33 | 34 | d, err := Download(tmpFile, "https://go.bug.st/test.txt") 35 | require.NoError(t, err) 36 | require.Equal(t, int64(0), d.Completed()) 37 | require.Equal(t, int64(8052), d.Size()) 38 | require.NoError(t, d.Run()) 39 | require.Equal(t, int64(8052), d.Completed()) 40 | require.Equal(t, int64(8052), d.Size()) 41 | 42 | file1, err := ioutil.ReadFile("testdata/test.txt") 43 | require.NoError(t, err) 44 | file2, err := ioutil.ReadFile(tmpFile) 45 | require.NoError(t, err) 46 | require.Equal(t, file1, file2) 47 | } 48 | 49 | func TestResume(t *testing.T) { 50 | tmpFile := makeTmpFile(t) 51 | defer os.Remove(tmpFile) 52 | 53 | part, err := ioutil.ReadFile("testdata/test.txt.part") 54 | require.NoError(t, err) 55 | err = ioutil.WriteFile(tmpFile, part, 0644) 56 | require.NoError(t, err) 57 | 58 | d, err := Download(tmpFile, "https://go.bug.st/test.txt") 59 | require.Equal(t, int64(3506), d.Completed()) 60 | require.Equal(t, int64(8052), d.Size()) 61 | require.NoError(t, err) 62 | require.NoError(t, d.Run()) 63 | require.Equal(t, int64(8052), d.Completed()) 64 | require.Equal(t, int64(8052), d.Size()) 65 | 66 | file1, err := ioutil.ReadFile("testdata/test.txt") 67 | require.NoError(t, err) 68 | file2, err := ioutil.ReadFile(tmpFile) 69 | require.NoError(t, err) 70 | require.Equal(t, file1, file2) 71 | } 72 | 73 | func TestNoResume(t *testing.T) { 74 | tmpFile := makeTmpFile(t) 75 | defer os.Remove(tmpFile) 76 | 77 | part, err := ioutil.ReadFile("testdata/test.txt.part") 78 | require.NoError(t, err) 79 | err = ioutil.WriteFile(tmpFile, part, 0644) 80 | require.NoError(t, err) 81 | 82 | d, err := Download(tmpFile, "https://go.bug.st/test.txt", NoResume) 83 | require.Equal(t, int64(0), d.Completed()) 84 | require.Equal(t, int64(8052), d.Size()) 85 | require.NoError(t, err) 86 | require.NoError(t, d.Run()) 87 | require.Equal(t, int64(8052), d.Completed()) 88 | require.Equal(t, int64(8052), d.Size()) 89 | 90 | file1, err := ioutil.ReadFile("testdata/test.txt") 91 | require.NoError(t, err) 92 | file2, err := ioutil.ReadFile(tmpFile) 93 | require.NoError(t, err) 94 | require.Equal(t, file1, file2) 95 | } 96 | 97 | func TestInvalidRequest(t *testing.T) { 98 | tmpFile := makeTmpFile(t) 99 | defer os.Remove(tmpFile) 100 | 101 | d, err := Download(tmpFile, "asd://go.bug.st/test.txt") 102 | require.Error(t, err) 103 | require.Nil(t, d) 104 | fmt.Println("ERROR:", err) 105 | 106 | d, err = Download(tmpFile, "://") 107 | require.Error(t, err) 108 | require.Nil(t, d) 109 | fmt.Println("ERROR:", err) 110 | } 111 | 112 | func TestRunAndPool(t *testing.T) { 113 | tmpFile := makeTmpFile(t) 114 | defer os.Remove(tmpFile) 115 | 116 | d, err := Download(tmpFile, "https://downloads.arduino.cc/cores/avr-1.6.20.tar.bz2") 117 | require.NoError(t, err) 118 | prevCurr := int64(0) 119 | callCount := 0 120 | callback := func(curr int64) { 121 | require.True(t, prevCurr <= curr) 122 | prevCurr = curr 123 | callCount++ 124 | } 125 | _ = d.RunAndPoll(callback, time.Millisecond) 126 | fmt.Printf("callback called %d times\n", callCount) 127 | require.True(t, callCount > 10) 128 | require.Equal(t, int64(4897949), d.Completed()) 129 | } 130 | 131 | func TestErrorOnFileOpening(t *testing.T) { 132 | tmpFile := makeTmpFile(t) 133 | defer os.Remove(tmpFile) 134 | 135 | require.NoError(t, ioutil.WriteFile(tmpFile, []byte{}, 0000)) 136 | d, err := Download(tmpFile, "http://go.bug.st/test.txt") 137 | require.Error(t, err) 138 | require.Nil(t, d) 139 | } 140 | 141 | type roundTripper struct { 142 | UserAgent string 143 | transport http.Transport 144 | } 145 | 146 | func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 147 | req.Header["User-Agent"] = []string{r.UserAgent} 148 | return r.transport.RoundTrip(req) 149 | } 150 | 151 | // TestApplyUserAgentHeaderUsingConfig test uses the https://postman-echo.com/ service 152 | func TestApplyUserAgentHeaderUsingConfig(t *testing.T) { 153 | type echoBody struct { 154 | Headers map[string]string 155 | } 156 | 157 | tmpFile := makeTmpFile(t) 158 | defer os.Remove(tmpFile) 159 | 160 | httpClient := http.Client{ 161 | Transport: &roundTripper{UserAgent: "go-downloader / 0.0.0-test"}, 162 | } 163 | config := Config{ 164 | HttpClient: httpClient, 165 | } 166 | 167 | d, err := DownloadWithConfig(tmpFile, "https://postman-echo.com/headers", config) 168 | require.NoError(t, err) 169 | 170 | testEchoBody := echoBody{} 171 | body, err := ioutil.ReadAll(d.Resp.Body) 172 | require.NoError(t, err) 173 | err = json.Unmarshal(body, &testEchoBody) 174 | require.NoError(t, err) 175 | require.Equal(t, "go-downloader / 0.0.0-test", testEchoBody.Headers["user-agent"]) 176 | } 177 | -------------------------------------------------------------------------------- /go-downloader/go.mod: -------------------------------------------------------------------------------- 1 | module go.bug.st/downloader/v2 2 | 3 | go 1.12 4 | 5 | require github.com/stretchr/testify v1.3.0 6 | -------------------------------------------------------------------------------- /go-downloader/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 7 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 8 | -------------------------------------------------------------------------------- /go-downloader/testdata/test.txt: -------------------------------------------------------------------------------- 1 | Hey! You've found the test file! 2 | 3 | `...` ` 4 | ```..-:-.`````` `.`` 5 | ````.....-:/:......` `-::-` 6 | .-.....-:::///-.....`` `---:--` 7 | .://:///:::/++///:--.` `---.---. 8 | `..-/o+ooo+///+oo++/:-``` --...---.` 9 | `....--/osoosoo++oooy+:-.--` .......---. 10 | `:+:-::/+s+//:-.:/oss+/:--. `-..-....--.` ```` 11 | .//:///+o+:-...../oo+/:-:` `-..--:------.` `..----.` 12 | .--///++/-.::--../oo///- `.----:-:::////::.` `.......---. 13 | ./+o++++:-++//::++///:. `-::/:///::-/o/////:---..`` ``.......-----. 14 | .yhysss+/+//:-:/+o+/-. `-:::---/:.``::///////o++//::-.```............-.---:. 15 | /dddddso+///--:+ss+-` `.-:+oss+-.```..---...`.-++///++//::--............-----` 16 | /yddddhys+//:-://+//- .:/::/hNMMMm: .--..`` -///:://+++/:::----.--......---.` 17 | `:+syyssoo+//:----:--` `:osy:`:dMmNoy-..-.`` `.....-////////:::---............` 18 | `/+ooso+os///:..---.` .++syys/--/os+:://-. `-+yyyo:--:////////::---------...`.-:-.` 19 | .:oyhhyyys+/+/://+: .ohhs+/-:--o+/://oo+/:/:/shNNNdmh+---:://////::::---..--.``:ooo+:.` 20 | ./oyhy/:os//:/++//- .+yys+-``` `yo/+yss///+yo:ydmNd+hhh:-----:////:///::--..```-+osoo+:--` 21 | .:+syho//+//::////. `+hhys:.`````/s+:::-``.:o/..:ossss+-`.::---:////:::::-....:+sssooo+++/-.` 22 | .:///oo/:/-::///+:``:yhhhy+-----:os:.` `.::/-.-.........-++:-::::::///-...-:+syssooo+o++/:.` 23 | .+/+///::://+++//-``+hdddho//+o+oos/-.` `....---:/+++oooo+++ss+////::::-..--:/oyyysssoo++oo+:-` 24 | :++osso+/+o////::::..ohhhhyo++-..-/+/-......``..-/ssssso++++osyo++o+/-...:/+++sssssssooooooo+:. 25 | `+sssyys++/:--:-::+s:.:sdhyyo/--....-:/:---..-..`.-+sso+/::---/syso+++/:..:/+/+ssssssssoooo++/:. 26 | -/ossoo+++::-:-:/oyhs+ymmddhs+::--....-//-:-:-..```-/osyys+:--:+sysooo+/:-.:/+ossssossssoo+++/:` 27 | :/+++/////:---:++shdhdNMNmdhyo+/::------////o+:....-:ososyyo/:-:/oysoooo+:--:+sssssosssooo++++/-` 28 | :+oo+/:--::/::///shddNMMMNdhhyso+/:::::------::-----:/::/+sso+::/osso+//++::+osssssssooo++o+++:.` 29 | `:+ooo++++++/-../hmmmNNNMMMmdhhysso++/://:::--:-:://::////+osyo+::+syso/::/:/+syyssssoooo+++++/:` 30 | `/+ooooo+/:-/osshdddNNNNNMMMmhhyyyyso++++oo++oo+o+ooso++o++osyyso+oosys+/://+syysssoooooo++++//:` 31 | `/+++++osyyyddddhhyhmNNNMMMMNmdhhmdhyssyssssoossooooossoossooossooosssso///+osssssssoooo++++++/-` 32 | /yhyhdddmmmmmddhsohNNNMMMMMNNmmdmddhhyssssso+osossoooosossssso++++ossssso+oosyyyssoo+++++++++/. 33 | :shdddddhddhhyyo+sdNNNMNNNNNNmmmmmmhhddhyhhoo+sssss++//+osssoo++++oosyysoooosyyssoo++o++o++++-` 34 | ./osyyyyyysssoo++hNNNNMNNNNNNmmdmmmdmmmddddhysyho/oo:/+osyyyhhhyysyysyyysossyssssoo+++//+++/:. 35 | `-/+ooo+oo++o+++sdNmNNNNmmmmmmddmNNmdddddmmNmNNNNmmmdhdhmmdmmddddhyyysssssyyyyssoo++//+++//:-` 36 | `-/+++oo//+++++ohmmNmmmmdddddmmNNmdhhhhhddhddmmNmNNmmdhddhhhyyyyysssosssyyyyssoo+++/+++++:.` 37 | `.:++o++o+//+oohmmmddddddddmddNmdhyyyyhhhyshhdhhdddhyyyyhyyyysssssssssyyyyyyssso+++++//:.` 38 | `-/+ooo++ssssyddhhyyhhhhddhddhyysss+oosssyyyhhsssossssosssssssssyhhhhhhyyyssoo+++oo+/-` 39 | `.:+sooossysyhhyhhyyyhhhhyyysso//:----::+oyyys++++ossooooossssyhhhhhhyyssssoooo+++/-` 40 | `:oshysyyyyyyyhhhyhhyhyysoo///::--.....--+syys+///+ooo+oossyyhhhhhhyyssssoooooo+/:.` 41 | `:shddhhhhyyyyyyyyssso+++++osso++/:-:-...-:+sso/://++osooyhhhhhhhyyssssooo++/////-. 42 | `-ohdmdddyysoo+oooo/:::---:/oosssoo+///:---:oso/::+++oyyyhhddhhhhysoossso++++/:::-` 43 | `./yhyyyso++////+o++++++/////++ossooso+++/--/sso//+oosyhddddhhhyyysooooo//::/-..--. 44 | ``-:++++++ooo+++++++///osso//+o+/+oss+oo++oo:-+so+/oyyhdddmddhhysssooo+++::-../...-. 45 | ``.--:::::///++++++/+//::+osso/+oo+/+oso////+++::ooooyhddmmmmddhyso+oooo+/----:.//-...` 46 | ``.--:::///:::///////////:::/+ys/:+s+/+oss/--:/oo//osyyhmmmmdddhhyso++++ssso+:/+/:++:..-` 47 | ```.-----::/+so/::://://::::::::+s+-:++//oos+-.-:++//oyyyhyhhhyysoo+//::----::::::-:-:-..` 48 | ````...---..:/:++:....::/:-...---:+o//://:-:///:-..//::////////:::---....`.```.`````````` 49 | `.-...-::+:..--.-::--...-.---.``.-/oo:-----......``````````..`````` 50 | ` `---/-:/.`.-.`-/:-/.`..``..:--.-//:.````` 51 | .:/::oo-`..:..-+/+:```````.::-.````` 52 | ``..-/:...--.`--::.```````..``` 53 | ``````` ``````` 54 | 55 | MEAOOOWWWWWWWW 56 | 57 | -------------------------------------------------------------------------------- /go-downloader/testdata/test.txt.part: -------------------------------------------------------------------------------- 1 | Hey! You've found the test file! 2 | 3 | `...` ` 4 | ```..-:-.`````` `.`` 5 | ````.....-:/:......` `-::-` 6 | .-.....-:::///-.....`` `---:--` 7 | .://:///:::/++///:--.` `---.---. 8 | `..-/o+ooo+///+oo++/:-``` --...---.` 9 | `....--/osoosoo++oooy+:-.--` .......---. 10 | `:+:-::/+s+//:-.:/oss+/:--. `-..-....--.` ```` 11 | .//:///+o+:-...../oo+/:-:` `-..--:------.` `..----.` 12 | .--///++/-.::--../oo///- `.----:-:::////::.` `.......---. 13 | ./+o++++:-++//::++///:. `-::/:///::-/o/////:---..`` ``.......-----. 14 | .yhysss+/+//:-:/+o+/-. `-:::---/:.``::///////o++//::-.```............-.---:. 15 | /dddddso+///--:+ss+-` `.-:+oss+-.```..---...`.-++///++//::--............-----` 16 | /yddddhys+//:-://+//- .:/::/hNMMMm: .--..`` -///:://+++/:::----.--......---.` 17 | `:+syyssoo+//:----:--` `:osy:`:dMmNoy-..-.`` `.....-////////:::---............` 18 | `/+ooso+os///:..---.` .++syys/--/os+:://-. `-+yyyo:--:////////::---------...`.-:-.` 19 | .:oyhhyyys+/+/://+: .ohhs+/-:--o+/://oo+/:/:/shNNNdmh+---:://////::::---..--.``:ooo+:.` 20 | ./oyhy/:os//:/++//- .+yys+-``` `yo/+yss///+yo:ydmNd+hhh:-----:////:///::--..```-+osoo+:--` 21 | .:+syho//+//::////. `+hhys:.`````/s+:::-``.:o/..:ossss+-`.::---:////:::::-....:+sssooo+++/-.` 22 | .:///oo/:/-::///+:``:yhhhy+-----:os:.` `.::/-.-.........-++:-::::::///-...-:+syssooo+o++/:.` 23 | .+/+///::://+++//-``+hdddho//+o+oos/-.` `....---:/+++oooo+++ss+////::::-..--:/oyyysssoo++oo+:-` 24 | :++osso+/+o////::::..ohhhhyo++-..-/+/-......``..-/ssssso++++osyo++o+/-...:/+++sssssssooooooo+:. 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ksrichard/apm 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/arduino/arduino-cli v0.0.0-20210413144851-088d4276190d 7 | github.com/manifoldco/promptui v0.8.0 8 | github.com/mitchellh/gox v1.0.1 // indirect 9 | github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 10 | github.com/spf13/cobra v1.1.3 11 | github.com/spf13/viper v1.7.1 // indirect 12 | google.golang.org/grpc v1.27.0 13 | ) 14 | 15 | replace go.bug.st/downloader/v2 => ./go-downloader/ 16 | -------------------------------------------------------------------------------- /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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 17 | github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= 18 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 19 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 20 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= 21 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 22 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 23 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 24 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 25 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 26 | github.com/arduino/arduino-cli v0.0.0-20210413144851-088d4276190d h1:M/AWWjUQiY7SMoV2Ha0Sq+BzkIuBmusNe+l/XcRZsJA= 27 | github.com/arduino/arduino-cli v0.0.0-20210413144851-088d4276190d/go.mod h1:5dWroFPvaWuBNVuYMV0X8osqIhyUG5otRgJlRHN831E= 28 | github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c h1:agh2JT96G8egU7FEb13L4dq3fnCN7lxXhJ86t69+W7s= 29 | github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c/go.mod h1:HK7SpkEax/3P+0w78iRQx1sz1vCDYYw9RXwHjQTB5i8= 30 | github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= 31 | github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= 32 | github.com/arduino/go-paths-helper v1.4.0 h1:ilnseAdxmN1bFnLxxXHRtcdmt9jBf3O4jtYfWfqule4= 33 | github.com/arduino/go-paths-helper v1.4.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= 34 | github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o= 35 | github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= 36 | github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4= 37 | github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ= 38 | github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b h1:3PjgYG5gVPA7cipp7vIR2lF96KkEJIFBJ+ANnuv6J20= 39 | github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8= 40 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 41 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 42 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 43 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 44 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 45 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 46 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 47 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 48 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 49 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 50 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 51 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 52 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 53 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 54 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 55 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 56 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 57 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 58 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 59 | github.com/cmaglie/go.rice v1.0.3 h1:ZBLmBdQp6ejc+n8eMNH0uuRSKkg6kKe6ORjXKnyHBYw= 60 | github.com/cmaglie/go.rice v1.0.3/go.mod h1:AF3bOWkvdOpp8/S3UL8qbQ4N7DiISIbJtj54GWFPAsc= 61 | github.com/cmaglie/pb v1.0.27 h1:ynGj8vBXR+dtj4B7Q/W/qGt31771Ux5iFfRQBnwdQiA= 62 | github.com/cmaglie/pb v1.0.27/go.mod h1:GilkKZMXYjBA4NxItWFfO+lwkp59PLHQ+IOW/b/kmZI= 63 | github.com/codeclysm/cc v1.2.2 h1:1ChS4EvWTjw6bH2sd6QiMcmih0itVVrWdh9MmOliX/I= 64 | github.com/codeclysm/cc v1.2.2/go.mod h1:XtW4ArCNgQwFphcRGG9+sPX5WM1J6/u0gMy5ZdV3obA= 65 | github.com/codeclysm/extract/v3 v3.0.2 h1:sB4LcE3Php7LkhZwN0n2p8GCwZe92PEQutdbGURf5xc= 66 | github.com/codeclysm/extract/v3 v3.0.2/go.mod h1:NKsw+hqua9H+Rlwy/w/3Qgt9jDonYEgB6wJu+25eOKw= 67 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 68 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 69 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 70 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 71 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 72 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 73 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 74 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 75 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 76 | github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= 77 | github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= 78 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 79 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 80 | github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= 81 | github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= 82 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 83 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 84 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 85 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 86 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 87 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 88 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 89 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 90 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 91 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 92 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 93 | github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2 h1:C6sOwknxwWfLBEQ91zhmptlfxf7pVEs5s6wOnDxNpS4= 94 | github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2/go.mod h1:c7sGIpDbBo0JZZ1tKyC1p5smWf8QcUjK4bFtZjHAecg= 95 | github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5 h1:R8jFW6G/bjoXjWPFrEfw9G5YQDlYhwV4AC+Eonu6wmk= 96 | github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5/go.mod h1:BEUDl7FG1cc76sM0J0x8dqr6RhiL4uqvk6oFkwuNyuM= 97 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= 98 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 99 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 100 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 101 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 102 | github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 103 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 104 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 105 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 106 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 107 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 108 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 109 | github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= 110 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 111 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 112 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 113 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 114 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 115 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 116 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 117 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 118 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 119 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 120 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 121 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 122 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 123 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 124 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 125 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 126 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 127 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 128 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 129 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 130 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 131 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 132 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 133 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 134 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 135 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 136 | github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= 137 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 138 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 139 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 140 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 141 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 142 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 143 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 144 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 145 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 146 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 147 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 148 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 149 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 150 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 151 | github.com/h2non/filetype v1.0.6/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= 152 | github.com/h2non/filetype v1.0.8 h1:le8gpf+FQA0/DlDABbtisA1KiTS0Xi+YSC/E8yY3Y14= 153 | github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= 154 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 155 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 156 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 157 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 158 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 159 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 160 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 161 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 162 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 163 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 164 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 165 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 166 | github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= 167 | github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 168 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 169 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 170 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 171 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 172 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 173 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 174 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 175 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 176 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 177 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 178 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 179 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 180 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 181 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 182 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 183 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 184 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 185 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 186 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 187 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= 188 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= 189 | github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= 190 | github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= 191 | github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= 192 | github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= 193 | github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= 194 | github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= 195 | github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= 196 | github.com/juju/retry v0.0.0-20160928201858-1998d01ba1c3/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= 197 | github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0 h1:+WWUkhnTjV6RNOxkcwk79qrjeyHEHvBzlneueBsatX4= 198 | github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbNBraRLZEhoQwFLMrjK8PSlO4D3nDjKYXo= 199 | github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= 200 | github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= 201 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 202 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= 203 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 204 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 205 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 206 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 207 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 208 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 209 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 210 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 211 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 212 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 213 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 214 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 215 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 216 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 217 | github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1aUQZ1tys= 218 | github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us= 219 | github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= 220 | github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 221 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 222 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 223 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 224 | github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= 225 | github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= 226 | github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84 h1:hyAgCuG5nqTMDeUD8KZs7HSPs6KprPgPP8QmGV8nyvk= 227 | github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M= 228 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 229 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 230 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 231 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 232 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 233 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 234 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 235 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 236 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 237 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 238 | github.com/mdlayher/genetlink v0.0.0-20190313224034-60417448a851/go.mod h1:EsbsAEUEs15qC1cosAwxgCWV0Qhd8TmkxnA9Kw1Vhl4= 239 | github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= 240 | github.com/mdlayher/taskstats v0.0.0-20190313225729-7cbba52ee072/go.mod h1:sGdS7A6CAETR53zkdjGkgoFlh1vSm7MtX+i8XfEsTMA= 241 | github.com/miekg/dns v1.0.5/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 242 | github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= 243 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 244 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 245 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 246 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 247 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 248 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 249 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 250 | github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= 251 | github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= 252 | github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= 253 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 254 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 255 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 256 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 257 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 258 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 259 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 260 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 261 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 262 | github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= 263 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 264 | github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 h1:Cvfd2dOlXIPTeEkOT/h8PyK4phBngOM4at9/jlgy7d4= 265 | github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228/go.mod h1:MGuVJ1+5TX1SCoO2Sx0eAnjpdRytYla2uC1YIZfkC9c= 266 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 267 | github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= 268 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 269 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 270 | github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= 271 | github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 272 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 273 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 274 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 275 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 276 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 277 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 278 | github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583 h1:ogHi8YLNeIxABOaH6UgtbwkODheuAK+ErP8gWXYQVj0= 279 | github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583/go.mod h1:sFPiU/UgDcsQVu3vkqpZLCXWFwUoQRpHGu9ATihPAl0= 280 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 281 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 282 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 283 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 284 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 285 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 286 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 287 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 288 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 289 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 290 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 291 | github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= 292 | github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= 293 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 294 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 295 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 296 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 297 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 298 | github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= 299 | github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= 300 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 301 | github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ= 302 | github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M= 303 | github.com/segmentio/objconv v1.0.1 h1:QjfLzwriJj40JibCV3MGSEiAoXixbp4ybhwfTB8RXOM= 304 | github.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys= 305 | github.com/segmentio/stats/v4 v4.5.3 h1:Y/DSUWZ4c8ICgqJ9rQohzKvGqGWbLPWad5zmxVoKN+Y= 306 | github.com/segmentio/stats/v4 v4.5.3/go.mod h1:LsaahUJR7iiSs8mnkvQvdQ/RLHAS5adGLxuntg0ydGo= 307 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 308 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 309 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 310 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 311 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 312 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 313 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 314 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 315 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 316 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 317 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 318 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 319 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 320 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 321 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 322 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 323 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 324 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 325 | github.com/spf13/cobra v1.0.1-0.20200710201246-675ae5f5a98c/go.mod h1:aeNIJzz/GSSVlS+gpCpQWZ83BKbsoW57mr90+YthtkQ= 326 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 327 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 328 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 329 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 330 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 331 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 332 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 333 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 334 | github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= 335 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 336 | github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= 337 | github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 338 | github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 339 | github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= 340 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 341 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 342 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 343 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 344 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 345 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 346 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 347 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 348 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 349 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 350 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 351 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 352 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 353 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 354 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 355 | github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= 356 | github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= 357 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 358 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 359 | go.bug.st/cleanup v1.0.0 h1:XVj1HZxkBXeq3gMT7ijWUpHyIC1j8XAoNSyQ06CskgA= 360 | go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk= 361 | go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18 h1:F1qxtaFuewctYc/SsHRn+Q7Dtwi+yJGPgVq8YLtQz98= 362 | go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18/go.mod h1:Cx1VqMtEhE9pIkEyUj3LVVVPkv89dgW8aCKrRPDR/uE= 363 | go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY= 364 | go.bug.st/serial v1.1.2/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY= 365 | go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI= 366 | go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= 367 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 368 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 369 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 370 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 371 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 372 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 373 | golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 374 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 375 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 376 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 377 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 378 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 379 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 380 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 381 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y= 382 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 383 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 384 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 385 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 386 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 387 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 388 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 389 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 390 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 391 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 392 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 393 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 394 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 395 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 396 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 397 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 398 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 399 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 400 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 401 | golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 402 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 403 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 404 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 405 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 406 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 407 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 408 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 409 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 410 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 411 | golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 412 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 413 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 414 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 415 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 416 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 417 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 418 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 419 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 420 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 421 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 422 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 423 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 424 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 425 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 426 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 427 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 428 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= 429 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 430 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 431 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 432 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 433 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 434 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 435 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 436 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 437 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 438 | golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 439 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 440 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 441 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 442 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 443 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 444 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 445 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 446 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= 447 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 448 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 449 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 450 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= 451 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 452 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 453 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 454 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 455 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 456 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 457 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 458 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 459 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 460 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 461 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 462 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 463 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 464 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 465 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 466 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 467 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 468 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 469 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 470 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 471 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 472 | golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= 473 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 474 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 475 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 476 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 477 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 478 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 479 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 480 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 481 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 482 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 483 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 484 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 485 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 486 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 487 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 488 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 489 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 490 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 491 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 492 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 493 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 494 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 495 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 496 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 497 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 498 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 499 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 500 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 501 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 502 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 503 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 504 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 505 | google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= 506 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 507 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 508 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 509 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 510 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 511 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 512 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 513 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 514 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 515 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 516 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 517 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 518 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 519 | gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 520 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 521 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 522 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 523 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 524 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 525 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 526 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 527 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 528 | gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 529 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= 530 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 531 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 532 | gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= 533 | gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 534 | gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= 535 | gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= 536 | gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= 537 | gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= 538 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 539 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 540 | gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 541 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 542 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 543 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 544 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 545 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 546 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 547 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 548 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 549 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 550 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 551 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 552 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 553 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 554 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 555 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 556 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | if ! command -v curl &> /dev/null 2 | then 3 | echo "'curl' could not be found, please install it!" 4 | exit 5 | fi 6 | 7 | if ! command -v jq &> /dev/null 8 | then 9 | echo "'jq' could not be found, please install it!" 10 | exit 11 | fi 12 | 13 | os="" 14 | target="" 15 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then 16 | os="linux" 17 | target="/usr/local/bin/apm" 18 | elif [[ "$OSTYPE" == "darwin"* ]]; then 19 | os="darwin" 20 | target="/usr/local/bin/apm" 21 | else 22 | echo "Not supported OS!" 23 | exit 1 24 | fi 25 | echo "Installing/Updating apm to ${target}..." 26 | rm -rf ${target} 27 | curl -L $(curl -s https://api.github.com/repos/ksrichard/apm/releases/latest | jq -r ".assets[] | select(.name | test(\"${os}_amd64\")) | .browser_download_url") --output ${target} 28 | chmod +x ${target} 29 | echo "apm is ready to use!" -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Richard Klavora 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import "github.com/ksrichard/apm/cmd" 19 | 20 | func main() { 21 | cmd.Execute() 22 | } 23 | -------------------------------------------------------------------------------- /project/project.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "github.com/ksrichard/apm/util" 8 | "github.com/spf13/cobra" 9 | "io/ioutil" 10 | "os" 11 | ) 12 | 13 | var ProjectDetailsFileName string = "apm.json" 14 | 15 | func GetProjectDir(cmd *cobra.Command) (string, error) { 16 | return cmd.Flags().GetString("project-dir") 17 | } 18 | 19 | func GetProjectDetails(cmd *cobra.Command) (*ProjectDetails, error) { 20 | var result ProjectDetails 21 | projectDir, err := GetProjectDir(cmd) 22 | if err != nil { 23 | return nil, err 24 | } 25 | jsonFilePath := fmt.Sprintf("%s/%s", projectDir, ProjectDetailsFileName) 26 | if util.FileExists(jsonFilePath) { 27 | jsonFile, err := ioutil.ReadFile(jsonFilePath) 28 | if err != nil { 29 | return nil, err 30 | } 31 | err = json.Unmarshal(jsonFile, &result) 32 | if err != nil { 33 | return nil, err 34 | } 35 | } else { 36 | return nil, errors.New(fmt.Sprintf("'%s' not found!", jsonFilePath)) 37 | } 38 | return &result, nil 39 | } 40 | 41 | func UpdateProjectDetails(cmd *cobra.Command, details *ProjectDetails) error { 42 | projectDir, err := GetProjectDir(cmd) 43 | if err != nil { 44 | return err 45 | } 46 | jsonFilePath := fmt.Sprintf("%s/%s", projectDir, ProjectDetailsFileName) 47 | if util.FileExists(jsonFilePath) { 48 | fileData, err := json.MarshalIndent(details, "", " ") 49 | if err != nil { 50 | return err 51 | } 52 | err = os.Remove(jsonFilePath) 53 | if err != nil { 54 | return err 55 | } 56 | err = ioutil.WriteFile(jsonFilePath, fileData, os.ModePerm) 57 | if err != nil { 58 | return err 59 | } 60 | } else { 61 | return errors.New(fmt.Sprintf("'%s' not found!", jsonFilePath)) 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /project/structs.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | type ProjectDetails struct { 4 | Board *ProjectBoard `json:"board"` 5 | Dependencies []ProjectDependency `json:"dependencies"` 6 | } 7 | 8 | type ProjectBoard struct { 9 | Package string `json:"package,omitempty"` 10 | Architecture string `json:"architecture,omitempty"` 11 | Version string `json:"version,omitempty"` 12 | BoardManagerUrl string `json:"board_manager_url,omitempty"` 13 | } 14 | 15 | type ProjectDependency struct { 16 | Library string `json:"library,omitempty"` 17 | Version string `json:"version,omitempty"` 18 | Git string `json:"git,omitempty"` 19 | Zip string `json:"zip,omitempty"` 20 | } 21 | -------------------------------------------------------------------------------- /service/library.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/ksrichard/apm/arduino" 7 | "github.com/ksrichard/apm/util" 8 | "strings" 9 | ) 10 | 11 | func SelectLibrary(cli *arduino.ArduinoCli) (string, string, error) { 12 | libName := "" 13 | libVersion := "" 14 | 15 | selectedLibName, err := util.AutoCompleteInput("Library search", "Search again...", "Cancel", 16 | func(query string) (m map[string]interface{}, err error) { 17 | result := make(map[string]interface{}) 18 | libs, err := cli.SearchLibrary(query) 19 | if err != nil { 20 | return nil, err 21 | } 22 | for _, lib := range libs { 23 | libTitle := fmt.Sprintf("%s (%s) - %s", lib.Name, lib.Latest.Version, lib.Latest.Author) 24 | result[libTitle] = lib.Name 25 | } 26 | return result, nil 27 | }) 28 | if err != nil { 29 | return "", "", err 30 | } 31 | libName = selectedLibName.(string) 32 | 33 | // select library version 34 | libVersionOptions := make(map[string]interface{}) 35 | libs, err := cli.SearchLibrary(libName) 36 | if err != nil { 37 | return "", "", err 38 | } 39 | for _, lib := range libs { 40 | if lib.Name == libName { 41 | for _, release := range lib.Releases { 42 | libVersionOptions[release.Version] = release.Version 43 | } 44 | break 45 | } 46 | } 47 | selectedLibVersion, err := util.Select("Select library version", []string{"latest"}, libVersionOptions) 48 | if err != nil { 49 | return "", "", err 50 | } 51 | libVersion = selectedLibVersion.(string) 52 | 53 | return libName, libVersion, nil 54 | } 55 | 56 | func CheckIfLibraryValid(cli *arduino.ArduinoCli, libName string, libVersion string, maxHints int) (string, error) { 57 | libs, err := cli.SearchLibrary(libName) 58 | if err != nil { 59 | return "", err 60 | } 61 | 62 | hints := []string{} 63 | libAllVersions := []string{"latest"} 64 | 65 | finalLibName := libName 66 | foundLibName := false 67 | foundLibVersion := false 68 | if strings.ToLower(libVersion) == "latest" { 69 | foundLibVersion = true 70 | } 71 | for _, lib := range libs { 72 | if strings.ToLower(lib.Name) == strings.ToLower(libName) { 73 | foundLibName = true 74 | finalLibName = lib.Name 75 | for _, release := range lib.Releases { 76 | libAllVersions = append(libAllVersions, release.Version) 77 | if release.Version == libVersion { 78 | foundLibVersion = true 79 | } 80 | } 81 | } 82 | 83 | // add hint if possible 84 | if (strings.Contains(strings.ToLower(lib.Name), strings.ToLower(libName)) || 85 | strings.Contains(lib.Latest.Sentence, libName)) && len(hints) <= maxHints { 86 | hints = append(hints, fmt.Sprintf("%s@%s", lib.Name, lib.Latest.Version)) 87 | } 88 | } 89 | 90 | if !foundLibName { 91 | fmt.Printf("Unknown library name '%s'!\n", libName) 92 | if len(hints) > 0 { 93 | fmt.Printf("You can may check also:\n %s \n", strings.Join(hints, "\n")) 94 | } 95 | return "", errors.New(fmt.Sprintf("Unknown library name '%s'!", libName)) 96 | } 97 | 98 | if !foundLibVersion { 99 | fmt.Printf("Unknown library version '%s'!\n", libVersion) 100 | fmt.Printf("You can use the following versions:\n %s", strings.Join(libAllVersions, "\n")) 101 | return "", errors.New(fmt.Sprintf("Unknown library version '%s'!", libVersion)) 102 | } 103 | 104 | return finalLibName, nil 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /util/cli.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/manifoldco/promptui" 7 | "strings" 8 | ) 9 | 10 | func Select(label string, initialItems []string, items map[string]interface{}) (interface{}, error) { 11 | var promptItems = initialItems 12 | for k, _ := range items { 13 | promptItems = append(promptItems, k) 14 | } 15 | 16 | prompt := promptui.Select{ 17 | Label: label, 18 | Items: promptItems, 19 | } 20 | 21 | _, promptResult, err := prompt.Run() 22 | 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | var result interface{} 28 | for k, v := range items { 29 | if k == promptResult { 30 | result = v 31 | } 32 | } 33 | 34 | for _, item := range initialItems { 35 | if item == promptResult { 36 | result = item 37 | } 38 | } 39 | 40 | return result, nil 41 | } 42 | 43 | func AutoCompleteInput(title string, searchAgainStr string, cancelStr string, results func(query string) (map[string]interface{}, error)) (interface{}, error) { 44 | var selectedOption interface{} 45 | validate := func(input string) error { 46 | if strings.TrimSpace(input) == "" { 47 | return errors.New("Input required!") 48 | } 49 | return nil 50 | } 51 | for { 52 | // search 53 | prompt := promptui.Prompt{ 54 | Label: title, 55 | Validate: validate, 56 | } 57 | 58 | query, err := prompt.Run() 59 | if err != nil { 60 | fmt.Println(err) 61 | continue 62 | } 63 | 64 | // select from result list 65 | items := make(map[string]interface{}) 66 | res, err := results(query) 67 | if err != nil { 68 | fmt.Println(err) 69 | continue 70 | } 71 | if res != nil && len(res) == 0 { 72 | fmt.Printf("No library found for search query '%s'!\n", query) 73 | } 74 | for k, v := range res { 75 | items[k] = v 76 | } 77 | 78 | result, err := Select(title, []string{cancelStr, searchAgainStr}, items) 79 | 80 | if result == cancelStr { 81 | return "", errors.New("cancelled") 82 | } 83 | 84 | if result != searchAgainStr { 85 | selectedOption = result 86 | break 87 | } 88 | } 89 | 90 | return selectedOption, nil 91 | } 92 | -------------------------------------------------------------------------------- /util/filesystem.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "os" 4 | 5 | func FileExists(filename string) bool { 6 | info, err := os.Stat(filename) 7 | if os.IsNotExist(err) { 8 | return false 9 | } 10 | return !info.IsDir() 11 | } 12 | --------------------------------------------------------------------------------