Goはどっからいれてますか？

こんにちは、@hiroyukim です。
この記事は、[Go4 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/go4)の24日の記事です。

さて、みなさん。Goをinstallするとき、どんな手法をとってらっしゃるでしょうか？私は、基本的には、公式サイトの[Download Page](https://golang.org/dl/)からダウンロードしてきて[Linux, Mac OS X, and FreeBSD tarballs](https://golang.org/doc/install#tarball)に記述されている以下の手法でinstallしています。

```sh
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
```

この方法、手順が以下になります。

1. Download Page(https://golang.org/dl/)にアクセス
2. 欲しいファイルをダウンロードする
3. ファイルからバージョン番号を抜き取る
4. tar を実行して /usr/local 以下にインストールする

毎日やるようなことでもないので、最近まで特に疑わずにこの手順を繰り返してたんですね。でも、改めて考えると *面倒くさい* なという事実に気づいてしまったわけです。

正規表現で抜き出してshellで完結するのも出来なくは無いけど、なんとなく嫌だなーと思って、とりあえずnodeででっち上げたのが以下になります。


```js
#!/usr/bin/env node

'use strict';
var os  = require('os'),
    util = require('util'),
    rp  = require('request-promise'),
    htmlparser  = require('htmlparser');

const GO_DOWNLOAD_URL = 'https://golang.org/dl/';

function getDownloadUrls(dom,urls) {
    dom.type == 'tag' &&
    dom.name == 'a'   &&
    dom.attribs       &&
    typeof(dom.attribs) == 'object'               &&
    dom.attribs.class   == 'download downloadBox' &&
        urls.push(dom.attribs.href);

    dom.children && dom.children.forEach(function(row){
        getDownloadUrls(row,urls);
    });
}

function main() {
    var urls = [];
    rp(GO_DOWNLOAD_URL)
        .then(function(htmlString){
            var handler = new htmlparser.DefaultHandler(function (error, dom) {
                if (error) {
                    console.log(error);
                } else {
                    dom.forEach(function(row){
                        getDownloadUrls(row,urls);
                    });
                }
            });
            var parser = new htmlparser.Parser(handler);
            parser.parseComplete(htmlString);
            var linux_download_url = urls.filter(function(url){ return /linux/.test(url) })[0];
            console.log("curl -sL " + linux_download_url + " | sudo tar zx -C /usr/local");

        })
        .catch(function(err){
            console.log(err);
        });
}

main();
```


面倒くさいのはLinuxでインストールするときだけだと思っていたので、対象がLinux onlyです。あと、installコマンドをサジェストするところまでで止めてます。目的は、概ね果たしたし、良いんじゃ無いかと思っていたのですが、知り合いに見せたところ、

**「Goじゃないんですか？」**

と至極もっともなことを言われてました。	


確かに、Goのinstall補助装置だからGoで書くべきですよね。

その通りだと思ったので、Goでも書きました。


```go
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strings"

	"golang.org/x/net/html"
)

const (
	DownloadUrl = "https://golang.org/dl/"
	OsName      = "linux"
)

func getDownloadPage(url string) (string, error) {
	res, err := http.Get(url)
	if err != nil {
		return "", errors.New("request error")
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", errors.New("ioutil.ReadAll erro")
	}

	return string(body), nil

}

func getDownloadUrls(html_string string) []string {
	urls := make([]string, 0, 0)
	doc, err := html.Parse(strings.NewReader(html_string))
	if err != nil {
		log.Fatal(err)
	}
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			isDownload := false
			for _, a := range n.Attr {
				if a.Key == "class" && a.Val == "download downloadBox" {
					isDownload = true
				}
				if isDownload && a.Key == "href" {
					urls = append(urls, a.Val)
					break
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	return urls
}

func getTargetUrl(os_name string, urls []string) (string, error) {
	re := regexp.MustCompile(os_name)
	for _, url := range urls {
		if re.MatchString(url) {
			return url, nil
		}
	}

	return "", errors.New("url not found.")
}

func createCmd(url string) string {
	return fmt.Sprintf("curl -sL %s | sudo tar zx -C /usr/local", url)
}

func main() {
	content, err := getDownloadPage(DownloadUrl)
	if err != nil {
		log.Fatal(err)
	}

	urls := getDownloadUrls(content)
	url, err := getTargetUrl(OsName, urls)
	if err != nil {
		log.Fatal(err)
	}

	cmd := createCmd(url)

	fmt.Println(cmd)
}

```


そして、もう一度知り合いに見せたところ、

**「ひょっとして linuxbrew で良いんじゃ無いか？」**

というご意見を頂きました。

[Linuxbrew](http://linuxbrew.sh/)を見てきたんですが、

これ便利ですね。

使ったことありませんが。

更新を楽したいが為に、色々回り道をしてしまった気がしますが、

楽しかったのでよしとします。

皆さん、Goの更新はどうされていますでしょうか？

本家派が多いとうれしいです（個人的に）。