package version import ( "fmt" "os" "strings" "time" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" ) // Version info populated at build time var ( Version string // Git tag or commit hash Commit string // Short commit hash BuildDate string // Build timestamp ) // Info returns version information type Info struct { Version string `json:"version"` Commit string `json:"commit"` BuildDate string `json:"build_date"` } // GetInfo returns version information func GetInfo() Info { v := Info{ Version: Version, Commit: Commit, BuildDate: BuildDate, } // If not set during build, try to get from git if v.Version == "" || v.Version == "unknown" || v.Version == "(devel)" { if gitVersion, gitCommit := getGitInfo(); gitVersion != "" { v.Version = gitVersion v.Commit = gitCommit } } // If build date not set, use current time if v.BuildDate == "" { v.BuildDate = time.Now().Format(time.RFC3339) } return v } // getGitInfo returns the latest tag or short commit hash and the commit hash func getGitInfo() (string, string) { // Get the current working directory dir, err := os.Getwd() if err != nil { return "", "" } // Open the git repository repo, err := git.PlainOpen(dir) if err != nil { return "", "" } // Get the HEAD reference head, err := repo.Head() if err != nil { return "", "" } commitHash := head.Hash().String()[:7] // Get all tags tagIter, err := repo.Tags() if err != nil { return commitHash, commitHash } // Get the commit for HEAD commit, err := repo.CommitObject(head.Hash()) if err != nil { return commitHash, commitHash } // Build ancestry map ancestry := make(map[string]bool) c := commit for c != nil { ancestry[c.Hash.String()] = true c, _ = c.Parent(0) } // Find the most recent tag that's an ancestor of HEAD var latestTag string err = tagIter.ForEach(func(ref *plumbing.Reference) error { // Get the target commit targetHash := ref.Hash() // Get the target commit targetCommit, err := repo.CommitObject(targetHash) if err != nil { return nil } // Check if this tag is an ancestor of HEAD checkCommit := targetCommit for checkCommit != nil { if ancestry[checkCommit.Hash.String()] { // Extract tag name (remove refs/tags/ prefix) tagName := strings.TrimPrefix(ref.Name().String(), "refs/tags/") if latestTag == "" || tagName > latestTag { latestTag = tagName } return nil } checkCommit, _ = checkCommit.Parent(0) } return nil }) if err != nil { return commitHash, commitHash } if latestTag != "" { return latestTag, commitHash } return commitHash, commitHash } // String returns a formatted version string func String() string { info := GetInfo() return fmt.Sprintf("Version: %s\nCommit: %s\nBuild Date: %s\n", info.Version, info.Commit, info.BuildDate) }