github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/cmd/getgo/download.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !plan9
     6  // +build !plan9
     7  
     8  package main
     9  
    10  import (
    11  	"archive/tar"
    12  	"archive/zip"
    13  	"compress/gzip"
    14  	"crypto/sha256"
    15  	"encoding/json"
    16  	"fmt"
    17  	"io"
    18  	"io/ioutil"
    19  	"net/http"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  )
    24  
    25  const (
    26  	downloadURLPrefix = "https://dl.google.com/go"
    27  )
    28  
    29  // downloadGoVersion downloads and upacks the specific go version to dest/go.
    30  func downloadGoVersion(version, ops, arch, dest string) error {
    31  	suffix := "tar.gz"
    32  	if ops == "windows" {
    33  		suffix = "zip"
    34  	}
    35  	uri := fmt.Sprintf("%s/%s.%s-%s.%s", downloadURLPrefix, version, ops, arch, suffix)
    36  
    37  	verbosef("Downloading %s", uri)
    38  
    39  	req, err := http.NewRequest("GET", uri, nil)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	req.Header.Add("User-Agent", fmt.Sprintf("golang.org-getgo/%s", version))
    44  
    45  	resp, err := http.DefaultClient.Do(req)
    46  	if err != nil {
    47  		return fmt.Errorf("Downloading Go from %s failed: %v", uri, err)
    48  	}
    49  	if resp.StatusCode > 299 {
    50  		return fmt.Errorf("Downloading Go from %s failed with HTTP status %s", uri, resp.Status)
    51  	}
    52  	defer resp.Body.Close()
    53  
    54  	tmpf, err := ioutil.TempFile("", "go")
    55  	if err != nil {
    56  		return err
    57  	}
    58  	defer os.Remove(tmpf.Name())
    59  
    60  	h := sha256.New()
    61  
    62  	w := io.MultiWriter(tmpf, h)
    63  	if _, err := io.Copy(w, resp.Body); err != nil {
    64  		return err
    65  	}
    66  
    67  	verbosef("Downloading SHA %s.sha256", uri)
    68  
    69  	sresp, err := http.Get(uri + ".sha256")
    70  	if err != nil {
    71  		return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed: %v", uri, err)
    72  	}
    73  	defer sresp.Body.Close()
    74  	if sresp.StatusCode > 299 {
    75  		return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed with HTTP status %s", uri, sresp.Status)
    76  	}
    77  
    78  	shasum, err := ioutil.ReadAll(sresp.Body)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	// Check the shasum.
    84  	sum := fmt.Sprintf("%x", h.Sum(nil))
    85  	if sum != string(shasum) {
    86  		return fmt.Errorf("Shasum mismatch %s vs. %s", sum, string(shasum))
    87  	}
    88  
    89  	unpackFunc := unpackTar
    90  	if ops == "windows" {
    91  		unpackFunc = unpackZip
    92  	}
    93  	if err := unpackFunc(tmpf.Name(), dest); err != nil {
    94  		return fmt.Errorf("Unpacking Go to %s failed: %v", dest, err)
    95  	}
    96  	return nil
    97  }
    98  
    99  func unpack(dest, name string, fi os.FileInfo, r io.Reader) error {
   100  	if strings.HasPrefix(name, "go/") {
   101  		name = name[len("go/"):]
   102  	}
   103  
   104  	path := filepath.Join(dest, name)
   105  	if fi.IsDir() {
   106  		return os.MkdirAll(path, fi.Mode())
   107  	}
   108  
   109  	f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
   110  	if err != nil {
   111  		return err
   112  	}
   113  	defer f.Close()
   114  
   115  	_, err = io.Copy(f, r)
   116  	return err
   117  }
   118  
   119  func unpackTar(src, dest string) error {
   120  	r, err := os.Open(src)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	defer r.Close()
   125  
   126  	archive, err := gzip.NewReader(r)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	defer archive.Close()
   131  
   132  	tarReader := tar.NewReader(archive)
   133  
   134  	for {
   135  		header, err := tarReader.Next()
   136  		if err == io.EOF {
   137  			break
   138  		} else if err != nil {
   139  			return err
   140  		}
   141  
   142  		if err := unpack(dest, header.Name, header.FileInfo(), tarReader); err != nil {
   143  			return err
   144  		}
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func unpackZip(src, dest string) error {
   151  	zr, err := zip.OpenReader(src)
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	for _, f := range zr.File {
   157  		fr, err := f.Open()
   158  		if err != nil {
   159  			return err
   160  		}
   161  		if err := unpack(dest, f.Name, f.FileInfo(), fr); err != nil {
   162  			return err
   163  		}
   164  		fr.Close()
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  func getLatestGoVersion() (string, error) {
   171  	resp, err := http.Get("https://golang.org/dl/?mode=json")
   172  	if err != nil {
   173  		return "", fmt.Errorf("Getting current Go version failed: %v", err)
   174  	}
   175  	defer resp.Body.Close()
   176  	if resp.StatusCode != http.StatusOK {
   177  		b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 1024))
   178  		return "", fmt.Errorf("Could not get current Go release: HTTP %d: %q", resp.StatusCode, b)
   179  	}
   180  	var releases []struct {
   181  		Version string
   182  	}
   183  	err = json.NewDecoder(resp.Body).Decode(&releases)
   184  	if err != nil {
   185  		return "", err
   186  	}
   187  	if len(releases) < 1 {
   188  		return "", fmt.Errorf("Could not get at least one Go release")
   189  	}
   190  	return releases[0].Version, nil
   191  }