| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build !plan9 |
| // +build !plan9 |
| |
| package main |
| |
| import ( |
| "archive/tar" |
| "archive/zip" |
| "compress/gzip" |
| "crypto/sha256" |
| "encoding/json" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "net/http" |
| "os" |
| "path/filepath" |
| "strings" |
| ) |
| |
| const ( |
| downloadURLPrefix = "https://dl.google.com/go" |
| ) |
| |
| // downloadGoVersion downloads and upacks the specific go version to dest/go. |
| func downloadGoVersion(version, ops, arch, dest string) error { |
| suffix := "tar.gz" |
| if ops == "windows" { |
| suffix = "zip" |
| } |
| uri := fmt.Sprintf("%s/%s.%s-%s.%s", downloadURLPrefix, version, ops, arch, suffix) |
| |
| verbosef("Downloading %s", uri) |
| |
| req, err := http.NewRequest("GET", uri, nil) |
| if err != nil { |
| return err |
| } |
| req.Header.Add("User-Agent", fmt.Sprintf("golang.org-getgo/%s", version)) |
| |
| resp, err := http.DefaultClient.Do(req) |
| if err != nil { |
| return fmt.Errorf("Downloading Go from %s failed: %v", uri, err) |
| } |
| if resp.StatusCode > 299 { |
| return fmt.Errorf("Downloading Go from %s failed with HTTP status %s", uri, resp.Status) |
| } |
| defer resp.Body.Close() |
| |
| tmpf, err := ioutil.TempFile("", "go") |
| if err != nil { |
| return err |
| } |
| defer os.Remove(tmpf.Name()) |
| |
| h := sha256.New() |
| |
| w := io.MultiWriter(tmpf, h) |
| if _, err := io.Copy(w, resp.Body); err != nil { |
| return err |
| } |
| |
| verbosef("Downloading SHA %s.sha256", uri) |
| |
| sresp, err := http.Get(uri + ".sha256") |
| if err != nil { |
| return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed: %v", uri, err) |
| } |
| defer sresp.Body.Close() |
| if sresp.StatusCode > 299 { |
| return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed with HTTP status %s", uri, sresp.Status) |
| } |
| |
| shasum, err := ioutil.ReadAll(sresp.Body) |
| if err != nil { |
| return err |
| } |
| |
| // Check the shasum. |
| sum := fmt.Sprintf("%x", h.Sum(nil)) |
| if sum != string(shasum) { |
| return fmt.Errorf("Shasum mismatch %s vs. %s", sum, string(shasum)) |
| } |
| |
| unpackFunc := unpackTar |
| if ops == "windows" { |
| unpackFunc = unpackZip |
| } |
| if err := unpackFunc(tmpf.Name(), dest); err != nil { |
| return fmt.Errorf("Unpacking Go to %s failed: %v", dest, err) |
| } |
| return nil |
| } |
| |
| func unpack(dest, name string, fi os.FileInfo, r io.Reader) error { |
| if strings.HasPrefix(name, "go/") { |
| name = name[len("go/"):] |
| } |
| |
| path := filepath.Join(dest, name) |
| if fi.IsDir() { |
| return os.MkdirAll(path, fi.Mode()) |
| } |
| |
| f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) |
| if err != nil { |
| return err |
| } |
| defer f.Close() |
| |
| _, err = io.Copy(f, r) |
| return err |
| } |
| |
| func unpackTar(src, dest string) error { |
| r, err := os.Open(src) |
| if err != nil { |
| return err |
| } |
| defer r.Close() |
| |
| archive, err := gzip.NewReader(r) |
| if err != nil { |
| return err |
| } |
| defer archive.Close() |
| |
| tarReader := tar.NewReader(archive) |
| |
| for { |
| header, err := tarReader.Next() |
| if err == io.EOF { |
| break |
| } else if err != nil { |
| return err |
| } |
| |
| if err := unpack(dest, header.Name, header.FileInfo(), tarReader); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func unpackZip(src, dest string) error { |
| zr, err := zip.OpenReader(src) |
| if err != nil { |
| return err |
| } |
| |
| for _, f := range zr.File { |
| fr, err := f.Open() |
| if err != nil { |
| return err |
| } |
| if err := unpack(dest, f.Name, f.FileInfo(), fr); err != nil { |
| return err |
| } |
| fr.Close() |
| } |
| |
| return nil |
| } |
| |
| func getLatestGoVersion() (string, error) { |
| resp, err := http.Get("https://golang.org/dl/?mode=json") |
| if err != nil { |
| return "", fmt.Errorf("Getting current Go version failed: %v", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != http.StatusOK { |
| b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 1024)) |
| return "", fmt.Errorf("Could not get current Go release: HTTP %d: %q", resp.StatusCode, b) |
| } |
| var releases []struct { |
| Version string |
| } |
| err = json.NewDecoder(resp.Body).Decode(&releases) |
| if err != nil { |
| return "", err |
| } |
| if len(releases) < 1 { |
| return "", fmt.Errorf("Could not get at least one Go release") |
| } |
| return releases[0].Version, nil |
| } |