semver

package module
v0.0.0-...-09c98ae Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2022 License: MIT Imports: 9 Imported by: 0

README

semver is a Semantic Versioning library written in golang. It fully covers spec version 2.0.0.

This fork of blang/semver has been updated to accept 4 digit versions. This goes against semver by incorporating a revision number which is used by many enterprise organizations.

Versioning

Old v1-v3 versions exist in the root of the repository for compatiblity reasons and will only receive bug fixes.

The current stable version is v4 and is fully go-mod compatible.

Usage

$ go get github.com/kong/semver/v4
# Or use fixed versions
$ go get github.com/kong/semver/v4@v4.0.0

Note: Always vendor your dependencies or fix on a specific version tag.

import github.com/kong/semver/v4
v1, err := semver.Make("1.0.0.0-beta")
v2, err := semver.Make("2.0.0.0-beta")
v1.Compare(v2)

Also check the GoDocs.

Why should I use this lib?

  • Fully spec compatible
  • No reflection
  • No regex
    • Regex was added to make parsing 3 and 4 digit versioning for readability of code
  • Fully tested (Coverage >99%)
  • Readable parsing/validation errors
  • Fast (See 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
  • Ranges >=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1, >=1.0.0.0 <2.0.0.0 || >=3.0.0 !3.0.1.0-beta.1
  • Wildcards >=1.x, <=2.5.x, >=1.2.3.x, <=2.5.x
  • Sortable (implements sort.Interface)
  • database/sql compatible (sql.Scanner/Valuer)
  • encoding/json compatible (json.Marshaler/Unmarshaler)

Ranges

A Range is a set of conditions which specify which versions satisfy the range.

A condition is composed of an operator and a version. The supported operators are:

  • <1.0.0 Less than 1.0.0
  • <=1.0.0 Less than or equal to 1.0.0
  • >1.0.0 Greater than 1.0.0
  • >=1.0.0 Greater than or equal to 1.0.0
  • 1.0.0, =1.0.0, ==1.0.0 Equal to 1.0.0
  • !1.0.0, !=1.0.0 Not equal to 1.0.0. Excludes version 1.0.0.

Note that spaces between the operator and the version will be gracefully tolerated.

A Range can link multiple Ranges separated by space:

Ranges can be linked by logical AND:

  • >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
  • >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

Ranges can also be linked by logical OR:

  • <2.0.0 || >=3.0.0 would match 1.x.x and 3.x.x but not 2.x.x

AND has a higher precedence than OR. It's not possible to use brackets.

Ranges can be combined by both AND and OR

  • >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

Note that all ranges can contain a mixture of 3 and 4 digit version numbers.

Range usage:

v, err := semver.Parse("1.2.3")
expectedRange, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
if expectedRange(v) {
    //valid
}

Example

Have a look at full examples in v4/examples/main.go

import github.com/kong/semver/v4

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-8             1604840    757.7 ns/op    224 B/op   2 allocs/op
BenchmarkParseComplex-8             480861     2351 ns/op    434 B/op   8 allocs/op
BenchmarkParseAverage-8             568860     2365 ns/op    341 B/op   5 allocs/op
BenchmarkParseTolerantAverage-8     423841     2881 ns/op    535 B/op   9 allocs/op
BenchmarkStringSimple-8           38751848    31.04 ns/op      5 B/op   1 allocs/op
BenchmarkStringLarger-8           16473154    71.87 ns/op     32 B/op   2 allocs/op
BenchmarkStringComplex-8          10292580    153.5 ns/op     80 B/op   3 allocs/op
BenchmarkStringAverage-8          11083524    109.2 ns/op     48 B/op   2 allocs/op
BenchmarkValidateSimple-8        300665788    3.944 ns/op      0 B/op   0 allocs/op
BenchmarkValidateComplex-8         6201321    217.8 ns/op      0 B/op   0 allocs/op
BenchmarkValidateAverage-8        11562688    106.9 ns/op      0 B/op   0 allocs/op
BenchmarkCompareSimple-8         175868226    6.688 ns/op      0 B/op   0 allocs/op
BenchmarkCompareComplex-8         81058132    15.18 ns/op      0 B/op   0 allocs/op
BenchmarkCompareAverage-8         51633844    23.23 ns/op      0 B/op   0 allocs/op
BenchmarkSort-8                    6390456    190.1 ns/op    264 B/op   2 allocs/op
BenchmarkRangeParseSimple-8        1061410     1169 ns/op    418 B/op   8 allocs/op
BenchmarkRangeParseAverage-8        504180     2263 ns/op    844 B/op  15 allocs/op
BenchmarkRangeParseComplex-8        170269     7123 ns/op   2906 B/op  45 allocs/op
BenchmarkRangeMatchSimple-8       71724456    16.70 ns/op      0 B/op   0 allocs/op
BenchmarkRangeMatchAverage-8      32207384    36.53 ns/op      0 B/op   0 allocs/op
BenchmarkRangeMatchComplex-8      12062602    100.4 ns/op      0 B/op   0 allocs/op

See benchmark cases at 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.

Motivation for fork

Kong Gateway and Kong Gateway OSS use different versioning systems; Kong Gateway doesn't follow strict semver. As Kong Gateway and Kong Gateway OSS are released the major.minor.patch versions are in lock step; however, Kong Gateway may have additional revisions that are based of the OSS upstream. In order to handle version comparisons the fork was a necessary evil.

Contribution

Feel free to make a pull request. For bigger changes create a issue first to discuss about it.

License

See LICENSE file.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// SpecVersion is the latest fully supported spec version of semver
	SpecVersion = Version{
		Major: 2,
		Minor: 0,
		Patch: 0,
	}

	// VersionRegex is the compiled 3 or 4 digit versioning that has strict rules'
	// - major,
	// - minor,
	// - patch,
	// - revision,
	// - pre-release,
	// - build metadata
	VersionRegex = regexp.MustCompile(threeFourDigitVersionRegexFormat)

	// PermissiveVersionRegex is the compiled 3 or 4 digit versioning that has loosened rule
	// restrictions.
	// - leading and trailing spaces,
	// - prefixing versions with a 'v' and empty capture groups in order to dynamically fill in the
	//   fields with defaults.
	PermissiveVersionRegex = regexp.MustCompile(threeFourDigitPermissiveVersionRegexFormat)
)

Functions

func FinalizeVersion

func FinalizeVersion(s string) (string, error)

FinalizeVersion returns the major, minor and patch number only and discards prerelease and build number.

func NewBuildVersion

func NewBuildVersion(s string) (string, error)

NewBuildVersion creates a new valid build version

func Sort

func Sort(versions []Version)

Sort sorts a slice of versions

Types

type PRVersion

type PRVersion struct {
	VersionStr string
	VersionNum uint64
	IsNum      bool
}

PRVersion represents a PreRelease Version

func NewPRVersion

func NewPRVersion(s string) (PRVersion, error)

NewPRVersion creates a new valid prerelease version

func (PRVersion) Compare

func (v PRVersion) Compare(o PRVersion) int

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 (PRVersion) IsNumeric

func (v PRVersion) IsNumeric() bool

IsNumeric checks if prerelease-version is numeric

func (PRVersion) String

func (v PRVersion) String() string

PreRelease version to string

type Range

type Range func(Version) bool

Range represents a range of versions. A Range can be used to check if a Version satisfies it:

range, err := semver.ParseRange(">1.0.0 <2.0.0")
range(semver.MustParse("1.1.1") // returns true

func MustParseRange

func MustParseRange(s string) Range

MustParseRange is like ParseRange but panics if the range cannot be parsed.

func ParseRange

func ParseRange(s string) (Range, error)

ParseRange parses a range and returns a Range. If the range could not be parsed an error is returned.

Valid ranges are:

  • "<1.0.0"
  • "<=1.0.0"
  • ">1.0.0"
  • ">=1.0.0"
  • "1.0.0", "=1.0.0", "==1.0.0"
  • "!1.0.0", "!=1.0.0"

A Range can consist of multiple ranges separated by space: Ranges can be linked by logical AND:

  • ">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"
  • ">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

Ranges can also be linked by logical OR:

  • "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"

AND has a higher precedence than OR. It's not possible to use brackets.

Ranges can be combined by both AND and OR

  • `>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`

func (Range) AND

func (rf Range) AND(f Range) Range

AND combines the existing Range with another Range using logical AND.

func (Range) OR

func (rf Range) OR(f Range) Range

OR combines the existing Range with another Range using logical OR.

type Version

type Version struct {
	Major    uint64
	Minor    uint64
	Patch    uint64
	Revision int64
	Pre      []PRVersion
	Build    []string //No Precedence
}

Version represents a semver compatible version

func Make

func Make(s string) (Version, error)

Make is an alias for Parse, parses version string and returns a validated Version or error

func MustParse

func MustParse(s string) Version

MustParse is like Parse but panics if the version cannot be parsed.

func New

func New(s string) (*Version, error)

New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error

func Parse

func Parse(s string) (Version, error)

Parse parses version string and returns a validated Version or error

func ParseTolerant

func ParseTolerant(s string) (Version, error)

ParseTolerant allows for certain version specifications that do not strictly adhere to semver specs to be parsed by this library. It does so by normalizing versions before passing them to Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions with only major and minor components specified, and removes leading 0s. It also allows for an optional revision field similar to the Parse function.

func (Version) Compare

func (v Version) Compare(o Version) int

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 (Version) EQ

func (v Version) EQ(o Version) bool

EQ checks if v is equal to o.

func (Version) Equals

func (v Version) Equals(o Version) bool

Equals checks if v is equal to o.

func (Version) FinalizeVersion

func (v Version) FinalizeVersion() string

FinalizeVersion discards prerelease and build number and only returns major, minor and patch number and optionally revision.

func (Version) GE

func (v Version) GE(o Version) bool

GE checks if v is greater than or equal to o.

func (Version) GT

func (v Version) GT(o Version) bool

GT checks if v is greater than o.

func (Version) GTE

func (v Version) GTE(o Version) bool

GTE checks if v is greater than or equal to o.

func (*Version) IncrementMajor

func (v *Version) IncrementMajor() error

IncrementMajor increments the major version

func (*Version) IncrementMinor

func (v *Version) IncrementMinor() error

IncrementMinor increments the minor version

func (*Version) IncrementPatch

func (v *Version) IncrementPatch() error

IncrementPatch increments the patch version

func (*Version) IncrementRevision

func (v *Version) IncrementRevision() error

IncrementRevision increments the revision version only if the revision has been set during the parsing of the version string. If the structure is used then the default revision of 0 is initialized and will cause the version to increment.

func (Version) LE

func (v Version) LE(o Version) bool

LE checks if v is less than or equal to o.

func (Version) LT

func (v Version) LT(o Version) bool

LT checks if v is less than o.

func (Version) LTE

func (v Version) LTE(o Version) bool

LTE checks if v is less than or equal to o.

func (Version) MarshalJSON

func (v Version) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface.

func (Version) NE

func (v Version) NE(o Version) bool

NE checks if v is not equal to o.

func (*Version) Scan

func (v *Version) Scan(src interface{}) (err error)

Scan implements the database/sql.Scanner interface.

func (Version) String

func (v Version) String() string

Version to string

func (*Version) UnmarshalJSON

func (v *Version) UnmarshalJSON(data []byte) (err error)

UnmarshalJSON implements the encoding/json.Unmarshaler interface.

func (Version) Validate

func (v Version) Validate() error

Validate validates v and returns error in case

func (Version) Value

func (v Version) Value() (driver.Value, error)

Value implements the database/sql/driver.Valuer interface.

type Versions

type Versions []Version

Versions represents multiple versions.

func (Versions) Len

func (s Versions) Len() int

Len returns length of version collection

func (Versions) Less

func (s Versions) Less(i, j int) bool

Less checks if version at index i is less than version at index j

func (Versions) Swap

func (s Versions) Swap(i, j int)

Swap swaps two versions inside the collection by its indices

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL