From 73f71a7260f59266c3c40f8616aebe7ede469c91 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Tue, 1 Dec 2015 09:53:46 -0800 Subject: [PATCH 1/2] godeps: add github.com/blang/semver --- Godeps/Godeps.json | 7 +- .../src/github.com/blang/semver/LICENSE | 22 + .../src/github.com/blang/semver/README.md | 142 +++++++ .../github.com/blang/semver/examples/main.go | 83 ++++ .../src/github.com/blang/semver/json.go | 23 + .../src/github.com/blang/semver/semver.go | 395 ++++++++++++++++++ .../src/github.com/blang/semver/sort.go | 28 ++ .../src/github.com/blang/semver/sql.go | 30 ++ 8 files changed, 729 insertions(+), 1 deletion(-) create mode 100644 Godeps/_workspace/src/github.com/blang/semver/LICENSE create mode 100644 Godeps/_workspace/src/github.com/blang/semver/README.md create mode 100644 Godeps/_workspace/src/github.com/blang/semver/examples/main.go create mode 100644 Godeps/_workspace/src/github.com/blang/semver/json.go create mode 100644 Godeps/_workspace/src/github.com/blang/semver/semver.go create mode 100644 Godeps/_workspace/src/github.com/blang/semver/sort.go create mode 100644 Godeps/_workspace/src/github.com/blang/semver/sql.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index acf7249b125..d13ee5a57c7 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "k8s.io/kubernetes", - "GoVersion": "go1.4.3", + "GoVersion": "go1.5.1", "Packages": [ "./..." ], @@ -101,6 +101,11 @@ "ImportPath": "github.com/beorn7/perks/quantile", "Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d" }, + { + "ImportPath": "github.com/blang/semver", + "Comment": "v3.0.1", + "Rev": "31b736133b98f26d5e078ec9eb591666edfd091f" + }, { "ImportPath": "github.com/boltdb/bolt", "Comment": "v1.0-119-g90fef38", diff --git a/Godeps/_workspace/src/github.com/blang/semver/LICENSE b/Godeps/_workspace/src/github.com/blang/semver/LICENSE new file mode 100644 index 00000000000..5ba5c86fcb0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Benedikt Lang + +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. + diff --git a/Godeps/_workspace/src/github.com/blang/semver/README.md b/Godeps/_workspace/src/github.com/blang/semver/README.md new file mode 100644 index 00000000000..5171c5c5571 --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/README.md @@ -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. diff --git a/Godeps/_workspace/src/github.com/blang/semver/examples/main.go b/Godeps/_workspace/src/github.com/blang/semver/examples/main.go new file mode 100644 index 00000000000..f36c983d90f --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/examples/main.go @@ -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) + } +} diff --git a/Godeps/_workspace/src/github.com/blang/semver/json.go b/Godeps/_workspace/src/github.com/blang/semver/json.go new file mode 100644 index 00000000000..a74bf7c4494 --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/json.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/blang/semver/semver.go b/Godeps/_workspace/src/github.com/blang/semver/semver.go new file mode 100644 index 00000000000..bbf85ce972d --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/semver.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/blang/semver/sort.go b/Godeps/_workspace/src/github.com/blang/semver/sort.go new file mode 100644 index 00000000000..e18f880826a --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/sort.go @@ -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)) +} diff --git a/Godeps/_workspace/src/github.com/blang/semver/sql.go b/Godeps/_workspace/src/github.com/blang/semver/sql.go new file mode 100644 index 00000000000..eb4d802666e --- /dev/null +++ b/Godeps/_workspace/src/github.com/blang/semver/sql.go @@ -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 +} From ac097bfe70bb9a3ab928d504f04f853e7a5cc710 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Tue, 1 Dec 2015 11:56:44 -0800 Subject: [PATCH 2/2] add semver parsing of gitversion to pkg/version --- pkg/version/semver.go | 50 ++++++++++++++++++++++++++++++++++++++ pkg/version/semver_test.go | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 pkg/version/semver.go create mode 100644 pkg/version/semver_test.go diff --git a/pkg/version/semver.go b/pkg/version/semver.go new file mode 100644 index 00000000000..3d18a10209c --- /dev/null +++ b/pkg/version/semver.go @@ -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 +} diff --git a/pkg/version/semver_test.go b/pkg/version/semver_test.go new file mode 100644 index 00000000000..4f4de75891c --- /dev/null +++ b/pkg/version/semver_test.go @@ -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) + } + } +}