利用Github Actions为Go程序添加git与编译信息
实现效果 可以通过/version获取到当前服务的git版本号以及相关信息,也可以看到编译时间与编译环境等信息。
实现原理 在进行Go程序的编译时,可以通过传入额外的参数来修改编译过程,比较常见的有:
o <output>
: 指定输出文件的名称。例如,go build -o myprogram
将生成名为 myprogram
的可执行文件。
ldflags "<flags>"
: 用于传递链接器(ld)的标志。您可以使用此选项将特定标志传递给链接器,例如设置版本信息或自定义链接行为。例如,go build -ldflags "-X main.version=1.0.0"
将在构建过程中设置名为 version
的变量的值为 1.0.0
。
gcflags "<flags>"
: 用于传递Go编译器(gc)的标志。您可以使用此选项传递特定标志给编译器,例如优化级别或其他调试信息。例如,go build -gcflags "-N -l"
将禁用优化并生成带有调试信息的可执行文件。
本次的实现则是利用ldflags "<flags>"
指令进行信息的注入。
核心构建指令 go build -o ./temp/release/linux_amd64/service -ldflags " -X 'home-network-watcher/utility/bin_utils.GitTag=v1.11.12' -X 'home-network-watcher/utility/bin_utils.GitCommitLog=8405a9a7a15c33399d8cc400f08836e14b9dc085' -X 'home-network-watcher/utility/bin_utils.BuildTime=2023.05.21.03:56:22' -X 'home-network-watcher/utility/bin_utils.BuildGoVersion=github@action golang:1.20-buster' " main.go
接下来进行Go代码与GitHub Actions、Dockerfile的编写。
Go美化注入代码 新建一个.Go文件,以变量的形式用以储存在程序中需要的git与版本信息,并创建一个函数用以美化显示信息。
创建需要注入信息的变量,设置默认值为unknown,等待编译注入再进行重新赋值。
package binInfoimport ( "fmt" "runtime" "strings" ) var ( GitTag = "unknown" GitCommitLog = "unknown" GitStatus = "unknown" BuildTime = "unknown" BuildGoVersion = "unknown" ) var ( VersionString = "GitTag:" + GitTag + "\n" + "GitCommitLog:" + GitCommitLog + "\n" + "GitStatus:" + GitStatus + "\n" + "BuildTime:" + BuildTime + "\n" + "BuildGoVersion:" + BuildGoVersion + "\n" ) func StringifyMultiLine () string { return fmt.Sprintf("GitTag=%s\nGitCommitLog=%s\nGitStatus=%s\nBuildTime=%s\nGoVersion=%s\nruntime=%s/%s\n" , GitTag, GitCommitLog, GitStatus, BuildTime, BuildGoVersion, runtime.GOOS, runtime.GOARCH) } func init () { beauty() } func beauty () { if GitStatus == "" { GitStatus = "cleanly" } else { GitStatus = strings.Replace(strings.Replace(GitStatus, "\r\n" , " |" , -1 ), "\n" , " |" , -1 ) } }
在Go项目中显示git与编译信息 可在main.go中设置命令行参数进行信息显示。
如下代码,在运行编译后的二进制文件时,通过-v
进行信息显示。
package mainimport ( "flag" "fmt" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" "github.com/gogf/gf/v2/os/gctx" "home-network-watcher/internal/cmd" _ "home-network-watcher/internal/logic" _ "home-network-watcher/internal/packed" binInfo "home-network-watcher/utility/bin_utils" "os" ) func main () { v := flag.Bool("v" , false , "Show bin info." ) flag.Parse() if *v { _, _ = fmt.Fprint(os.Stderr, binInfo.StringifyMultiLine()) os.Exit(1 ) } cmd.Main.Run(gctx.New()) }
或者可以通过绑定接口的形式,在访问服务/version
时进行信息的显示。
group.ALL("/version" , func (r *ghttp.Request) { r.Response.Write(binInfo.VersionString) })
Dockerfile编写 首先定义Go构建器
FROM golang:1.20 -buster AS builder
设置需要注入的构建参数的默认值
ARG GIT_TAG="unknown" ARG GIT_COMMIT_LOG="unknown" ARG BUILD_TIME="unknown" ARG BUILD_GO_VERSION="github@action golang:1.20-buster"
关键的注入则是通过在docker进行构建时也可以进行参数的绑定这个特性来完成的。
GitHub Action将ARG的参数传入docker build后,接下来则将信息传入到gf build或者go build中,首先构建传入的信息。
ENV LDFLAGS=" \ -X 'home-network-watcher/utility/bin_utils.GitTag=${GIT_TAG}' \ -X 'home-network-watcher/utility/bin_utils.GitCommitLog=${GIT_COMMIT_LOG}' \ -X 'home-network-watcher/utility/bin_utils.BuildTime=${BUILD_TIME}' \ -X 'home-network-watcher/utility/bin_utils.BuildGoVersion=${BUILD_GO_VERSION}' \ "
以'home-network-watcher/utility/bin_utils.GitTag=${GIT_TAG}'
为例
go通过追溯包名进行参数数据的传递,在上述定义存储变量的Go文件中,模块为"home-network-watcher/utility/bin_utils”
,则在构建时也是传入包名加内部的变量名进行数据传递,在这里我们将上面获取到的GIT_TAG传递给GitTag变量。
最后则是通过go build传递进去
RUN go build -ldflags "${LDFLAGS} " -o temp/release/linux_amd64/service main.go
以下为完整Dockerfile
FROM golang:1.20 -buster AS builderARG GIT_TAG="unknown" ARG GIT_COMMIT_LOG="unknown" ARG BUILD_TIME="unknown" ARG BUILD_GO_VERSION="github@action golang:1.20-buster" WORKDIR /go/src/app COPY . . RUN echo "GIT_TAG=${GIT_TAG} " RUN echo "GIT_COMMIT_LOG=${GIT_COMMIT_LOG} " RUN echo "BUILD_TIME=${BUILD_TIME} " RUN echo "BUILD_GO_VERSION=${BUILD_GO_VERSION} " ENV LDFLAGS=" \ -X 'home-network-watcher/utility/bin_utils.GitTag=${GIT_TAG}' \ -X 'home-network-watcher/utility/bin_utils.GitCommitLog=${GIT_COMMIT_LOG}' \ -X 'home-network-watcher/utility/bin_utils.BuildTime=${BUILD_TIME}' \ -X 'home-network-watcher/utility/bin_utils.BuildGoVersion=${BUILD_GO_VERSION}' \ " RUN go get github.com/gogf/gf/cmd/gf/v2 RUN go install github.com/gogf/gf/cmd/gf/v2 RUN gf build -e "-ldflags \"${LDFLAGS} \" " FROM loads/alpine:3.8 LABEL maintainer="Hamster <liaolaixin@gmail.com>" ENV WORKDIR /app/main COPY --from=builder /go/src/app/temp/release/linux_amd64/service $WORKDIR /service RUN chmod +x $WORKDIR /service EXPOSE 10401 WORKDIR $WORKDIR CMD ["./service" ]
Github Actions编写 十分简单,只需要利用Actions里面可以方便获取的git信息即可,通过—build-arg
传递入docker build中
name: Build and push Docker image on: push: tags: - 'v*' env: IMAGE_NAME: ${{ secrets.DOCKER_IMAGE_NAME }} jobs: build-and-push: runs-on: ubuntu-latest steps: - name: Get version id: get_version run: echo "CURRENT_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - name: Get Git Commit Log id: git-commit-log run: echo "GIT_COMMIT_LOG=${{ github.sha }} " >> $GITHUB_ENV - name: Get Build Time id: build-time run: echo "BUILD_TIME=$(date +'%Y.%m.%d.%H:%M:%S')" >> $GITHUB_ENV - name: Checkout code uses: actions/checkout@v3 - name: Login to Dockerhub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - name: Build Docker image run: docker build -t $IMAGE_NAME:${{ github.sha }} . --build-arg GIT_TAG=${{env.CURRENT_VERSION}} --build-arg GIT_COMMIT_LOG="${{env.GIT_COMMIT_LOG}}" --build-arg BUILD_TIME=${{env.BUILD_TIME}} - name: Print environment variables run: | echo "${{env.GIT_COMMIT_LOG}} " echo "${{env.BUILD_TIME}} " echo "${{env.CURRENT_VERSION}} " - name: Tag Docker image run: docker tag $IMAGE_NAME:${{ github.sha }} $IMAGE_NAME:${{ env.CURRENT_VERSION }} - name: Tag Docker image as latest run: docker tag $IMAGE_NAME:${{ github.sha }} $IMAGE_NAME:latest - name: Push Docker image run: | docker push $IMAGE_NAME:${{ env.CURRENT_VERSION }} docker push $IMAGE_NAME:latest
将获取到的信息首先储存在$GITHUB_ENV
中,在构建docker build指令时进行传递
docker build -t $IMAGE_NAME :${{ github.sha } } . --build-arg GIT_TAG=${{env.CURRENT_VERSION} } --build-arg GIT_COMMIT_LOG="${{env.GIT_COMMIT_LOG} }" --build-arg BUILD_TIME=${{env.BUILD_TIME} }
这样子便完成了git与版本信息的注入,十分的自动便捷。
结语 主要的难点还是如何将信息在整个构建的过程中传递下去,只要可以传递好所需的信息在构建的时候即可以方便地通过go build的-ldflags将信息传递入二进制文件中。