SoFunction
Updated on 2025-03-04

Example of a method to manage versions using Go

Introduction

If you have ever run docker version,

You will find that it provides a lot of information:

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

For compiled binary files, it is very important to obtain version information.
Provide detailed information as much as possible, which is conducive to later maintenance and troubleshooting.

How to implement it

There are two ways to make version information, etc.

A type of acquisition from the outside, such as configuration files,

Another way to get it from the source code, write configuration information to the source code.

Both of these are not very good, for example, the compilation time is not very easy to determine.
It is best to be able to determine this information when go build.

Fortunately, go build provides an option called -ldflags '[pattern=]arg list'.

-X =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.

This allows us to specify the values ​​of certain variables when compiling and generating binary files.

For example, we have a file that is part of the company/buildinfo package.

package buildinfo

var BuildTime string

Run go build -ldflags="-X 'company/=$(date)'" to record the compilation time,

Set the value of BuildTime to the time at compile time, that is, the time taken from $(date).

refer to:

Compile packages and dependencies
Command link
Including build information in the executable

practice

Added a pkg/version package to obtain version information.

package version

// These values ​​should be passed from outsidevar (
  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"
)

// Version information at build timetype 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 
}

func Get() VersionInfo {
  return VersionInfo{
    GitTag:    gitTag,
    GitCommit:  gitCommit,
    GitTreeState: gitTreeState,
    BuildDate:  buildDate,
    GoVersion:  (),
    Compiler:   ,
    Platform:   ("%s/%s", , ),
  }
}

It mainly defines a structure to maintain version information.

Some information can be obtained through runtime, while others are transmitted in at compile time.

There is no clear version number here, but a git tag is used as the version tag.

Finally, define a command version.

package cmd

import (
  "encoding/json"
  "fmt"

  "/spf13/cobra"
  "/web/pkg/version"
)

var versionCmd = &{
  Use:  "version",
  Short: "Print the version info of server",
  Long: "Print the version info of server",
  Run: func(cmd *, args []string) {
    printVersion()
  },
}

func printVersion() {
  info := ()
  infoj, err := (&info, "", " ") // Add a little indentation  if err != nil {
    ("Error encountered: %v\n", err)
  }
  (string(infoj))
}

Don't forget to add subcommands using AddCommand.

// Initialize, set flag, etc.func init() {
  (initConfig)
  ().StringVarP(&cfgFile, "config", "c", "./conf/", "config file (default: ./conf/)")
  (versionCmd)
}

Therefore, the code has basically been modified, and there is still the last point left, modify the Makefile file,

In order to simplify the operation process.

Modify Makefile

SHELL := /bin/bash
BASEDIR = $(shell pwd)

# build with version infos
versionDir = "/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

The first line defines the running shell, the default is /bin/sh, and it has been changed to the more commonly used /bin/bash.

Then, a lot of required parameters are defined.
Added the parameter -ldflags ${ldflags} when running go build.

In this way, you can generate a binary file with version information in the future just by using make build.

After compilation, you can run ./web version to view specific version information.

Summarize

By adding extra information to compile time, more interactive binary files can be generated.
At the same time, you can also experience the convenience brought by Makefile.

The code for the current part
As versionv0.12.0

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.