github.com/safing/portbase@v0.19.5/info/version.go (about) 1 package info 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "runtime" 8 "runtime/debug" 9 "strings" 10 "sync" 11 ) 12 13 var ( 14 name string 15 license string 16 17 version = "dev build" 18 versionNumber = "0.0.0" 19 buildSource = "unknown" 20 buildTime = "unknown" 21 22 info *Info 23 loadInfo sync.Once 24 ) 25 26 func init() { 27 // Replace space placeholders. 28 buildSource = strings.ReplaceAll(buildSource, "_", " ") 29 buildTime = strings.ReplaceAll(buildTime, "_", " ") 30 31 // Convert version string from git tag to expected format. 32 version = strings.TrimSpace(strings.ReplaceAll(strings.TrimPrefix(version, "v"), "_", " ")) 33 versionNumber = strings.TrimSpace(strings.TrimSuffix(version, "dev build")) 34 if versionNumber == "" { 35 versionNumber = "0.0.0" 36 } 37 38 // Get build info. 39 buildInfo, _ := debug.ReadBuildInfo() 40 buildSettings := make(map[string]string) 41 for _, setting := range buildInfo.Settings { 42 buildSettings[setting.Key] = setting.Value 43 } 44 45 // Add "dev build" to version if repo is dirty. 46 if buildSettings["vcs.modified"] == "true" && 47 !strings.HasSuffix(version, "dev build") { 48 version += " dev build" 49 } 50 } 51 52 // Info holds the programs meta information. 53 type Info struct { //nolint:maligned 54 Name string 55 Version string 56 VersionNumber string 57 License string 58 59 Source string 60 BuildTime string 61 CGO bool 62 63 Commit string 64 CommitTime string 65 Dirty bool 66 67 debug.BuildInfo 68 } 69 70 // Set sets meta information via the main routine. This should be the first thing your program calls. 71 func Set(setName string, setVersion string, setLicenseName string) { 72 name = setName 73 license = setLicenseName 74 75 if setVersion != "" { 76 version = setVersion 77 } 78 } 79 80 // GetInfo returns all the meta information about the program. 81 func GetInfo() *Info { 82 loadInfo.Do(func() { 83 buildInfo, _ := debug.ReadBuildInfo() 84 buildSettings := make(map[string]string) 85 for _, setting := range buildInfo.Settings { 86 buildSettings[setting.Key] = setting.Value 87 } 88 89 info = &Info{ 90 Name: name, 91 Version: version, 92 VersionNumber: versionNumber, 93 License: license, 94 Source: buildSource, 95 BuildTime: buildTime, 96 CGO: buildSettings["CGO_ENABLED"] == "1", 97 Commit: buildSettings["vcs.revision"], 98 CommitTime: buildSettings["vcs.time"], 99 Dirty: buildSettings["vcs.modified"] == "true", 100 BuildInfo: *buildInfo, 101 } 102 103 if info.Commit == "" { 104 info.Commit = "unknown" 105 } 106 if info.CommitTime == "" { 107 info.CommitTime = "unknown" 108 } 109 }) 110 111 return info 112 } 113 114 // Version returns the annotated version. 115 func Version() string { 116 return version 117 } 118 119 // VersionNumber returns the version number only. 120 func VersionNumber() string { 121 return versionNumber 122 } 123 124 // FullVersion returns the full and detailed version string. 125 func FullVersion() string { 126 info := GetInfo() 127 builder := new(strings.Builder) 128 129 // Name and version. 130 builder.WriteString(fmt.Sprintf("%s %s\n", info.Name, version)) 131 132 // Build info. 133 cgoInfo := "-cgo" 134 if info.CGO { 135 cgoInfo = "+cgo" 136 } 137 builder.WriteString(fmt.Sprintf("\nbuilt with %s (%s %s) for %s/%s\n", runtime.Version(), runtime.Compiler, cgoInfo, runtime.GOOS, runtime.GOARCH)) 138 builder.WriteString(fmt.Sprintf(" at %s\n", info.BuildTime)) 139 140 // Commit info. 141 dirtyInfo := "clean" 142 if info.Dirty { 143 dirtyInfo = "dirty" 144 } 145 builder.WriteString(fmt.Sprintf("\ncommit %s (%s)\n", info.Commit, dirtyInfo)) 146 builder.WriteString(fmt.Sprintf(" at %s\n", info.CommitTime)) 147 builder.WriteString(fmt.Sprintf(" from %s\n", info.Source)) 148 149 builder.WriteString(fmt.Sprintf("\nLicensed under the %s license.", license)) 150 151 return builder.String() 152 } 153 154 // CheckVersion checks if the metadata is ok. 155 func CheckVersion() error { 156 switch { 157 case strings.HasSuffix(os.Args[0], ".test"): 158 return nil // testing on linux/darwin 159 case strings.HasSuffix(os.Args[0], ".test.exe"): 160 return nil // testing on windows 161 default: 162 // check version information 163 if name == "" || license == "" { 164 return errors.New("must call SetInfo() before calling CheckVersion()") 165 } 166 } 167 168 return nil 169 }