-
Notifications
You must be signed in to change notification settings - Fork 3
/
version.go
143 lines (121 loc) · 3.94 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
package clio
import (
"bytes"
"encoding/json"
"fmt"
"runtime"
"github.com/iancoleman/strcase"
"github.com/spf13/cobra"
)
// Identification defines the application name and version details (generally from build information)
type Identification struct {
Name string `json:"application,omitempty"` // application name
Version string `json:"version,omitempty"` // application semantic version
GitCommit string `json:"gitCommit,omitempty"` // git SHA at build-time
GitDescription string `json:"gitDescription,omitempty"` // indication of git tree (either "clean" or "dirty") at build-time
BuildDate string `json:"buildDate,omitempty"` // date of the build
}
type runtimeInfo struct {
Identification
GoVersion string `json:"goVersion,omitempty"` // go runtime version at build-time
Compiler string `json:"compiler,omitempty"` // compiler used at build-time
Platform string `json:"platform,omitempty"` // GOOS and GOARCH at build-time
}
type versionAddition = func() (name string, value any)
func VersionCommand(id Identification, additions ...versionAddition) *cobra.Command {
var format string
cmd := &cobra.Command{
Use: "version",
Short: "show version information",
Args: cobra.NoArgs,
// note: we intentionally do not execute through the application infrastructure (no app config is required for this command)
RunE: func(_ *cobra.Command, _ []string) error {
info := runtimeInfo{
Identification: id,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
value, err := versionInfo(info, format, additions...)
if err == nil {
fmt.Print(value)
}
return err
},
}
flags := cmd.Flags()
flags.StringVarP(&format, "output", "o", "text", "the format to show the results (allowable: [text json])")
return cmd
}
func versionInfo(info runtimeInfo, format string, additions ...versionAddition) (string, error) {
buf := &bytes.Buffer{}
switch format {
case "text", "":
type additionType struct {
name string
value any
}
var add []additionType
pad := 10
for _, addition := range additions {
name, value := addition()
if fmt.Sprintf("%v", value) == "" {
continue
}
if pad < len(name) {
pad = len(name)
}
add = append(add, additionType{name: name, value: value})
}
appendLine(buf, "Application", pad, info.Name)
appendLine(buf, "Version", pad, info.Identification.Version)
appendLine(buf, "BuildDate", pad, info.BuildDate)
appendLine(buf, "GitCommit", pad, info.GitCommit)
appendLine(buf, "GitDescription", pad, info.GitDescription)
appendLine(buf, "Platform", pad, info.Platform)
appendLine(buf, "GoVersion", pad, info.GoVersion)
appendLine(buf, "Compiler", pad, info.Compiler)
for _, a := range add {
appendLine(buf, a.name, pad, a.value)
}
case "json":
var info any = info
if len(additions) > 0 {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
err := enc.Encode(info)
if err != nil {
return "", fmt.Errorf("failed to show version information: %w", err)
}
var data map[string]any
dec := json.NewDecoder(buf)
err = dec.Decode(&data)
if err != nil {
return "", fmt.Errorf("failed to show version information: %w", err)
}
for _, addition := range additions {
name, value := addition()
name = strcase.ToLowerCamel(name)
data[name] = value
}
info = data
}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
err := enc.Encode(info)
if err != nil {
return "", fmt.Errorf("failed to show version information: %w", err)
}
default:
return "", fmt.Errorf("unsupported output format: %s", format)
}
return buf.String(), nil
}
func appendLine(buf *bytes.Buffer, title string, width int, value any) {
if fmt.Sprintf("%v", value) == "" {
return
}
_, _ = fmt.Fprintf(buf, "%-*s %v\n", width+1, title+":", value)
}