Skip to content

Commit

Permalink
fix pre-release checks in constraints
Browse files Browse the repository at this point in the history
Pre-releases should not be automatically accepted as candidate versions
for constraints without a pre-release component. This follows the logic
documented for RubyGems which appears to be the inspiration for the
constraint operators, and matches the behavior of other version
packages.

Adding a pre-release component to a pessimistic constraint will limit
accepted versions to pre-releases only matching the base segments.
Adding a pre-release to other constraint types will accept any
comparable version, but pre-releases must still match the base segments.
  • Loading branch information
jbardin committed Mar 16, 2018
1 parent 4fe82ae commit fbe76cf
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
34 changes: 30 additions & 4 deletions constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package version

import (
"fmt"
"reflect"
"regexp"
"strings"
)
Expand Down Expand Up @@ -113,6 +114,26 @@ func parseSingle(v string) (*Constraint, error) {
}, nil
}

func prereleaseCheck(v, c *Version) bool {
switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
case cPre && vPre:
// A constraint with a pre-release can only match a pre-release version
// with the same base segments.
return reflect.DeepEqual(c.Segments64(), v.Segments64())

case !cPre && vPre:
// A constraint without a pre-release can only match a version without a
// pre-release.
return false

case cPre && !vPre:
// OK, except with the pessimistic operator
case !cPre && !vPre:
// OK
}
return true
}

//-------------------------------------------------------------------
// Constraint functions
//-------------------------------------------------------------------
Expand All @@ -126,22 +147,27 @@ func constraintNotEqual(v, c *Version) bool {
}

func constraintGreaterThan(v, c *Version) bool {
return v.Compare(c) == 1
return prereleaseCheck(v, c) && v.Compare(c) == 1
}

func constraintLessThan(v, c *Version) bool {
return v.Compare(c) == -1
return prereleaseCheck(v, c) && v.Compare(c) == -1
}

func constraintGreaterThanEqual(v, c *Version) bool {
return v.Compare(c) >= 0
return prereleaseCheck(v, c) && v.Compare(c) >= 0
}

func constraintLessThanEqual(v, c *Version) bool {
return v.Compare(c) <= 0
return prereleaseCheck(v, c) && v.Compare(c) <= 0
}

func constraintPessimistic(v, c *Version) bool {
// Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
return false
}

// If the version being checked is naturally less than the constraint, then there
// is no way for the version to be valid against the constraint
if v.LessThan(c) {
Expand Down
13 changes: 13 additions & 0 deletions constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ func TestConstraintCheck(t *testing.T) {
{"~> 1.0.9.5", "1.0.9.6", true},
{"~> 1.0.9.5", "1.0.9.5.0", true},
{"~> 1.0.9.5", "1.0.9.5.1", true},
{"~> 2.0", "2.1.0-beta", false},
{"~> 2.1.0-a", "2.2.0", false},
{"~> 2.1.0-a", "2.1.0", false},
{"~> 2.1.0-a", "2.1.0-beta", true},
{"~> 2.1.0-a", "2.2.0-alpha", false},
{"> 2.0", "2.1.0-beta", false},
{">= 2.1.0-a", "2.1.0-beta", true},
{">= 2.1.0-a", "2.1.1-beta", false},
{">= 2.0.0", "2.1.0-beta", false},
{">= 2.1.0-a", "2.1.1", true},
{">= 2.1.0-a", "2.1.1-beta", false},
{">= 2.1.0-a", "2.1.0", true},
{"<= 2.1.0-a", "2.0.0", true},
}

for _, tc := range cases {
Expand Down

0 comments on commit fbe76cf

Please sign in to comment.