1. 程式人生 > 程式設計 >使用 Go 管理版本的方法示例

使用 Go 管理版本的方法示例

簡介

如果你曾經執行過 docker version,

就會發現它提供了很多資訊:

PS C:\Users\tzh> docker version
Client: Docker Engine - Community
 Version:      19.03.4
 API version:    1.40
 Go version:    go1.12.10
 Git commit:    9013bf5
 Built:       Thu Oct 17 23:44:48 2019
 OS/Arch:      windows/amd64
 Experimental:   false

Server: Docker Engine - Community
 Engine:
 Version:     19.03.4
 API version:   1.40 (minimum version 1.12)
 Go version:    go1.12.10
 Git commit:    9013bf5
 Built:      Thu Oct 17 23:50:38 2019
 OS/Arch:     linux/amd64
 Experimental:   false
 containerd:
 Version:     v1.2.10
 GitCommit:    b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
 Version:     1.0.0-rc8+dev
 GitCommit:    3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
 Version:     0.18.0
 GitCommit:    fec3683

對於編譯好的二進位制檔案而言,獲取版本資訊是非常重要的.
儘可能地提供詳細資訊,有利於後期的維護和排錯.

如何實現

對於版本資訊等,有兩種方式,

一種從外部獲取,比如配置檔案等,

另一種從原始碼中獲取,將配置資訊寫死在原始碼中.

這兩種都不太好,比如編譯時間就不太好確定.
最好是能在 go build 時確定這些資訊.

幸好,go build 提供了一個選項叫做 -ldflags '[pattern=]arg list'.

-X importpath.name=value
  Set the value of the string variable in importpath named name to value.
  This is only effective if the variable is declared in the source code either uninitialized
  or initialized to a constant string expression. -X will not work if the initializer makes
  a function call or refers to other variables.
  Note that before Go 1.5 this option took two separate arguments.

這使得我們可以在編譯生成二進位制檔案時,指定某些變數的值.

比如我們有個檔案是 company/buildinfo 包的一部分.

package buildinfo

var BuildTime string

執行 go build -ldflags="-X 'company/buildinfo.BuildTime=$(date)'" 會記錄編譯時間,

將 BuildTime 的值設定為編譯時的時間,即從 $(date) 中獲取的時間.

參考:

Compile packages and dependencies
Command link
Including build information in the executable

實踐

新增 pkg/version 包,用於獲取版本資訊.

package version

// 這些值應該是從外部傳入的
var (
  gitTag    string = ""
  gitCommit  string = "$Format:%H$"     // sha1 from git,output of $(git rev-parse HEAD)
  gitTreeState string = "not a git tree"    // state of git tree,either "clean" or "dirty"
  buildDate  string = "1970-01-01T00:00:00Z" // build date in ISO8601 format,output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)

package version

import (
  "fmt"
  "runtime"
)

// 構建時的版本資訊
type VersionInfo struct {
  GitTag    string `json:"git_tag"`
  GitCommit  string `json:"git_commit"`
  GitTreeState string `json:"git_tree_state"`
  BuildDate  string `json:"build_date"`
  GoVersion  string `json:"go_version"`
  Compiler   string `json:"compiler"`
  Platform   string `json:"platform"`
}

func (info VersionInfo) String() string {
  return info.GitTag
}

func Get() VersionInfo {
  return VersionInfo{
    GitTag:    gitTag,GitCommit:  gitCommit,GitTreeState: gitTreeState,BuildDate:  buildDate,GoVersion:  runtime.Version(),Compiler:   runtime.Compiler,Platform:   fmt.Sprintf("%s/%s",runtime.GOOS,runtime.GOARCH),}
}

主要定義了一個結構體,保持版本資訊.

有些資訊可以通過 runtime 獲取,有些是編譯時傳進來的.

這裡沒有明確的版本號,而是使用 git tag 作為版本標籤.

最後,定義一個命令 version.

package cmd

import (
  "encoding/json"
  "fmt"

  "github.com/spf13/cobra"
  "tzh.com/web/pkg/version"
)

var versionCmd = &cobra.Command{
  Use:  "version",Short: "Print the version info of server",Long: "Print the version info of server",Run: func(cmd *cobra.Command,args []string) {
    printVersion()
  },}

func printVersion() {
  info := version.Get()
  infoj,err := json.MarshalIndent(&info,""," ") // 加一點縮排
  if err != nil {
    fmt.Printf("遇到了錯誤: %v\n",err)
  }
  fmt.Println(string(infoj))
}

別忘了使用 AddCommand 新增子命令.

// 初始化,設定 flag 等
func init() {
  cobra.OnInitialize(initConfig)
  rootCmd.PersistentFlags().StringVarP(&cfgFile,"config","c","./conf/config.yaml","config file (default: ./conf/config.yaml)")
  rootCmd.AddCommand(versionCmd)
}

由此,程式碼基本已經改完了,還剩下最後一點,修改 Makefile 檔案,

以便簡化操作過程.

修改 Makefile

SHELL := /bin/bash
BASEDIR = $(shell pwd)

# build with version infos
versionDir = "tzh.com/web/pkg/version"
gitTag = $(shell if [ "`git describe --tags --abbrev=0 2>/dev/null`" != "" ];then git describe --tags --abbrev=0; else git log --pretty=format:'%h' -n 1; fi)
buildDate = $(shell TZ=UTC date +%FT%T%z)
gitCommit = $(shell git log --pretty=format:'%H' -n 1)
gitTreeState = $(shell if git status|grep -q 'clean';then echo clean; else echo dirty; fi)

ldflags="-w -X ${versionDir}.gitTag=${gitTag} -X ${versionDir}.buildDate=${buildDate} -X ${versionDir}.gitCommit=${gitCommit} -X ${versionDir}.gitTreeState=${gitTreeState}"

all: gotool build
build:
  go build -ldflags ${ldflags} ./
run:
  go run -ldflags ${ldflags} ./
docker:
  go run -ldflags ${ldflags} ./ -c ./conf/config_docker.yaml

首行定義了執行的 shell,預設是 /bin/sh,這裡改成了更常用的 /bin/bash.

然後,就是定義了一大堆需要的引數.
在執行 go build 的時候添加了引數 -ldflags ${ldflags}.

如此,以後只要使用 make build 就能生成具有版本資訊的二進位制檔案了.

編譯好之後,可以執行 ./web version 檢視具體的版本資訊.

總結

通過為編譯時新增額外資訊,可以生成更具互動性的二進位制檔案.
同時,也能體會到 Makefile 帶來的便捷.

當前部分的程式碼
作為版本 v0.12.0

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。