/
version.go
86 lines (80 loc) · 2.42 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
package version
import (
"bufio"
"fmt"
"net/http"
"strings"
"github.com/blang/semver"
"gopkg.in/urfave/cli.v1"
)
// Version represents the value of the current semantic version
const Version = "3.6.1"
// PrintVersion handles the version command for sops
func PrintVersion(c *cli.Context) {
out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version)
upstreamVersion, err := RetrieveLatestVersionFromUpstream()
if err != nil {
out += fmt.Sprintf("\n[warning] failed to retrieve latest version from upstream: %v\n", err)
}
outdated, err := AIsNewerThanB(upstreamVersion, Version)
if err != nil {
out += fmt.Sprintf("\n[warning] failed to compare current version with latest: %v\n", err)
}
if outdated {
out += fmt.Sprintf("\n[info] sops %s is available, update with `go get -u go.mozilla.org/sops/v3/cmd/sops`\n", upstreamVersion)
} else {
out += " (latest)\n"
}
fmt.Fprintf(c.App.Writer, "%s", out)
}
// AIsNewerThanB takes 2 semver strings are returns true
// is the A is newer than B, false otherwise
func AIsNewerThanB(A, B string) (bool, error) {
if strings.HasPrefix(B, "1.") {
// sops 1.0 doesn't use the semver format, which will
// fail the call to `make` below. Since we now we're
// more recent than 1.X anyway, return true right away
return true, nil
}
vA, err := semver.Make(A)
if err != nil {
return false, err
}
vB, err := semver.Make(B)
if err != nil {
return false, err
}
if vA.Compare(vB) > 0 {
// vA is newer than vB
return true, nil
}
return false, nil
}
// RetrieveLatestVersionFromUpstream gets the latest version from the source code at Github
func RetrieveLatestVersionFromUpstream() (string, error) {
resp, err := http.Get("https://raw.githubusercontent.com/mozilla/sops/master/version/version.go")
if err != nil {
return "", err
}
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, `const Version = "`) {
comps := strings.Split(line, `"`)
if len(comps) < 2 {
return "", fmt.Errorf("Failed to parse version from upstream source")
}
// try to parse the version as semver
_, err := semver.Make(comps[1])
if err != nil {
return "", fmt.Errorf("Retrieved version %q does not match semver format: %v", comps[1], err)
}
return comps[1], nil
}
}
if err := scanner.Err(); err != nil {
return "", err
}
return "", fmt.Errorf("Version information not found in upstream file")
}