Files
query-orchestration/internal/serviceconfig/build/build.go
T
Jay Brown a6e081e5cd Merged in feature/log-version (pull request #185)
inject and print version at startup for all services

* inject and print version

* coverage
2025-09-18 21:06:21 +00:00

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())
}
}