LoginSignup
4
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-23

こんにちは、@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の更新はどうされていますでしょうか?

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

4
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2