Go
golang
Makefile

Golang のバイナリにバージョンを埋め込む #golang

More than 1 year has passed since last update.

以前の記事では、(Golang と Line Notify を利用して API の証明書期限切れチェック)を作りましたが今回は作成したバイナリにバージョンの追加を行います。

概要

ざっくり調べたことややりたいことを書こうと思います。

  • Golang でビルドしたバイナリはデフォルトでバージョンが埋め込まれるわけではない
    • 「main.go」などに変数を用意してビルド時などに埋め込む必要がある
    • ビルド時のオプションで変数に値を設定可能
    • 「flag」パッケージを利用して、オプションの処理を実装する必要がある
  • バージョンはバージョン番号とリビジョンに分けて管理
    • バージョンは git の最新のタグを利用(v1.0.0 なイメージ)
    • リビジョンは git の最新のタグのショートリビジョンを利用(e5a4228なイメージ)

ソースコード

apichecker.go
package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "strings"
    "time"
)

var version = "unknown"
var revision = "unknown"

func main() {
    var endpoint = flag.String("endpoint", "", "check target Endpoint URL")
    var lineToken = flag.String("token", "", "LINE notify token")
    var showVersion = false
    flag.BoolVar(&showVersion, "v", false, "show application version")
    flag.BoolVar(&showVersion, "version", false, "show application version")
    flag.Parse()

    if showVersion {
        fmt.Println("version(", version+"."+revision, ")")
    } else {
        var apiResult = getAPI(*endpoint)
        var result = postLINE(*lineToken, apiResult)

        fmt.Printf("LINE Post result [%t]\n", result)
    }
}

func getAPI(endpoint string) string {
    if endpoint == "" {
        log.Println("not endpoint")
        return "not endpoint"
    }

    var result = ""
    resp, err := http.Get(endpoint)
    if err != nil {
        result = fmt.Sprintf("NG\n%s", err)
    } else {
        defer resp.Body.Close()
        expire := "-"
        if len(resp.TLS.PeerCertificates) > 0 {
            expireUTCTime := resp.TLS.PeerCertificates[0].NotAfter
            expireJSTTime := expireUTCTime.In(time.FixedZone("Asia/Tokyo", 9*60*60))
            expire = expireJSTTime.Format("06/01/02 15:04")
        }
        result = fmt.Sprintf("OK (expire=%s)\n%s", expire, endpoint)
    }

    return result
}

func postLINE(token string, message string) bool {
    if token == "" {
        log.Println("not token")
        return false
    } else if message == "" {
        log.Println("not text")
        return false
    }

    data := url.Values{"message": {message}}
    r, _ := http.NewRequest("POST", "https://notify-api.line.me/api/notify", strings.NewReader(data.Encode()))
    r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
    resp, err := http.DefaultClient.Do(r)
    if err != nil {
        log.Println(err)
        return false
    }
    defer resp.Body.Close()
    _, err = ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Println(err)
        return false
    }

    return true
}

git コマンドで最新のタグとハッシュ値のショートバージョンを取得

最新のタグの取得

$ git describe --tags --abbrev=0
v1.0.0

ハッシュ値のショートバージョンの取得

$ git rev-parse --short HEAD
e5a4228

ビルド時にバージョンとリビジョンを埋め込む

go build 時に ldflags オプションを利用することで、go ファイルに定義した変数に値を設定することが可能です。

ビルド

$ GOOS=darwin GOARCH=amd64 go build -ldflags "-X main.version=$(git describe --tags --abbrev=0) -X main.revision=$(git rev-parse --short HEAD)"

バージョンチェック

$ ./apichecker -v
version( v1.0.0.e5a4228 )

まとめ

コマンドラインツールは長期間利用していると、どのバージョンのバイナリで運用しているかがわからなくなるので必ずバージョンを埋め込むような癖を作らないといけないかなと思っています。
ただ、よく使うのでここらへんは標準機能として実装されるとより便利かなと思ってます。

Appendix