最近使用 Golang 开发的网站项目比较多,所以把一些常用的功能写成了一个公共库,放在 GitHub 上,方便在其他项目中通过 go get 使用。 这个公共库的版本号需要通过 git tag 来管理。每次发布新版本时,都会在 git 上打一个 tag,然后在其他项目中通过 go get 来获取最新版本。例如:
go get github.com/sunzhongwei/hades@v1.0.2
手动打 git tag 的烦恼
手动设置很容易漏掉 v 前缀,例如,v1.0.2 一不小心就写成了 1.0.2。 这个问题还不容易被察觉 🥲,在其他项目中需要 go get 新版本时,如果还继续使用 v 开头的版本号,就会收到提示找不到对应的版本号。 排查半天才发现是漏掉了 v 前缀。
第二个问题是,VSCode 中通过 GUI 界面打 tag,操作很繁琐。最后还需要点一堆按钮才能找到 Push Tags 的选项。
所以,我决定搞一个自动化的方案,来解决这两个问题。
方案一(采用)
我参考 gin 的做法,在项目中创建一个 version.go 文件,里面定义版本号常量。 例如 version.go 文件:
package hades
const Version = "v1.0.3"
然后在 Makefile 中实现一个命令,自动读取当前 version.go 文件中的版本号,然后在当前版本号上增加一个小版本号。例如,当前是 v1.0.1, 那么下一个小版本号就是 v1.0.2。把新版本号更新到 version.go,并且 commit ,然后添加 git tag,并将 git tag push 到 remote。
让 AI 实现了这段 Makefile 代码,具体参考:
https://github.com/sunzhongwei/hades/blob/main/Makefile
SHELL := /bin/bash
.PHONY: tag
tag:
@current=$$(grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' version.go | head -n1 | tr -d 'v'); \
if [ -z "$$current" ]; then echo "version not found in version.go"; exit 1; fi; \
maj=$$(echo $$current | cut -d. -f1); \
min=$$(echo $$current | cut -d. -f2); \
patch=$$(echo $$current | cut -d. -f3); \
newpatch=$$(expr $$patch + 1); \
new="v$$maj.$$min.$$newpatch"; \
printf "Bump: v%s -> %s\n" "$$current" "$$new"; \
sed -E -i.bak 's/(const Version = ")([^"]+)(")/\1'"$$new"'\3/' version.go; \
git add version.go; \
git commit -m "chore(release): $$new"; \
git push origin HEAD; \
git tag -a "$$new" -m "release $$new"; \
git push origin "$$new"; \
rm -f version.go.bak
这样就只需要一行命令,就能自动升级版本号了
make tag
命令说明:
grep 是提取 version.go 文件中的版本号:
$ grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' version.go | head -n1 | tr -d 'v'
1.0.3
sed 命令是更新 version.go 文件中的版本号:
因为 Makefile 用了 sed 的 -i.bak 选项:sed -i.bak ... version.go 会在就地修改前把原文件复制一份为 version.go.bak 作为备份,修改失败或需要回退时可以用备份恢复。随后通过 rm -f version.go.bak 删除该备份。这样写也提高了在 macOS/BSD(需要 -i 带扩展名)和 GNU sed 之间的兼容性。
看看效果
本地的 git 历史:

github remote 端的 tags:

方案二(未采用)
第二个方案是,使用 git tag 命令,来或者最新的 tag,然后在这个 tag 的基础上增加小版本号,生成新的 tag。
例如,按语义化版本号排序(v1.2.3 格式)
$ git tag --sort=-version:refname | head -1
v1.0.2
然后把这个 tag 解析成主版本号、次版本号和小版本号,增加小版本号,生成新的 tag。
但是,我担心这个方案不够直观。我倾向于能够直接在代码中看到当前版本号是什么,而不是需要通过 git 命令去查询最新的 tag。 如果出现不兼容性的改动,也能直接通过判断代码中的版本号,来做兼容性处理。
关于作者 🌱
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式