🤖
プライベートリポジトリで管理しているCLIのセルフアップデート機能を実装する
はじめに
GitHubのプライベートリポジトリでCLIアプリケーションを管理しており、今まではReleasesページからダウンロードできるようになっていました。
しかしながら、更新があるたびにブラウザからダウンロードして、バイナリを置き換えるのは頻度が少ないとはいえ、まあ手間です。
今回は、CLIアプリケーションの機能としてセルフアップデートを実装できたので、手順をまとめます。
手順
Release一覧から最新バージョンの名前を取得
プライベートリポジトリの情報を取得するため、ghコマンドを使います。
ただし、直接ghコマンドを扱うわけではなく、公式がライブラリを提供しているのでこちらを使います。
import (
"encoding/json"
"fmt"
"github.com/cli/go-gh/v2"
"github.com/cockroachdb/errors"
)
const REPO = "org/repo" // リポジトリ名
type ReleaseInfo struct {
Name string `json:"name"`
IsLatest bool `json:"isLatest"`
}
func GetLatestReleaseName() (string, error) {
releaseListRes, _, err := gh.Exec("release", "list", "-R", REPO, "--json", "isLatest,name")
if err != nil {
return "", errors.Wrap(err, "failed to get latest release. have you set up gh?")
}
var releases []ReleaseInfo
if err := json.Unmarshal(releaseListRes.Bytes(), &releases); err != nil {
return "", errors.Wrap(err, "failed to unmarshal release list")
}
var latestRelease *ReleaseInfo
for i := range releases {
release := releases[i]
if release.IsLatest {
latestRelease = &release
break
}
}
if latestRelease == nil {
return "", errors.New("failed to find latest release")
}
return latestRelease.Name, nil
}
現在のバージョンと最新バージョンを比較
ここではsemverを使って比較します。
import "golang.org/x/mod/semver"
func shouldUpdate(currentVersion, latestVersion string) bool {
return semver.Compare(currentVersion, latestVersion) < 0
}
成果物を取得
タグ名と成果物名を指定してダウンロードします。
import (
"github.com/cli/go-gh/v2"
"github.com/cockroachdb/errors"
)
const ASSET_NAME = "asset" // 成果物名
func fetchRelease(tag string) ([]byte, error) {
res, _, err := gh.Exec("release", "download", "-O", "-", "-R", REPO, "-p", ASSET_NAME, tag)
if err != nil {
return nil, errors.Wrap(err, "failed to download release")
}
return res.Bytes(), nil
}
セルフアップデート
go-updateを使用してセルフアップデートを行います。
import (
"bytes"
"github.com/cockroachdb/errors"
"github.com/inconshreveable/go-update"
)
func selfUpdate(b []byte) error {
if err := update.Apply(bytes.NewReader(b), update.Options{}); err != nil {
return errors.Wrap(err, "failed to apply update")
}
return nil
}
参考
https://cli.github.com/manual/gh
https://github.com/cli/go-gh
https://github.com/inconshreveable/go-update
https://zenn.dev/kamina_zzz/articles/e9d68da8a88cf2
Discussion