diff options
| author | Christine Dodrill <me@christine.website> | 2019-01-11 15:11:43 -0800 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2019-01-11 15:11:43 -0800 |
| commit | 2059adef99a8ca2ed4b0264cd19f84a9cb9e4420 (patch) | |
| tree | 5ebf5b3e7788fc1aa353071e9b14ee5b10bb031e /version | |
| parent | 99785a9a69cc2c44ed6f161d227eee2369770940 (diff) | |
| download | x-2059adef99a8ca2ed4b0264cd19f84a9cb9e4420.tar.xz x-2059adef99a8ca2ed4b0264cd19f84a9cb9e4420.zip | |
fix build
Diffstat (limited to 'version')
| -rw-r--r-- | version/ko/main.go | 21 | ||||
| -rw-r--r-- | version/version.go | 455 |
2 files changed, 0 insertions, 476 deletions
diff --git a/version/ko/main.go b/version/ko/main.go deleted file mode 100644 index c1a30b6..0000000 --- a/version/ko/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "os" - "runtime" - - "github.com/Xe/x/version" - _ "github.com/joho/godotenv/autoload" -) - -func main() { - ver := os.Getenv("GO_VERSION") - - if ver == "" { - ver = runtime.Version() - } else { - ver = "go" + ver - } - - version.Run(ver) -} diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 7ae7894..0000000 --- a/version/version.go +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2016 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. - -// The version package permits running a specific version of Go. -package version - -import ( - "archive/tar" - "archive/zip" - "compress/gzip" - "crypto/sha256" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "os/exec" - "os/user" - "path" - "path/filepath" - "runtime" - "strings" - "time" - - "golang.org/x/build/envutil" -) - -func init() { - http.DefaultTransport = &userAgentTransport{http.DefaultTransport} -} - -// Run runs the "go" tool of the provided Go version. -func Run(version string) { - log.SetFlags(0) - - root, err := goroot(version) - if err != nil { - log.Fatalf("%s: %v", version, err) - } - - if len(os.Args) == 2 && os.Args[1] == "download" { - if err := install(root, version); err != nil { - log.Fatalf("%s: download failed: %v", version, err) - } - os.Exit(0) - } - - if _, err := os.Stat(filepath.Join(root, unpackedOkay)); err != nil { - log.Fatalf("%s: not downloaded. Run '%s download' to install to %v", version, os.Args[0], root) - } - - gobin := filepath.Join(root, "bin", "go"+exe()) - cmd := exec.Command(gobin, os.Args[1:]...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = envutil.Dedup(caseInsensitiveEnv, append(os.Environ(), "GOROOT="+root)) - if err := cmd.Run(); err != nil { - // TODO: return the same exit status maybe. - os.Exit(1) - } - os.Exit(0) -} - -// install installs a version of Go to the named target directory, creating the -// directory as needed. -func install(targetDir, version string) error { - if _, err := os.Stat(filepath.Join(targetDir, unpackedOkay)); err == nil { - log.Printf("%s: already downloaded in %v", version, targetDir) - return nil - } - - goURL, err := versionArchiveURL(version) - if err != nil { - return err - } - if err := os.MkdirAll(targetDir, 0755); err != nil { - return err - } - res, err := http.Head(goURL) - if err != nil { - return err - } - if res.StatusCode == http.StatusNotFound { - return fmt.Errorf("no binary release of %v for %v/%v at %v", version, getOS(), runtime.GOARCH, goURL) - } - if res.StatusCode != http.StatusOK { - return fmt.Errorf("server returned %v checking size of %v", http.StatusText(res.StatusCode), goURL) - } - base := path.Base(goURL) - archiveFile := filepath.Join(targetDir, base) - defer os.RemoveAll(archiveFile) - if fi, err := os.Stat(archiveFile); err != nil || fi.Size() != res.ContentLength { - if err != nil && !os.IsNotExist(err) { - // Something weird. Don't try to download. - return err - } - if err := copyFromURL(archiveFile, goURL); err != nil { - return fmt.Errorf("error downloading %v: %v", goURL, err) - } - fi, err = os.Stat(archiveFile) - if err != nil { - return err - } - if fi.Size() != res.ContentLength { - return fmt.Errorf("downloaded file %s size %v doesn't match server size %v", archiveFile, fi.Size(), res.ContentLength) - } - } - wantSHA, err := slurpURLToString(goURL + ".sha256") - if err != nil { - return err - } - if err := verifySHA256(archiveFile, strings.TrimSpace(wantSHA)); err != nil { - return fmt.Errorf("error verifying SHA256 of %v: %v", archiveFile, err) - } - log.Printf("Unpacking %v ...", archiveFile) - if err := unpackArchive(targetDir, archiveFile); err != nil { - return fmt.Errorf("extracting archive %v: %v", archiveFile, err) - } - if err := ioutil.WriteFile(filepath.Join(targetDir, unpackedOkay), nil, 0644); err != nil { - return err - } - log.Printf("Success. You may now run '%v'", os.Args[0]) - return nil -} - -// unpackArchive unpacks the provided archive zip or tar.gz file to targetDir, -// removing the "go/" prefix from file entries. -func unpackArchive(targetDir, archiveFile string) error { - switch { - case strings.HasSuffix(archiveFile, ".zip"): - return unpackZip(targetDir, archiveFile) - case strings.HasSuffix(archiveFile, ".tar.gz"): - return unpackTarGz(targetDir, archiveFile) - default: - return errors.New("unsupported archive file") - } -} - -// unpackTarGz is the tar.gz implementation of unpackArchive. -func unpackTarGz(targetDir, archiveFile string) error { - r, err := os.Open(archiveFile) - if err != nil { - return err - } - defer r.Close() - madeDir := map[string]bool{} - zr, err := gzip.NewReader(r) - if err != nil { - return err - } - tr := tar.NewReader(zr) - for { - f, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return err - } - if !validRelPath(f.Name) { - return fmt.Errorf("tar file contained invalid name %q", f.Name) - } - rel := filepath.FromSlash(strings.TrimPrefix(f.Name, "go/")) - abs := filepath.Join(targetDir, rel) - - fi := f.FileInfo() - mode := fi.Mode() - switch { - case mode.IsRegular(): - // Make the directory. This is redundant because it should - // already be made by a directory entry in the tar - // beforehand. Thus, don't check for errors; the next - // write will fail with the same error. - dir := filepath.Dir(abs) - if !madeDir[dir] { - if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil { - return err - } - madeDir[dir] = true - } - wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm()) - if err != nil { - return err - } - n, err := io.Copy(wf, tr) - if closeErr := wf.Close(); closeErr != nil && err == nil { - err = closeErr - } - if err != nil { - return fmt.Errorf("error writing to %s: %v", abs, err) - } - if n != f.Size { - return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size) - } - if !f.ModTime.IsZero() { - if err := os.Chtimes(abs, f.ModTime, f.ModTime); err != nil { - // benign error. Gerrit doesn't even set the - // modtime in these, and we don't end up relying - // on it anywhere (the gomote push command relies - // on digests only), so this is a little pointless - // for now. - log.Printf("error changing modtime: %v", err) - } - } - case mode.IsDir(): - if err := os.MkdirAll(abs, 0755); err != nil { - return err - } - madeDir[abs] = true - default: - return fmt.Errorf("tar file entry %s contained unsupported file type %v", f.Name, mode) - } - } - return nil -} - -// unpackZip is the zip implementation of unpackArchive. -func unpackZip(targetDir, archiveFile string) error { - zr, err := zip.OpenReader(archiveFile) - if err != nil { - return err - } - defer zr.Close() - - for _, f := range zr.File { - name := strings.TrimPrefix(f.Name, "go/") - - outpath := filepath.Join(targetDir, name) - if f.FileInfo().IsDir() { - if err := os.MkdirAll(outpath, 0755); err != nil { - return err - } - continue - } - - rc, err := f.Open() - if err != nil { - return err - } - - // File - if err := os.MkdirAll(filepath.Dir(outpath), 0755); err != nil { - return err - } - out, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - _, err = io.Copy(out, rc) - rc.Close() - if err != nil { - out.Close() - return err - } - if err := out.Close(); err != nil { - return err - } - } - return nil -} - -// verifySHA256 reports whether the named file has contents with -// SHA-256 of the given wantHex value. -func verifySHA256(file, wantHex string) error { - f, err := os.Open(file) - if err != nil { - return err - } - defer f.Close() - hash := sha256.New() - if _, err := io.Copy(hash, f); err != nil { - return err - } - if fmt.Sprintf("%x", hash.Sum(nil)) != wantHex { - return fmt.Errorf("%s corrupt? does not have expected SHA-256 of %v", file, wantHex) - } - return nil -} - -// slurpURLToString downloads the given URL and returns it as a string. -func slurpURLToString(url_ string) (string, error) { - res, err := http.Get(url_) - if err != nil { - return "", err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return "", fmt.Errorf("%s: %v", url_, res.Status) - } - slurp, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", fmt.Errorf("reading %s: %v", url_, err) - } - return string(slurp), nil -} - -// copyFromURL downloads srcURL to dstFile. -func copyFromURL(dstFile, srcURL string) (err error) { - f, err := os.Create(dstFile) - if err != nil { - return err - } - defer func() { - if err != nil { - f.Close() - os.Remove(dstFile) - } - }() - c := &http.Client{ - Transport: &userAgentTransport{&http.Transport{ - // It's already compressed. Prefer accurate ContentLength. - // (Not that GCS would try to compress it, though) - DisableCompression: true, - DisableKeepAlives: true, - Proxy: http.ProxyFromEnvironment, - }}, - } - res, err := c.Get(srcURL) - if err != nil { - return err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return errors.New(res.Status) - } - pw := &progressWriter{w: f, total: res.ContentLength} - n, err := io.Copy(pw, res.Body) - if err != nil { - return err - } - if res.ContentLength != -1 && res.ContentLength != n { - return fmt.Errorf("copied %v bytes; expected %v", n, res.ContentLength) - } - pw.update() // 100% - return f.Close() -} - -type progressWriter struct { - w io.Writer - n int64 - total int64 - last time.Time -} - -func (p *progressWriter) update() { - end := " ..." - if p.n == p.total { - end = "" - } - fmt.Fprintf(os.Stderr, "Downloaded %0.1f%% (%d / %d bytes)%s\n", - (100.0*float64(p.n))/float64(p.total), - p.n, p.total, end) -} - -func (p *progressWriter) Write(buf []byte) (n int, err error) { - n, err = p.w.Write(buf) - p.n += int64(n) - if now := time.Now(); now.Unix() != p.last.Unix() { - p.update() - p.last = now - } - return -} - -// getOS returns runtime.GOOS. It exists as a function just for lazy -// testing of the Windows zip path when running on Linux/Darwin. -func getOS() string { - return runtime.GOOS -} - -// versionArchiveURL returns the zip or tar.gz URL of the given Go version. -func versionArchiveURL(version string) (string, error) { - goos := getOS() - - // TODO: Maybe we should parse - // https://storage.googleapis.com/go-builder-data/dl-index.txt ? - // Let's just guess the URL for now and see if it's there. - // Then we don't have to maintain that txt file too. - ext := ".tar.gz" - if goos == "windows" { - ext = ".zip" - } - arch := runtime.GOARCH - if goos == "linux" && runtime.GOARCH == "arm" { - arch = "armv6l" - } - // https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz - return "https://dl.google.com/go/" + version + "." + goos + "-" + arch + ext, nil -} - -const caseInsensitiveEnv = runtime.GOOS == "windows" - -// unpackedOkay is a sentinel zero-byte file to indicate that the Go -// version was downloaded and unpacked successfully. -const unpackedOkay = ".unpacked-success" - -func exe() string { - if runtime.GOOS == "windows" { - return ".exe" - } - return "" -} - -func goroot(version string) (string, error) { - home, err := homedir() - if err != nil { - return "", fmt.Errorf("failed to get home directory: %v", err) - } - return filepath.Join(home, ".ko", "installed", version), nil -} - -func homedir() (string, error) { - switch getOS() { - case "plan9": - return "", fmt.Errorf("%q not yet supported", runtime.GOOS) - case "windows": - if dir := os.Getenv("USERPROFILE"); dir != "" { - return dir, nil - } - return "", errors.New("can't find user home directory; %USERPROFILE% is empty") - default: - if dir := os.Getenv("HOME"); dir != "" { - return dir, nil - } - if u, err := user.Current(); err == nil && u.HomeDir != "" { - return u.HomeDir, nil - } - return "", errors.New("can't find user home directory; $HOME is empty") - } -} - -func validRelPath(p string) bool { - if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") { - return false - } - return true -} - -type userAgentTransport struct { - rt http.RoundTripper -} - -func (uat userAgentTransport) RoundTrip(r *http.Request) (*http.Response, error) { - version := runtime.Version() - if strings.Contains(version, "devel") { - // Strip the SHA hash and date. We don't want spaces or other tokens (see RFC2616 14.43) - version = "devel" - } - r.Header.Set("User-Agent", "github-com-Xe-x/"+version) - return uat.rt.RoundTrip(r) -} |
