前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【Golang】从人工到智能:编译时自动化注入Git版本信息

【Golang】从人工到智能:编译时自动化注入Git版本信息

作者头像
编码如写诗
发布2025-03-03 14:21:59
发布2025-03-03 14:21:59
4500
代码可运行
举报
文章被收录于专栏:编码如写诗编码如写诗
运行总次数:0
代码可运行

1.前言

版本控制是软件开发中的核心环节。传统上,我们通过配置文件控制、数据库记录控制和硬编码来管理版本信息。然而,随着自动化技术的不断发展,这些方法往往需要手动维护,容易受到篡改和人为疏忽的影响,导致版本信息滞后或错误。之前查看kubesphere/kubekey源码时,发现其和kubernetes都使用了编译时自动注入Git版本信息的方式来控制版本。该方式通过自动化、强一致性和防篡改性,确保版本信息的准确性和可靠性,成为生产环境的首选方案。

PS:与docker镜像版本不同。

对比说明:

‌特性‌

‌编译时注入 Git 信息‌

‌配置文件写入版本信息‌

‌数据库记录版本‌

‌硬编码版本‌

‌自动化程度‌

✅ ‌完全自动‌:通过构建脚本动态获取 Git 信息,无需人工维护‌

❌ ‌手动维护‌:需人工更新配置文件,易遗忘或出错‌

⚠️ ‌半自动‌:需应用启动时写入数据库,依赖代码逻辑‌

❌ ‌完全手动‌:版本号直接写在代码中,需修改源码‌

‌准确性‌

✅ ‌实时精准‌:直接关联当前代码的 Commit Hash、分支/标签‌

❌ ‌可能滞后‌:配置文件可能未及时更新‌

⚠️ ‌依赖写入时机‌:若启动时未更新,可能不准确‌

❌ ‌固定不变‌:代码修改后需重新编译,否则版本信息失效‌

‌防篡改性‌

✅ ‌不可篡改‌:版本信息编译进二进制文件‌

❌ ‌可能被篡改‌:配置文件易被修改或覆盖‌

⚠️ ‌依赖权限控制‌:数据库记录可能被误删或篡改‌

❌ ‌可修改‌:需重新编译代码才能更新版本‌

‌依赖项‌

❌ ‌依赖构建环境‌:需安装 Git 工具链(如 git describe)‌

✅ ‌无额外依赖‌:仅需读取静态文件‌

❌ ‌依赖数据库‌:需数据库服务可用‌

✅ ‌无依赖‌:版本信息直接内置于代码‌

‌适用场景‌

✅ ‌生产环境‌:需严格追踪版本、审计或快速回滚‌

⚠️ ‌开发/测试‌:简单场景,无需精准版本控制‌

✅ ‌数据关联场景‌:需与数据库变更记录绑定‌

❌ ‌原型验证‌:仅临时或小型项目适用‌

‌与代码一致性‌

✅ ‌强一致‌:自动绑定当前代码状态(包括未提交的改动)‌

❌ ‌弱一致‌:需手动同步代码与配置文件‌

⚠️ ‌间接关联‌:依赖应用启动时写入数据库的逻辑‌

❌ ‌完全脱节‌:版本信息与代码更新需手动同步‌

‌持久化与追溯性‌

⚠️ ‌仅限日志‌:需额外记录日志供审计‌

❌ ‌短期存储‌:文件可能丢失或覆盖‌

✅ ‌长期追溯‌:数据库支持历史版本查询‌

❌ ‌无历史记录‌:无法追溯旧版本信息‌

2.ldflags简介

ldflags 是 Go 语言编译时的一个重要选项,用于传递链接器(linker)标志。通过使用 ldflags,开发者可以在编译过程中注入变量、修改包的属性或控制链接器的行为。

在 Go 中,ldflags 通常与 go buildgo install 命令一起使用。本文将使用-ldflags传参的形式,在go build时将包中的version变量的值修改为git版本。

3.涉及的代码

  • 低需求:

只记录version版本信息

在原有代码基础上新增version包,并新增version.go,填写如下代码:

代码语言:javascript
代码运行次数:0
复制
package version

var version = "3.0.0"

func GetVersion() string {
    return version
}

本文将主要介绍该种模式

编译时注入Git版本信息,通过如下命令编译后version.version的值为${GIT_VERSION}的值

代码语言:javascript
代码运行次数:0
复制
go build  -ldflags ="-X 'github.com/gjing1st/hertz-admin/version.version=${GIT_VERSION}" -o ha cmd/ha/main.go
  • 高要求:

记录详细git版本信息,该模块主要参考kubesphere/kubekeyk8s

在原有代码基础上新增version包,并新增version.go,填写如下代码:

代码语言:javascript
代码运行次数:0
复制
package version

import (
    "fmt"
    "runtime"
)

var appName = "ha-server"

func GetAppName() string {
    return appName
}

var (
    gitMajor     string// major version, always numeric
    gitMinor     string// minor version, numeric possibly followed by "+"
    gitVersion   string// semantic version, derived by build scripts
    gitCommit    string// sha1 from git, output of $(git rev-parse HEAD)
    gitTreeState string// state of git tree, either "clean" or "dirty"
    buildDate    string// build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)

// Info exposes information about the version used for the current running code.
type Info struct {
    Major        string`json:"major,omitempty"`
    Minor        string`json:"minor,omitempty"`
    GitVersion   string`json:"gitVersion,omitempty"`
    GitCommit    string`json:"gitCommit,omitempty"`
    GitTreeState string`json:"gitTreeState,omitempty"`
    BuildDate    string`json:"buildDate,omitempty"`
    GoVersion    string`json:"goVersion,omitempty"`
    Compiler     string`json:"compiler,omitempty"`
    Platform     string`json:"platform,omitempty"`
}

// Get returns an Info object with all the information about the current running code.
func Get() Info {
    return Info{
       Major:        gitMajor,
       Minor:        gitMinor,
       GitVersion:   gitVersion,
       GitCommit:    gitCommit,
       GitTreeState: gitTreeState,
       BuildDate:    buildDate,
       GoVersion:    runtime.Version(),
       Compiler:     runtime.Compiler,
       Platform:     fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
    }
}

// String returns info as a human-friendly version string.
func (info Info) String() string {
    return info.GitVersion
}

该阶段编译时Git版本注入可查看博主开源框架hertz-admin:基于字节跳动hertz建的后台管理框架。这里不再介绍。

hertz-admin[1]

4.获取Git版本信息

获取Git版本信息主要从以下三个方面介绍

  • Makefile中直接获取
  • 通过shell获取
  • CI/CD中获取

Makefile中获取

获取git tag作为版本信息

代码语言:javascript
代码运行次数:0
复制
GIT_VERSION=$(shell git describe --tags --abbrev=14 --match "v[0-9]*" 2>/dev/null | sed 's/^v//')

获取git分支中的版本信息:1.1.0

分支结构如下:

代码语言:javascript
代码运行次数:0
复制
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
GIT_VERSION := $(shell echo "$(GIT_BRANCH)" | sed -e 's/^v//' -e 's/[^0-9.]*$$//')

通过shell获取

可参考kubesphere/kubekeyk8s中的shell,由于篇幅限制这里不再记录,若需要可查看

version.sh[2]

CI/CD

这里只介绍基于gitlab-runnerci/cd其他可参考编写

代码语言:javascript
代码运行次数:0
复制
GIT_VERSION=`echo $CI_COMMIT_REF_NAME | sed 's/\(v[0-9\x2e]*\).*/\1/'`

5.编译涉及的阶段

  • 编译可执行文件
  • Docker 镜像打包
  • CI/CD 流水线

接下来介绍每个阶段时如何获取Git版本信息并传递至编译阶段。

直接编译

这里使用Makefile去编译并使用Git tag为例:

代码语言:javascript
代码运行次数:0
复制
# 获取git 版本信息
GIT_VERSION=$(shell git describe --tags --abbrev=14 --match "v[0-9]*" 2>/dev/null | sed 's/^v//')
#编译参数
LDFLAGS="-s -w -extldflags -static -X github.com/gjing1st/hertz-admin/version.VERSION=$(GIT_VERSION)'"
# 编译 go build
.PHONY: build
build:
    go build -ldflags $(LDFLAGS) -o ha-server cmd/ha/main.go

Docker 镜像打包

Makefile中定义docker目标

代码语言:javascript
代码运行次数:0
复制
.PHONY: docker
TAG := $(IMAGE_NAME):$(GIT_VERSION)
docker: ## 打包成docker镜像
    @echo "Building image with tag '$(TAG)'"
    docker build --build-arg LDFLAGS="$(LDFLAGS)" -f ./build/docker/Dockerfile -t $(TAG) .

Dockerfile中代码如下:

代码语言:javascript
代码运行次数:0
复制
ARG GO_VERSION=1.24.0
ARG ALPINE_VERSION=3.21
FROM  golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS build
ARG LDFLAGS
WORKDIR /src
ENV GOPROXY https://goproxy.cn,direct
RUN --mount=type=cache,target=/go/pkg/mod/ \
    --mount=type=bind,source=go.sum,target=go.sum \
    --mount=type=bind,source=go.mod,target=go.mod \
    go mod download -x
RUN --mount=type=cache,target=/go/pkg/mod/ \
    --mount=type=bind,target=. \
    CGO_ENABLED=0 GOOS=linux  go build -trimpath -ldflags="-s -w ${LDFLAGS}" -o /bin/server ./cmd/ha/main.go

FROM alpine:${ALPINE_VERSION}
COPY --from=build /bin/server /bin/
EXPOSE 9680
ENTRYPOINT [ "/bin/server" ]

CI/CD

这里使用GitLab-Runner实现,并使用git分支中的版本号,将代码提交次数作为最后一位。

如v1.0.0版本第一次提交代码后的版本为:1.0.0.0,第三次提交代码后的版本为:1.0.0.2

.gitlab-ci.yml中部分代码如下:

代码语言:javascript
代码运行次数:0
复制
- VERSION=`echo $CI_COMMIT_REF_NAME | sed 's/\(v[0-9\x2e]*\).*/\1/'`
- echo ${CI_COMMIT_SHA}
# c8679fbb719994631bc6a9930d41617f7a1b382d 是上一步某次提交的哈希值
- COMMIT_COUNT=$(git rev-list --count 8844e6726e5e2c8837e1d5c348864cd97864f951..HEAD)
- echo ${COMMIT_COUNT}
# 版本信息
- echo ${VERSION:1}.${COMMIT_COUNT}
#打包 docker 
- docker build --build-arg APP_VERSION=${VERSION:1}.${COMMIT_COUNT}  -t ha-server:${VERSION}  .

Dockerfile中编译部分

代码语言:javascript
代码运行次数:0
复制
go build -ldflags="-s -w -X has/version.version=${APP_VERSION}"  -o has ./cmd/has/main.go

6.总结

本文主要对比了 Go 项目版本管理的常见方案,重点解析 基于 Git 的编译时自动化注入 的实践优势。通过各阶段式代码实现(信息提取、清洗、注入与校验),开发者可快速落地自动化流程。最终,以“最小运维成本”实现版本可靠性与可追溯性的平衡。

引用链接

[1]

基于hertz的后台管理框架: https://github.com/gjing1st/hertz-admin。

[2]

hertz-admin: https://github.com/gjing1st/hertz-admin/blob/master/version/version.sh

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编码如写诗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.前言
    • 对比说明:
  • 2.ldflags简介
  • 3.涉及的代码
  • 4.获取Git版本信息
    • Makefile中获取
    • 通过shell获取
    • CI/CD
  • 5.编译涉及的阶段
    • 直接编译
    • Docker 镜像打包
    • CI/CD
  • 6.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档