// Copyright IBM Corp. 2020, 2026 // SPDX-License-Identifier: BUSL-1.1 package version import ( "bytes" "fmt" "strings" gvers "github.com/hashicorp/go-version" ) const ( // BoundaryPrefix is the release version prefix used on the wire. It is // kept as the emitted prefix so that this binary stays compatible with // upstream HashiCorp Boundary peers that parse it. BoundaryPrefix = "Boundary v" // ThresholdPrefix is the release version prefix used for display and // accepted additively when parsing. ThresholdPrefix = "Threshold v" ) // TrimReleasePrefix removes a leading (or embedded) "Boundary v" or // "Threshold v" release prefix from s, if present. func TrimReleasePrefix(s string) string { for _, p := range []string{BoundaryPrefix, ThresholdPrefix} { if idx := strings.Index(s, p); idx >= 0 { return s[idx+len(p):] } } return s } // Info type Info struct { Revision string `json:"revision,omitempty"` Version string `json:"version,omitempty"` VersionPrerelease string `json:"version_prerelease,omitempty"` VersionMetadata string `json:"version_metadata,omitempty"` BuildDate string `json:"build_date,omitempty"` CgoEnabled bool `json:"cgo_enabled,omitempty"` } func Get() *Info { ver := Version rel := VersionPrerelease md := VersionMetadata bd := BuildDate if GitDescribe != "" { ver = GitDescribe } if GitDescribe == "" && rel == "" && VersionPrerelease != "" { rel = "dev" } // Remove metadata string from version output for oss if md == "oss" { md = "" } return &Info{ CgoEnabled: CgoEnabled, Revision: GitCommit, Version: ver, VersionPrerelease: rel, VersionMetadata: md, BuildDate: bd, } } func (c *Info) VersionNumber() string { if Version == "unknown" && VersionPrerelease == "unknown" { return "(version unknown)" } version := c.Version if c.VersionPrerelease != "" { version = fmt.Sprintf("%s-%s", version, c.VersionPrerelease) } if c.VersionMetadata != "" && c.VersionMetadata != "oss" { version = fmt.Sprintf("%s+%s", version, c.VersionMetadata) } return version } // Semver returns a *gvers.Version if the Info is parseable as // a semantic version. Otherwise it returns nil. func (c *Info) Semver() *gvers.Version { if c == nil { return nil } v, err := gvers.NewSemver(c.VersionNumber()) if err != nil { return nil } return v } // FromVersionString returns an *Info containing the version, or nil if the // string was unable to be parsed. func FromVersionString(s string) *Info { s = strings.TrimSpace(s) s = strings.TrimPrefix(s, BoundaryPrefix) s = strings.TrimPrefix(s, ThresholdPrefix) i := Info{} // Get the revision startOfRevIdx := strings.LastIndex(s, "(") endOfRevIdx := strings.LastIndex(s, ")") if startOfRevIdx > 0 && endOfRevIdx > 0 { if endOfRevIdx < startOfRevIdx { return nil } i.Revision, s = s[startOfRevIdx+1:endOfRevIdx], strings.TrimSpace(s[:startOfRevIdx]) } v, err := gvers.NewSemver(s) if err != nil { return nil } if md := v.Metadata(); len(md) > 0 && md != "oss" { i.VersionMetadata = md } if pr := v.Prerelease(); len(pr) > 0 { i.VersionPrerelease = pr } i.Version = v.Core().String() return &i } // FullVersionNumber returns the wire-format release version, which uses the // upstream "Boundary v" prefix for compatibility with peers. func (c *Info) FullVersionNumber(rev bool) string { return c.fullVersionNumber(BoundaryPrefix, "Boundary", rev) } // DisplayVersionNumber returns the release version for display to users, // using the "Threshold v" prefix. Do not send this value over the wire. func (c *Info) DisplayVersionNumber(rev bool) string { return c.fullVersionNumber(ThresholdPrefix, "Threshold", rev) } func (c *Info) fullVersionNumber(prefix, name string, rev bool) string { var versionString bytes.Buffer if Version == "unknown" && VersionPrerelease == "unknown" { return name + " (version unknown)" } fmt.Fprintf(&versionString, "%s%s", prefix, c.Version) if c.VersionPrerelease != "" { fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease) } if c.VersionMetadata != "" && c.VersionMetadata != "oss" { fmt.Fprintf(&versionString, "+%s", c.VersionMetadata) } if rev && c.Revision != "" { fmt.Fprintf(&versionString, " (%s)", c.Revision) } return versionString.String() }