5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Golang】"go install" でアプリのバージョン情報に自動対応させる【Go 1.16+】

Last updated at Posted at 2021-09-04

この記事は Go 1.16 より上のバージョン向けです。

Go アプリのバージョン表示 with go install

Go 言語(以下 Golang)の俺様アプリをバージョン情報表示に対応させたい

自家製 Golang アプリでバージョン情報を表示するには、一般的にソースにバージョン情報を埋め込むか、ビルド時にオプションで渡す必要があります。

ソースにバージョン情報を埋め込む場合、更新するのが面倒だったり更新忘れがあったりします。そのため、git tag で打ったタグを、ビルド用スクリプトなどで読み込んでビルド時の引数に渡すなどの工夫をします。

しかし、ビルド時に引数でバージョンを渡すと、今度は go install でインストールされた場合は反映されないのが問題となります。

つまり、go install でインストールされたとしても、アプリのバージョン情報が git tag に追随するようにしたいのです。

ちなみに、go install でインストールされた Golang アプリを一括アップデートしたい場合は gup コマンドを使うと便利です。

TL; DR (今北産業)

  1. debug.ReadBuildInfo で、ビルド時の git タグを取得できる。

  2. サンプル
    このサンプルは、git tag などでバージョンをタグ付けしておけば、アプリを go install されてもリポジトリのバージョンに追随します。(go build 除く)

    main.go
    package main
    
    import (
    	"flag"
    	"fmt"
    	"runtime/debug"
    )
    
    // Version はビルド時に `-ldflags` 経由で渡されるアプリのバージョン情報です。
    var Version string
    
    func main() {
    	var versionFlag = flag.Bool("version", false, "")
    
    	flag.Parse()
    
    	if *versionFlag {
    		// "go build" された時用のバージョン対応(従来の手法。要 LDFLAGS)
    		if Version != "" {
    			fmt.Println(Version)
    
    			return
    		}
    
    		// "go install" された時用のバージョン対応
    		if buildInfo, ok := debug.ReadBuildInfo(); ok {
    			// clone された VCS の最終タグから取得するので、ローカルの場合は
    			// デフォルトの "(devel)" がセットされる。
    			fmt.Println(buildInfo.Main.Version)
    
    			return
    		}
    
    		fmt.Println("(unknown)")
    
    		return
    	}
    
    	fmt.Println("Hello, world!")
    }
    
  3. 必須条件

    • git などの VCS で、バージョン用のタグを "vX.Y.Z" 形式で付けておくこと。
    • ソースをモジュールモードに対応させておくこと。(go.modgo.sum を置くこと)
    • go install でパッケージがインストールされた場合のみ反映される。
      そのため、リリース用のバイナリ作成やテストなど、go build する場合は反映されない。go build では、従来通り LDFLAGS オプションを指定(go build -ldflags="-X main.Version=$(git describe --tags)")してビルドする基本は変わらない。(Go 1.18 で改善される可能性があるそうです)
    • 対応 Go バージョン
      • go.modgo 1.16 以上に設定したパッケージで使う方がいい。
      • go.modgo 1.17 なら確実なのでオススメ。

🐒  この仕組みのポイントは Go 1.12 で実装された debug.readBuildInfo() を使うところです。readBuildInfo()は、ビルド時に埋め込まれたデバッグ情報のオブジェクトを返すのですが、obj.Main.Version フィールドに、main.go に付いていた最終タグが入っています(デフォルトは (devel))。サンプルでは、これを Version 変数が空の場合に利用しています。
しかし、実際には Go のモジュール・モードと go install に依存します。そのため、go get -u でパッケージをバイナリとしてインストールする習慣がまだある(go install を使わない)ユーザのことを考えると、 go.mod のバージョンを go 1.17 以上に設定している場合に、この方法を使うのが確実だと思います。(Go 1.17 は go install が必須になったため)

TIPS

  • git タグによるバージョン情報の取得コマンド

    git describe --tag
    
  • git のコミット ID の取得コマンド

    git rev-parse --short HEAD
    
  • バージョン情報をビルド時に渡す例

    TAG_APP=$(git describe --tag)
    REV_APP=$(git rev-parse --short HEAD)
    VER_APP="${TAG_APP}+${REV_APP}"
    
    go build -ldflags="-s -w -extldflags \"-static\" -X 'main.versionApp=${VER_APP}'"
    

参考文献

5
1
0

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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?