1package main
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"runtime/debug"
 7	"strings"
 8
 9	"github.com/git-bug/git-bug/commands"
10	"golang.org/x/mod/semver"
11)
12
13var (
14	version = "undefined"
15)
16
17// getVersion returns a string representing the version information defined when
18// the binary was built, or a sane default indicating a local build. a string is
19// always returned. an error may be returned along with the string in the event
20// that we detect a local build but are unable to get build metadata.
21//
22// TODO: support validation of the version (that it's a real version)
23// TODO: support notifying the user if their version is out of date
24func getVersion() (string, error) {
25	var arch string
26	var commit string
27	var modified bool
28	var platform string
29
30	var v strings.Builder
31
32	// this supports overriding the default version if the deprecated var used
33	// for setting the exact version for releases is supplied. we are doing this
34	// in order to give downstream package maintainers a longer window to
35	// migrate.
36	//
37	// TODO: 0.12.0: remove support for old build tags
38	if version == "undefined" && commands.GitExactTag != "" {
39		version = commands.GitExactTag
40	}
41
42	// automatically add the v prefix if it's missing
43	if version != "undefined" && !strings.HasPrefix(version, "v") {
44		version = fmt.Sprintf("v%s", version)
45	}
46
47	// reset the version string to undefined if it is invalid
48	if ok := semver.IsValid(version); !ok {
49		version = "undefined"
50	}
51
52	v.WriteString(version)
53
54	info, ok := debug.ReadBuildInfo()
55	if !ok {
56		v.WriteString(fmt.Sprintf(" (no build info)\n"))
57		return v.String(), errors.New("unable to read build metadata")
58	}
59
60	for _, kv := range info.Settings {
61		switch kv.Key {
62		case "GOOS":
63			platform = kv.Value
64		case "GOARCH":
65			arch = kv.Value
66		case "vcs.modified":
67			if kv.Value == "true" {
68				modified = true
69			}
70		case "vcs.revision":
71			commit = kv.Value
72		}
73	}
74
75	if commit != "" {
76		v.WriteString(fmt.Sprintf(" %.12s", commit))
77	}
78
79	if modified {
80		v.WriteString("/dirty")
81	}
82
83	v.WriteString(fmt.Sprintf(" %s", info.GoVersion))
84
85	if platform != "" {
86		v.WriteString(fmt.Sprintf(" %s", platform))
87	}
88
89	if arch != "" {
90		v.WriteString(fmt.Sprintf(" %s", arch))
91	}
92
93	return fmt.Sprint(v.String(), "\n"), nil
94}