137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
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)
|
|
}
|