Edited at
Go4Day 24

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

More than 1 year has passed since last update.

こんにちは、@hiroyukim です。

この記事は、Go4 Advent Calendar 2017の24日の記事です。

さて、みなさん。Goをinstallするとき、どんな手法をとってらっしゃるでしょうか?私は、基本的には、公式サイトのDownload PageからダウンロードしてきてLinux, Mac OS X, and FreeBSD tarballsに記述されている以下の手法でinstallしています。

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ででっち上げたのが以下になります。

#!/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でも書きました。

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を見てきたんですが、

これ便利ですね。

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

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

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

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

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