/* * BSD 3-Clause License * Copyright (c) 2024, NetFireâ„¢ * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package version import ( `fmt` `runtime` `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. // If you remove it and this func breaks, now you know why. // TODO: how much of this can be replaced by (runtime/debug).ReadBuildInfo()? var raw map[string]string = map[string]string{ "repoUri": repoUri, "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"], GoVersion: runtime.Version(), RepoURI: raw["repoUri"], 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 }