-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathversion.go
163 lines (142 loc) · 4.17 KB
/
version.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package version
import (
"encoding/json"
"fmt"
"os"
"runtime"
"runtime/debug"
"strconv"
"sync"
"gopkg.in/yaml.v3"
)
// Info contains versioning information.
type Info struct {
Version string `yaml:"version,omitempty" json:"version,omitempty"`
GitCommit string `yaml:"gitCommit,omitempty" json:"gitCommit,omitempty"`
BuildDate string `yaml:"buildDate,omitempty" json:"buildDate,omitempty"`
CommitDate string `yaml:"commitDate,omitempty" json:"commitDate,omitempty"`
DirtyBuild bool `yaml:"dirtyBuild,omitempty" json:"dirtyBuild,omitempty"`
GoVersion string `yaml:"goVersion,omitempty" json:"goVersion,omitempty"`
Compiler string `yaml:"compiler,omitempty" json:"compiler,omitempty"`
Platform string `yaml:"platform,omitempty" json:"platform,omitempty"`
Meta Meta `json:"-" yaml:"-"`
ExtraFields ExtraFields `json:"-" yaml:"-"`
}
// MarshalJSON marshal data into JSON but ensures that extra fields are inlined.
func (p *Info) MarshalJSON() ([]byte, error) {
if p == nil {
return nil, nil
}
type out map[string]interface{}
type Alias Info
f, err := json.Marshal(p.ExtraFields)
if err != nil {
return nil, fmt.Errorf("while marshaling extra fields: %w", err)
}
i, err := json.Marshal(Alias(*p))
if err != nil {
return nil, fmt.Errorf("while marshaling info fields: %w", err)
}
var ff out
err = json.Unmarshal(f, &ff)
if err != nil {
return nil, fmt.Errorf("while unmarsahling extra fields: %w", err)
}
err = json.Unmarshal(i, &ff)
if err != nil {
return nil, fmt.Errorf("while unmarsahling info fields: %w", err)
}
return json.Marshal(ff)
}
// MarshalYAML marshal data into YAML but ensures that extra fields are inlined.
func (p *Info) MarshalYAML() (interface{}, error) {
if p == nil {
return nil, nil
}
type (
InlinedExtra map[string]interface{}
Alias Info
Output struct {
Alias `yaml:",inline"`
InlinedExtra `yaml:",omitempty,inline"`
}
)
extra, err := yaml.Marshal(p.ExtraFields)
if err != nil {
return nil, fmt.Errorf("while marshaling extra fields: %w", err)
}
var mapExtra InlinedExtra
err = yaml.Unmarshal(extra, &mapExtra)
if err != nil {
return nil, fmt.Errorf("while unmarsahling extra fields: %w", err)
}
return &Output{
Alias: Alias(*p),
InlinedExtra: mapExtra,
}, nil
}
type (
// Meta holds additional version metadata.
Meta struct {
CLIName string
}
// ExtraFields provides an option to pass extra fields.
ExtraFields any
)
var collectOnce sync.Once
// Get returns the overall codebase version.
// It's for detecting what code a binary was built from.
//
// Version related variables are resolved in such order:
// 1. Go -ldflags
// 2. or debug.ReadBuildInfo() in Go 1.18+
// * version is set only if the binary is built with "go install url/tool@version".
// * commit is taken from the vcs.revision tag.
// * commitDate is taken from the vcs.time tag.
// * dirtyBuild is taken from the vcs.modified tag.
// 3. in their absence fallback to the settings in ./base.go.
func Get() *Info {
collectOnce.Do(collectFromBuildInfo)
dirty, _ := strconv.ParseBool(dirtyBuild)
if name == unknownProperty {
name = os.Args[0]
}
return &Info{
Meta: Meta{CLIName: name},
Version: version,
GitCommit: commit,
BuildDate: buildDate,
CommitDate: commitDate,
DirtyBuild: dirty,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}
// collectFromBuildInfo tries to set the build information embedded in the running binary via Go module.
// It doesn't override data if were already set by Go -ldflags.
func collectFromBuildInfo() {
info, ok := debug.ReadBuildInfo()
if !ok {
return
}
if version == unknownVersion && info.Main.Version != "" {
version = info.Main.Version
}
for _, kv := range info.Settings {
switch kv.Key {
case "vcs.revision":
if commit == unknownProperty && kv.Value != "" {
commit = kv.Value
}
case "vcs.time":
if commitDate == unknownProperty && kv.Value != "" {
commitDate = kv.Value
}
case "vcs.modified":
if dirtyBuild == unknownProperty && kv.Value != "" {
dirtyBuild = kv.Value
}
}
}
}