From 4d2af9ae20a9c2d0020439824012df5377707f53 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Sun, 8 Aug 2021 16:34:22 -0500 Subject: [PATCH] Initial commit Signed-off-by: jolheiser --- .drone.yml | 41 ++++++++++++++ .gitignore | 2 + LICENSE | 21 +++++++ README.md | 9 +++ cmd/godl/godl.go | 144 +++++++++++++++++++++++++++++++++++++++++++++++ dl.go | 49 ++++++++++++++++ dl_test.go | 34 +++++++++++ go.mod | 5 ++ go.sum | 31 ++++++++++ 9 files changed, 336 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/godl/godl.go create mode 100644 dl.go create mode 100644 dl_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..1b04fa8 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,41 @@ +--- +kind: pipeline +name: compliance +trigger: + event: + - pull_request +steps: + - name: test + pull: always + image: golang:1.16 + commands: + - go test -race ./... + - name: check + pull: always + image: golang:1.16 + commands: + - go vet ./... + +--- +kind: pipeline +name: release +trigger: + event: + - push + branch: + - main +steps: + - name: build + pull: always + image: golang:1.16 + commands: + - go build git.jojodev.com/golang/dl/cmd/godl + - name: gitea-release + pull: always + image: jolheiser/drone-gitea-main:latest + settings: + token: + from_secret: gitea_token + base: https://git.jojodev.com + files: + - "godl" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c949ff7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +/godl* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c2a7c4d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 John Olheiser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..db21ea1 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# dl + +`https://golang.org/dl/?mode=json` + +**NOTE:** This URL may not remain permanent, it is simply usable at the time of writing this library. + +## License + +[MIT](LICENSE) \ No newline at end of file diff --git a/cmd/godl/godl.go b/cmd/godl/godl.go new file mode 100644 index 0000000..96b7dd4 --- /dev/null +++ b/cmd/godl/godl.go @@ -0,0 +1,144 @@ +package main + +import ( + "context" + "flag" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "git.jojodev.com/golang/dl" + + "github.com/schollz/progressbar/v3" +) + +func main() { + installFlag := flag.Bool("install", false, "Install/Update Go (Linux only)") + versionFlag := flag.Bool("version", false, "Only print version") + outFlag := flag.String("out", ".", "Where to put the downloaded file") + flag.Parse() + + latest, err := latestVersion() + if err != nil { + fmt.Println(err) + return + } + + if *versionFlag { + fmt.Println(latest.Version) + return + } + + outPath, err := download(latest.Filename, *outFlag) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(outPath) + + if !*installFlag { + return + } + + if !strings.EqualFold(runtime.GOOS, "linux") { + fmt.Println("Install option is only for Linux. Mac and Windows users should run the downloaded installer.") + return + } + + if os.Geteuid() != 0 { + fmt.Println(`This command must be run as root to perform the install. +Alternatively, you can run the following command manually, which is taken from https://golang.org/doc/install#install + +rm -rf /usr/local/go && tar -C /usr/local -xzf ` + outPath) + return + } + + fmt.Println("removing local installation") + rm := exec.Command("rm", "-rf", "/usr/local/go") + if err := rm.Run(); err != nil { + fmt.Printf("could not remove local go installation: %v\n", err) + return + } + + fmt.Println("extracting new installation") + tar := exec.Command("tar", "-C", "/usr/local", "-xzf", outPath) + if err := tar.Run(); err != nil { + fmt.Printf("could not extract new installation: %v\n", err) + return + } + + rmDl := exec.Command("rm", outPath) + if err := rmDl.Run(); err != nil { + fmt.Printf("could not remove downloaded file: %v\n", err) + return + } + + goVersion := exec.Command("/usr/local/go/bin/go", "version") + out, err := goVersion.Output() + if err != nil { + fmt.Printf("could not check go version: %v\n", err) + return + } + + fmt.Printf("Successfully installed: %s\n", string(out)) +} + +func latestVersion() (*dl.File, error) { + versions, err := dl.Versions(context.Background()) + if err != nil { + return nil, fmt.Errorf("could not get latest Go version(s): %v", err) + } + if len(versions) < 1 { + return nil, fmt.Errorf("no latest Go version(s) found: %v", err) + } + + var version dl.File + for _, file := range versions[0].Files { + if strings.EqualFold(file.OS, runtime.GOOS) && strings.EqualFold(file.Arch, runtime.GOARCH) { + version = file + break + } + } + if version.Filename == "" { + return nil, fmt.Errorf("could not find a download for OS = %s | Arch = %s", runtime.GOOS, runtime.GOARCH) + } + + return &version, nil +} + +func download(filename, out string) (string, error) { + outPath := filepath.Join(out, filename) + outFile, err := os.Create(outPath) + if err != nil { + return outPath, fmt.Errorf("could not create file for download: %v", err) + } + defer outFile.Close() + + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://golang.org/dl/%s", filename), nil) + if err != nil { + return outPath, fmt.Errorf("could not create request: %v", err) + } + req.Header.Set("User-Agent", "git.jojodev.com/golang/dl/cmd/godl") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return outPath, fmt.Errorf("could not download file: %v", err) + } + defer res.Body.Close() + + bar := progressbar.DefaultBytes( + res.ContentLength, + fmt.Sprintf("downloading %s", filename), + ) + + if _, err := io.Copy(io.MultiWriter(outFile, bar), res.Body); err != nil { + return outPath, err + } + + return outPath, nil +} diff --git a/dl.go b/dl.go new file mode 100644 index 0000000..4a485e7 --- /dev/null +++ b/dl.go @@ -0,0 +1,49 @@ +package dl + +import ( + "context" + "encoding/json" + "net/http" +) + +const dlURL = "https://golang.org/dl/?mode=json" + +// Version is a Go version +type Version struct { + Version string `json:"version"` + Stable bool `json:"stable"` + Files []File `json:"files"` +} + +// File is a dl file +type File struct { + Filename string `json:"filename"` + OS string `json:"os"` + Arch string `json:"arch"` + Version string `json:"version"` + Sha256 string `json:"sha256"` + Size int `json:"size"` + Kind string `json:"kind"` +} + +// Versions gets the latest Version(s) from dl +func Versions(ctx context.Context) ([]Version, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, dlURL, nil) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", "git.jojodev.com/golang/dl") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var versions []Version + if err := json.NewDecoder(res.Body).Decode(&versions); err != nil { + return nil, err + } + + return versions, nil +} diff --git a/dl_test.go b/dl_test.go new file mode 100644 index 0000000..bcab308 --- /dev/null +++ b/dl_test.go @@ -0,0 +1,34 @@ +package dl + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +var dlResp = []byte(`[{"version":"go1.16.7","stable":true,"files":[{"filename":"go1.16.7.src.tar.gz","os":"","arch":"","version":"go1.16.7","sha256":"1a9f2894d3d878729f7045072f30becebe243524cf2fce4e0a7b248b1e0654ac","size":20922206,"kind":"source"},{"filename":"go1.16.7.darwin-amd64.tar.gz","os":"darwin","arch":"amd64","version":"go1.16.7","sha256":"8018bf556e833912d455fab7ea279caa542239b6675c6b3861e9002380c70080","size":130203260,"kind":"archive"},{"filename":"go1.16.7.darwin-amd64.pkg","os":"darwin","arch":"amd64","version":"go1.16.7","sha256":"24e95aa60f516cfc34139cfd1192efe97724ea62ab36ad9404df36b926b1f879","size":130588410,"kind":"installer"},{"filename":"go1.16.7.darwin-arm64.tar.gz","os":"darwin","arch":"arm64","version":"go1.16.7","sha256":"7721706560d6a17b80b1f68efc0ebef27028bd51547127362ae0c0dac287b24b","size":125703153,"kind":"archive"},{"filename":"go1.16.7.darwin-arm64.pkg","os":"darwin","arch":"arm64","version":"go1.16.7","sha256":"c0924334dec1b39226af6ac8c63473b4f9e7689bd9837fdd5e1508d9a831fae1","size":126064386,"kind":"installer"},{"filename":"go1.16.7.freebsd-386.tar.gz","os":"freebsd","arch":"386","version":"go1.16.7","sha256":"09d2db7b6e8636cce9af249d75ffaaf5f1fda7042725f46e43e8c3e9e012da4f","size":102930482,"kind":"archive"},{"filename":"go1.16.7.freebsd-amd64.tar.gz","os":"freebsd","arch":"amd64","version":"go1.16.7","sha256":"cf43ecac8a68c040354e8a45ba167ebc631091976ac370b6f1e444623bc77f37","size":129011611,"kind":"archive"},{"filename":"go1.16.7.linux-386.tar.gz","os":"linux","arch":"386","version":"go1.16.7","sha256":"5c0c8891fa88993f2193fbc9dd5cca6c250c89aa8c12bbaa382b6ff38139bcc3","size":103084876,"kind":"archive"},{"filename":"go1.16.7.linux-amd64.tar.gz","os":"linux","arch":"amd64","version":"go1.16.7","sha256":"7fe7a73f55ba3e2285da36f8b085e5c0159e9564ef5f63ee0ed6b818ade8ef04","size":129034961,"kind":"archive"},{"filename":"go1.16.7.linux-arm64.tar.gz","os":"linux","arch":"arm64","version":"go1.16.7","sha256":"63d6b53ecbd2b05c1f0e9903c92042663f2f68afdbb67f4d0d12700156869bac","size":99603989,"kind":"archive"},{"filename":"go1.16.7.linux-armv6l.tar.gz","os":"linux","arch":"armv6l","version":"go1.16.7","sha256":"b2973ceeae234866368baf9469fb7b9444857e50dc785ba879d98a0aa208a12b","size":100281970,"kind":"archive"},{"filename":"go1.16.7.linux-ppc64le.tar.gz","os":"linux","arch":"ppc64le","version":"go1.16.7","sha256":"03e02b2ac6dc1601203f335385b9bbe15a55677066d9a1a1280b5fcfa6ec4738","size":98094639,"kind":"archive"},{"filename":"go1.16.7.linux-s390x.tar.gz","os":"linux","arch":"s390x","version":"go1.16.7","sha256":"5f691c9551710ebb17bbda04389944aa7332f42ab28f92516a69fbd7860e7e9f","size":103222266,"kind":"archive"},{"filename":"go1.16.7.windows-386.zip","os":"windows","arch":"386","version":"go1.16.7","sha256":"53b32b48ee2797acf2c5fa8f83c0d42406ae6b5df7e3a57ccbe94cf6272faeec","size":117859438,"kind":"archive"},{"filename":"go1.16.7.windows-386.msi","os":"windows","arch":"386","version":"go1.16.7","sha256":"a8e4ed71d5bc393884f981928c4ca74e95130a6d032a7ba3e414e8e2ac2b5710","size":102830080,"kind":"installer"},{"filename":"go1.16.7.windows-amd64.zip","os":"windows","arch":"amd64","version":"go1.16.7","sha256":"56b3a9024268f226f679c3a8ffb21f4214a75f84050b2c395b362ae2cc8e53e9","size":143997221,"kind":"archive"},{"filename":"go1.16.7.windows-amd64.msi","os":"windows","arch":"amd64","version":"go1.16.7","sha256":"75597307c368ae1f728f9e7a2c2d5814225664b5b8d915d34c0d4eb2d53d0831","size":124407808,"kind":"installer"}]},{"version":"go1.15.15","stable":true,"files":[{"filename":"go1.15.15.src.tar.gz","os":"","arch":"","version":"go1.15.15","sha256":"0662ae3813330280d5f1a97a2ee23bbdbe3a5a7cfa6001b24a9873a19a0dc7ec","size":23042945,"kind":"source"},{"filename":"go1.15.15.darwin-amd64.tar.gz","os":"darwin","arch":"amd64","version":"go1.15.15","sha256":"2f4c119524450ee94062a1ce7112fb88ce0fe4bb0303a302e002183a550c25c2","size":122412248,"kind":"archive"},{"filename":"go1.15.15.darwin-amd64.pkg","os":"darwin","arch":"amd64","version":"go1.15.15","sha256":"f0e1877902ca88001cb768ae0dd8e974e58084ea6e64fb13f46289f842ef53ff","size":122786419,"kind":"installer"},{"filename":"go1.15.15.freebsd-386.tar.gz","os":"freebsd","arch":"386","version":"go1.15.15","sha256":"7174078a53e330cf351dc20bed6682033f44066d8aed754139bcaef52e53c214","size":100310476,"kind":"archive"},{"filename":"go1.15.15.freebsd-amd64.tar.gz","os":"freebsd","arch":"amd64","version":"go1.15.15","sha256":"1f80a20419b2618182ef5b9615dd990b32b952d81b354b373c6fd304527bb70c","size":121000844,"kind":"archive"},{"filename":"go1.15.15.linux-386.tar.gz","os":"linux","arch":"386","version":"go1.15.15","sha256":"3310fb0e48b0907bb520f6e3c6dcff63cc0913b92a76456f12980d0eb13b77d4","size":100580037,"kind":"archive"},{"filename":"go1.15.15.linux-amd64.tar.gz","os":"linux","arch":"amd64","version":"go1.15.15","sha256":"0885cf046a9f099e260d98d9ec5d19ea9328f34c8dc4956e1d3cd87daaddb345","size":121104410,"kind":"archive"},{"filename":"go1.15.15.linux-arm64.tar.gz","os":"linux","arch":"arm64","version":"go1.15.15","sha256":"714abb01af210473dd6af331094ad6847162eff81a7fc7241d24f5a85496c9fa","size":97717057,"kind":"archive"},{"filename":"go1.15.15.linux-armv6l.tar.gz","os":"linux","arch":"armv6l","version":"go1.15.15","sha256":"7192603af50afb23c9d8cd14d2b2c19e0985a34d3eca685fa098df7893000d19","size":97989611,"kind":"archive"},{"filename":"go1.15.15.linux-ppc64le.tar.gz","os":"linux","arch":"ppc64le","version":"go1.15.15","sha256":"37f3b99e21d0324a6583159e14e42e57e56561abbf7bf68bef3d8f57b29e39c0","size":96442143,"kind":"archive"},{"filename":"go1.15.15.linux-s390x.tar.gz","os":"linux","arch":"s390x","version":"go1.15.15","sha256":"eae39d97df6b758636d5427be0b083dbf9d49007b302825ac6c8645de039aaab","size":101198583,"kind":"archive"},{"filename":"go1.15.15.windows-386.zip","os":"windows","arch":"386","version":"go1.15.15","sha256":"4036abdeb36c7db380d05f3ffd087b754c34df06b202ee381da77f4c5a44aa58","size":118256667,"kind":"archive"},{"filename":"go1.15.15.windows-386.msi","os":"windows","arch":"386","version":"go1.15.15","sha256":"04d406e45da74f67151ca9b720899d18a8707d77e83f07ba11f9b099ced3fff1","size":103342080,"kind":"installer"},{"filename":"go1.15.15.windows-amd64.zip","os":"windows","arch":"amd64","version":"go1.15.15","sha256":"7df7bf948dcc8ec0a3902e3301d17cbb5c2ebb01297d686ee2302e41f4ac6e10","size":139064376,"kind":"archive"},{"filename":"go1.15.15.windows-amd64.msi","os":"windows","arch":"amd64","version":"go1.15.15","sha256":"c76656a253cc4c7a0ba7ae006cadc0bf2d6df7066acb8df84460eac94dc3a7d1","size":120983552,"kind":"installer"}]}]`) + +func TestVersions(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dlResp) + })) + http.DefaultClient = server.Client() + + versions, err := Versions(context.Background()) + if err != nil { + t.Log(err) + t.FailNow() + } + + if len(versions) != 2 { + t.Logf("Expected 2 versions but got %d\n", len(versions)) + t.FailNow() + } + + if !strings.EqualFold(versions[0].Version, "go1.16.7") { + t.Logf("Expected version go1.16.7 but got %s\n", versions[0].Version) + t.FailNow() + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..eb4efd4 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.jojodev.com/golang/dl + +go 1.16 + +require github.com/schollz/progressbar/v3 v3.8.2 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7249795 --- /dev/null +++ b/go.sum @@ -0,0 +1,31 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/schollz/progressbar/v3 v3.8.2 h1:2kZJwZCpb+E/V79kGO7daeq+hUwUJW0A5QD1Wv455dA= +github.com/schollz/progressbar/v3 v3.8.2/go.mod h1:9KHLdyuXczIsyStQwzvW8xiELskmX7fQMaZdN23nAv8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=