Repository: Masterminds/semver
Pull request #111 (2019-08-30 19:38:01)

Constraints are brought in line with other tools
Warning, this is a break from the existing range semantics.

* >11 now is looking for 12.0.0 and up rather than >11.0.0
  This is more intuitive and what other tools like npm do. This
  applies to all segments. >1.2 is now looking for 1.3.0 and up
  instead of 1.2.0 and up.
* Validation for = or "" is always the same message. It is a
  basic message even when ranges occur.

Note, nodejs semver does not follow strict semver on prereleases.
That semver package for a range of >1.2.3-alpha.1 would not find
2.3.4-beta.2 to be in the range. Prerelease search is scoped to
the version rather than all versions. In their notes they say
this is not technically semver to do if you follow the precidence
rules. This package follows the semver precidence rules.

mattfarina: Fixes #93

Patch:
diff --git a/constraints.go b/constraints.go
index 107be38..45b9cb4 100644
--- a/constraints.go
+++ b/constraints.go
@@ -189,14 +189,14 @@ func parseConstraint(c string) (*constraint, error) {
 	minorDirty := false
 	patchDirty := false
 	dirty := false
-	if isX(m[3]) {
+	if isX(m[3]) || m[3] == "" {
 		ver = "0.0.0"
 		dirty = true
 	} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
 		minorDirty = true
 		dirty = true
 		ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
-	} else if isX(strings.TrimPrefix(m[5], ".")) {
+	} else if isX(strings.TrimPrefix(m[5], ".")) || m[5] == "" {
 		dirty = true
 		patchDirty = true
 		ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
@@ -240,9 +240,15 @@ func constraintNotEqual(v Version, c *constraint) bool {
 			return true
 		} else if c.minorDirty {
 			return false
+		} else if c.con.Patch() != v.Patch() && !c.patchDirty {
+			return true
+		} else if c.patchDirty {
+			// Need to handle prereleases if present
+			if v.Prerelease() != "" || c.con.Prerelease() != "" {
+				return comparePrerelease(v.Prerelease(), c.con.Prerelease()) != 0
+			}
+			return false
 		}
-
-		return false
 	}
 
 	return !v.Equal(c.con)
@@ -257,6 +263,26 @@ func constraintGreaterThan(v Version, c *constraint) bool {
 		return false
 	}
 
+	if !c.dirty {
+		return v.Compare(c.con) == 1
+	}
+
+	if v.Major() > c.con.Major() {
+		return true
+	} else if v.Major() < c.con.Major() {
+		return false
+	} else if c.minorDirty {
+		// This is a range case such as >11. When the version is something like
+		// 11.1.0 is it not > 11. For that we would need 12 or higher
+		return false
+	} else if c.patchDirty {
+		// This is for ranges such as >11.1. A version of 11.1.1 is not greater
+		// which one of 11.2.1 is greater
+		return v.Minor() > c.con.Minor()
+	}
+
+	// If we have gotten here we are not comparing pre-preleases and can use the
+	// Compare function to accomplish that.
 	return v.Compare(c.con) == 1
 }
 
@@ -268,17 +294,7 @@ func constraintLessThan(v Version, c *constraint) bool {
 		return false
 	}
 
-	if !c.dirty {
-		return v.Compare(c.con) < 0
-	}
-
-	if v.Major() > c.con.Major() {
-		return false
-	} else if v.Minor() > c.con.Minor() && !c.minorDirty {
-		return false
-	}
-
-	return true
+	return v.Compare(c.con) < 0
 }
 
 func constraintGreaterThanEqual(v Version, c *constraint) bool {
@@ -361,7 +377,6 @@ func constraintTildeOrEqual(v Version, c *constraint) bool {
 	}
 
 	if c.dirty {
-		c.msg = constraintMsg["~"]
 		return constraintTilde(v, c)
 	}
 
@@ -395,7 +410,7 @@ func constraintCaret(v Version, c *constraint) bool {
 
 var constraintRangeRegex *regexp.Regexp
 
-const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
+const cvRegex string = `v?([0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
 	`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
 	`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
 


Test patch:
diff --git a/constraints_test.go b/constraints_test.go
index 3b6a79d..75eeeb9 100644
--- a/constraints_test.go
+++ b/constraints_test.go
@@ -56,39 +56,83 @@ func TestConstraintCheck(t *testing.T) {
 		version    string
 		check      bool
 	}{
-		{"= 2.0", "1.2.3", false},
-		{"= 2.0", "2.0.0", true},
+		{"=2.0.0", "1.2.3", false},
+		{"=2.0.0", "2.0.0", true},
+		{"=2.0", "1.2.3", false},
+		{"=2.0", "2.0.0", true},
+		{"=2.0", "2.0.1", true},
 		{"4.1", "4.1.0", true},
+		{"!=4.1.0", "4.1.0", false},
+		{"!=4.1.0", "4.1.1", true},
 		{"!=4.1", "4.1.0", false},
+		{"!=4.1", "4.1.1", false},
+		{"!=4.1", "5.1.0-alpha.1", false},
+		{"!=4.1-alpha", "4.1.0", true},
 		{"!=4.1", "5.1.0", true},
-		{">1.1", "4.1.0", true},
-		{">1.1", "1.1.0", false},
+		{"<11", "0.1.0", true},
+		{"<11", "11.1.0", false},
 		{"<1.1", "0.1.0", true},
 		{"<1.1", "1.1.0", false},
 		{"<1.1", "1.1.1", false},
-		{">=1.1", "4.1.0", true},
-		{">=1.1", "1.1.0", true},
-		{">=1.1", "0.0.9", false},
+		{"<=11", "1.2.3", true},
+		{"<=11", "12.2.3", false},
+		{"<=11", "11.2.3", true},
+		{"<=1.1", "1.2.3", false},
 		{"<=1.1", "0.1.0", true},
 		{"<=1.1", "1.1.0", true},
-		{"<=1.1", "1.1.1", false},
+		{"<=1.1", "1.1.1", true},
+		{">1.1", "4.1.0", true},
+		{">1.1", "1.1.0", false},
 		{">0", "0", false},
+		{">0", "1", true},
 		{">0", "0.0.1-alpha", false},
 		{">0.0", "0.0.1-alpha", false},
-		{">0-0", "0.0.1-alpha", true},
-		{">0.0-0", "0.0.1-alpha", true},
+		{">0-0", "0.0.1-alpha", false},
+		{">0.0-0", "0.0.1-alpha", false},
 		{">0", "0.0.0-alpha", false},
-		{">0-0", "0.0.0-alpha", true},
+		{">0-0", "0.0.0-alpha", false},
+		{">0.0.0-0", "0.0.0-alpha", true},
+		{">1.2.3-alpha.1", "1.2.3-alpha.2", true},
+		{">1.2.3-alpha.1", "1.3.3-alpha.2", true},
+		{">11", "11.1.0", false},
+		{">11.1", "11.1.0", false},
+		{">11.1", "11.1.1", false},
+		{">11.1", "11.2.1", true},
+		{">=11", "11.1.2", true},
+		{">=11.1", "11.1.2", true},
+		{">=11.1", "11.0.2", false},
+		{">=1.1", "4.1.0", true},
+		{">=1.1", "1.1.0", true},
+		{">=1.1", "0.0.9", false},
 		{">=0", "0.0.1-alpha", false},
 		{">=0.0", "0.0.1-alpha", false},
 		{">=0-0", "0.0.1-alpha", true},
 		{">=0.0-0", "0.0.1-alpha", true},
 		{">=0", "0.0.0-alpha", false},
 		{">=0-0", "0.0.0-alpha", true},
+		{">=0.0.0-0", "0.0.0-alpha", true},
+		{">=0.0.0-0", "1.2.3", true},
+		{">=0.0.0-0", "3.4.5-beta.1", true},
 		{"<0", "0.0.0-alpha", false},
 		{"<0-z", "0.0.0-alpha", true},
 		{">=0", "0", true},
 		{"=0", "1", false},
+		{"*", "1", true},
+		{"*", "4.5.6", true},
+		{"*", "1.2.3-alpha.1", false},
+		{"2.*", "1", false},
+		{"2.*", "3.4.5", false},
+		{"2.*", "2.1.1", true},
+		{"2.1.*", "2.1.1", true},
+		{"2.1.*", "2.2.1", false},
+		{"", "1", true}, // An empty string is treated as * or wild card
+		{"", "4.5.6", true},
+		{"", "1.2.3-alpha.1", false},
+		{"2", "1", false},
+		{"2", "3.4.5", false},
+		{"2", "2.1.1", true},
+		{"2.1", "2.1.1", true},
+		{"2.1", "2.2.1", false},
 	}
 
 	for _, tc := range tests {
@@ -176,6 +220,7 @@ func TestConstraintsCheck(t *testing.T) {
 		{"1.x", "1.4", true},
 		{"!=4.1", "4.1.0", false},
 		{"!=4.1-alpha", "4.1.0-alpha", false},
+		{"!=4.1-alpha", "4.1.1-alpha", false},
 		{"!=4.1-alpha", "4.1.0", true},
 		{"!=4.1", "5.1.0", true},
 		{"!=4.x", "5.1.0", true},
@@ -187,10 +232,12 @@ func TestConstraintsCheck(t *testing.T) {
 		{"<1.1", "0.1.0", true},
 		{"<1.1", "1.1.0", false},
 		{"<1.1", "1.1.1", false},
-		{"<1.x", "1.1.1", true},
+		{"<1.x", "1.1.1", false},
+		{"<1.x", "0.1.1", true},
 		{"<1.x", "2.0.0", false},
 		{"<1.1.x", "1.2.1", false},
-		{"<1.1.x", "1.1.500", true},
+		{"<1.1.x", "1.1.500", false},
+		{"<1.1.x", "1.0.500", true},
 		{"<1.2.x", "1.1.1", true},
 		{">=1.1", "4.1.0", true},
 		{">=1.1", "4.1.0-beta", false},
@@ -202,12 +249,14 @@ func TestConstraintsCheck(t *testing.T) {
 		{"<=1.1", "1.1.0", true},
 		{"<=1.x", "1.1.0", true},
 		{"<=2.x", "3.0.0", false},
-		{"<=1.1", "1.1.1", false},
+		{"<=1.1", "1.1.1", true},
 		{"<=1.1.x", "1.2.500", false},
-		{">1.1, <2", "1.1.1", true},
+		{">1.1, <2", "1.1.1", false},
+		{">1.1, <2", "1.2.1", true},
 		{">1.1, <3", "4.3.2", false},
 		{">=1.1, <2, !=1.2.3", "1.2.3", false},
-		{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true},
+		{">=1.1, <2, !=1.2.3 || > 3", "4.1.2", true},
+		{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", false},
 		{">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true},
 		{">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false},
 		{">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false},
@@ -323,10 +372,11 @@ func TestConstraintsValidate(t *testing.T) {
 		{"<1.1", "0.1.0", true},
 		{"<1.1", "1.1.0", false},
 		{"<1.1", "1.1.1", false},
-		{"<1.x", "1.1.1", true},
+		{"<1.x", "1.1.1", false},
+		{"<2.x", "1.1.1", true},
 		{"<1.x", "2.1.1", false},
 		{"<1.1.x", "1.2.1", false},
-		{"<1.1.x", "1.1.500", true},
+		{"<1.1.x", "1.1.500", false},
 		{"<1.2.x", "1.1.1", true},
 		{">=1.1", "4.1.0", true},
 		{">=1.1", "1.1.0", true},
@@ -335,12 +385,14 @@ func TestConstraintsValidate(t *testing.T) {
 		{"<=1.1", "1.1.0", true},
 		{"<=1.x", "1.1.0", true},
 		{"<=2.x", "3.1.0", false},
-		{"<=1.1", "1.1.1", false},
+		{"<=1.1", "1.1.1", true},
 		{"<=1.1.x", "1.2.500", false},
-		{">1.1, <2", "1.1.1", true},
+		{">1.1, <2", "1.1.1", false},
+		{">1.1, <2", "1.2.1", true},
 		{">1.1, <3", "4.3.2", false},
 		{">=1.1, <2, !=1.2.3", "1.2.3", false},
-		{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true},
+		{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", false},
+		{">=1.1, <2, !=1.2.3 || > 3", "4.1.2", true},
 		{">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true},
 		{">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false},
 		{">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false},
@@ -426,6 +478,8 @@ func TestConstraintsValidate(t *testing.T) {
 	tests2 := []struct {
 		constraint, version, msg string
 	}{
+		{"2.x", "1.2.3", "1.2.3 is not equal to 2.x"},
+		{"2", "1.2.3", "1.2.3 is not equal to 2"},
 		{"= 2.0", "1.2.3", "1.2.3 is not equal to 2.0"},
 		{"!=4.1", "4.1.0", "4.1.0 is equal to 4.1"},
 		{"!=4.x", "4.1.0", "4.1.0 is equal to 4.x"},
@@ -437,7 +491,7 @@ func TestConstraintsValidate(t *testing.T) {
 		{"<1.1.x", "1.2.1", "1.2.1 is greater than or equal to 1.1.x"},
 		{">=1.1", "0.0.9", "0.0.9 is less than 1.1"},
 		{"<=2.x", "3.1.0", "3.1.0 is greater than 2.x"},
-		{"<=1.1", "1.1.1", "1.1.1 is greater than 1.1"},
+		{"<=1.1", "1.2.1", "1.2.1 is greater than 1.1"},
 		{"<=1.1.x", "1.2.500", "1.2.500 is greater than 1.1.x"},
 		{">1.1, <3", "4.3.2", "4.3.2 is greater than or equal to 3"},
 		{">=1.1, <2, !=1.2.3", "1.2.3", "1.2.3 is equal to 1.2.3"},
@@ -470,9 +524,13 @@ func TestConstraintsValidate(t *testing.T) {
 		}
 
 		_, msgs := c.Validate(v)
-		e := msgs[0].Error()
-		if e != tc.msg {
-			t.Errorf("Did not get expected message %q: %s", tc.msg, e)
+		if len(msgs) == 0 {
+			t.Errorf("Did not get error message on constraint %q", tc.constraint)
+		} else {
+			e := msgs[0].Error()
+			if e != tc.msg {
+				t.Errorf("Did not get expected message %q: %s", tc.msg, e)
+			}
 		}
 	}
 }