a6e081e5cd
inject and print version at startup for all services * inject and print version * coverage
125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"runtime/debug"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
SystemName = "doczy"
|
|
)
|
|
|
|
// These variables will be set via ldflags during build
|
|
var (
|
|
version = "(devel)"
|
|
gitCommit = "unknown"
|
|
buildTime = "unknown"
|
|
)
|
|
|
|
func IsValidUnixVersion(v int64) error {
|
|
currentVersion := GetVersionUnixTimestamp()
|
|
|
|
if v < 1 || v > currentVersion {
|
|
return fmt.Errorf("version must be in the range 0 < version <= %d", currentVersion)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var startupTime time.Time
|
|
var startupTimeOnce sync.Once
|
|
|
|
func GetVersionTimestamp() time.Time {
|
|
startupTimeOnce.Do(getStartTime(&startupTime, debug.ReadBuildInfo, time.Now))
|
|
|
|
return startupTime
|
|
}
|
|
|
|
func getStartTime(startupTime *time.Time, readBuildInfo func() (info *debug.BuildInfo, ok bool), clock func() time.Time) func() {
|
|
return func() {
|
|
info, ok := readBuildInfo()
|
|
if !ok {
|
|
*startupTime = clock()
|
|
return
|
|
}
|
|
|
|
// Look for vcs.time in build settings
|
|
for _, setting := range info.Settings {
|
|
if setting.Key == "vcs.time" {
|
|
// Parse the build timestamp
|
|
buildTime, err := time.Parse(time.RFC3339, setting.Value)
|
|
if err == nil {
|
|
*startupTime = buildTime
|
|
return
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
// Return current time if no build time found
|
|
*startupTime = clock()
|
|
}
|
|
}
|
|
|
|
func GetVersionUnixTimestamp() int64 {
|
|
return GetVersionTimestamp().UnixMilli()
|
|
}
|
|
|
|
// GetVersion returns the version of the application.
|
|
// It first checks if a version was set via ldflags during build.
|
|
// If not set via ldflags, it falls back to the Golang build info format.
|
|
// When building from a commit with an associated Git tag (e.g., v1.2.3),
|
|
//
|
|
// the runtime version uses the tag name without a timestamp.
|
|
//
|
|
// The "+dirty" suffix is appended when there are uncommitted changes
|
|
//
|
|
// present in the version control system (VCS) at the time of building.
|
|
//
|
|
// An example of a version v0.0.2-0.20250310175756-a91372dc0602+dirty.
|
|
func GetVersion() string {
|
|
// Check if version was set via ldflags
|
|
if version != "" && version != "(devel)" {
|
|
return version
|
|
}
|
|
|
|
// Fall back to debug.ReadBuildInfo
|
|
info, ok := debug.ReadBuildInfo()
|
|
if !ok {
|
|
return "unknown"
|
|
}
|
|
return info.Main.Version
|
|
}
|
|
|
|
// GetGitCommit returns the git commit hash set during build.
|
|
// Returns "unknown" if not set via ldflags.
|
|
func GetGitCommit() string {
|
|
return gitCommit
|
|
}
|
|
|
|
// GetBuildTime returns the build time set during build.
|
|
// Returns "unknown" if not set via ldflags.
|
|
func GetBuildTime() string {
|
|
return buildTime
|
|
}
|
|
|
|
// PrintVersionInfo prints version information to stderr.
|
|
// This should be called at the very beginning of main() before any environment checks.
|
|
// It prints to stderr to ensure output even if stdout is redirected or logging isn't initialized.
|
|
// Format: Starting <service> version=<version> buildTime=<buildTime> commit=<commit>
|
|
func PrintVersionInfo(serviceName string) {
|
|
// If version already contains timestamp and commit (format: YYYYMMDD.HHMMSS-commit),
|
|
// just use that. Otherwise, construct from parts.
|
|
if version != "(devel)" && version != "" {
|
|
fmt.Fprintf(os.Stderr, "Starting %s version=%s buildTime=%s commit=%s\n",
|
|
serviceName, version, buildTime, gitCommit)
|
|
} else {
|
|
// Fallback format when not built via Docker
|
|
fmt.Fprintf(os.Stderr, "Starting %s version=%s commit=%s\n",
|
|
serviceName, GetVersion(), GetGitCommit())
|
|
}
|
|
}
|