来探讨下git工作流下golang项目的version信息该如何处理比较符合标准
之前自己写的几个简单的小工具都是没有把version信息注入到编译出来的文件里,发布版本多了后也无法溯源了.想着有必要看下这方面的知识了.
观察现有的一些
之前我博客docker panic那次事故就是根据docker info里的containerd的commit id找到了相关的代码来回溯.我们先看看各个项目的version信息
docker:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28$ docker version
Client: Docker Engine - Community
Version: 19.03.2
API version: 1.40
Go version: go1.12.8
Git commit: 6a30dfc
Built: Thu Aug 29 05:28:55 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.2
API version: 1.40 (minimum version 1.12)
Go version: go1.12.8
Git commit: 6a30dfc
Built: Thu Aug 29 05:27:34 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
GitCommit: fec3683etcd:
1
2
3
4
5$ etcd --version
etcd Version: 3.3.13
Git SHA: 98d3084
Go Version: go1.10.8
Go OS/Arch: linux/amd64k8s:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25$ kubectl version -o json
{
"clientVersion": {
"major": "1",
"minor": "13",
"gitVersion": "v1.13.12",
"gitCommit": "a8b52209ee172232b6db7a6e0ce2adc77458829f",
"gitTreeState": "clean",
"buildDate": "2019-10-15T12:12:15Z",
"goVersion": "go1.11.13",
"compiler": "gc",
"platform": "linux/amd64"
},
"serverVersion": {
"major": "1",
"minor": "13",
"gitVersion": "v1.13.12",
"gitCommit": "a8b52209ee172232b6db7a6e0ce2adc77458829f",
"gitTreeState": "clean",
"buildDate": "2019-10-15T12:04:30Z",
"goVersion": "go1.11.13",
"compiler": "gc",
"platform": "linux/amd64"
}
}
如何注入
对比下都有如下信息:
- version
- go version
- arch info and os
- git commit id
- build date
github上一些star比较多的项目的makefile
都是类似的如下内容
1 | BUILD_VERSION := $(shell cat version) |
搜索了下资料是通过go build -ldflags
注入变量的,例如下面:
1 | $cat>test.go<<EOF |
包名.变量名
形式注入到编译过程里,可以覆盖掉原有变量的值,变量首字母可以不用大写也会注入.知道了实现方法,来思考下如何优雅.
市面上很多都是单独整了个version文件,直接从里面cat的.github上不少人这样version或者go文件里写版本号,xxx-dev,测试通过后再改下commit推上去,这样会多一个无用的commit
实际都是master代码测试完美了后发布tag,tag触发release.可以根据tag号做版本号来外部注入.总的来说就是k8s这方面最规范.去看它的源码.
从k8s源码学习
查看了下源码结构找到了核心部分staging/src/k8s.io/component-base/version/version.go
1 | func Get() apimachineryversion.Info { |
以及文件staging/src/k8s.io/component-base/version/base.go
里等待被注入的变量.查看了下makefile的逻辑,复杂的逻辑和变量处理是仍shell脚本里处理的,因为makefile并不是一个功能强的脚本语言.
脚本hack/lib/init.sh
比较先执行,里面有执行
1 | # The root of the build/dist directory |
version脚本里逻辑可能对于非运维看比较麻烦,简单说下上面version信息:
Major
就是大版本号,例如1.18.2
就是1
Minor
就是小版本号,例如1.18.2
就是18
GitVersion
就是release的版本,如果你是master代码下载编译则是最新的$release-dirty
字样GitCommit
是取编译时候的git commit idGitTreeState
是你代码有没有commit,修改了代码没有commit则是dirty,你提issue社区人员看到这个字段值是dirty后不会过多理你来浪费时间- 其余几个没啥可说的
个人实现
我简单说下现在我推荐的逻辑判断:
- 获取项目的目录,也就是上面的
KUBE_ROOT
设置下命令别名git,让git只对项目目录生效:1
git=(git --work-tree "${KUBE_ROOT}") #后面使用git命令用:"${git[@]}"
- 获取构建时间
1
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
- 获取当前HEAD的commit id
1
HEAD=$("${git[@]}" rev-parse "HEAD^{commit}")
- tag名不为空的时候(指定编译之前的tag版本传入tag变量),或者ci是tag触发的.判断下该tag名存在否
1
2
3
4
5
6if [ -n "$TAG" ]; then
TAG_COMMITID=$("${git[@]}" rev-parse $TAG 2>/dev/null)
if [ "$?" -ne 0 ];then
echo no such tag: $TAG
exit 1
fi - 否则tag为空下也就是master的代码,获取下离master最近的也就是最新的的tag号:
1
2
3
4else #默认取最新的tag
TAG_COMMITID=$("${git[@]}" rev-list --tags --max-count=1)
TAG=$("${git[@]}" describe --tags ${TAG_COMMITID})
fi - 取tag号
1
2"${git[@]}" checkout $TAG 2>/dev/null
BUILD_VERSION=${TAG} - 设置git tree state,没提交代码则是dirty
1
2
3
4
5if [ -z "$("${git[@]}" status --porcelain 2>/dev/null)" ];then
GIT_TREE_STATE='clean'
else
GIT_TREE_STATE='dirty'
fi - 对比tag的commit id是否和head一致,这步是针对master的ci触发的version设置为上一个
$tag-dirty
的值1
2
3
4
5
6
7if [ "${HEAD}" != "${TAG_COMMITID}" ];then
#tag的基础上改动,所以tag版本号-dirty
BUILD_VERSION+="-dirty"
COMMIT_ID=${HEAD}
else
COMMIT_ID=${TAG_COMMITID}
fi - 最后git切回去,因为前面都是取变量的值,不会动代码
1
"${git[@]}" checkout $HEAD 2>/dev/null
版本号信息可以输出到一个gitignore的文件里,编译的时候读取文件里的值拼接成go build
的参数.我个人实现可能不是最优,我个人的整个细节性都在我github上
https://github.com/zhangguanzhang/gonelist
入口脚本是 https://github.com/zhangguanzhang/gonelist/blob/master/build/build.sh
1 | # 使用master的代码构建 |
版本信息的打印我是一行一条,方便脚本来做升级比对的正则提取,花样多的话可以像k8s那样支持 yaml 和 json 格式输出啥的
2021/02/02 版本号的部分可以直接这样获取,下面是切到 1.16.0
后修改文件,然后提交
1 | git log |