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  }