mirror of https://github.com/coder/coder.git
141 lines
3.2 KiB
Go
141 lines
3.2 KiB
Go
package buildinfo
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime/debug"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/mod/semver"
|
|
)
|
|
|
|
var (
|
|
buildInfo *debug.BuildInfo
|
|
buildInfoValid bool
|
|
readBuildInfo sync.Once
|
|
|
|
externalURL string
|
|
readExternalURL sync.Once
|
|
|
|
version string
|
|
readVersion sync.Once
|
|
|
|
// Updated by buildinfo_slim.go on start.
|
|
slim bool
|
|
|
|
// Injected with ldflags at build, see scripts/build_go.sh
|
|
tag string
|
|
agpl string // either "true" or "false", ldflags does not support bools
|
|
)
|
|
|
|
const (
|
|
// develPrefix is prefixed to developer versions of the application.
|
|
develPrefix = "v0.0.0-devel"
|
|
)
|
|
|
|
// Version returns the semantic version of the build.
|
|
// Use golang.org/x/mod/semver to compare versions.
|
|
func Version() string {
|
|
readVersion.Do(func() {
|
|
revision, valid := revision()
|
|
if valid {
|
|
revision = "+" + revision[:7]
|
|
}
|
|
if tag == "" {
|
|
// This occurs when the tag hasn't been injected,
|
|
// like when using "go run".
|
|
version = develPrefix + revision
|
|
return
|
|
}
|
|
version = "v" + tag
|
|
// The tag must be prefixed with "v" otherwise the
|
|
// semver library will return an empty string.
|
|
if semver.Build(version) == "" {
|
|
version += revision
|
|
}
|
|
})
|
|
return version
|
|
}
|
|
|
|
// VersionsMatch compares the two versions. It assumes the versions match if
|
|
// the major and the minor versions are equivalent. Patch versions are
|
|
// disregarded. If it detects that either version is a developer build it
|
|
// returns true.
|
|
func VersionsMatch(v1, v2 string) bool {
|
|
// Developer versions are disregarded...hopefully they know what they are
|
|
// doing.
|
|
if strings.HasPrefix(v1, develPrefix) || strings.HasPrefix(v2, develPrefix) {
|
|
return true
|
|
}
|
|
|
|
return semver.MajorMinor(v1) == semver.MajorMinor(v2)
|
|
}
|
|
|
|
// IsDev returns true if this is a development build.
|
|
func IsDev() bool {
|
|
return strings.HasPrefix(Version(), develPrefix)
|
|
}
|
|
|
|
// IsSlim returns true if this is a slim build.
|
|
func IsSlim() bool {
|
|
return slim
|
|
}
|
|
|
|
// IsAGPL returns true if this is an AGPL build.
|
|
func IsAGPL() bool {
|
|
return strings.Contains(agpl, "t")
|
|
}
|
|
|
|
// ExternalURL returns a URL referencing the current Coder version.
|
|
// For production builds, this will link directly to a release.
|
|
// For development builds, this will link to a commit.
|
|
func ExternalURL() string {
|
|
readExternalURL.Do(func() {
|
|
repo := "https://github.com/coder/coder"
|
|
revision, valid := revision()
|
|
if !valid {
|
|
externalURL = repo
|
|
return
|
|
}
|
|
externalURL = fmt.Sprintf("%s/commit/%s", repo, revision)
|
|
})
|
|
return externalURL
|
|
}
|
|
|
|
// Time returns when the Git revision was published.
|
|
func Time() (time.Time, bool) {
|
|
value, valid := find("vcs.time")
|
|
if !valid {
|
|
return time.Time{}, false
|
|
}
|
|
parsed, err := time.Parse(time.RFC3339, value)
|
|
if err != nil {
|
|
panic("couldn't parse time: " + err.Error())
|
|
}
|
|
return parsed, true
|
|
}
|
|
|
|
// revision returns the Git hash of the build.
|
|
func revision() (string, bool) {
|
|
return find("vcs.revision")
|
|
}
|
|
|
|
// find panics if a setting with the specific key was not
|
|
// found in the build info.
|
|
func find(key string) (string, bool) {
|
|
readBuildInfo.Do(func() {
|
|
buildInfo, buildInfoValid = debug.ReadBuildInfo()
|
|
})
|
|
if !buildInfoValid {
|
|
panic("couldn't read build info")
|
|
}
|
|
for _, setting := range buildInfo.Settings {
|
|
if setting.Key != key {
|
|
continue
|
|
}
|
|
return setting.Value, true
|
|
}
|
|
return "", false
|
|
}
|