Browse Source

know if there is a higher released version

bryanl 2 years ago
parent
commit
95008bd994

+ 1
- 0
.gitignore View File

@@ -9,3 +9,4 @@ project.json
9 9
 dist
10 10
 public
11 11
 docs/content
12
+doctl

+ 1
- 1
commands/version.go View File

@@ -39,7 +39,7 @@ func Version() *Command {
39 39
 					doit.DoitVersion.Label = doit.Label
40 40
 				}
41 41
 
42
-				fmt.Println(doit.DoitVersion.Complete())
42
+				fmt.Println(doit.DoitVersion.Complete(&doit.GithubLatestVersioner{}))
43 43
 			},
44 44
 		},
45 45
 	}

+ 54
- 4
doit.go View File

@@ -2,10 +2,15 @@ package doit
2 2
 
3 3
 import (
4 4
 	"bytes"
5
+	"encoding/json"
6
+	"errors"
5 7
 	"fmt"
6 8
 	"log"
9
+	"net/http"
7 10
 	"strconv"
11
+	"strings"
8 12
 
13
+	"github.com/blang/semver"
9 14
 	"github.com/bryanl/doit/pkg/runner"
10 15
 	"github.com/bryanl/doit/pkg/ssh"
11 16
 	"github.com/digitalocean/godo"
@@ -17,6 +22,9 @@ import (
17 22
 const (
18 23
 	// NSRoot is a configuration key that signifies this value is at the root.
19 24
 	NSRoot = "doit"
25
+
26
+	// LatestReleaseURL is the latest release URL endpoint.
27
+	LatestReleaseURL = "https://api.github.com/repos/bryanl/doit/releases/latest"
20 28
 )
21 29
 
22 30
 var (
@@ -71,19 +79,61 @@ func (v Version) String() string {
71 79
 }
72 80
 
73 81
 // Complete is the complete version for doit.
74
-func (v Version) Complete() string {
82
+func (v Version) Complete(lv LatestVersioner) string {
75 83
 	var buffer bytes.Buffer
76
-	buffer.WriteString(fmt.Sprintf("doit version %s", v.String()))
77
-
78
-	buffer.WriteString(fmt.Sprintf(" %q", v.Name))
84
+	buffer.WriteString(fmt.Sprintf("doctl version %s", v.String()))
79 85
 
80 86
 	if v.Build != "" {
81 87
 		buffer.WriteString(fmt.Sprintf("\nGit commit hash: %s", v.Build))
82 88
 	}
83 89
 
90
+	if tagName, err := lv.LatestVersion(); err == nil {
91
+		v0, err1 := semver.Make(tagName)
92
+		v1, err2 := semver.Make(v.String())
93
+
94
+		if err1 == nil && err2 == nil && v0.GT(v1) {
95
+			buffer.WriteString(fmt.Sprintf("\n%q is a newer release than %q", tagName, v.String()))
96
+		}
97
+	}
98
+
84 99
 	return buffer.String()
85 100
 }
86 101
 
102
+// LatestVersioner an interface for detecting the latest version.
103
+type LatestVersioner interface {
104
+	LatestVersion() (string, error)
105
+}
106
+
107
+// GithubLatestVersioner retrieves the latest version from Github.
108
+type GithubLatestVersioner struct{}
109
+
110
+var _ LatestVersioner = &GithubLatestVersioner{}
111
+
112
+// LatestVersion retrieves the latest version from Github or returns
113
+// an error.
114
+func (glv *GithubLatestVersioner) LatestVersion() (string, error) {
115
+	u := LatestReleaseURL
116
+	res, err := http.Get(u)
117
+	if err != nil {
118
+		return "", err
119
+	}
120
+
121
+	defer res.Body.Close()
122
+
123
+	var m map[string]interface{}
124
+	if err = json.NewDecoder(res.Body).Decode(&m); err != nil {
125
+		return "", err
126
+	}
127
+
128
+	tn, ok := m["tag_name"]
129
+	if !ok {
130
+		return "", errors.New("could not find tag name in response")
131
+	}
132
+
133
+	tagName := tn.(string)
134
+	return strings.TrimPrefix(tagName, "v"), nil
135
+}
136
+
87 137
 // Config is an interface that represent doit's config.
88 138
 type Config interface {
89 139
 	GetGodoClient(trace bool) *godo.Client

+ 29
- 7
doit_test.go View File

@@ -10,28 +10,42 @@ func TestMain(m *testing.M) {
10 10
 }
11 11
 
12 12
 func TestVersion(t *testing.T) {
13
+	slr1 := &stubLatestRelease{version: "0.1.0"}
14
+	slr2 := &stubLatestRelease{version: "1.0.0"}
15
+
13 16
 	cases := []struct {
14 17
 		v   Version
15 18
 		s   string
16 19
 		ver string
20
+		slr LatestVersioner
17 21
 	}{
18 22
 		// version with no label
19 23
 		{
20
-			v:   Version{Major: 0, Minor: 1, Patch: 2, Name: "Version"},
21
-			s:   `doit version 0.1.2 "Version"`,
24
+			v:   Version{Major: 0, Minor: 1, Patch: 2},
25
+			s:   `doctl version 0.1.2`,
22 26
 			ver: "0.1.2",
27
+			slr: slr1,
23 28
 		},
24 29
 		// version with label
25 30
 		{
26
-			v:   Version{Major: 0, Minor: 1, Patch: 2, Name: "Version", Label: "dev"},
27
-			s:   `doit version 0.1.2-dev "Version"`,
31
+			v:   Version{Major: 0, Minor: 1, Patch: 2, Label: "dev"},
32
+			s:   `doctl version 0.1.2-dev`,
28 33
 			ver: "0.1.2-dev",
34
+			slr: slr1,
29 35
 		},
30 36
 		// version with label and build
31 37
 		{
32
-			v:   Version{Major: 0, Minor: 1, Patch: 2, Name: "Version", Label: "dev", Build: "12345"},
33
-			s:   "doit version 0.1.2-dev \"Version\"\nGit commit hash: 12345",
38
+			v:   Version{Major: 0, Minor: 1, Patch: 2, Label: "dev", Build: "12345"},
39
+			s:   "doctl version 0.1.2-dev\nGit commit hash: 12345",
34 40
 			ver: "0.1.2-dev",
41
+			slr: slr1,
42
+		},
43
+		// version with no label and higher released version
44
+		{
45
+			v:   Version{Major: 0, Minor: 1, Patch: 2},
46
+			s:   "doctl version 0.1.2\n\"1.0.0\" is a newer release than \"0.1.2\"",
47
+			ver: `0.1.2`,
48
+			slr: slr2,
35 49
 		},
36 50
 	}
37 51
 
@@ -39,8 +53,16 @@ func TestVersion(t *testing.T) {
39 53
 		if got, want := c.v.String(), c.ver; got != want {
40 54
 			t.Errorf("version string for %#v = %q; want = %q", c.v, got, want)
41 55
 		}
42
-		if got, want := c.v.Complete(), c.s; got != want {
56
+		if got, want := c.v.Complete(c.slr), c.s; got != want {
43 57
 			t.Errorf("complete version string for %#v = %q; want = %q", c.v, got, want)
44 58
 		}
45 59
 	}
46 60
 }
61
+
62
+type stubLatestRelease struct {
63
+	version string
64
+}
65
+
66
+func (slr stubLatestRelease) LatestVersion() (string, error) {
67
+	return slr.version, nil
68
+}

+ 22
- 0
vendor/github.com/blang/semver/LICENSE View File

@@ -0,0 +1,22 @@
1
+The MIT License
2
+
3
+Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.
22
+

+ 191
- 0
vendor/github.com/blang/semver/README.md View File

@@ -0,0 +1,191 @@
1
+semver for golang [![Build Status](https://drone.io/github.com/blang/semver/status.png)](https://drone.io/github.com/blang/semver/latest) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master)
2
+======
3
+
4
+semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
5
+
6
+Usage
7
+-----
8
+```bash
9
+$ go get github.com/blang/semver
10
+```
11
+Note: Always vendor your dependencies or fix on a specific version tag.
12
+
13
+```go
14
+import github.com/blang/semver
15
+v1, err := semver.Make("1.0.0-beta")
16
+v2, err := semver.Make("2.0.0-beta")
17
+v1.Compare(v2)
18
+```
19
+
20
+Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
21
+
22
+Why should I use this lib?
23
+-----
24
+
25
+- Fully spec compatible
26
+- No reflection
27
+- No regex
28
+- Fully tested (Coverage >99%)
29
+- Readable parsing/validation errors
30
+- Fast (See [Benchmarks](#benchmarks))
31
+- Only Stdlib
32
+- Uses values instead of pointers
33
+- Many features, see below
34
+
35
+
36
+Features
37
+-----
38
+
39
+- Parsing and validation at all levels
40
+- Comparator-like comparisons
41
+- Compare Helper Methods
42
+- InPlace manipulation
43
+- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
44
+- Sortable (implements sort.Interface)
45
+- database/sql compatible (sql.Scanner/Valuer)
46
+- encoding/json compatible (json.Marshaler/Unmarshaler)
47
+
48
+Ranges
49
+------
50
+
51
+A `Range` is a set of conditions which specify which versions satisfy the range.
52
+
53
+A condition is composed of an operator and a version. The supported operators are:
54
+
55
+- `<1.0.0` Less than `1.0.0`
56
+- `<=1.0.0` Less than or equal to `1.0.0`
57
+- `>1.0.0` Greater than `1.0.0`
58
+- `>=1.0.0` Greater than or equal to `1.0.0`
59
+- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
60
+- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
61
+
62
+A `Range` can link multiple `Ranges` separated by space:
63
+
64
+Ranges can be linked by logical AND:
65
+
66
+  - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
67
+  - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
68
+
69
+Ranges can also be linked by logical OR:
70
+
71
+  - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
72
+
73
+AND has a higher precedence than OR. It's not possible to use brackets.
74
+
75
+Ranges can be combined by both AND and OR
76
+
77
+  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
78
+
79
+Range usage:
80
+
81
+```
82
+v, err := semver.Parse("1.2.3")
83
+range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
84
+if range(v) {
85
+    //valid
86
+}
87
+
88
+```
89
+
90
+Example
91
+-----
92
+
93
+Have a look at full examples in [examples/main.go](examples/main.go)
94
+
95
+```go
96
+import github.com/blang/semver
97
+
98
+v, err := semver.Make("0.0.1-alpha.preview+123.github")
99
+fmt.Printf("Major: %d\n", v.Major)
100
+fmt.Printf("Minor: %d\n", v.Minor)
101
+fmt.Printf("Patch: %d\n", v.Patch)
102
+fmt.Printf("Pre: %s\n", v.Pre)
103
+fmt.Printf("Build: %s\n", v.Build)
104
+
105
+// Prerelease versions array
106
+if len(v.Pre) > 0 {
107
+    fmt.Println("Prerelease versions:")
108
+    for i, pre := range v.Pre {
109
+        fmt.Printf("%d: %q\n", i, pre)
110
+    }
111
+}
112
+
113
+// Build meta data array
114
+if len(v.Build) > 0 {
115
+    fmt.Println("Build meta data:")
116
+    for i, build := range v.Build {
117
+        fmt.Printf("%d: %q\n", i, build)
118
+    }
119
+}
120
+
121
+v001, err := semver.Make("0.0.1")
122
+// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
123
+v001.GT(v) == true
124
+v.LT(v001) == true
125
+v.GTE(v) == true
126
+v.LTE(v) == true
127
+
128
+// Or use v.Compare(v2) for comparisons (-1, 0, 1):
129
+v001.Compare(v) == 1
130
+v.Compare(v001) == -1
131
+v.Compare(v) == 0
132
+
133
+// Manipulate Version in place:
134
+v.Pre[0], err = semver.NewPRVersion("beta")
135
+if err != nil {
136
+    fmt.Printf("Error parsing pre release version: %q", err)
137
+}
138
+
139
+fmt.Println("\nValidate versions:")
140
+v.Build[0] = "?"
141
+
142
+err = v.Validate()
143
+if err != nil {
144
+    fmt.Printf("Validation failed: %s\n", err)
145
+}
146
+```
147
+
148
+
149
+Benchmarks
150
+-----
151
+
152
+    BenchmarkParseSimple-4           5000000    390    ns/op    48 B/op   1 allocs/op
153
+    BenchmarkParseComplex-4          1000000   1813    ns/op   256 B/op   7 allocs/op
154
+    BenchmarkParseAverage-4          1000000   1171    ns/op   163 B/op   4 allocs/op
155
+    BenchmarkStringSimple-4         20000000    119    ns/op    16 B/op   1 allocs/op
156
+    BenchmarkStringLarger-4         10000000    206    ns/op    32 B/op   2 allocs/op
157
+    BenchmarkStringComplex-4         5000000    324    ns/op    80 B/op   3 allocs/op
158
+    BenchmarkStringAverage-4         5000000    273    ns/op    53 B/op   2 allocs/op
159
+    BenchmarkValidateSimple-4      200000000      9.33 ns/op     0 B/op   0 allocs/op
160
+    BenchmarkValidateComplex-4       3000000    469    ns/op     0 B/op   0 allocs/op
161
+    BenchmarkValidateAverage-4       5000000    256    ns/op     0 B/op   0 allocs/op
162
+    BenchmarkCompareSimple-4       100000000     11.8  ns/op     0 B/op   0 allocs/op
163
+    BenchmarkCompareComplex-4       50000000     30.8  ns/op     0 B/op   0 allocs/op
164
+    BenchmarkCompareAverage-4       30000000     41.5  ns/op     0 B/op   0 allocs/op
165
+    BenchmarkSort-4                  3000000    419    ns/op   256 B/op   2 allocs/op
166
+    BenchmarkRangeParseSimple-4      2000000    850    ns/op   192 B/op   5 allocs/op
167
+    BenchmarkRangeParseAverage-4     1000000   1677    ns/op   400 B/op  10 allocs/op
168
+    BenchmarkRangeParseComplex-4      300000   5214    ns/op  1440 B/op  30 allocs/op
169
+    BenchmarkRangeMatchSimple-4     50000000     25.6  ns/op     0 B/op   0 allocs/op
170
+    BenchmarkRangeMatchAverage-4    30000000     56.4  ns/op     0 B/op   0 allocs/op
171
+    BenchmarkRangeMatchComplex-4    10000000    153    ns/op     0 B/op   0 allocs/op
172
+
173
+See benchmark cases at [semver_test.go](semver_test.go)
174
+
175
+
176
+Motivation
177
+-----
178
+
179
+I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
180
+
181
+
182
+Contribution
183
+-----
184
+
185
+Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
186
+
187
+
188
+License
189
+-----
190
+
191
+See [LICENSE](LICENSE) file.

+ 83
- 0
vendor/github.com/blang/semver/examples/main.go View File

@@ -0,0 +1,83 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"github.com/blang/semver"
6
+)
7
+
8
+func main() {
9
+	v, err := semver.Parse("0.0.1-alpha.preview.222+123.github")
10
+	if err != nil {
11
+		fmt.Printf("Error while parsing (not valid): %q", err)
12
+	}
13
+	fmt.Printf("Version to string: %q\n", v)
14
+
15
+	fmt.Printf("Major: %d\n", v.Major)
16
+	fmt.Printf("Minor: %d\n", v.Minor)
17
+	fmt.Printf("Patch: %d\n", v.Patch)
18
+
19
+	// Prerelease versions
20
+	if len(v.Pre) > 0 {
21
+		fmt.Println("Prerelease versions:")
22
+		for i, pre := range v.Pre {
23
+			fmt.Printf("%d: %q\n", i, pre)
24
+		}
25
+	}
26
+
27
+	// Build meta data
28
+	if len(v.Build) > 0 {
29
+		fmt.Println("Build meta data:")
30
+		for i, build := range v.Build {
31
+			fmt.Printf("%d: %q\n", i, build)
32
+		}
33
+	}
34
+
35
+	// Make == Parse (Value), New for Pointer
36
+	v001, err := semver.Make("0.0.1")
37
+
38
+	fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):")
39
+	fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v))
40
+	fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001))
41
+	fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v))
42
+
43
+	fmt.Println("\nUse comparison helpers returning booleans:")
44
+	fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v))
45
+	fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v))
46
+	fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v))
47
+	fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001))
48
+	fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001))
49
+	fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v))
50
+
51
+	fmt.Println("\nManipulate Version in place:")
52
+	v.Pre[0], err = semver.NewPRVersion("beta")
53
+	if err != nil {
54
+		fmt.Printf("Error parsing pre release version: %q", err)
55
+	}
56
+	fmt.Printf("Version to string: %q\n", v)
57
+
58
+	fmt.Println("\nCompare Prerelease versions:")
59
+	pre1, _ := semver.NewPRVersion("123")
60
+	pre2, _ := semver.NewPRVersion("alpha")
61
+	pre3, _ := semver.NewPRVersion("124")
62
+	fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2))
63
+	fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1))
64
+	fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1))
65
+
66
+	fmt.Println("\nValidate versions:")
67
+	v.Build[0] = "?"
68
+
69
+	err = v.Validate()
70
+	if err != nil {
71
+		fmt.Printf("Validation failed: %s\n", err)
72
+	}
73
+
74
+	fmt.Println("Create valid build meta data:")
75
+	b1, _ := semver.NewBuildVersion("build123")
76
+	v.Build[0] = b1
77
+	fmt.Printf("Version with new build version %q\n", v)
78
+
79
+	_, err = semver.NewBuildVersion("build?123")
80
+	if err != nil {
81
+		fmt.Printf("Create build version failed: %s\n", err)
82
+	}
83
+}

+ 23
- 0
vendor/github.com/blang/semver/json.go View File

@@ -0,0 +1,23 @@
1
+package semver
2
+
3
+import (
4
+	"encoding/json"
5
+)
6
+
7
+// MarshalJSON implements the encoding/json.Marshaler interface.
8
+func (v Version) MarshalJSON() ([]byte, error) {
9
+	return json.Marshal(v.String())
10
+}
11
+
12
+// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
13
+func (v *Version) UnmarshalJSON(data []byte) (err error) {
14
+	var versionString string
15
+
16
+	if err = json.Unmarshal(data, &versionString); err != nil {
17
+		return
18
+	}
19
+
20
+	*v, err = Parse(versionString)
21
+
22
+	return
23
+}

+ 45
- 0
vendor/github.com/blang/semver/json_test.go View File

@@ -0,0 +1,45 @@
1
+package semver
2
+
3
+import (
4
+	"encoding/json"
5
+	"strconv"
6
+	"testing"
7
+)
8
+
9
+func TestJSONMarshal(t *testing.T) {
10
+	versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
11
+	v, err := Parse(versionString)
12
+	if err != nil {
13
+		t.Fatal(err)
14
+	}
15
+
16
+	versionJSON, err := json.Marshal(v)
17
+	if err != nil {
18
+		t.Fatal(err)
19
+	}
20
+
21
+	quotedVersionString := strconv.Quote(versionString)
22
+
23
+	if string(versionJSON) != quotedVersionString {
24
+		t.Fatalf("JSON marshaled semantic version not equal: expected %q, got %q", quotedVersionString, string(versionJSON))
25
+	}
26
+}
27
+
28
+func TestJSONUnmarshal(t *testing.T) {
29
+	versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
30
+	quotedVersionString := strconv.Quote(versionString)
31
+
32
+	var v Version
33
+	if err := json.Unmarshal([]byte(quotedVersionString), &v); err != nil {
34
+		t.Fatal(err)
35
+	}
36
+
37
+	if v.String() != versionString {
38
+		t.Fatalf("JSON unmarshaled semantic version not equal: expected %q, got %q", versionString, v.String())
39
+	}
40
+
41
+	badVersionString := strconv.Quote("3.1.4.1.5.9.2.6.5-other-digits-of-pi")
42
+	if err := json.Unmarshal([]byte(badVersionString), &v); err == nil {
43
+		t.Fatal("expected JSON unmarshal error, got nil")
44
+	}
45
+}

+ 224
- 0
vendor/github.com/blang/semver/range.go View File

@@ -0,0 +1,224 @@
1
+package semver
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+	"unicode"
7
+)
8
+
9
+type comparator func(Version, Version) bool
10
+
11
+var (
12
+	compEQ comparator = func(v1 Version, v2 Version) bool {
13
+		return v1.Compare(v2) == 0
14
+	}
15
+	compNE = func(v1 Version, v2 Version) bool {
16
+		return v1.Compare(v2) != 0
17
+	}
18
+	compGT = func(v1 Version, v2 Version) bool {
19
+		return v1.Compare(v2) == 1
20
+	}
21
+	compGE = func(v1 Version, v2 Version) bool {
22
+		return v1.Compare(v2) >= 0
23
+	}
24
+	compLT = func(v1 Version, v2 Version) bool {
25
+		return v1.Compare(v2) == -1
26
+	}
27
+	compLE = func(v1 Version, v2 Version) bool {
28
+		return v1.Compare(v2) <= 0
29
+	}
30
+)
31
+
32
+type versionRange struct {
33
+	v Version
34
+	c comparator
35
+}
36
+
37
+// rangeFunc creates a Range from the given versionRange.
38
+func (vr *versionRange) rangeFunc() Range {
39
+	return Range(func(v Version) bool {
40
+		return vr.c(v, vr.v)
41
+	})
42
+}
43
+
44
+// Range represents a range of versions.
45
+// A Range can be used to check if a Version satisfies it:
46
+//
47
+//     range, err := semver.ParseRange(">1.0.0 <2.0.0")
48
+//     range(semver.MustParse("1.1.1") // returns true
49
+type Range func(Version) bool
50
+
51
+// OR combines the existing Range with another Range using logical OR.
52
+func (rf Range) OR(f Range) Range {
53
+	return Range(func(v Version) bool {
54
+		return rf(v) || f(v)
55
+	})
56
+}
57
+
58
+// AND combines the existing Range with another Range using logical AND.
59
+func (rf Range) AND(f Range) Range {
60
+	return Range(func(v Version) bool {
61
+		return rf(v) && f(v)
62
+	})
63
+}
64
+
65
+// ParseRange parses a range and returns a Range.
66
+// If the range could not be parsed an error is returned.
67
+//
68
+// Valid ranges are:
69
+//   - "<1.0.0"
70
+//   - "<=1.0.0"
71
+//   - ">1.0.0"
72
+//   - ">=1.0.0"
73
+//   - "1.0.0", "=1.0.0", "==1.0.0"
74
+//   - "!1.0.0", "!=1.0.0"
75
+//
76
+// A Range can consist of multiple ranges separated by space:
77
+// Ranges can be linked by logical AND:
78
+//   - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
79
+//   - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
80
+//
81
+// Ranges can also be linked by logical OR:
82
+//   - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
83
+//
84
+// AND has a higher precedence than OR. It's not possible to use brackets.
85
+//
86
+// Ranges can be combined by both AND and OR
87
+//
88
+//  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
89
+func ParseRange(s string) (Range, error) {
90
+	parts := splitAndTrim(s)
91
+	orParts, err := splitORParts(parts)
92
+	if err != nil {
93
+		return nil, err
94
+	}
95
+	var orFn Range
96
+	for _, p := range orParts {
97
+		var andFn Range
98
+		for _, ap := range p {
99
+			opStr, vStr, err := splitComparatorVersion(ap)
100
+			if err != nil {
101
+				return nil, err
102
+			}
103
+			vr, err := buildVersionRange(opStr, vStr)
104
+			if err != nil {
105
+				return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
106
+			}
107
+			rf := vr.rangeFunc()
108
+
109
+			// Set function
110
+			if andFn == nil {
111
+				andFn = rf
112
+			} else { // Combine with existing function
113
+				andFn = andFn.AND(rf)
114
+			}
115
+		}
116
+		if orFn == nil {
117
+			orFn = andFn
118
+		} else {
119
+			orFn = orFn.OR(andFn)
120
+		}
121
+
122
+	}
123
+	return orFn, nil
124
+}
125
+
126
+// splitORParts splits the already cleaned parts by '||'.
127
+// Checks for invalid positions of the operator and returns an
128
+// error if found.
129
+func splitORParts(parts []string) ([][]string, error) {
130
+	var ORparts [][]string
131
+	last := 0
132
+	for i, p := range parts {
133
+		if p == "||" {
134
+			if i == 0 {
135
+				return nil, fmt.Errorf("First element in range is '||'")
136
+			}
137
+			ORparts = append(ORparts, parts[last:i])
138
+			last = i + 1
139
+		}
140
+	}
141
+	if last == len(parts) {
142
+		return nil, fmt.Errorf("Last element in range is '||'")
143
+	}
144
+	ORparts = append(ORparts, parts[last:])
145
+	return ORparts, nil
146
+}
147
+
148
+// buildVersionRange takes a slice of 2: operator and version
149
+// and builds a versionRange, otherwise an error.
150
+func buildVersionRange(opStr, vStr string) (*versionRange, error) {
151
+	c := parseComparator(opStr)
152
+	if c == nil {
153
+		return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
154
+	}
155
+	v, err := Parse(vStr)
156
+	if err != nil {
157
+		return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
158
+	}
159
+
160
+	return &versionRange{
161
+		v: v,
162
+		c: c,
163
+	}, nil
164
+
165
+}
166
+
167
+// splitAndTrim splits a range string by spaces and cleans leading and trailing spaces
168
+func splitAndTrim(s string) (result []string) {
169
+	last := 0
170
+	for i := 0; i < len(s); i++ {
171
+		if s[i] == ' ' {
172
+			if last < i-1 {
173
+				result = append(result, s[last:i])
174
+			}
175
+			last = i + 1
176
+		}
177
+	}
178
+	if last < len(s)-1 {
179
+		result = append(result, s[last:])
180
+	}
181
+	// parts := strings.Split(s, " ")
182
+	// for _, x := range parts {
183
+	// 	if s := strings.TrimSpace(x); len(s) != 0 {
184
+	// 		result = append(result, s)
185
+	// 	}
186
+	// }
187
+	return
188
+}
189
+
190
+// splitComparatorVersion splits the comparator from the version.
191
+// Spaces between the comparator and the version are not allowed.
192
+// Input must be free of leading or trailing spaces.
193
+func splitComparatorVersion(s string) (string, string, error) {
194
+	i := strings.IndexFunc(s, unicode.IsDigit)
195
+	if i == -1 {
196
+		return "", "", fmt.Errorf("Could not get version from string: %q", s)
197
+	}
198
+	return strings.TrimSpace(s[0:i]), s[i:], nil
199
+}
200
+
201
+func parseComparator(s string) comparator {
202
+	switch s {
203
+	case "==":
204
+		fallthrough
205
+	case "":
206
+		fallthrough
207
+	case "=":
208
+		return compEQ
209
+	case ">":
210
+		return compGT
211
+	case ">=":
212
+		return compGE
213
+	case "<":
214
+		return compLT
215
+	case "<=":
216
+		return compLE
217
+	case "!":
218
+		fallthrough
219
+	case "!=":
220
+		return compNE
221
+	}
222
+
223
+	return nil
224
+}

+ 442
- 0
vendor/github.com/blang/semver/range_test.go View File

@@ -0,0 +1,442 @@
1
+package semver
2
+
3
+import (
4
+	"reflect"
5
+	"strings"
6
+	"testing"
7
+)
8
+
9
+type comparatorTest struct {
10
+	input      string
11
+	comparator func(comparator) bool
12
+}
13
+
14
+func TestParseComparator(t *testing.T) {
15
+	compatorTests := []comparatorTest{
16
+		{">", testGT},
17
+		{">=", testGE},
18
+		{"<", testLT},
19
+		{"<=", testLE},
20
+		{"", testEQ},
21
+		{"=", testEQ},
22
+		{"==", testEQ},
23
+		{"!=", testNE},
24
+		{"!", testNE},
25
+		{"-", nil},
26
+		{"<==", nil},
27
+		{"<<", nil},
28
+		{">>", nil},
29
+	}
30
+
31
+	for _, tc := range compatorTests {
32
+		if c := parseComparator(tc.input); c == nil {
33
+			if tc.comparator != nil {
34
+				t.Errorf("Comparator nil for case %q\n", tc.input)
35
+			}
36
+		} else if !tc.comparator(c) {
37
+			t.Errorf("Invalid comparator for case %q\n", tc.input)
38
+		}
39
+	}
40
+}
41
+
42
+var (
43
+	v1 = MustParse("1.2.2")
44
+	v2 = MustParse("1.2.3")
45
+	v3 = MustParse("1.2.4")
46
+)
47
+
48
+func testEQ(f comparator) bool {
49
+	return f(v1, v1) && !f(v1, v2)
50
+}
51
+
52
+func testNE(f comparator) bool {
53
+	return !f(v1, v1) && f(v1, v2)
54
+}
55
+
56
+func testGT(f comparator) bool {
57
+	return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1)
58
+}
59
+
60
+func testGE(f comparator) bool {
61
+	return f(v2, v1) && f(v3, v2) && !f(v1, v2)
62
+}
63
+
64
+func testLT(f comparator) bool {
65
+	return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1)
66
+}
67
+
68
+func testLE(f comparator) bool {
69
+	return f(v1, v2) && f(v2, v3) && !f(v2, v1)
70
+}
71
+
72
+func TestSplitAndTrim(t *testing.T) {
73
+	tests := []struct {
74
+		i string
75
+		s []string
76
+	}{
77
+		{"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}},
78
+		{"     1.2.3     1.2.3     ", []string{"1.2.3", "1.2.3"}}, // Spaces
79
+		{"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
80
+		{"      1.2.3      ||     >=1.2.3     <1.2.3    ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
81
+	}
82
+
83
+	for _, tc := range tests {
84
+		p := splitAndTrim(tc.i)
85
+		if !reflect.DeepEqual(p, tc.s) {
86
+			t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
87
+		}
88
+	}
89
+}
90
+
91
+func TestSplitComparatorVersion(t *testing.T) {
92
+	tests := []struct {
93
+		i string
94
+		p []string
95
+	}{
96
+		{">1.2.3", []string{">", "1.2.3"}},
97
+		{">=1.2.3", []string{">=", "1.2.3"}},
98
+		{"<1.2.3", []string{"<", "1.2.3"}},
99
+		{"<=1.2.3", []string{"<=", "1.2.3"}},
100
+		{"1.2.3", []string{"", "1.2.3"}},
101
+		{"=1.2.3", []string{"=", "1.2.3"}},
102
+		{"==1.2.3", []string{"==", "1.2.3"}},
103
+		{"!=1.2.3", []string{"!=", "1.2.3"}},
104
+		{"!1.2.3", []string{"!", "1.2.3"}},
105
+		{"error", nil},
106
+	}
107
+	for _, tc := range tests {
108
+		if op, v, err := splitComparatorVersion(tc.i); err != nil {
109
+			if tc.p != nil {
110
+				t.Errorf("Invalid for case %q: Expected %q, got error %q", tc.i, tc.p, err)
111
+			}
112
+		} else if op != tc.p[0] {
113
+			t.Errorf("Invalid operator for case %q: Expected %q, got: %q", tc.i, tc.p[0], op)
114
+		} else if v != tc.p[1] {
115
+			t.Errorf("Invalid version for case %q: Expected %q, got: %q", tc.i, tc.p[1], v)
116
+		}
117
+
118
+	}
119
+}
120
+
121
+func TestBuildVersionRange(t *testing.T) {
122
+	tests := []struct {
123
+		opStr string
124
+		vStr  string
125
+		c     func(comparator) bool
126
+		v     string
127
+	}{
128
+		{">", "1.2.3", testGT, "1.2.3"},
129
+		{">=", "1.2.3", testGE, "1.2.3"},
130
+		{"<", "1.2.3", testLT, "1.2.3"},
131
+		{"<=", "1.2.3", testLE, "1.2.3"},
132
+		{"", "1.2.3", testEQ, "1.2.3"},
133
+		{"=", "1.2.3", testEQ, "1.2.3"},
134
+		{"==", "1.2.3", testEQ, "1.2.3"},
135
+		{"!=", "1.2.3", testNE, "1.2.3"},
136
+		{"!", "1.2.3", testNE, "1.2.3"},
137
+		{">>", "1.2.3", nil, ""},  // Invalid comparator
138
+		{"=", "invalid", nil, ""}, // Invalid version
139
+	}
140
+
141
+	for _, tc := range tests {
142
+		if r, err := buildVersionRange(tc.opStr, tc.vStr); err != nil {
143
+			if tc.c != nil {
144
+				t.Errorf("Invalid for case %q: Expected %q, got error %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tc.v, err)
145
+			}
146
+		} else if r == nil {
147
+			t.Errorf("Invalid for case %q: got nil", strings.Join([]string{tc.opStr, tc.vStr}, ""))
148
+		} else {
149
+			// test version
150
+			if tv := MustParse(tc.v); !r.v.EQ(tv) {
151
+				t.Errorf("Invalid for case %q: Expected version %q, got: %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tv, r.v)
152
+			}
153
+			// test comparator
154
+			if r.c == nil {
155
+				t.Errorf("Invalid for case %q: got nil comparator", strings.Join([]string{tc.opStr, tc.vStr}, ""))
156
+				continue
157
+			}
158
+			if !tc.c(r.c) {
159
+				t.Errorf("Invalid comparator for case %q\n", strings.Join([]string{tc.opStr, tc.vStr}, ""))
160
+			}
161
+		}
162
+	}
163
+
164
+}
165
+
166
+func TestSplitORParts(t *testing.T) {
167
+	tests := []struct {
168
+		i []string
169
+		o [][]string
170
+	}{
171
+		{[]string{">1.2.3", "||", "<1.2.3", "||", "=1.2.3"}, [][]string{
172
+			[]string{">1.2.3"},
173
+			[]string{"<1.2.3"},
174
+			[]string{"=1.2.3"},
175
+		}},
176
+		{[]string{">1.2.3", "<1.2.3", "||", "=1.2.3"}, [][]string{
177
+			[]string{">1.2.3", "<1.2.3"},
178
+			[]string{"=1.2.3"},
179
+		}},
180
+		{[]string{">1.2.3", "||"}, nil},
181
+		{[]string{"||", ">1.2.3"}, nil},
182
+	}
183
+	for _, tc := range tests {
184
+		o, err := splitORParts(tc.i)
185
+		if err != nil && tc.o != nil {
186
+			t.Errorf("Unexpected error for case %q: %s", tc.i, err)
187
+		}
188
+		if !reflect.DeepEqual(tc.o, o) {
189
+			t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
190
+		}
191
+	}
192
+}
193
+
194
+func TestVersionRangeToRange(t *testing.T) {
195
+	vr := versionRange{
196
+		v: MustParse("1.2.3"),
197
+		c: compLT,
198
+	}
199
+	rf := vr.rangeFunc()
200
+	if !rf(MustParse("1.2.2")) || rf(MustParse("1.2.3")) {
201
+		t.Errorf("Invalid conversion to range func")
202
+	}
203
+}
204
+
205
+func TestRangeAND(t *testing.T) {
206
+	v := MustParse("1.2.2")
207
+	v1 := MustParse("1.2.1")
208
+	v2 := MustParse("1.2.3")
209
+	rf1 := Range(func(v Version) bool {
210
+		return v.GT(v1)
211
+	})
212
+	rf2 := Range(func(v Version) bool {
213
+		return v.LT(v2)
214
+	})
215
+	rf := rf1.AND(rf2)
216
+	if rf(v1) {
217
+		t.Errorf("Invalid rangefunc, accepted: %s", v1)
218
+	}
219
+	if rf(v2) {
220
+		t.Errorf("Invalid rangefunc, accepted: %s", v2)
221
+	}
222
+	if !rf(v) {
223
+		t.Errorf("Invalid rangefunc, did not accept: %s", v)
224
+	}
225
+}
226
+
227
+func TestRangeOR(t *testing.T) {
228
+	tests := []struct {
229
+		v Version
230
+		b bool
231
+	}{
232
+		{MustParse("1.2.0"), true},
233
+		{MustParse("1.2.2"), false},
234
+		{MustParse("1.2.4"), true},
235
+	}
236
+	v1 := MustParse("1.2.1")
237
+	v2 := MustParse("1.2.3")
238
+	rf1 := Range(func(v Version) bool {
239
+		return v.LT(v1)
240
+	})
241
+	rf2 := Range(func(v Version) bool {
242
+		return v.GT(v2)
243
+	})
244
+	rf := rf1.OR(rf2)
245
+	for _, tc := range tests {
246
+		if r := rf(tc.v); r != tc.b {
247
+			t.Errorf("Invalid for case %q: Expected %t, got %t", tc.v, tc.b, r)
248
+		}
249
+	}
250
+}
251
+
252
+func TestParseRange(t *testing.T) {
253
+	type tv struct {
254
+		v string
255
+		b bool
256
+	}
257
+	tests := []struct {
258
+		i string
259
+		t []tv
260
+	}{
261
+		// Simple expressions
262
+		{">1.2.3", []tv{
263
+			{"1.2.2", false},
264
+			{"1.2.3", false},
265
+			{"1.2.4", true},
266
+		}},
267
+		{">=1.2.3", []tv{
268
+			{"1.2.3", true},
269
+			{"1.2.4", true},
270
+			{"1.2.2", false},
271
+		}},
272
+		{"<1.2.3", []tv{
273
+			{"1.2.2", true},
274
+			{"1.2.3", false},
275
+			{"1.2.4", false},
276
+		}},
277
+		{"<=1.2.3", []tv{
278
+			{"1.2.2", true},
279
+			{"1.2.3", true},
280
+			{"1.2.4", false},
281
+		}},
282
+		{"1.2.3", []tv{
283
+			{"1.2.2", false},
284
+			{"1.2.3", true},
285
+			{"1.2.4", false},
286
+		}},
287
+		{"=1.2.3", []tv{
288
+			{"1.2.2", false},
289
+			{"1.2.3", true},
290
+			{"1.2.4", false},
291
+		}},
292
+		{"==1.2.3", []tv{
293
+			{"1.2.2", false},
294
+			{"1.2.3", true},
295
+			{"1.2.4", false},
296
+		}},
297
+		{"!=1.2.3", []tv{
298
+			{"1.2.2", true},
299
+			{"1.2.3", false},
300
+			{"1.2.4", true},
301
+		}},
302
+		{"!1.2.3", []tv{
303
+			{"1.2.2", true},
304
+			{"1.2.3", false},
305
+			{"1.2.4", true},
306
+		}},
307
+		// Simple Expression errors
308
+		{">>1.2.3", nil},
309
+		{"!1.2.3", nil},
310
+		{"1.0", nil},
311
+		{"string", nil},
312
+		{"", nil},
313
+
314
+		// AND Expressions
315
+		{">1.2.2 <1.2.4", []tv{
316
+			{"1.2.2", false},
317
+			{"1.2.3", true},
318
+			{"1.2.4", false},
319
+		}},
320
+		{"<1.2.2 <1.2.4", []tv{
321
+			{"1.2.1", true},
322
+			{"1.2.2", false},
323
+			{"1.2.3", false},
324
+			{"1.2.4", false},
325
+		}},
326
+		{">1.2.2 <1.2.5 !=1.2.4", []tv{
327
+			{"1.2.2", false},
328
+			{"1.2.3", true},
329
+			{"1.2.4", false},
330
+			{"1.2.5", false},
331
+		}},
332
+		{">1.2.2 <1.2.5 !1.2.4", []tv{
333
+			{"1.2.2", false},
334
+			{"1.2.3", true},
335
+			{"1.2.4", false},
336
+			{"1.2.5", false},
337
+		}},
338
+		// OR Expressions
339
+		{">1.2.2 || <1.2.4", []tv{
340
+			{"1.2.2", true},
341
+			{"1.2.3", true},
342
+			{"1.2.4", true},
343
+		}},
344
+		{"<1.2.2 || >1.2.4", []tv{
345
+			{"1.2.2", false},
346
+			{"1.2.3", false},
347
+			{"1.2.4", false},
348
+		}},
349
+		// Combined Expressions
350
+		{">1.2.2 <1.2.4 || >=2.0.0", []tv{
351
+			{"1.2.2", false},
352
+			{"1.2.3", true},
353
+			{"1.2.4", false},
354
+			{"2.0.0", true},
355
+			{"2.0.1", true},
356
+		}},
357
+		{">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{
358
+			{"1.2.2", false},
359
+			{"1.2.3", true},
360
+			{"1.2.4", false},
361
+			{"2.0.0", true},
362
+			{"2.0.1", true},
363
+			{"2.9.9", true},
364
+			{"3.0.0", false},
365
+		}},
366
+	}
367
+
368
+	for _, tc := range tests {
369
+		r, err := ParseRange(tc.i)
370
+		if err != nil && tc.t != nil {
371
+			t.Errorf("Error parsing range %q: %s", tc.i, err)
372
+			continue
373
+		}
374
+		for _, tvc := range tc.t {
375
+			v := MustParse(tvc.v)
376
+			if res := r(v); res != tvc.b {
377
+				t.Errorf("Invalid for case %q matching %q: Expected %t, got: %t", tc.i, tvc.v, tvc.b, res)
378
+			}
379
+		}
380
+
381
+	}
382
+}
383
+
384
+func BenchmarkRangeParseSimple(b *testing.B) {
385
+	const VERSION = ">1.0.0"
386
+	b.ReportAllocs()
387
+	b.ResetTimer()
388
+	for n := 0; n < b.N; n++ {
389
+		ParseRange(VERSION)
390
+	}
391
+}
392
+
393
+func BenchmarkRangeParseAverage(b *testing.B) {
394
+	const VERSION = ">=1.0.0 <2.0.0"
395
+	b.ReportAllocs()
396
+	b.ResetTimer()
397
+	for n := 0; n < b.N; n++ {
398
+		ParseRange(VERSION)
399
+	}
400
+}
401
+
402
+func BenchmarkRangeParseComplex(b *testing.B) {
403
+	const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
404
+	b.ReportAllocs()
405
+	b.ResetTimer()
406
+	for n := 0; n < b.N; n++ {
407
+		ParseRange(VERSION)
408
+	}
409
+}
410
+
411
+func BenchmarkRangeMatchSimple(b *testing.B) {
412
+	const VERSION = ">1.0.0"
413
+	r, _ := ParseRange(VERSION)
414
+	v := MustParse("2.0.0")
415
+	b.ReportAllocs()
416
+	b.ResetTimer()
417
+	for n := 0; n < b.N; n++ {
418
+		r(v)
419
+	}
420
+}
421
+
422
+func BenchmarkRangeMatchAverage(b *testing.B) {
423
+	const VERSION = ">=1.0.0 <2.0.0"
424
+	r, _ := ParseRange(VERSION)
425
+	v := MustParse("1.2.3")
426
+	b.ReportAllocs()
427
+	b.ResetTimer()
428
+	for n := 0; n < b.N; n++ {
429
+		r(v)
430
+	}
431
+}
432
+
433
+func BenchmarkRangeMatchComplex(b *testing.B) {
434
+	const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
435
+	r, _ := ParseRange(VERSION)
436
+	v := MustParse("5.0.1")
437
+	b.ReportAllocs()
438
+	b.ResetTimer()
439
+	for n := 0; n < b.N; n++ {
440
+		r(v)
441
+	}
442
+}

+ 395
- 0
vendor/github.com/blang/semver/semver.go View File

@@ -0,0 +1,395 @@
1
+package semver
2
+
3
+import (
4
+	"errors"
5
+	"fmt"
6
+	"strconv"
7
+	"strings"
8
+)
9
+
10
+const (
11
+	numbers  string = "0123456789"
12
+	alphas          = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
13
+	alphanum        = alphas + numbers
14
+)
15
+
16
+// SpecVersion is the latest fully supported spec version of semver
17
+var SpecVersion = Version{
18
+	Major: 2,
19
+	Minor: 0,
20
+	Patch: 0,
21
+}
22
+
23
+// Version represents a semver compatible version
24
+type Version struct {
25
+	Major uint64
26
+	Minor uint64
27
+	Patch uint64
28
+	Pre   []PRVersion
29
+	Build []string //No Precendence
30
+}
31
+
32
+// Version to string
33
+func (v Version) String() string {
34
+	b := make([]byte, 0, 5)
35
+	b = strconv.AppendUint(b, v.Major, 10)
36
+	b = append(b, '.')
37
+	b = strconv.AppendUint(b, v.Minor, 10)
38
+	b = append(b, '.')
39
+	b = strconv.AppendUint(b, v.Patch, 10)
40
+
41
+	if len(v.Pre) > 0 {
42
+		b = append(b, '-')
43
+		b = append(b, v.Pre[0].String()...)
44
+
45
+		for _, pre := range v.Pre[1:] {
46
+			b = append(b, '.')
47
+			b = append(b, pre.String()...)
48
+		}
49
+	}
50
+
51
+	if len(v.Build) > 0 {
52
+		b = append(b, '+')
53
+		b = append(b, v.Build[0]...)
54
+
55
+		for _, build := range v.Build[1:] {
56
+			b = append(b, '.')
57
+			b = append(b, build...)
58
+		}
59
+	}
60
+
61
+	return string(b)
62
+}
63
+
64
+// Equals checks if v is equal to o.
65
+func (v Version) Equals(o Version) bool {
66
+	return (v.Compare(o) == 0)
67
+}
68
+
69
+// EQ checks if v is equal to o.
70
+func (v Version) EQ(o Version) bool {
71
+	return (v.Compare(o) == 0)
72
+}
73
+
74
+// NE checks if v is not equal to o.
75
+func (v Version) NE(o Version) bool {
76
+	return (v.Compare(o) != 0)
77
+}
78
+
79
+// GT checks if v is greater than o.
80
+func (v Version) GT(o Version) bool {
81
+	return (v.Compare(o) == 1)
82
+}
83
+
84
+// GTE checks if v is greater than or equal to o.
85
+func (v Version) GTE(o Version) bool {
86
+	return (v.Compare(o) >= 0)
87
+}
88
+
89
+// GE checks if v is greater than or equal to o.
90
+func (v Version) GE(o Version) bool {
91
+	return (v.Compare(o) >= 0)
92
+}
93
+
94
+// LT checks if v is less than o.
95
+func (v Version) LT(o Version) bool {
96
+	return (v.Compare(o) == -1)
97
+}
98
+
99
+// LTE checks if v is less than or equal to o.
100
+func (v Version) LTE(o Version) bool {
101
+	return (v.Compare(o) <= 0)
102
+}
103
+
104
+// LE checks if v is less than or equal to o.
105
+func (v Version) LE(o Version) bool {
106
+	return (v.Compare(o) <= 0)
107
+}
108
+
109
+// Compare compares Versions v to o:
110
+// -1 == v is less than o
111
+// 0 == v is equal to o
112
+// 1 == v is greater than o
113
+func (v Version) Compare(o Version) int {
114
+	if v.Major != o.Major {
115
+		if v.Major > o.Major {
116
+			return 1
117
+		}
118
+		return -1
119
+	}
120
+	if v.Minor != o.Minor {
121
+		if v.Minor > o.Minor {
122
+			return 1
123
+		}
124
+		return -1
125
+	}
126
+	if v.Patch != o.Patch {
127
+		if v.Patch > o.Patch {
128
+			return 1
129
+		}
130
+		return -1
131
+	}
132
+
133
+	// Quick comparison if a version has no prerelease versions
134
+	if len(v.Pre) == 0 && len(o.Pre) == 0 {
135
+		return 0
136
+	} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
137
+		return 1
138
+	} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
139
+		return -1
140
+	}
141
+
142
+	i := 0
143
+	for ; i < len(v.Pre) && i < len(o.Pre); i++ {
144
+		if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
145
+			continue
146
+		} else if comp == 1 {
147
+			return 1
148
+		} else {
149
+			return -1
150
+		}
151
+	}
152
+
153
+	// If all pr versions are the equal but one has further prversion, this one greater
154
+	if i == len(v.Pre) && i == len(o.Pre) {
155
+		return 0
156
+	} else if i == len(v.Pre) && i < len(o.Pre) {
157
+		return -1
158
+	} else {
159
+		return 1
160
+	}
161
+
162
+}
163
+
164
+// Validate validates v and returns error in case
165
+func (v Version) Validate() error {
166
+	// Major, Minor, Patch already validated using uint64
167
+
168
+	for _, pre := range v.Pre {
169
+		if !pre.IsNum { //Numeric prerelease versions already uint64
170
+			if len(pre.VersionStr) == 0 {
171
+				return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
172
+			}
173
+			if !containsOnly(pre.VersionStr, alphanum) {
174
+				return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
175
+			}
176
+		}
177
+	}
178
+
179
+	for _, build := range v.Build {
180
+		if len(build) == 0 {
181
+			return fmt.Errorf("Build meta data can not be empty %q", build)
182
+		}
183
+		if !containsOnly(build, alphanum) {
184
+			return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
185
+		}
186
+	}
187
+
188
+	return nil
189
+}
190
+
191
+// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
192
+func New(s string) (vp *Version, err error) {
193
+	v, err := Parse(s)
194
+	vp = &v
195
+	return
196
+}
197
+
198
+// Make is an alias for Parse, parses version string and returns a validated Version or error
199
+func Make(s string) (Version, error) {
200
+	return Parse(s)
201
+}
202
+
203
+// Parse parses version string and returns a validated Version or error
204
+func Parse(s string) (Version, error) {
205
+	if len(s) == 0 {
206
+		return Version{}, errors.New("Version string empty")
207
+	}
208
+
209
+	// Split into major.minor.(patch+pr+meta)
210
+	parts := strings.SplitN(s, ".", 3)
211
+	if len(parts) != 3 {
212
+		return Version{}, errors.New("No Major.Minor.Patch elements found")
213
+	}
214
+
215
+	// Major
216
+	if !containsOnly(parts[0], numbers) {
217
+		return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
218
+	}
219
+	if hasLeadingZeroes(parts[0]) {
220
+		return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
221
+	}
222
+	major, err := strconv.ParseUint(parts[0], 10, 64)
223
+	if err != nil {
224
+		return Version{}, err
225
+	}
226
+
227
+	// Minor
228
+	if !containsOnly(parts[1], numbers) {
229
+		return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
230
+	}
231
+	if hasLeadingZeroes(parts[1]) {
232
+		return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
233
+	}
234
+	minor, err := strconv.ParseUint(parts[1], 10, 64)
235
+	if err != nil {
236
+		return Version{}, err
237
+	}
238
+
239
+	v := Version{}
240
+	v.Major = major
241
+	v.Minor = minor
242
+
243
+	var build, prerelease []string
244
+	patchStr := parts[2]
245
+
246
+	if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
247
+		build = strings.Split(patchStr[buildIndex+1:], ".")
248
+		patchStr = patchStr[:buildIndex]
249
+	}
250
+
251
+	if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
252
+		prerelease = strings.Split(patchStr[preIndex+1:], ".")
253
+		patchStr = patchStr[:preIndex]
254
+	}
255
+
256
+	if !containsOnly(patchStr, numbers) {
257
+		return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
258
+	}
259
+	if hasLeadingZeroes(patchStr) {
260
+		return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
261
+	}
262
+	patch, err := strconv.ParseUint(patchStr, 10, 64)
263
+	if err != nil {
264
+		return Version{}, err
265
+	}
266
+
267
+	v.Patch = patch
268
+
269
+	// Prerelease
270
+	for _, prstr := range prerelease {
271
+		parsedPR, err := NewPRVersion(prstr)
272
+		if err != nil {
273
+			return Version{}, err
274
+		}
275
+		v.Pre = append(v.Pre, parsedPR)
276
+	}
277
+
278
+	// Build meta data
279
+	for _, str := range build {
280
+		if len(str) == 0 {
281
+			return Version{}, errors.New("Build meta data is empty")
282
+		}
283
+		if !containsOnly(str, alphanum) {
284
+			return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
285
+		}
286
+		v.Build = append(v.Build, str)
287
+	}
288
+
289
+	return v, nil
290
+}
291
+
292
+// MustParse is like Parse but panics if the version cannot be parsed.
293
+func MustParse(s string) Version {
294
+	v, err := Parse(s)
295
+	if err != nil {
296
+		panic(`semver: Parse(` + s + `): ` + err.Error())
297
+	}
298
+	return v
299
+}
300
+
301
+// PRVersion represents a PreRelease Version
302
+type PRVersion struct {
303
+	VersionStr string
304
+	VersionNum uint64
305
+	IsNum      bool
306
+}
307
+
308
+// NewPRVersion creates a new valid prerelease version
309
+func NewPRVersion(s string) (PRVersion, error) {
310
+	if len(s) == 0 {
311
+		return PRVersion{}, errors.New("Prerelease is empty")
312
+	}
313
+	v := PRVersion{}
314
+	if containsOnly(s, numbers) {
315
+		if hasLeadingZeroes(s) {
316
+			return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
317
+		}
318
+		num, err := strconv.ParseUint(s, 10, 64)
319
+
320
+		// Might never be hit, but just in case
321
+		if err != nil {
322
+			return PRVersion{}, err
323
+		}
324
+		v.VersionNum = num
325
+		v.IsNum = true
326
+	} else if containsOnly(s, alphanum) {
327
+		v.VersionStr = s
328
+		v.IsNum = false
329
+	} else {
330
+		return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
331
+	}
332
+	return v, nil
333
+}
334
+
335
+// IsNumeric checks if prerelease-version is numeric
336
+func (v PRVersion) IsNumeric() bool {
337
+	return v.IsNum
338
+}
339
+
340
+// Compare compares two PreRelease Versions v and o:
341
+// -1 == v is less than o
342
+// 0 == v is equal to o
343
+// 1 == v is greater than o
344
+func (v PRVersion) Compare(o PRVersion) int {
345
+	if v.IsNum && !o.IsNum {
346
+		return -1
347
+	} else if !v.IsNum && o.IsNum {
348
+		return 1
349
+	} else if v.IsNum && o.IsNum {
350
+		if v.VersionNum == o.VersionNum {
351
+			return 0
352
+		} else if v.VersionNum > o.VersionNum {
353
+			return 1
354
+		} else {
355
+			return -1
356
+		}
357
+	} else { // both are Alphas
358
+		if v.VersionStr == o.VersionStr {
359
+			return 0
360
+		} else if v.VersionStr > o.VersionStr {
361
+			return 1
362
+		} else {
363
+			return -1
364
+		}
365
+	}
366
+}
367
+
368
+// PreRelease version to string
369
+func (v PRVersion) String() string {
370
+	if v.IsNum {
371
+		return strconv.FormatUint(v.VersionNum, 10)
372
+	}
373
+	return v.VersionStr
374
+}
375
+
376
+func containsOnly(s string, set string) bool {
377
+	return strings.IndexFunc(s, func(r rune) bool {
378
+		return !strings.ContainsRune(set, r)
379
+	}) == -1
380
+}
381
+
382
+func hasLeadingZeroes(s string) bool {
383
+	return len(s) > 1 && s[0] == '0'
384
+}
385
+
386
+// NewBuildVersion creates a new valid build version
387
+func NewBuildVersion(s string) (string, error) {
388
+	if len(s) == 0 {
389
+		return "", errors.New("Buildversion is empty")
390
+	}
391
+	if !containsOnly(s, alphanum) {
392
+		return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
393
+	}
394
+	return s, nil
395
+}

+ 417
- 0
vendor/github.com/blang/semver/semver_test.go View File

@@ -0,0 +1,417 @@
1
+package semver
2
+
3
+import (
4
+	"testing"
5
+)
6
+
7
+func prstr(s string) PRVersion {
8
+	return PRVersion{s, 0, false}
9
+}
10
+
11
+func prnum(i uint64) PRVersion {
12
+	return PRVersion{"", i, true}
13
+}
14
+
15
+type formatTest struct {
16
+	v      Version
17
+	result string
18
+}
19
+
20
+var formatTests = []formatTest{
21
+	{Version{1, 2, 3, nil, nil}, "1.2.3"},
22
+	{Version{0, 0, 1, nil, nil}, "0.0.1"},
23
+	{Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"},
24
+	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"},
25
+	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"},
26
+	{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"},
27
+	// Prereleases and build metadata hyphens
28
+	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"},
29
+	{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"},
30
+	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
31
+}
32
+
33
+func TestStringer(t *testing.T) {
34
+	for _, test := range formatTests {
35
+		if res := test.v.String(); res != test.result {
36
+			t.Errorf("Stringer, expected %q but got %q", test.result, res)
37
+		}
38
+	}
39
+}
40
+
41
+func TestParse(t *testing.T) {
42
+	for _, test := range formatTests {
43
+		if v, err := Parse(test.result); err != nil {
44
+			t.Errorf("Error parsing %q: %q", test.result, err)
45
+		} else if comp := v.Compare(test.v); comp != 0 {
46
+			t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
47
+		} else if err := v.Validate(); err != nil {
48
+			t.Errorf("Error validating parsed version %q: %q", test.v, err)
49
+		}
50
+	}
51
+}
52
+
53
+func TestMustParse(t *testing.T) {
54
+	_ = MustParse("32.2.1-alpha")
55
+}
56
+
57
+func TestMustParse_panic(t *testing.T) {
58
+	defer func() {
59
+		if recover() == nil {
60
+			t.Errorf("Should have panicked")
61
+		}
62
+	}()
63
+	_ = MustParse("invalid version")
64
+}
65
+
66
+func TestValidate(t *testing.T) {
67
+	for _, test := range formatTests {
68
+		if err := test.v.Validate(); err != nil {
69
+			t.Errorf("Error validating %q: %q", test.v, err)
70
+		}
71
+	}
72
+}
73
+
74
+type compareTest struct {
75
+	v1     Version
76
+	v2     Version
77
+	result int
78
+}
79
+
80
+var compareTests = []compareTest{
81
+	{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 0},
82
+	{Version{2, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 1},
83
+	{Version{0, 1, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 0},
84
+	{Version{0, 2, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 1},
85
+	{Version{0, 0, 1, nil, nil}, Version{0, 0, 1, nil, nil}, 0},
86
+	{Version{0, 0, 2, nil, nil}, Version{0, 0, 1, nil, nil}, 1},
87
+	{Version{1, 2, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 0},
88
+	{Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1},
89
+	{Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
90
+	{Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
91
+
92
+	// Spec Examples #11
93
+	{Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1},
94
+	{Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1},
95
+	{Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1},
96
+
97
+	// Spec Examples #9
98
+	{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, 1},
99
+	{Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, -1},
100
+	{Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1},
101
+	{Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, -1},
102
+	{Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, -1},
103
+	{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, -1},
104
+	{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, -1},
105
+	{Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1},
106
+
107
+	// Ignore Build metadata
108
+	{Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0},
109
+}
110
+
111
+func TestCompare(t *testing.T) {
112
+	for _, test := range compareTests {
113
+		if res := test.v1.Compare(test.v2); res != test.result {
114
+			t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res)
115
+		}
116
+		//Test counterpart
117
+		if res := test.v2.Compare(test.v1); res != -test.result {
118
+			t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res)
119
+		}
120
+	}
121
+}
122
+
123
+type wrongformatTest struct {
124
+	v   *Version
125
+	str string
126
+}
127
+
128
+var wrongformatTests = []wrongformatTest{
129
+	{nil, ""},
130
+	{nil, "."},
131
+	{nil, "1."},
132
+	{nil, ".1"},
133
+	{nil, "a.b.c"},
134
+	{nil, "1.a.b"},
135
+	{nil, "1.1.a"},
136
+	{nil, "1.a.1"},
137
+	{nil, "a.1.1"},
138
+	{nil, ".."},
139
+	{nil, "1.."},
140
+	{nil, "1.1."},
141
+	{nil, "1..1"},
142
+	{nil, "1.1.+123"},
143
+	{nil, "1.1.-beta"},
144
+	{nil, "-1.1.1"},
145
+	{nil, "1.-1.1"},
146
+	{nil, "1.1.-1"},
147
+	// giant numbers
148
+	{nil, "20000000000000000000.1.1"},
149
+	{nil, "1.20000000000000000000.1"},
150
+	{nil, "1.1.20000000000000000000"},
151
+	{nil, "1.1.1-20000000000000000000"},
152
+	// Leading zeroes
153
+	{nil, "01.1.1"},
154
+	{nil, "001.1.1"},
155
+	{nil, "1.01.1"},
156
+	{nil, "1.001.1"},
157
+	{nil, "1.1.01"},
158
+	{nil, "1.1.001"},
159
+	{nil, "1.1.1-01"},
160
+	{nil, "1.1.1-001"},
161
+	{nil, "1.1.1-beta.01"},
162
+	{nil, "1.1.1-beta.001"},
163
+	{&Version{0, 0, 0, []PRVersion{prstr("!")}, nil}, "0.0.0-!"},
164
+	{&Version{0, 0, 0, nil, []string{"!"}}, "0.0.0+!"},
165
+	// empty prversion
166
+	{&Version{0, 0, 0, []PRVersion{prstr(""), prstr("alpha")}, nil}, "0.0.0-.alpha"},
167
+	// empty build meta data
168
+	{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{""}}, "0.0.0-alpha+"},
169
+	{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{"test", ""}}, "0.0.0-alpha+test."},
170
+}
171
+
172
+func TestWrongFormat(t *testing.T) {
173
+	for _, test := range wrongformatTests {
174
+
175
+		if res, err := Parse(test.str); err == nil {
176
+			t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
177
+		}
178
+
179
+		if test.v != nil {
180
+			if err := test.v.Validate(); err == nil {
181
+				t.Errorf("Validating wrong format version %q (%q), expected error", test.v, test.str)
182
+			}
183
+		}
184
+	}
185
+}
186
+
187
+func TestCompareHelper(t *testing.T) {
188
+	v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}
189
+	v1 := Version{1, 0, 0, nil, nil}
190
+	if !v.EQ(v) {
191
+		t.Errorf("%q should be equal to %q", v, v)
192
+	}
193
+	if !v.Equals(v) {
194
+		t.Errorf("%q should be equal to %q", v, v)
195
+	}
196
+	if !v1.NE(v) {
197
+		t.Errorf("%q should not be equal to %q", v1, v)
198
+	}
199
+	if !v.GTE(v) {
200
+		t.Errorf("%q should be greater than or equal to %q", v, v)
201
+	}
202
+	if !v.LTE(v) {
203
+		t.Errorf("%q should be less than or equal to %q", v, v)
204
+	}
205
+	if !v.LT(v1) {
206
+		t.Errorf("%q should be less than %q", v, v1)
207
+	}
208
+	if !v.LTE(v1) {
209
+		t.Errorf("%q should be less than or equal %q", v, v1)
210
+	}
211
+	if !v.LE(v1) {
212
+		t.Errorf("%q should be less than or equal %q", v, v1)
213
+	}
214
+	if !v1.GT(v) {
215
+		t.Errorf("%q should be greater than %q", v1, v)
216
+	}
217
+	if !v1.GTE(v) {
218
+		t.Errorf("%q should be greater than or equal %q", v1, v)
219
+	}
220
+	if !v1.GE(v) {
221
+		t.Errorf("%q should be greater than or equal %q", v1, v)
222
+	}
223
+}
224
+
225
+func TestPreReleaseVersions(t *testing.T) {
226
+	p1, err := NewPRVersion("123")
227
+	if !p1.IsNumeric() {
228
+		t.Errorf("Expected numeric prversion, got %q", p1)
229
+	}
230
+	if p1.VersionNum != 123 {
231
+		t.Error("Wrong prversion number")
232
+	}
233
+	if err != nil {
234
+		t.Errorf("Not expected error %q", err)
235
+	}
236
+	p2, err := NewPRVersion("alpha")
237
+	if p2.IsNumeric() {
238
+		t.Errorf("Expected non-numeric prversion, got %q", p2)
239
+	}
240
+	if p2.VersionStr != "alpha" {
241
+		t.Error("Wrong prversion string")
242
+	}
243
+	if err != nil {
244
+		t.Errorf("Not expected error %q", err)
245
+	}
246
+}
247
+
248
+func TestBuildMetaDataVersions(t *testing.T) {
249
+	_, err := NewBuildVersion("123")
250
+	if err != nil {
251
+		t.Errorf("Unexpected error %q", err)
252
+	}
253
+
254
+	_, err = NewBuildVersion("build")
255
+	if err != nil {
256
+		t.Errorf("Unexpected error %q", err)
257
+	}
258
+
259
+	_, err = NewBuildVersion("test?")
260
+	if err == nil {
261
+		t.Error("Expected error, got none")
262
+	}
263
+
264
+	_, err = NewBuildVersion("")
265
+	if err == nil {
266
+		t.Error("Expected error, got none")
267
+	}
268
+}
269
+
270
+func TestNewHelper(t *testing.T) {
271
+	v, err := New("1.2.3")
272
+	if err != nil {
273
+		t.Fatalf("Unexpected error %q", err)
274
+	}
275
+
276
+	// New returns pointer
277
+	if v == nil {
278
+		t.Fatal("Version is nil")
279
+	}
280
+	if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
281
+		t.Fatal("Unexpected comparison problem")
282
+	}
283
+}
284
+
285
+func TestMakeHelper(t *testing.T) {
286
+	v, err := Make("1.2.3")
287
+	if err != nil {
288
+		t.Fatalf("Unexpected error %q", err)
289
+	}
290
+	if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
291
+		t.Fatal("Unexpected comparison problem")
292
+	}
293
+}
294
+
295
+func BenchmarkParseSimple(b *testing.B) {
296
+	const VERSION = "0.0.1"
297
+	b.ReportAllocs()
298
+	b.ResetTimer()
299
+	for n := 0; n < b.N; n++ {
300
+		Parse(VERSION)
301
+	}
302
+}
303
+
304
+func BenchmarkParseComplex(b *testing.B) {
305
+	const VERSION = "0.0.1-alpha.preview+123.456"
306
+	b.ReportAllocs()
307
+	b.ResetTimer()
308
+	for n := 0; n < b.N; n++ {
309
+		Parse(VERSION)
310
+	}
311
+}
312
+
313
+func BenchmarkParseAverage(b *testing.B) {
314
+	l := len(formatTests)
315
+	b.ReportAllocs()
316
+	b.ResetTimer()
317
+	for n := 0; n < b.N; n++ {
318
+		Parse(formatTests[n%l].result)
319
+	}
320
+}
321
+
322
+func BenchmarkStringSimple(b *testing.B) {
323
+	const VERSION = "0.0.1"
324
+	v, _ := Parse(VERSION)
325
+	b.ReportAllocs()
326
+	b.ResetTimer()
327
+	for n := 0; n < b.N; n++ {
328
+		v.String()
329
+	}
330
+}
331
+
332
+func BenchmarkStringLarger(b *testing.B) {
333
+	const VERSION = "11.15.2012"
334
+	v, _ := Parse(VERSION)
335
+	b.ReportAllocs()
336
+	b.ResetTimer()
337
+	for n := 0; n < b.N; n++ {
338
+		v.String()
339
+	}
340
+}
341
+
342
+func BenchmarkStringComplex(b *testing.B) {
343
+	const VERSION = "0.0.1-alpha.preview+123.456"
344
+	v, _ := Parse(VERSION)
345
+	b.ReportAllocs()
346
+	b.ResetTimer()
347
+	for n := 0; n < b.N; n++ {
348
+		v.String()
349
+	}
350
+}
351
+
352
+func BenchmarkStringAverage(b *testing.B) {
353
+	l := len(formatTests)
354
+	b.ReportAllocs()
355
+	b.ResetTimer()
356
+	for n := 0; n < b.N; n++ {
357
+		formatTests[n%l].v.String()
358
+	}
359
+}
360
+
361
+func BenchmarkValidateSimple(b *testing.B) {
362
+	const VERSION = "0.0.1"
363
+	v, _ := Parse(VERSION)
364
+	b.ReportAllocs()
365
+	b.ResetTimer()
366
+	for n := 0; n < b.N; n++ {
367
+		v.Validate()
368
+	}
369
+}
370
+
371
+func BenchmarkValidateComplex(b *testing.B) {
372
+	const VERSION = "0.0.1-alpha.preview+123.456"
373
+	v, _ := Parse(VERSION)
374
+	b.ReportAllocs()
375
+	b.ResetTimer()
376
+	for n := 0; n < b.N; n++ {
377
+		v.Validate()
378
+	}
379
+}
380
+
381
+func BenchmarkValidateAverage(b *testing.B) {
382
+	l := len(formatTests)
383
+	b.ReportAllocs()
384
+	b.ResetTimer()
385
+	for n := 0; n < b.N; n++ {
386
+		formatTests[n%l].v.Validate()
387
+	}
388
+}
389
+
390
+func BenchmarkCompareSimple(b *testing.B) {
391
+	const VERSION = "0.0.1"
392
+	v, _ := Parse(VERSION)
393
+	b.ReportAllocs()
394
+	b.ResetTimer()
395
+	for n := 0; n < b.N; n++ {
396
+		v.Compare(v)
397
+	}
398
+}
399
+
400
+func BenchmarkCompareComplex(b *testing.B) {
401
+	const VERSION = "0.0.1-alpha.preview+123.456"
402
+	v, _ := Parse(VERSION)
403
+	b.ReportAllocs()
404
+	b.ResetTimer()
405
+	for n := 0; n < b.N; n++ {
406
+		v.Compare(v)
407
+	}
408
+}
409
+
410
+func BenchmarkCompareAverage(b *testing.B) {
411
+	l := len(compareTests)
412
+	b.ReportAllocs()
413
+	b.ResetTimer()
414
+	for n := 0; n < b.N; n++ {
415
+		compareTests[n%l].v1.Compare((compareTests[n%l].v2))
416
+	}
417
+}

+ 28
- 0
vendor/github.com/blang/semver/sort.go View File

@@ -0,0 +1,28 @@
1
+package semver
2
+
3
+import (
4
+	"sort"
5
+)
6
+
7
+// Versions represents multiple versions.
8
+type Versions []Version
9
+
10
+// Len returns length of version collection
11
+func (s Versions) Len() int {
12
+	return len(s)
13
+}
14
+
15
+// Swap swaps two versions inside the collection by its indices
16
+func (s Versions) Swap(i, j int) {
17
+	s[i], s[j] = s[j], s[i]
18
+}
19
+
20
+// Less checks if version at index i is less than version at index j
21
+func (s Versions) Less(i, j int) bool {
22
+	return s[i].LT(s[j])
23
+}
24
+
25
+// Sort sorts a slice of versions
26
+func Sort(versions []Version) {
27
+	sort.Sort(Versions(versions))
28
+}

+ 30
- 0
vendor/github.com/blang/semver/sort_test.go View File

@@ -0,0 +1,30 @@
1
+package semver
2
+
3
+import (
4
+	"reflect"
5
+	"testing"
6
+)
7
+
8
+func TestSort(t *testing.T) {
9
+	v100, _ := Parse("1.0.0")
10
+	v010, _ := Parse("0.1.0")
11
+	v001, _ := Parse("0.0.1")
12
+	versions := []Version{v010, v100, v001}
13
+	Sort(versions)
14
+
15
+	correct := []Version{v001, v010, v100}
16
+	if !reflect.DeepEqual(versions, correct) {
17
+		t.Fatalf("Sort returned wrong order: %s", versions)
18
+	}
19
+}
20
+
21
+func BenchmarkSort(b *testing.B) {
22
+	v100, _ := Parse("1.0.0")
23
+	v010, _ := Parse("0.1.0")
24
+	v001, _ := Parse("0.0.1")
25
+	b.ReportAllocs()
26
+	b.ResetTimer()
27
+	for n := 0; n < b.N; n++ {
28
+		Sort([]Version{v010, v100, v001})
29
+	}
30
+}

+ 30
- 0
vendor/github.com/blang/semver/sql.go View File

@@ -0,0 +1,30 @@
1
+package semver
2
+
3
+import (
4
+	"database/sql/driver"
5
+	"fmt"
6
+)
7
+
8
+// Scan implements the database/sql.Scanner interface.
9
+func (v *Version) Scan(src interface{}) (err error) {
10
+	var str string
11
+	switch src := src.(type) {
12
+	case string:
13
+		str = src
14
+	case []byte:
15
+		str = string(src)
16
+	default:
17
+		return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
18
+	}
19
+
20
+	if t, err := Parse(str); err == nil {
21
+		*v = t
22
+	}
23
+
24
+	return
25
+}
26
+
27
+// Value implements the database/sql/driver.Valuer interface.
28
+func (v Version) Value() (driver.Value, error) {
29
+	return v.String(), nil
30
+}

+ 38
- 0
vendor/github.com/blang/semver/sql_test.go View File

@@ -0,0 +1,38 @@
1
+package semver
2
+
3
+import (
4
+	"testing"
5
+)
6
+
7
+type scanTest struct {
8
+	val         interface{}
9
+	shouldError bool
10
+	expected    string
11
+}
12
+
13
+var scanTests = []scanTest{
14
+	{"1.2.3", false, "1.2.3"},
15
+	{[]byte("1.2.3"), false, "1.2.3"},
16
+	{7, true, ""},
17
+	{7e4, true, ""},
18
+	{true, true, ""},
19
+}
20
+
21
+func TestScanString(t *testing.T) {
22
+	for _, tc := range scanTests {
23
+		s := &Version{}
24
+		err := s.Scan(tc.val)
25
+		if tc.shouldError {
26
+			if err == nil {
27
+				t.Fatalf("Scan did not return an error on %v (%T)", tc.val, tc.val)
28
+			}
29
+		} else {
30
+			if err != nil {
31
+				t.Fatalf("Scan returned an unexpected error: %s (%T) on %v (%T)", tc.val, tc.val, tc.val, tc.val)
32
+			}
33
+			if val, _ := s.Value(); val != tc.expected {
34
+				t.Errorf("Wrong Value returned, expected %q, got %q", tc.expected, val)
35
+			}
36
+		}
37
+	}
38
+}

+ 6
- 0
vendor/manifest View File

@@ -14,6 +14,12 @@
14 14
 			"branch": "master"
15 15
 		},
16 16
 		{
17
+			"importpath": "github.com/blang/semver",
18
+			"repository": "https://github.com/blang/semver",
19
+			"revision": "aea32c919a18e5ef4537bbd283ff29594b1b0165",
20
+			"branch": "master"
21
+		},
22
+		{
17 23
 			"importpath": "github.com/bryanl/doit-server",
18 24
 			"repository": "https://github.com/bryanl/doit-server",
19 25
 			"revision": "6fb91d751dc13dc5122708d487ce3367e785fedd",