145 lines
3.8 KiB
Go
145 lines
3.8 KiB
Go
|
package version
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"golang.org/x/mod/semver"
|
||
|
)
|
||
|
|
||
|
// Version returns the build information. See build.sh.
|
||
|
func Version() (b *BuildInfo, err error) {
|
||
|
|
||
|
var n int
|
||
|
var s string
|
||
|
var sb strings.Builder
|
||
|
var ok bool
|
||
|
var canonical string
|
||
|
var build strings.Builder
|
||
|
// Why a map?
|
||
|
// I forget but I had a reason for it once upon a time.
|
||
|
var raw map[string]string = map[string]string{
|
||
|
"sourceControl": sourceControl,
|
||
|
"tag": version,
|
||
|
"hash": commitHash,
|
||
|
"shortHash": commitShort,
|
||
|
"postTagCommits": numCommitsAfterTag,
|
||
|
"dirty": isDirty,
|
||
|
"time": buildTime,
|
||
|
"user": buildUser,
|
||
|
"sudoUser": buildSudoUser,
|
||
|
"host": buildHost,
|
||
|
}
|
||
|
var i BuildInfo = BuildInfo{
|
||
|
SourceControl: raw["sourceControl"],
|
||
|
TagVersion: raw["tag"],
|
||
|
// PostTagCommits: 0,
|
||
|
CommitHash: raw["hash"],
|
||
|
CommitId: raw["shortHash"],
|
||
|
BuildUser: raw["user"],
|
||
|
RealBuildUser: raw["sudoUser"],
|
||
|
// BuildTime: time.Time{},
|
||
|
BuildHost: raw["host"],
|
||
|
Dirty: false,
|
||
|
isDefined: false,
|
||
|
raw: raw,
|
||
|
}
|
||
|
|
||
|
if s, ok = raw["postTagCommits"]; ok && strings.TrimSpace(s) != "" {
|
||
|
if n, err = strconv.Atoi(s); err == nil {
|
||
|
i.PostTagCommits = uint(n)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if s, ok = raw["time"]; ok && strings.TrimSpace(s) != "" {
|
||
|
if n, err = strconv.Atoi(s); err == nil {
|
||
|
i.BuildTime = time.Unix(int64(n), 0).UTC()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch strings.ToLower(raw["dirty"]) {
|
||
|
case "1":
|
||
|
i.Dirty = true
|
||
|
case "0", "":
|
||
|
i.Dirty = false
|
||
|
}
|
||
|
|
||
|
// Build the short form. We use this for both BuildInfo.short and BuildInfo.verSplit.
|
||
|
if i.TagVersion == "" {
|
||
|
sb.WriteString(i.SourceControl)
|
||
|
} else {
|
||
|
sb.WriteString(i.TagVersion)
|
||
|
}
|
||
|
/*
|
||
|
Now the mess. In order to conform to SemVer 2.0 (the spec this code targets):
|
||
|
|
||
|
1.) MAJOR.
|
||
|
2.) MINOR.
|
||
|
3.) PATCH
|
||
|
4.) -PRERELEASE (OPTIONAL)
|
||
|
(git commit, if building against a commit made past 1-3. Always included if untagged.)
|
||
|
5.) +BUILDINFO (OPTIONAL)
|
||
|
("+x[.y]", where x is # of commits past 4, or tag commit if 4 is empty. 0 is valid.
|
||
|
y is optional, and is the string "dirty" if it is a "dirty" build - that is, uncommitted/unstaged changes.
|
||
|
if x and y would be 0 and empty, respectively, then 5 is not included.)
|
||
|
|
||
|
1-3 are already written, or the source control software used if not.
|
||
|
|
||
|
Technically 4 and 5 are only included if 3 is present. We force patch to 0 if it's a tagged release and patch isn't present --
|
||
|
so this is not relevant.
|
||
|
*/
|
||
|
// PRERELEASE
|
||
|
if i.TagVersion == "" || i.PostTagCommits > 0 {
|
||
|
// We use the full commit hash for git versions, short identifier for tagged releases.
|
||
|
if i.TagVersion == "" {
|
||
|
i.Pre = i.CommitHash
|
||
|
} else {
|
||
|
i.Pre = i.CommitId
|
||
|
}
|
||
|
sb.WriteString(fmt.Sprintf("-%v", i.Pre))
|
||
|
}
|
||
|
// BUILD
|
||
|
if i.PostTagCommits > 0 || i.Dirty {
|
||
|
build.WriteString(strconv.Itoa(int(i.PostTagCommits)))
|
||
|
if i.Dirty {
|
||
|
build.WriteString(".dirty")
|
||
|
}
|
||
|
i.Build = build.String()
|
||
|
sb.WriteString(fmt.Sprintf("+%v", i.Build))
|
||
|
}
|
||
|
|
||
|
i.short = sb.String()
|
||
|
if semver.IsValid(i.short) {
|
||
|
// DON'T DO THIS. It strips the prerelease and build info.
|
||
|
// i.short = semver.Canonical(i.short)
|
||
|
// Do this instead.
|
||
|
canonical = semver.Canonical(i.short)
|
||
|
// Numeric versions...
|
||
|
if n, err = strconv.Atoi(strings.TrimPrefix(semver.Major(canonical), "v")); err != nil {
|
||
|
err = nil
|
||
|
} else {
|
||
|
i.Major = uint(n)
|
||
|
}
|
||
|
if n, err = strconv.Atoi(strings.Split(semver.MajorMinor(canonical), ".")[1]); err != nil {
|
||
|
err = nil
|
||
|
} else {
|
||
|
i.Minor = uint(n)
|
||
|
}
|
||
|
if n, err = strconv.Atoi(patchReIsolated.FindStringSubmatch(strings.Split(canonical, ".")[2])[1]); err != nil {
|
||
|
err = nil
|
||
|
} else {
|
||
|
i.Patch = uint(n)
|
||
|
}
|
||
|
// The other tag assignments were performed above.
|
||
|
}
|
||
|
// The default is 0 for the numerics, so no big deal.
|
||
|
|
||
|
i.isDefined = true
|
||
|
|
||
|
b = &i
|
||
|
|
||
|
return
|
||
|
}
|