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

 更新时间:2019年10月31日 10:17:30   作者:帅气猫咪  
这篇文章主要介绍了使用 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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Go语言题解LeetCode455分发饼干示例详解

    Go语言题解LeetCode455分发饼干示例详解

    这篇文章主要为大家介绍了Go语言题解LeetCode455分发饼干示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Golang服务的请求调度的实现

    Golang服务的请求调度的实现

    Golang服务请求调度是一种使用Go语言实现的服务请求管理方法,本文主要介绍了Golang服务的请求调度的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • Go导入不同目录下包报错的解决方法

    Go导入不同目录下包报错的解决方法

    包(package)是多个Go源码的集合,是一种高级的代码复用方案,下面这篇文章主要给大家介绍了关于Go导入不同目录下包报错的解决方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Go使用sync.Map来解决map的并发操作问题

    Go使用sync.Map来解决map的并发操作问题

    在 Golang 中 map 不是并发安全的,sync.Map 的引入确实解决了 map 的并发安全问题,本文就详细的介绍一下如何使用,感兴趣的可以了解一下
    2021-10-10
  • go语言中的interface使用实例

    go语言中的interface使用实例

    这篇文章主要介绍了go语言中的interface使用实例,go语言中的interface是一组未实现的方法的集合,如果某个对象实现了接口中的所有方法,那么此对象就实现了此接口,需要的朋友可以参考下
    2015-05-05
  • Golang交叉编译(跨平台编译)的使用

    Golang交叉编译(跨平台编译)的使用

    本文主要介绍了Golang交叉编译(跨平台编译)的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • Golang 定时器(Timer 和 Ticker),这篇文章就够了

    Golang 定时器(Timer 和 Ticker),这篇文章就够了

    这篇文章主要介绍了Golang 定时器(Timer 和 Ticker),这篇文章就够了,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • go语言context包功能及操作使用详解

    go语言context包功能及操作使用详解

    这篇文章主要为大家介绍了go语言context包功能及操作使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • 深入了解Golang中的格式化输出

    深入了解Golang中的格式化输出

    fmt是Go语言中用于控制文本输出的常用标准库,文中将通过示例详细讲解一下Go语言中不同形式的格式化输出,感兴趣的小伙伴可以了解一下
    2022-11-11
  • Go语言使用protojson库实现Protocol Buffers与JSON转换

    Go语言使用protojson库实现Protocol Buffers与JSON转换

    本文主要介绍Google开源的工具库Protojson库如何Protocol Buffers与JSON进行转换,以及和标准库encoding/json的性能对比,需要的朋友可以参考下
    2023-09-09

最新评论