-
Notifications
You must be signed in to change notification settings - Fork 7
/
version.go
125 lines (114 loc) · 3.36 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
package nerd
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/dghubble/sling"
"github.com/pkg/errors"
)
const (
//GHReleasesURL is the GitHub API URL that lists all releases.
GHReleasesURL = "https://api.github.com/repos/nerdalize/nerd/releases"
//BuiltFromSourceVersion is the version when building form source (no real version).
BuiltFromSourceVersion = "built.from.src"
)
//GHRelease is a `release` object from the GitHub API.
type GHRelease struct {
Name string `json:"name"`
HTMLURL string `json:"html_url"`
}
//GHError is an error object from the GitHub API.
type GHError struct {
Message string `json:"message"`
URL string `json:"documentation_url"`
}
//VersionMessage shows a message to the user if a new CLI version is available.
func VersionMessage(current string) {
if current == BuiltFromSourceVersion {
return
}
var releases []GHRelease
e := new(GHError)
s := sling.New().Get(GHReleasesURL)
_, err := s.Receive(&releases, e)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to access GitHub releases page"))
return
}
if e.Message != "" {
fmt.Fprintf(os.Stderr, "Received GitHub error message: %v (%v)\n", e.Message, e.URL)
return
}
if len(releases) > 0 {
latest := releases[0]
latestVersion, err := ParseSemVer(strings.Replace(latest.Name, "v", "", 1))
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to parse latest semantic version"))
return
}
currentVersion, err := ParseSemVer(current)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to parse current semantic version"))
return
}
if latestVersion.GreaterThan(currentVersion) {
fmt.Fprintf(os.Stderr, "A new version (%v) of the nerd CLI is available. Your current version is %v. Please visit %v to get the latest version.\n", latestVersion.ToString(), currentVersion.ToString(), latest.HTMLURL)
}
}
}
//SemVer is a semantic version.
type SemVer struct {
Major int
Minor int
Patch int
}
//ParseSemVer parses a semantic version from string.
func ParseSemVer(ver string) (*SemVer, error) {
parts := strings.Split(ver, "-")
ver = parts[0]
parts = strings.Split(ver, ".")
if len(parts) != 3 {
return nil, errors.Errorf("failed to parse semantic version '%v', because does not consist of 3 parts", ver)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return nil, errors.Wrapf(err, "failed to parse semantic version '%v', because the major version is not an integer", ver)
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return nil, errors.Wrapf(err, "failed to parse semantic version '%v', because the minor version is not an integer", ver)
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return nil, errors.Wrapf(err, "failed to parse semantic version '%v', because the patch version is not an integer", ver)
}
return &SemVer{
Major: major,
Minor: minor,
Patch: patch,
}, nil
}
//GreaterThan checks if `s` is a greater semantic version than `other`
func (s *SemVer) GreaterThan(other *SemVer) bool {
if s.Major > other.Major {
return true
}
if s.Major < other.Major {
return false
}
if s.Minor > other.Minor {
return true
}
if s.Minor < other.Minor {
return false
}
if s.Patch > other.Patch {
return true
}
return false
}
//ToString converts a SemVer to a string
func (s *SemVer) ToString() string {
return fmt.Sprintf("%v.%v.%v", s.Major, s.Minor, s.Patch)
}