mirror of https://github.com/k3s-io/k3s
Merge pull request #18032 from mikedanese/semver
add semver parsing of gitversion to pkg/versionpull/6/head
commit
2c49893733
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kubernetes",
|
"ImportPath": "k8s.io/kubernetes",
|
||||||
"GoVersion": "go1.4.3",
|
"GoVersion": "go1.5.1",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./..."
|
"./..."
|
||||||
],
|
],
|
||||||
|
@ -101,6 +101,11 @@
|
||||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/blang/semver",
|
||||||
|
"Comment": "v3.0.1",
|
||||||
|
"Rev": "31b736133b98f26d5e078ec9eb591666edfd091f"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/boltdb/bolt",
|
"ImportPath": "github.com/boltdb/bolt",
|
||||||
"Comment": "v1.0-119-g90fef38",
|
"Comment": "v1.0-119-g90fef38",
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
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)
|
||||||
|
======
|
||||||
|
|
||||||
|
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
```bash
|
||||||
|
$ go get github.com/blang/semver
|
||||||
|
```
|
||||||
|
Note: Always vendor your dependencies or fix on a specific version tag.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import github.com/blang/semver
|
||||||
|
v1, err := semver.Make("1.0.0-beta")
|
||||||
|
v2, err := semver.Make("2.0.0-beta")
|
||||||
|
v1.Compare(v2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
||||||
|
|
||||||
|
Why should I use this lib?
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Fully spec compatible
|
||||||
|
- No reflection
|
||||||
|
- No regex
|
||||||
|
- Fully tested (Coverage >99%)
|
||||||
|
- Readable parsing/validation errors
|
||||||
|
- Fast (See [Benchmarks](#benchmarks))
|
||||||
|
- Only Stdlib
|
||||||
|
- Uses values instead of pointers
|
||||||
|
- Many features, see below
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Parsing and validation at all levels
|
||||||
|
- Comparator-like comparisons
|
||||||
|
- Compare Helper Methods
|
||||||
|
- InPlace manipulation
|
||||||
|
- Sortable (implements sort.Interface)
|
||||||
|
- database/sql compatible (sql.Scanner/Valuer)
|
||||||
|
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
-----
|
||||||
|
|
||||||
|
Have a look at full examples in [examples/main.go](examples/main.go)
|
||||||
|
|
||||||
|
```go
|
||||||
|
import github.com/blang/semver
|
||||||
|
|
||||||
|
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
||||||
|
fmt.Printf("Major: %d\n", v.Major)
|
||||||
|
fmt.Printf("Minor: %d\n", v.Minor)
|
||||||
|
fmt.Printf("Patch: %d\n", v.Patch)
|
||||||
|
fmt.Printf("Pre: %s\n", v.Pre)
|
||||||
|
fmt.Printf("Build: %s\n", v.Build)
|
||||||
|
|
||||||
|
// Prerelease versions array
|
||||||
|
if len(v.Pre) > 0 {
|
||||||
|
fmt.Println("Prerelease versions:")
|
||||||
|
for i, pre := range v.Pre {
|
||||||
|
fmt.Printf("%d: %q\n", i, pre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build meta data array
|
||||||
|
if len(v.Build) > 0 {
|
||||||
|
fmt.Println("Build meta data:")
|
||||||
|
for i, build := range v.Build {
|
||||||
|
fmt.Printf("%d: %q\n", i, build)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v001, err := semver.Make("0.0.1")
|
||||||
|
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
||||||
|
v001.GT(v) == true
|
||||||
|
v.LT(v001) == true
|
||||||
|
v.GTE(v) == true
|
||||||
|
v.LTE(v) == true
|
||||||
|
|
||||||
|
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
||||||
|
v001.Compare(v) == 1
|
||||||
|
v.Compare(v001) == -1
|
||||||
|
v.Compare(v) == 0
|
||||||
|
|
||||||
|
// Manipulate Version in place:
|
||||||
|
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error parsing pre release version: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nValidate versions:")
|
||||||
|
v.Build[0] = "?"
|
||||||
|
|
||||||
|
err = v.Validate()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Validation failed: %s\n", err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Benchmarks
|
||||||
|
-----
|
||||||
|
|
||||||
|
BenchmarkParseSimple 5000000 328 ns/op 49 B/op 1 allocs/op
|
||||||
|
BenchmarkParseComplex 1000000 2105 ns/op 263 B/op 7 allocs/op
|
||||||
|
BenchmarkParseAverage 1000000 1301 ns/op 168 B/op 4 allocs/op
|
||||||
|
BenchmarkStringSimple 10000000 130 ns/op 5 B/op 1 allocs/op
|
||||||
|
BenchmarkStringLarger 5000000 280 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkStringComplex 3000000 512 ns/op 80 B/op 3 allocs/op
|
||||||
|
BenchmarkStringAverage 5000000 387 ns/op 47 B/op 2 allocs/op
|
||||||
|
BenchmarkValidateSimple 500000000 7.92 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkValidateComplex 2000000 923 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkValidateAverage 5000000 452 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkCompareSimple 100000000 11.2 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkCompareComplex 50000000 40.9 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkCompareAverage 50000000 43.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkSort 5000000 436 ns/op 259 B/op 2 allocs/op
|
||||||
|
|
||||||
|
See benchmark cases at [semver_test.go](semver_test.go)
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
-----
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Contribution
|
||||||
|
-----
|
||||||
|
|
||||||
|
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-----
|
||||||
|
|
||||||
|
See [LICENSE](LICENSE) file.
|
|
@ -0,0 +1,83 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/blang/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
v, err := semver.Parse("0.0.1-alpha.preview.222+123.github")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error while parsing (not valid): %q", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Version to string: %q\n", v)
|
||||||
|
|
||||||
|
fmt.Printf("Major: %d\n", v.Major)
|
||||||
|
fmt.Printf("Minor: %d\n", v.Minor)
|
||||||
|
fmt.Printf("Patch: %d\n", v.Patch)
|
||||||
|
|
||||||
|
// Prerelease versions
|
||||||
|
if len(v.Pre) > 0 {
|
||||||
|
fmt.Println("Prerelease versions:")
|
||||||
|
for i, pre := range v.Pre {
|
||||||
|
fmt.Printf("%d: %q\n", i, pre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build meta data
|
||||||
|
if len(v.Build) > 0 {
|
||||||
|
fmt.Println("Build meta data:")
|
||||||
|
for i, build := range v.Build {
|
||||||
|
fmt.Printf("%d: %q\n", i, build)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make == Parse (Value), New for Pointer
|
||||||
|
v001, err := semver.Make("0.0.1")
|
||||||
|
|
||||||
|
fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):")
|
||||||
|
fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v))
|
||||||
|
fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001))
|
||||||
|
fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v))
|
||||||
|
|
||||||
|
fmt.Println("\nUse comparison helpers returning booleans:")
|
||||||
|
fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v))
|
||||||
|
fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v))
|
||||||
|
fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v))
|
||||||
|
fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001))
|
||||||
|
fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001))
|
||||||
|
fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v))
|
||||||
|
|
||||||
|
fmt.Println("\nManipulate Version in place:")
|
||||||
|
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error parsing pre release version: %q", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Version to string: %q\n", v)
|
||||||
|
|
||||||
|
fmt.Println("\nCompare Prerelease versions:")
|
||||||
|
pre1, _ := semver.NewPRVersion("123")
|
||||||
|
pre2, _ := semver.NewPRVersion("alpha")
|
||||||
|
pre3, _ := semver.NewPRVersion("124")
|
||||||
|
fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2))
|
||||||
|
fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1))
|
||||||
|
fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1))
|
||||||
|
|
||||||
|
fmt.Println("\nValidate versions:")
|
||||||
|
v.Build[0] = "?"
|
||||||
|
|
||||||
|
err = v.Validate()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Validation failed: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Create valid build meta data:")
|
||||||
|
b1, _ := semver.NewBuildVersion("build123")
|
||||||
|
v.Build[0] = b1
|
||||||
|
fmt.Printf("Version with new build version %q\n", v)
|
||||||
|
|
||||||
|
_, err = semver.NewBuildVersion("build?123")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Create build version failed: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalJSON implements the encoding/json.Marshaler interface.
|
||||||
|
func (v Version) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
|
||||||
|
func (v *Version) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
var versionString string
|
||||||
|
|
||||||
|
if err = json.Unmarshal(data, &versionString); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*v, err = Parse(versionString)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,395 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
numbers string = "0123456789"
|
||||||
|
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
||||||
|
alphanum = alphas + numbers
|
||||||
|
)
|
||||||
|
|
||||||
|
// SpecVersion is the latest fully supported spec version of semver
|
||||||
|
var SpecVersion = Version{
|
||||||
|
Major: 2,
|
||||||
|
Minor: 0,
|
||||||
|
Patch: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version represents a semver compatible version
|
||||||
|
type Version struct {
|
||||||
|
Major uint64
|
||||||
|
Minor uint64
|
||||||
|
Patch uint64
|
||||||
|
Pre []PRVersion
|
||||||
|
Build []string //No Precendence
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version to string
|
||||||
|
func (v Version) String() string {
|
||||||
|
b := make([]byte, 0, 5)
|
||||||
|
b = strconv.AppendUint(b, v.Major, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Minor, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Patch, 10)
|
||||||
|
|
||||||
|
if len(v.Pre) > 0 {
|
||||||
|
b = append(b, '-')
|
||||||
|
b = append(b, v.Pre[0].String()...)
|
||||||
|
|
||||||
|
for _, pre := range v.Pre[1:] {
|
||||||
|
b = append(b, '.')
|
||||||
|
b = append(b, pre.String()...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.Build) > 0 {
|
||||||
|
b = append(b, '+')
|
||||||
|
b = append(b, v.Build[0]...)
|
||||||
|
|
||||||
|
for _, build := range v.Build[1:] {
|
||||||
|
b = append(b, '.')
|
||||||
|
b = append(b, build...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals checks if v is equal to o.
|
||||||
|
func (v Version) Equals(o Version) bool {
|
||||||
|
return (v.Compare(o) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EQ checks if v is equal to o.
|
||||||
|
func (v Version) EQ(o Version) bool {
|
||||||
|
return (v.Compare(o) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NE checks if v is not equal to o.
|
||||||
|
func (v Version) NE(o Version) bool {
|
||||||
|
return (v.Compare(o) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GT checks if v is greater than o.
|
||||||
|
func (v Version) GT(o Version) bool {
|
||||||
|
return (v.Compare(o) == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GTE checks if v is greater than or equal to o.
|
||||||
|
func (v Version) GTE(o Version) bool {
|
||||||
|
return (v.Compare(o) >= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GE checks if v is greater than or equal to o.
|
||||||
|
func (v Version) GE(o Version) bool {
|
||||||
|
return (v.Compare(o) >= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LT checks if v is less than o.
|
||||||
|
func (v Version) LT(o Version) bool {
|
||||||
|
return (v.Compare(o) == -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LTE checks if v is less than or equal to o.
|
||||||
|
func (v Version) LTE(o Version) bool {
|
||||||
|
return (v.Compare(o) <= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LE checks if v is less than or equal to o.
|
||||||
|
func (v Version) LE(o Version) bool {
|
||||||
|
return (v.Compare(o) <= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares Versions v to o:
|
||||||
|
// -1 == v is less than o
|
||||||
|
// 0 == v is equal to o
|
||||||
|
// 1 == v is greater than o
|
||||||
|
func (v Version) Compare(o Version) int {
|
||||||
|
if v.Major != o.Major {
|
||||||
|
if v.Major > o.Major {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if v.Minor != o.Minor {
|
||||||
|
if v.Minor > o.Minor {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if v.Patch != o.Patch {
|
||||||
|
if v.Patch > o.Patch {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick comparison if a version has no prerelease versions
|
||||||
|
if len(v.Pre) == 0 && len(o.Pre) == 0 {
|
||||||
|
return 0
|
||||||
|
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
|
||||||
|
return 1
|
||||||
|
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
|
||||||
|
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
|
||||||
|
continue
|
||||||
|
} else if comp == 1 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all pr versions are the equal but one has further prversion, this one greater
|
||||||
|
if i == len(v.Pre) && i == len(o.Pre) {
|
||||||
|
return 0
|
||||||
|
} else if i == len(v.Pre) && i < len(o.Pre) {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates v and returns error in case
|
||||||
|
func (v Version) Validate() error {
|
||||||
|
// Major, Minor, Patch already validated using uint64
|
||||||
|
|
||||||
|
for _, pre := range v.Pre {
|
||||||
|
if !pre.IsNum { //Numeric prerelease versions already uint64
|
||||||
|
if len(pre.VersionStr) == 0 {
|
||||||
|
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
|
||||||
|
}
|
||||||
|
if !containsOnly(pre.VersionStr, alphanum) {
|
||||||
|
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, build := range v.Build {
|
||||||
|
if len(build) == 0 {
|
||||||
|
return fmt.Errorf("Build meta data can not be empty %q", build)
|
||||||
|
}
|
||||||
|
if !containsOnly(build, alphanum) {
|
||||||
|
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||||
|
func New(s string) (vp *Version, err error) {
|
||||||
|
v, err := Parse(s)
|
||||||
|
vp = &v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||||
|
func Make(s string) (Version, error) {
|
||||||
|
return Parse(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses version string and returns a validated Version or error
|
||||||
|
func Parse(s string) (Version, error) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return Version{}, errors.New("Version string empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into major.minor.(patch+pr+meta)
|
||||||
|
parts := strings.SplitN(s, ".", 3)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return Version{}, errors.New("No Major.Minor.Patch elements found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Major
|
||||||
|
if !containsOnly(parts[0], numbers) {
|
||||||
|
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
|
||||||
|
}
|
||||||
|
if hasLeadingZeroes(parts[0]) {
|
||||||
|
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
|
||||||
|
}
|
||||||
|
major, err := strconv.ParseUint(parts[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return Version{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor
|
||||||
|
if !containsOnly(parts[1], numbers) {
|
||||||
|
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
|
||||||
|
}
|
||||||
|
if hasLeadingZeroes(parts[1]) {
|
||||||
|
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
|
||||||
|
}
|
||||||
|
minor, err := strconv.ParseUint(parts[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return Version{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := Version{}
|
||||||
|
v.Major = major
|
||||||
|
v.Minor = minor
|
||||||
|
|
||||||
|
var build, prerelease []string
|
||||||
|
patchStr := parts[2]
|
||||||
|
|
||||||
|
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
|
||||||
|
build = strings.Split(patchStr[buildIndex+1:], ".")
|
||||||
|
patchStr = patchStr[:buildIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
|
||||||
|
prerelease = strings.Split(patchStr[preIndex+1:], ".")
|
||||||
|
patchStr = patchStr[:preIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !containsOnly(patchStr, numbers) {
|
||||||
|
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
|
||||||
|
}
|
||||||
|
if hasLeadingZeroes(patchStr) {
|
||||||
|
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
|
||||||
|
}
|
||||||
|
patch, err := strconv.ParseUint(patchStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return Version{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Patch = patch
|
||||||
|
|
||||||
|
// Prerelease
|
||||||
|
for _, prstr := range prerelease {
|
||||||
|
parsedPR, err := NewPRVersion(prstr)
|
||||||
|
if err != nil {
|
||||||
|
return Version{}, err
|
||||||
|
}
|
||||||
|
v.Pre = append(v.Pre, parsedPR)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build meta data
|
||||||
|
for _, str := range build {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return Version{}, errors.New("Build meta data is empty")
|
||||||
|
}
|
||||||
|
if !containsOnly(str, alphanum) {
|
||||||
|
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
|
||||||
|
}
|
||||||
|
v.Build = append(v.Build, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse is like Parse but panics if the version cannot be parsed.
|
||||||
|
func MustParse(s string) Version {
|
||||||
|
v, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(`semver: Parse(` + s + `): ` + err.Error())
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRVersion represents a PreRelease Version
|
||||||
|
type PRVersion struct {
|
||||||
|
VersionStr string
|
||||||
|
VersionNum uint64
|
||||||
|
IsNum bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPRVersion creates a new valid prerelease version
|
||||||
|
func NewPRVersion(s string) (PRVersion, error) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return PRVersion{}, errors.New("Prerelease is empty")
|
||||||
|
}
|
||||||
|
v := PRVersion{}
|
||||||
|
if containsOnly(s, numbers) {
|
||||||
|
if hasLeadingZeroes(s) {
|
||||||
|
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
|
||||||
|
}
|
||||||
|
num, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
|
||||||
|
// Might never be hit, but just in case
|
||||||
|
if err != nil {
|
||||||
|
return PRVersion{}, err
|
||||||
|
}
|
||||||
|
v.VersionNum = num
|
||||||
|
v.IsNum = true
|
||||||
|
} else if containsOnly(s, alphanum) {
|
||||||
|
v.VersionStr = s
|
||||||
|
v.IsNum = false
|
||||||
|
} else {
|
||||||
|
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNumeric checks if prerelease-version is numeric
|
||||||
|
func (v PRVersion) IsNumeric() bool {
|
||||||
|
return v.IsNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares two PreRelease Versions v and o:
|
||||||
|
// -1 == v is less than o
|
||||||
|
// 0 == v is equal to o
|
||||||
|
// 1 == v is greater than o
|
||||||
|
func (v PRVersion) Compare(o PRVersion) int {
|
||||||
|
if v.IsNum && !o.IsNum {
|
||||||
|
return -1
|
||||||
|
} else if !v.IsNum && o.IsNum {
|
||||||
|
return 1
|
||||||
|
} else if v.IsNum && o.IsNum {
|
||||||
|
if v.VersionNum == o.VersionNum {
|
||||||
|
return 0
|
||||||
|
} else if v.VersionNum > o.VersionNum {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
} else { // both are Alphas
|
||||||
|
if v.VersionStr == o.VersionStr {
|
||||||
|
return 0
|
||||||
|
} else if v.VersionStr > o.VersionStr {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreRelease version to string
|
||||||
|
func (v PRVersion) String() string {
|
||||||
|
if v.IsNum {
|
||||||
|
return strconv.FormatUint(v.VersionNum, 10)
|
||||||
|
}
|
||||||
|
return v.VersionStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsOnly(s string, set string) bool {
|
||||||
|
return strings.IndexFunc(s, func(r rune) bool {
|
||||||
|
return !strings.ContainsRune(set, r)
|
||||||
|
}) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasLeadingZeroes(s string) bool {
|
||||||
|
return len(s) > 1 && s[0] == '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuildVersion creates a new valid build version
|
||||||
|
func NewBuildVersion(s string) (string, error) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "", errors.New("Buildversion is empty")
|
||||||
|
}
|
||||||
|
if !containsOnly(s, alphanum) {
|
||||||
|
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Versions represents multiple versions.
|
||||||
|
type Versions []Version
|
||||||
|
|
||||||
|
// Len returns length of version collection
|
||||||
|
func (s Versions) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps two versions inside the collection by its indices
|
||||||
|
func (s Versions) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less checks if version at index i is less than version at index j
|
||||||
|
func (s Versions) Less(i, j int) bool {
|
||||||
|
return s[i].LT(s[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts a slice of versions
|
||||||
|
func Sort(versions []Version) {
|
||||||
|
sort.Sort(Versions(versions))
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scan implements the database/sql.Scanner interface.
|
||||||
|
func (v *Version) Scan(src interface{}) (err error) {
|
||||||
|
var str string
|
||||||
|
switch src := src.(type) {
|
||||||
|
case string:
|
||||||
|
str = src
|
||||||
|
case []byte:
|
||||||
|
str = string(src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := Parse(str); err == nil {
|
||||||
|
*v = t
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the database/sql/driver.Valuer interface.
|
||||||
|
func (v Version) Value() (driver.Value, error) {
|
||||||
|
return v.String(), nil
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/blang/semver"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(gitversion string) (semver.Version, error) {
|
||||||
|
// optionally trim leading spaces then one v
|
||||||
|
var seen bool
|
||||||
|
gitversion = strings.TrimLeftFunc(gitversion, func(ch rune) bool {
|
||||||
|
if seen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ch == 'v' {
|
||||||
|
seen = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return unicode.IsSpace(ch)
|
||||||
|
})
|
||||||
|
|
||||||
|
return semver.Make(gitversion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParse(gitversion string) semver.Version {
|
||||||
|
v, err := Parse(gitversion)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("failed to parse semver from giversion %q: %v", gitversion, err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParseVersion(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
version string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{version: "v1.0.1-alpha"},
|
||||||
|
{version: "v0.19.3"},
|
||||||
|
{version: "0.19.3"},
|
||||||
|
{version: "v1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||||
|
{version: "1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||||
|
{version: "1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||||
|
{version: "1.0.0"},
|
||||||
|
{version: "\t v1.0.0"},
|
||||||
|
{version: "vv1.0.0", expectErr: true},
|
||||||
|
{version: "blah1.0.0", expectErr: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
_, err := Parse(c.version)
|
||||||
|
if err != nil && !c.expectErr {
|
||||||
|
t.Errorf("[%v]unexpected error: %v", i, err)
|
||||||
|
}
|
||||||
|
if err == nil && c.expectErr {
|
||||||
|
t.Errorf("[%v]expected error for %s", i, c.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue