Go言語バイナリをセルフアップデートする
Go の CLIツールのセルフアップデート機能の実装方法を解説した記事です。[minio/selfupdate](https://github.com/minio/selfupdate)ライブラリを使用し、バイナリのダウンロードとアーカイブからの読み取りを通じてセルフアップデートを実現します。
最近開発している vss という CLI の静的サイトジェネレーターを開発していて、セルフアップデートの仕組みを追加したので実装方法を残しておきます。
リポジトリはこちら -> https://github.com/vssio/go-vss
Go のバイナリをセルフアップデートするためのライブラリ
セルフアップデートを実現するために、今回は minio/selfupdate を利用しました。
公式の Example code を見ると非常に簡単にセルフアップデートの仕組みを導入することができるとわかります。
import (
"fmt"
"net/http"
"github.com/minio/selfupdate"
)
func doUpdate(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil {
// error handling
}
return err
}
引用: https://github.com/minio/selfupdate/blob/master/README.md
セルフアップデート部分はライブラリが実装してくれているので、自分で実装するのは以下の 2 点です。
- バイナリをダウンロードする
- バイナリファイルを読み取る io.Reader を作成する
バイナルをセルフアップデートする仕組み
簡単に仕組みを説明すると、以下のようになります。
※ Linux を例にしますので、ダウンロードファイルは tar.gz を前提に説明していきます。Windows & Mac は zip ですが、基本的な流れは同じです。
- GitHub Releases からバイナリをダウンロードする(デフォルト latest)。
- アーカイブファイルを 1 ファイルずつ読み取る。
vss
というファイル名のファイルを探す(アーカイブファイルには vss 以外にも README などのファイルが含まれているため)。- vss が見つかったら、そのファイルを読み取る io.Reader を selfupdate.Apply に渡す。
- err が nil であれば、アップデート成功。
Go でセルフアップデートするコード
上記の流れを実装すると以下のようになります。
func downloadAndExtract(url string) error {
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Create a new gzip reader
gr, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
defer gr.Close()
// Create a tar reader
tr := tar.NewReader(gr)
// Iterate through the files in the archive
for {
header, err := tr.Next()
if err == io.EOF {
break // End of archive
}
if err != nil {
return err
}
// Open the output file
if header.FileInfo().IsDir() {
continue
}
filename := filepath.Join(strings.Split(header.Name, "/")[1:]...)
if filename == "vss" {
err = selfupdate.Apply(tr, selfupdate.Options{})
if err != nil {
return err
}
break // end of update binary
} else {
continue
}
}
return nil
}
この関数にダウンロード対象の URL を渡してやれば、セルフアップデートが実行されます 🦭
まとめ
- Go でバイナリをセルフアップデートする方法を紹介しました。
- minio/selfupdate というライブラリを使うと非常に簡単に実装できます。
- 今回は tar.gz をダウンロードして、その中からバイナリを探して読み取る 方法でしたが、zip でも同じように実装できます。
- 毎回同様のダウンロード方法になるなら、minio/selfupdate をラップした、tar.gz と zip に対応したセルフアップデート用のライブラリを作っておくのも良いかもしれません。