Git Product home page Git Product logo

go-version's Introduction

Versioning Library for Go

Build Status GoDoc

go-version is a library for parsing versions and version constraints, and verifying versions against a set of constraints. go-version can sort a collection of versions properly, handles prerelease/beta versions, can increment versions, etc.

Versions used with go-version must follow SemVer.

Installation and Usage

Package documentation can be found on GoDoc.

Installation can be done with a normal go get:

$ go get github.com/hashicorp/go-version

Version Parsing and Comparison

v1, err := version.NewVersion("1.2")
v2, err := version.NewVersion("1.5+metadata")

// Comparison example. There is also GreaterThan, Equal, and just
// a simple Compare that returns an int allowing easy >=, <=, etc.
if v1.LessThan(v2) {
    fmt.Printf("%s is less than %s", v1, v2)
}

Version Constraints

v1, err := version.NewVersion("1.2")

// Constraints example.
constraints, err := version.NewConstraint(">= 1.0, < 1.4")
if constraints.Check(v1) {
	fmt.Printf("%s satisfies constraints %s", v1, constraints)
}

Version Sorting

versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"}
versions := make([]*version.Version, len(versionsRaw))
for i, raw := range versionsRaw {
    v, _ := version.NewVersion(raw)
    versions[i] = v
}

// After this, the versions are properly sorted
sort.Sort(version.Collection(versions))

Issues and Contributing

If you find an issue with this library, please report an issue. If you'd like, we welcome any contributions. Fork this library and submit a pull request.

go-version's People

Contributors

captn3m0 avatar claire-labry avatar cotarg avatar ctdk avatar dependabot[bot] avatar filhodanuvem avatar findkim avatar hashicorp-copywrite[bot] avatar hashicorp-tsccr[bot] avatar jbardin avatar jukie avatar justincampbell avatar kaleworsley avatar kmoe avatar lalyos avatar liamcervante avatar mattfarina avatar mdeggies avatar mitchellh avatar pcasaretto avatar pjebs avatar radeksimko avatar sean- avatar shawnps avatar snackmgmg avatar sofuture avatar svanharmelen avatar vladrassokhin avatar vsimon avatar xiehan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-version's Issues

Errors with Debian versions

Hello - I'm running into some trouble with Debian package versioning. The trailing +deb8u92-style patch notations aren't taken into account:

package main

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

func main() {
	v1, _ := version.NewVersion("1.5+deb8u9")
	v2, _ := version.NewVersion("1.5+deb8u7")

	fmt.Printf("%d\n", v1.Compare(v2))
	fmt.Printf("%+v\n", v1.Segments64())
}

Output:

0
[1 5 0]

Also, leading 1: Epochs cause panics:

package main

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

func main() {
	v3, _ := version.NewVersion("1.3+deb8u7")
	v4, _ := version.NewVersion("2:1.3+deb8u7")

	fmt.Printf("%+v\n", v4.Segments64())
	fmt.Printf("%d\n", v3.Compare(v4))
}

Output:

shanssian:hashicorp-version-problem $ go run test.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x4af950]

goroutine 1 [running]:
github.com/hashicorp/go-version.(*Version).Segments64(...)
        /home/shanssian/go/src/github.com/hashicorp/go-version/version.go:344
main.main()
        /home/shanssian/scratch/hashicorp-version-problem/test.go:13 +0x90
exit status 2

Comparison to github.com/Masterminds/semver

@mitchellh Here is the comparison to github.com/Masterminds/semver we spoke about.

  • On the Version type being able to access the Major, Minor, and Patch parts by name.
  • Constraints support the || operator. For example, >= 1.2.3, < 2 || >= 4.0.0. The or operator separates groups of and operators.
  • Support ~ for patch level equals in addition to ~> on constraints.
  • Support ^ for major version equality on constraints.
  • Support a range syntax like 1.2.3 - 1.3.4 on constraints.
  • A validator that provides reasons why a constraint failed (note, this is a PR for semver that should land soon).

I think this is a good start. These features, except the last, are all in use today. The last line was a feature request for a project outside of mine.

Please let me know what you think about these.

Panic runtime error

panic: runtime error: invalid memory address or nil pointer dereference

package main

import (
    "fmt"

    "github.com/hashicorp/go-version"
)

const (
    FIREFOX_COPY_SUPPORT = "41.0"
    CHROME_COPY_SUPPORT = "43.0.2356"
)

func main() {

  browserName := "Chrome"
  //browserVer := "44.0.2403.125 m (64-bit)" // NOT WORKING
  //browserVer := "44.0.2403" // WORKING
  //browserVer := "44.0" // WORKING
  //browserVer := "44" // WORKING
  browserVer := "44.0.2403.125" // NOT WORKING

  browserVersion, err := version.NewVersion(browserVer)
  chromeConstraint, err := version.NewConstraint(">= " + CHROME_COPY_SUPPORT)
  firefoxConstraint, err := version.NewConstraint(">= " + FIREFOX_COPY_SUPPORT)

  if (browserName == "Chrome" && chromeConstraint.Check(browserVersion)) || (browserName == "Firefox" && firefoxConstraint.Check(browserVersion)) {
    fmt.Println("IT IS TRUE")
  } else {
    fmt.Println("IT IS FALSE")
  }

  if err != nil { fmt.Println("JUST TO AVOID err NOT USED") }

}

comparePart not comparing ints

v1, _ := version.NewVersion("v0.9.0-beta.4")
v2, _ := version.NewVersion("v0.9.0-beta.15")
fmt.Println(v1.Compare(v2))

output: 1

I believe this happens because the comparePart() func is just doing string comparison on "4" and "15", of which 4 is deemed the larger. Whereas I think the comparison should be done on int64 when both 'self' and 'other' are ints, and comparison on strings when both are strings (and the existing logic for when one but not the other is an int)

overall result should be that the output of my example is "-1"

semver (http://semver.org/#spec-item-11) says:

identifiers consisting of only digits are compared numerically and identifiers with letters or hyphens are compared lexically in ASCII sort order.

When using a specific pattern, unintended results are returned

When using a specific pattern, unintended results are returned

func main(){
	ver1, err1 := version.NewVersion("1.2.beta")
	ver2, err2 := version.NewVersion("1.22.beta")

	fmt.Println(ver1)
	fmt.Println(err1)
	fmt.Println()
	fmt.Println(ver2)
	fmt.Println(err2)
}
$ go run main.go                                                                                                                                                           
<nil>
Malformed version: 1.2.beta

1.2.0-2.beta
<nil>

Bug: Constraints::Check(...) function mistakenly returns false, expected true

Hey,

the following example shows an unexpected behavior of the constraints' check function:

v1 := MustVersion("1.7.3")
v2 := MustVersion("1.7.4~pre.0")
v3 := MustVersion("1.7.4")
if !v1.LessThan(v2) {
	panic("v1 not less than v2")
}
if !v2.LessThan(v3) {
	panic("v2 not less than v3")
}
if !v1.LessThan(v3) {
	panic("v1 not less than v3")
}
c := MustConstraint(">= 1.7.3")
if !c.Check(v1) {
	panic("constraint failed for v1")
}
if !c.Check(v3) {
	panic("constraint failed for v3")
}
if !c.Check(v2) {
	panic("constraint failed for v2")
}

My output is constraint failed for v2. The above code shows, that 1.7.3 is less than 1.7.4~pre.0 but 1.7.4~pre.0 does not match constraint >= 1.7.3.

Tested commit: v1.1.0 d40cf49b3a77bba84a7afdbd7f1dc295d114efb1

Thanks,
Marcel Gebhardt

Incorrectly says some inputs are semver

When provided an input like a formatted date, this library incorrectly parses it as semver.

package main

import (
	"fmt"
	"regexp"
)

const (
	VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
		`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
		`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
		`?`

	// SemverRegexpRaw requires a separator between version and prerelease
	SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
		`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
		`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
		`?`
)

// mostly from https://semver.org, but v added in front for npm compatibility
const validatingRE = `^[v]?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`

var semverOrgMatcher = regexp.MustCompile(validatingRE)

func main() {
	version := "2020-02-26T7-36-46"
	versionRegexp := regexp.MustCompile("^" + VersionRegexpRaw + "$")
	semverRegexp := regexp.MustCompile("^" + SemverRegexpRaw + "$")
	fmt.Println(versionRegexp.FindStringSubmatch(version))
	fmt.Println(semverRegexp.FindStringSubmatch(version))
	fmt.Println("is semver according to semver.org:", semverOrgMatcher.MatchString(version))
}

Go playground

I would have expected this to return an error about a malformed version rather than [2020-02-26T7-36-46 2020 -02-26T7-36-46 02-26T7-36-46 ]

Avoiding the Version label in the base package

I have been messing around with a few semver libs and trying to understand how versioning could work in golang, my initial thought was to follow the lead of serf with each containing a version.go in the base path of the module/app as per https://github.com/hashicorp/serf/blob/master/version.go

So i hacked around an example based on this using an existing open source module called go-semver.

https://github.com/wolfeidau/go-semver

The first thing I had to ensure change was the name of the struct for the version management as it clashed.

I was also interested in your view of this convention and whether you would consider it a good idea to rename Version.

Look forward to hearing your thoughts on this.

Malformed version for linux kernel

when trying to create a newVersion for the amazon1/2 (and other distros) kernel version (output of uname -r) it reports a Malformed version.
for example: 4.14.77-70.59.amzn1.x86_64
A short investigation shows that the "_" in the x86_64 is the problem.

A short code to reproduce it:
package main

import (
"fmt"
version "github.com/hashicorp/go-version"
)

func main() {
v1, err := version.NewVersion("4.4.23-31.54.amzn1.x86_64")
if err != nil {
fmt.Printf("Error %v\n", err)
return
} else {
fmt.Printf("Got version %v\n", v1)
}
}

Prerelease constraint handling

The logic in fbe76cf doesn't seem quite right. In particular, consider the use-case here: https://github.com/gesellix/couchdb-prometheus-exporter/blob/master/lib/couchdb-client.go#L52-L69 where we are checking if the server version is >= 2.0 and the reported version is e.g. 2.2.0-302bd8b. Clearly, in this scenario, the constraint check should return true (but it returns false). At the very least, the constraint needs to be able to opt-out of this mode.

https://semver.org/#spec-item-11 implies that 2.2.0-pre >= 2.0 should be true:
"1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." I can certainly see the case for not wanting to match pre-releases at all in certain circumstances.

Proposal: Add version diff

go-version currently supports comparing versions as a whole, e.g. 2.3.4 > 1.2.3. Sometimes we need to determine if the difference between versions is a major change, a minor change, etc.

Since Compare() supports an arbitrary number of components, the diff should as well.

Only the first diff is likely to be relevant, as in the diff of 2.3.4 vs 1.2.3, we care about the major version change, and the minor version changes are not meaningful.

I can work on this if this sounds good. We're going to be using a very pared-down version of this in the AWS Provider.

Should 2.2.0-alpha be >= 1.2-alpha?

Regarding constraints: The 2.2.0-alpha.20181119-metadata used to be >= 1.2-alpha.20170101 (e.g. as of fc61389). This is no longer the case. It seems to me that it should be. CC @benesch

Either way, the specific rules that are used should be documented in the comment for Constraint.

Memory increasing every Version.String() called

Version.String() currently returns every new string from buffer.
Also, Version.Compare calles Version.String() internally.

When I should compare many Versions, every Compare func creates new string.
It requires too many memories, and cases too heavy GC.

I propose result of Version.String() should be cached internally (like original).
How do you think about?

GreaterThanOrEqualTo and LessThanOrEqualTo

It would be nice if we had a GreaterThanOrEqualTo rather than having to do !LessThan.
It just seems easier to read in code and it's trivial to implement at package level.

Version `v1.0.2-alpha.0` should be greater than `v1.0.1` but `version.NewConstraint(...).Check` returns false

Hey guys ... I stumbled upon this issue while implementing a tool to validate versions when I release one of my github projects. I want to make sure, that the version for my next release is greater than the latest version.

When checking for pre-release versions (alpha / beta) I stumbled upon a bug (from my point of view):

  • Given my latest version is v1.0.1 which is a stable release
  • When I check if v1.0.2 is greater than v1.0.1 then everything works fine
  • When I check if v1.0.2-alpha.0 is greater than v1.0.1 then the result is false ... my expectation is that this check would pass, because I increased the minor version and marked it as unstable.
  • When I check if v1.0.2-alpha.1 is greater than v1.0.2-alpha.0 then everything works fine again.

I wrote a short testcase in Go to validate

issue.go:

package services

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The next version should be greater that the current version
func IsGreater(current string, next string) bool {
	parsedCurrent, err := version.NewVersion(current)
	if err != nil {
		fmt.Print(err)
		return false
	}

	parsedNext, err := version.NewVersion(next)
	if err != nil {
		fmt.Print(err)
		return false
	}

	// should be greater that current version
	constraint, err := version.NewConstraint("> " + parsedCurrent.Original())
	if err != nil {
		fmt.Print(err)
		return false
	}

	if !constraint.Check(parsedNext) {
		fmt.Print("next version " + parsedNext.Original() + " is not greater that current version " + parsedCurrent.Original())
	}
	return constraint.Check(parsedNext)
}

issue_test.go:

package services

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_ShouldBeGreater(t *testing.T) {
	testCases := []struct {
		currentVersion string
		nextVersion    string
		shouldBeValid  bool
	}{
		{"v1.0.1", "v1.0.2", true},
		{"v1.0.1", "v1.1.0", true},
		{"v1.0.1", "v2.0.0", true},
		{"v1.0.1", "v1.0.2-alpha.0", true}, // fails but should be true
		{"v1.0.1", "v1.0.2-beta.0", true},  // fails but should be true
		{"v1.0.2-alpha.0", "v1.0.2-alpha.1", true},
		{"v1.0.2-alpha.0", "v1.0.2-beta.0", true},
		{"v1.0.2-beta.1", "v1.0.2-beta.2", true},
		{"v1.0.2-alpha.0", "v1.0.2", true},
		{"v1.0.2-beta.0", "v1.0.2", true},
		{"v1.0.2", "v1.0.2", false},
		{"v1.0.2", "v1.0.1", false},
		{"v1.0.2-beta.0", "v1.0.2-alpha.0", false},
		{"v1.0.2-beta.2", "v1.0.2-beta.1", false},
		{"v2.0.0", "v2.0.0", false},
	}
	for _, tc := range testCases {
		assert := assert.New(t)
		got := IsGreater(tc.currentVersion, tc.nextVersion)

		assert.NotNil(got)
		assert.Equal(tc.shouldBeValid, got, "Result did not meet expectation - next version = "+tc.nextVersion+", current version = "+tc.currentVersion)
	}
}

Could you take a look into this please? Feel free to reach out to me for information if you feel the need. Best regards.

PreRelease comparison bug?

In looking at the ComparePreReleases test cases...

func TestComparePreReleases(t *testing.T) {
	cases := []struct {
		v1       string
		v2       string
		expected int
	}{
...
            {"5.4-alpha", "5.4-alpha.beta", 1},
}

I'm noticing that 5.4-alpha is larger (has a higher precedence) than 5.4-alpha.beta. However when reading over the precedence rules of pre-releases in semver (https://semver.org/#spec-item-11)

A larger set of pre-release fields has a higher precedence than a smaller set, 
if all of the preceding identifiers are equal. 
Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

In this test case, all the preceding identifiers are equal - "5.4" - and the larger set "alpha.beta" has a lower precedence than the smaller set "alpha". Shouldn't it have a higher precedence?

Unable to determine RC versions

I can't start minio after successfull compilation. The error is:

minio: <ERROR> Unable to determine current go version. Malformed version: 1.7rc1

I tracked the problem to this library. 1.7rc1 is the official release candidate.

go version go1.7rc1 linux/amd64

please tag and version this project

Hello,

Can you please tag and version this project?

I am the Debian Maintainer for go-version and versioning would help Debian keep up with development.

Incorrect hanling of prerelease in constraints

  v, _ := version.NewVersion("1.1.8-alpha1")
  if fast_check, err := version.NewConstraint(">= 1.1.7"); err == nil {
    if fast_check.Check(v) {
      fmt.Printf("OK: %v >= 1.1.7\n", v)
    } else {
      fmt.Printf("BUG: %v < 1.1.7\n", v)
    }

Constraint check incorrectly returns false, which is obviously a bug.

Prerelease String shouldn't always be treated as lower version

Hey,
I've found some irregularity where I have the following two versions for tcpdump
4.9.3-4ubuntu0.1
4.9.3

Using the example code from the readme we get that 4.9.3 is greater than 4.9.3-4ubuntu0.1 even though that's not the case
Link to playground to see my tests.

I'm pretty sure that 4ubuntu0.1 doesn't mean it's a prerelease version and it's a widely used suffix.
In other compiled binaries for ubuntu.

I'm not sure what's the correct approach to solve this, but I was wondering whether we should narrow down the behavior when evaluating the prerelease string.
WDYT?

Leading zeros are explicitly forbidden in Semantic Versioning

This call version.NewSemver("1.010.2") should return an error due to the leading zero on the minor version, but it does not. When you add a leading zero, it is not clear if the number is in octal or base 10.

The wording of the specification (https://semver.org/) is:

A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.

Prerelease bug hidden due to bad tests

Please explain the logic behind this test: https://github.com/hashicorp/go-version/blob/master/constraint_test.go#L73

{">= 2.0.0", "2.1.0-beta", false},

Quoting SemVer:

Precedence is determined by the first difference when comparing each of these identifiers from left to right as follows: Major, minor, and patch versions are always compared numerically. Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version.

Let's compare 2.0.0 and 2.1.0-beta:

  1. Major versions are equal
  2. Minor versions differ -- the latter is greater. Done!

The constraint has been satisfied. There is no reason to even consider the prerelease version because we've already determined that the constraint is satisfied.

Incorrectly evaluates contraints involving prereleases

According to the spec, 1.1.0-a is greater than 1.0.0, but the constraints incorrectly evaluate this case

v, _ := version.NewVersion("1.1.0-a")
c, _ := version.NewConstraint("> 1.0.0")
fmt.Println(c.Check(v)) // prints false

Support for zero padded numbers

With the latest version scheme of Docker I found an interesting case where zero padded numbers are no longer zero padded when converted to a Version and then back to a string. Example, 17.03.0-ce becomes 17.3.0-ce

Potentially related to #16 ?

Store and allow retrieval of the string of the version in its original form

For my use case (a little API that grabs a GitHub repository's tags and then matches semver patterns against it) it would be helpful if there were a way back to the string of the version in its original form.

https://api.mattandre.ws/semver/github/hashicorp/terraform/<v0.5

$ curl https://api.mattandre.ws/semver/github/hashicorp/terraform/%3Cv0.5
v0.4.2

With one small tweak, this is very easy to achieve with your go-version library, see here:-
matthew-andrews@401ade5

Could this (or something like it) be something you could consider merging into the proper library?

Patch version comparison bug

The version with patch version prefix is lower than the same version without:

v1, _ := version.NewVersion("1.0.0-rc1")
v2, _ := version.NewVersion("1.0.0")
v1.LessThan(v2) // true

Comparing alpine packages versions

Alpine packages version can be in the form of:
0.8_alpha-r0
0.8_beta19-r0:
0.8_rc1-r0
0.8_rc1
0.8_alpha
0.8-r0
0.8.r0
0.8

when trying to create a NewVersion("0.8_rc1-r0") I get an error:
Malformed version: 0.8_rc1-r0
I noticed that if I move rc1 to the end with a dot (0.8-r0.rc1) it works fine
This code reproduces the issue

package main

import (
	"fmt"
	"os"
	hVersion "github.com/hashicorp/go-version"
)

func verFunc(ver1 string, ver2 string) {
	vera, erra := hVersion.NewVersion(ver1)
	verb, errb := hVersion.NewVersion(ver2)

	fmt.Printf("(%v %v) installed %s < Fixed %s %v\n", erra, errb, vera, verb, vera.LessThan(verb))

}

func main() {
	ver1 := os.Args[1]
	ver2 := os.Args[2]

	verFunc(ver1, ver2)
}

VersionCheck

Hello,

Not really an issue, but wanted to share with you guys that I wrote a quick proof of concept for an API to make using go-version easier to integrate into something like a boot process for an app, where it can check it's dependencies and alert on any mismatches

https://github.com/StabbyCutyou/versioncheck

Big fan of the libraries you guys have been putting out, wanted to contribute in some small way.

Invalid semver versions are treated as "valid"

As discovered by @rclark

The semver spec is clear that build or prerelease identifiers MUST come after a -.

Versions, such as the following

1.2.-3
1.2.03
1.2
1.2.3.4.5.6
1.2.3.alpha
1.2.3-preview.01
1.2.3-preview..1
1.2.3-preview!
1.2.3+one+two
1.2.3+one.2!

are treated as "valid". I intentionally put valid in quotes because it's clear that the internal representation of such a version is wrong anyway. All the consumer can reliably do with that parsed version is to call String() on it to reconstruct the same (invalid) version.
https://go.dev/play/p/hyp8ZZJQO8-

The Readme of go-version explicitly says

Versions used with go-version must follow SemVer.

Therefore I would treat this as a bug.

Malformed version for go1.11

OS: Ubuntu 18.10 x86_64,
Golang: go1.11.5

package main

import (
	"fmt"
	"runtime"

	version "github.com/hashicorp/go-version"
)

func main() {
	fmt.Println(runtime.Version())
	_, err := version.NewVersion(runtime.Version())
	if err != nil {
		panic(err)
	}
}

The runtime.Version() returns go1.11. And err is Malformed version: go1.11.

Careful with PRs

This is just a minor issue - I don't need it corrected or anything, just something I wanted to bring to your attention so that no future contributors get bit by it.

I'm about to pull this into a new project and was reviewing the change history just to get up to date with the project (it's a small history, so easy to pick through the changes) and I noticed something:

This commit from a PR a few months ago: 0929ab3

It has what appears to be a rogue merge-commit that de-attributes my contributions from the contributor graph from a PR a few weeks prior to that one: b662985

It looks like the contributor had merged changes into his local fork, did a merge commit, then pushed that commit up with his PR, essentially overwriting my own (instead of rebasing the changes cleanly on top of his local copy first). Since the commit wouldn't have shown up in the diff (since all the changes were applied already), I'm guessing it was simply easy to overlook. My commits are still in the history, but it no longer counts the contributions towards me personally. Which, to be honest, is not that big of a deal. I don't think anyone had any ill-intent.

I'm only opening the issue so that someone on your end can acknowledge it and close it out, and then be on the lookout for it so that no future contributors have it happen to them as well.

Love the stuff you all do, thanks again for all the hard work.

Double-digit prerelease sorting broken

According to semver specs:

Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

With this package, beta11 ranks higher than beta2.

package main

import (
	"fmt"
	"github.com/hashicorp/go-version"

)

func main() {
  	a, _ := version.NewVersion("1.0.0-beta11")
	b, _ := version.NewVersion("1.0.0-beta2")
	fmt.Printf("%s is greater than %s: %t", a, b, a.GreaterThan(b))
}

Outputs:

1.0.0-beta11 is greater than 1.0.0-beta2: false

playground

When i compare with Empty version string Getting Error

v1, _ := version.NewVersion("")
	v2, _ := version.NewVersion("10.3")
	if v1.LessThan(v2) {
		fmt.Println("v1<v2")
	} else {
		fmt.Println("v2<v1")
	}
panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x4a3bd3]

goroutine 1 [running]:
github.com/hashicorp/go-version.(*Version).String(0x0)
/go/pkg/mod/github.com/hashicorp/[email protected]/version.go:369 +0x33
github.com/hashicorp/go-version.(*Version).Compare(0xc000080140, 0x0)
/go/pkg/mod/github.com/hashicorp/[email protected]/version.go:116 +0x39
github.com/hashicorp/go-version.(*Version).GreaterThan(...)
/go/pkg/mod/github.com/hashicorp/[email protected]/version.go:298
main.main()
/workspaces/learning/rc/string.go:18 +0x95
exit status 2

[question] which is bigger according the constraint?

I have a range of the versions and a version that I need to check if it is inside the range.
the constraint that I build was:

element 0: >=3.6.6
element 1: <3.6.8-15.1

(see screenshot for clearer vision)
image

The version that I'm checking is 3.6.8
The check is returning false on the "less than" element (<3.6.8-15.1)

Why does 3.6.8-15.1 considered not bigger than 3.6.8?

Must() method for Constraint

Hi,

The NewVersion() method supports a Must() method to work with its returned Version struct. It would be extremely useful to also have a Must() method that works with NewConstraint(). My use case is hardcoded configuration of "required version" constraints.

It's obvious why this doesn't already exist–oversight when Must() was created for Version, which lives in the same package as Constraints, at which point the name conflicted. This wasn't an intentional omission, but rather a lack of foresight whereby two structs' constructors in the same package want to support a Must() method.

It's not possible to be backwards-compatible by accepting an interface{} as first argument and type-switch between Version and Constraints (aka. []*Constraint) due to the return type which would also have to be interface{}... not only backwards-incompatible, but unacceptable.

The only backwards-compatible workaround is to add a MustConstraint() function, and as such that is what I recommend. Ideally Must() would go away and be replaced by MustVersion() and MustConstraint(), or Version and Constraint would be split into separate subpackages (eg. version/version and version/constraint so that each can have its own method named Must(); but either of those options are breaking changes.

All I know is that it's horrifically inconsistent to offer version.Must(version.NewVersion()) but no analog for version.Must(version.NewConstraint()). If nothing else, version.Must() should be removed simply to make the error handling between NewVersion() and NewConstraint() consistent.

Skipping some versions

Hello,
I want to make a smooth migration for AWS provider from version 3 to 4.9.0 or newer
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade
In Node.js world it is possible to define allowed version of a package like this:
<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0
So for my scenario Node.js-like version constraint would look like this:
>= 3.0 < 4.0 || >= 4.9 < 5.0

But when I try to do something similar for AWS provider in terraform this is the only working possibility
>= 3.0, != 4.0, != 4.1, != 4.2, != 4.3, != 4.4, != 4.5, != 4.6, != 4.7, != 4.8, < 5.0
or am I missing something?

Segments vs Segments64 - there's a significant difference!

Related commit: efb921a#diff-ccde124315e74e68fae484840d08dff5L272

Previously Segments() simply returned v.segments, while now it creates a new slice and fills up the new slice with the values, but Segments64 does not!

The difference is that if you simply return v.segments that will be returned by reference and not by value.

So, e.g.:

ver.Segments64()[segmentIdx] = ver.Segments64()[segmentIdx] + 1

ver will actually change, but with Segments()

ver.Segments()[segmentIdx] = ver.Segments()[segmentIdx] + 1

ver will not change!

Before the linked commit ver.Segments() did return the reference, so it did bump the version, but since the linked commit it does not.

A related question: if the intention here is that ver.Segments() should return a "copy" and not a reference of segments, what would be the best way to bump the version? Maybe a new version.NewVersionFromSegments method?

Pessimistic operator is including the ceiling

The pessimistic constraint operator behavior seems to have changed from:

"~> 0.11" == ">= 0.11, < 0.12"

to

"~> 0.11" == ">= 0.11, <= 0.12"

☝️ this is problematic and an undocumented (as far as i can tell) breaking change.


0.12 Implicit (with pessimistic operator)

→ tf version
Terraform v0.12.0


→ cat main.tf
terraform {
  required_version = "~> 0.11"
}

output "foo" {
  value = "we shouldn't see this when using terraform 0.12.0"
}


→ tf init

Initializing the backend...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

/tmp/main
→ tf apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

foo = we shouldn't see this when using terraform 0.12.0

0.12 Explicit (without pessimistic operator)

→ cat main.tf
terraform {
  required_version = ">= 0.11, < 0.12"
}

output "foo" {
  value = "we shouldn't see this when using terraform 0.12.0"
}


→ tf init

Error: Unsupported Terraform Core version

This configuration does not support Terraform version 0.12.0. To proceed,
either choose another supported Terraform version or update the root module's
version constraint. Version constraints are normally set for good reason, so
updating the constraint may lead to other errors or unexpected behavior.

2-segment versions

Hey guys,

We've noticed that versions like 1.0 do not produce an error during parsing and are interpreted like 1.0.0, even though it does not appear to be a correct SemVer.

From http://semver.org/:

A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers...

The README also says Versions used with go-version must follow SemVer, yet the examples show plenty of two-segment versions.

So given all that:

  • Is this intended behavior?
  • Are there any plans/thoughts to make the parser stricter and reject incorrect SemVer versions? or
  • Would you accept a PR that implements this? Maybe do not touch NewVersion() for backward compatibility but introduce NewVersionStrict() or something like this.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.