aboutsummaryrefslogtreecommitdiff
path: root/version
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2019-01-11 15:11:43 -0800
committerChristine Dodrill <me@christine.website>2019-01-11 15:11:43 -0800
commit2059adef99a8ca2ed4b0264cd19f84a9cb9e4420 (patch)
tree5ebf5b3e7788fc1aa353071e9b14ee5b10bb031e /version
parent99785a9a69cc2c44ed6f161d227eee2369770940 (diff)
downloadx-2059adef99a8ca2ed4b0264cd19f84a9cb9e4420.tar.xz
x-2059adef99a8ca2ed4b0264cd19f84a9cb9e4420.zip
fix build
Diffstat (limited to 'version')
-rw-r--r--version/ko/main.go21
-rw-r--r--version/version.go455
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)
-}