package version import ( "fmt" "log/slog" "runtime" "runtime/debug" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/cobra" "golang.org/x/mod/semver" ) // VERSION has the current software version (set in the build process) var VERSION string var buildTime string var gitVersion string var gitModified bool var info Info type Info struct { Version string `json:",omitempty"` GitRev string `json:",omitempty"` GitRevShort string `json:",omitempty"` BuildTime string `json:",omitempty"` } func init() { info.BuildTime = buildTime info.GitRev = gitVersion if len(VERSION) == 0 { VERSION = "dev-snapshot" } else { if !strings.HasPrefix(VERSION, "v") { VERSION = "v" + VERSION } if !semver.IsValid(VERSION) { slog.Default().Warn("invalid version number", "version", VERSION) } } if bi, ok := debug.ReadBuildInfo(); ok { for _, h := range bi.Settings { // logger.Setup().Info("build info", "k", h.Key, "v", h.Value) switch h.Key { case "vcs.time": if len(buildTime) == 0 { buildTime = h.Value } info.BuildTime = h.Value case "vcs.revision": // https://blog.carlmjohnson.net/post/2023/golang-git-hash-how-to/ // todo: use BuildInfo.Main.Version if revision is empty info.GitRev = h.Value // if gitVersion != h.Value { // logger.Setup().Warn("gitVersion and info.GitRev differs", "gitVersion", gitVersion, "gitRev", info.GitRev) // } gitVersion = h.Value case "vcs.modified": if h.Value == "true" { gitModified = true } } } } info.GitRevShort = info.GitRev if len(info.GitRevShort) > 7 { info.GitRevShort = info.GitRevShort[:7] } info.Version = VERSION Version() } func VersionCmd(name string) *cobra.Command { versionCmd := &cobra.Command{ Use: "version", Short: "Print version and build information", Run: func(cmd *cobra.Command, args []string) { ver := Version() fmt.Printf("%s %s\n", name, ver) }, } return versionCmd } func RegisterMetric(name string, registry prometheus.Registerer) { if len(name) > 0 { name = strings.ReplaceAll(name, "-", "_") name = name + "_build_info" } else { name = "build_info" } buildInfo := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: name, Help: "Build information", }, []string{ "version", "buildtime", "gittime", "git", }, ) registry.MustRegister(buildInfo) info := VersionInfo() buildInfo.WithLabelValues( fmt.Sprintf("%s/%s", info.Version, info.GitRevShort, ), buildTime, info.BuildTime, info.GitRev, ).Set(1) } var v string func VersionInfo() Info { return info } func Version() string { if len(v) > 0 { return v } extra := []string{} if len(buildTime) > 0 { extra = append(extra, buildTime) } extra = append(extra, runtime.Version()) v := VERSION if len(gitVersion) > 0 { g := gitVersion if len(g) > 7 { g = g[0:7] } v += "/" + g if gitModified { v += "-M" } } v = fmt.Sprintf("%s (%s)", v, strings.Join(extra, ", ")) return v } func CheckVersion(version, minimumVersion string) bool { if version == "dev-snapshot" { return true } if semver.Compare(version, minimumVersion) < 0 { // log.Debug("version too old", "v", cl.Version.Version) return false } return true }