Go言語のツールが最新バージョンであるかをユーザに伝えるためのgo-latestというパッケージをつくった
Webアプリケーションとは異なり,コマンドラインツールやモバイルアプリはバージョンアップがユーザに委ねられる.そのため一度リリースしてしまうとバージョンアップをしてもらうのが難しくなる(バグを含めてしまった場合にロールバックもできない cf. “Mobile First Development at COOKPAD #deploygate”).とにかくしっかりテストをしてそもそもバクを含めないというのも大切だが,完璧なソフトウェアは存在しないので,アップデートは常に必要になる.
モバイルアプリとは異なり,Go言語でツールを書いきバイナリとして配布した場合は,最新のバージョンがすでに存在していることをユーザに伝える仕組みはそもそもない.ので,最新のバージョンをリリースしたことをユーザに伝えることが難しくなる.go-latestを使うと,ユーザが使っているツールが最新バージョンであるかをチェックし,古い場合にそれを伝えバージョンアップを促すということが可能になる.
インストール
go get
でインストールできる.
$ go get -d github.com/tcnksm/go-latest
使い方
go-latest
にはSource
という概念がある.Source
は最新バージョンの問い合わせ先である.デフォルトではGitHub上のタグ,HTMLのmetaタグ(もしくはオリジナルのスクレイピング),JSONレスポンスを利用することができる(Source
はただのインターフェースなので自分で実装することもできる).
これらの使い方を簡単に説明する.
GithubTag
まず,GitHub上のタグを使う方法.例えば,https://github.com/tcnksm/ghrというツールにおいて,バージョン0.1.0
が最新であるかをチェックするには以下のようにする.
githubTag := &latest.GithubTag{
Owner: "tcnksm",
Repository: "ghr",
}
res, _ := latest.Check(githubTag, "0.1.0")
if res.Outdated {
fmt.Printf("0.1.0 is not latest, you should upgrade to %s", res.Current)
}
レポジトリのユーザ名とレポジトリ名を指定してCheck()
を呼ぶだけ.レスポンスの詳細は,https://godoc.org/github.com/tcnksm/go-latestを参照.
HTML metaタグ
次に,特定のmetaタグをHTMLに仕込む方法.例えば,reduce-worker
というツールがあるとする.この場合は,まず,以下のようなバージョン情報を含んだmetaタグをHTMLに仕込んでおく,
<meta name="go-latest" content="reduce-worker 0.1.2 New version include security update">
バージョン0.1.0
が最新のバージョンであるかをチェックするには以下のようにする.
html := &latest.HTMLMeta{
URL: "http://example.com/info",
Name: "reduce-worker",
}
res, _ := latest.Check(html, "0.1.0")
if res.Outdated {
fmt.Printf("0.1.0 is not latest, %s, upgrade to %s", res.Meta.Message, res.Current)
}
URL
にmetaタグを含んだHTMLのURLを,Name
にツールの名前を指定してリクエストを投げるだけ.
この方法は,GitHubが死んでいても使えるし,レスポンスにメッセージを含めることもできる.metaタグの詳しい仕様はここに定義してある.この仕様が気に食わないときは,自分でオリジナルのスクレイピング関数を定義することもできる.
なお,この手法は,Go言語のRemote import pathやACIのディスカバリーを参考にしている.
JSON
最後にJSON APIを使う方法.例えば,以下のようなJSONレスポンスを返すAPIがあるとする.
{
"version":"1.2.3",
"message":"Fix crash bug when XXX",
}
バージョン0.1.0
が最新のバージョンであるかをチェックするには以下のようにする.
json := &latest.JSON{
URL: "http://example.com/version",
}
res, _ := latest.Check(json, "0.1.0")
if res.Outdated {
fmt.Printf("0.1.0 is not latest, %s, upgrade to %s", res.Meta.Message, res.Current)
}
URL
はJSONのエンドポイントを指定してリクエストを投げるだけ.
この方法は,APIクライアントを作成しているときに便利.自分でオリジナルの構造体を定義して好きなJSONレスポンスを受け取ることもできる.詳しくは,https://godoc.org/github.com/tcnksm/go-latestを参照.
使い所
例えば,自分が作っているコマンドラインツールの場合は,--version
が呼ばれたときにgo-latest
を実行し,最新版かどうかを提示するようにしている.
$ my-tool --version
my-tool version v0.3.1, build aded5ca
Your version is out of date! The latest version is v0.4.0
ベータ版のリリースなどで開発のサイクルが早く,どんどんアップデートを促したい場合は,コマンドが実行される度に呼び出しても良いかもしれない(今回は社内向けのツールでこれをしたくてつくった).またデーモンとして動くツールなどではSIGHUP
で実行しても良さそう.