こんにちは、@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
この方法、手順が以下になります。
- Download Page(https://golang.org/dl/)にアクセス
- 欲しいファイルをダウンロードする
- ファイルからバージョン番号を抜き取る
- 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の更新はどうされていますでしょうか?
本家派が多いとうれしいです(個人的に)。