Skip to content

Commit 057c901

Browse files
authored
Merge pull request #267 from mattfarina/fix-259
Handle pre-releases on all in an and group
2 parents ebda872 + abab1c2 commit 057c901

File tree

2 files changed

+75
-59
lines changed

2 files changed

+75
-59
lines changed

constraints.go

Lines changed: 59 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
// checked against.
1313
type Constraints struct {
1414
constraints [][]*constraint
15+
containsPre []bool
1516
}
1617

1718
// NewConstraint returns a Constraints instance that a Version instance can
@@ -22,11 +23,10 @@ func NewConstraint(c string) (*Constraints, error) {
2223
c = rewriteRange(c)
2324

2425
ors := strings.Split(c, "||")
25-
or := make([][]*constraint, len(ors))
26+
lenors := len(ors)
27+
or := make([][]*constraint, lenors)
28+
hasPre := make([]bool, lenors)
2629
for k, v := range ors {
27-
28-
// TODO: Find a way to validate and fetch all the constraints in a simpler form
29-
3030
// Validate the segment
3131
if !validConstraintRegex.MatchString(v) {
3232
return nil, fmt.Errorf("improper constraint: %s", v)
@@ -43,12 +43,22 @@ func NewConstraint(c string) (*Constraints, error) {
4343
return nil, err
4444
}
4545

46+
// If one of the constraints has a prerelease record this.
47+
// This information is used when checking all in an "and"
48+
// group to ensure they all check for prereleases.
49+
if pc.con.pre != "" {
50+
hasPre[k] = true
51+
}
52+
4653
result[i] = pc
4754
}
4855
or[k] = result
4956
}
5057

51-
o := &Constraints{constraints: or}
58+
o := &Constraints{
59+
constraints: or,
60+
containsPre: hasPre,
61+
}
5262
return o, nil
5363
}
5464

@@ -57,10 +67,10 @@ func (cs Constraints) Check(v *Version) bool {
5767
// TODO(mattfarina): For v4 of this library consolidate the Check and Validate
5868
// functions as the underlying functions make that possible now.
5969
// loop over the ORs and check the inner ANDs
60-
for _, o := range cs.constraints {
70+
for i, o := range cs.constraints {
6171
joy := true
6272
for _, c := range o {
63-
if check, _ := c.check(v); !check {
73+
if check, _ := c.check(v, cs.containsPre[i]); !check {
6474
joy = false
6575
break
6676
}
@@ -83,12 +93,12 @@ func (cs Constraints) Validate(v *Version) (bool, []error) {
8393
// Capture the prerelease message only once. When it happens the first time
8494
// this var is marked
8595
var prerelesase bool
86-
for _, o := range cs.constraints {
96+
for i, o := range cs.constraints {
8797
joy := true
8898
for _, c := range o {
8999
// Before running the check handle the case there the version is
90100
// a prerelease and the check is not searching for prereleases.
91-
if c.con.pre == "" && v.pre != "" {
101+
if !cs.containsPre[i] && v.pre != "" {
92102
if !prerelesase {
93103
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
94104
e = append(e, em)
@@ -98,7 +108,7 @@ func (cs Constraints) Validate(v *Version) (bool, []error) {
98108

99109
} else {
100110

101-
if _, err := c.check(v); err != nil {
111+
if _, err := c.check(v, cs.containsPre[i]); err != nil {
102112
e = append(e, err)
103113
joy = false
104114
}
@@ -227,16 +237,16 @@ type constraint struct {
227237
}
228238

229239
// Check if a version meets the constraint
230-
func (c *constraint) check(v *Version) (bool, error) {
231-
return constraintOps[c.origfunc](v, c)
240+
func (c *constraint) check(v *Version, includePre bool) (bool, error) {
241+
return constraintOps[c.origfunc](v, c, includePre)
232242
}
233243

234244
// String prints an individual constraint into a string
235245
func (c *constraint) string() string {
236246
return c.origfunc + c.orig
237247
}
238248

239-
type cfunc func(v *Version, c *constraint) (bool, error)
249+
type cfunc func(v *Version, c *constraint, includePre bool) (bool, error)
240250

241251
func parseConstraint(c string) (*constraint, error) {
242252
if len(c) > 0 {
@@ -305,16 +315,14 @@ func parseConstraint(c string) (*constraint, error) {
305315
}
306316

307317
// Constraint functions
308-
func constraintNotEqual(v *Version, c *constraint) (bool, error) {
309-
if c.dirty {
310-
311-
// If there is a pre-release on the version but the constraint isn't looking
312-
// for them assume that pre-releases are not compatible. See issue 21 for
313-
// more details.
314-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
315-
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
316-
}
318+
func constraintNotEqual(v *Version, c *constraint, includePre bool) (bool, error) {
319+
// The existence of prereleases is checked at the group level and passed in.
320+
// Exit early if the version has a prerelease but those are to be ignored.
321+
if v.Prerelease() != "" && !includePre {
322+
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
323+
}
317324

325+
if c.dirty {
318326
if c.con.Major() != v.Major() {
319327
return true, nil
320328
}
@@ -345,12 +353,11 @@ func constraintNotEqual(v *Version, c *constraint) (bool, error) {
345353
return true, nil
346354
}
347355

348-
func constraintGreaterThan(v *Version, c *constraint) (bool, error) {
356+
func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, error) {
349357

350-
// If there is a pre-release on the version but the constraint isn't looking
351-
// for them assume that pre-releases are not compatible. See issue 21 for
352-
// more details.
353-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
358+
// The existence of prereleases is checked at the group level and passed in.
359+
// Exit early if the version has a prerelease but those are to be ignored.
360+
if v.Prerelease() != "" && !includePre {
354361
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
355362
}
356363

@@ -391,11 +398,10 @@ func constraintGreaterThan(v *Version, c *constraint) (bool, error) {
391398
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
392399
}
393400

394-
func constraintLessThan(v *Version, c *constraint) (bool, error) {
395-
// If there is a pre-release on the version but the constraint isn't looking
396-
// for them assume that pre-releases are not compatible. See issue 21 for
397-
// more details.
398-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
401+
func constraintLessThan(v *Version, c *constraint, includePre bool) (bool, error) {
402+
// The existence of prereleases is checked at the group level and passed in.
403+
// Exit early if the version has a prerelease but those are to be ignored.
404+
if v.Prerelease() != "" && !includePre {
399405
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
400406
}
401407

@@ -406,12 +412,11 @@ func constraintLessThan(v *Version, c *constraint) (bool, error) {
406412
return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig)
407413
}
408414

409-
func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) {
415+
func constraintGreaterThanEqual(v *Version, c *constraint, includePre bool) (bool, error) {
410416

411-
// If there is a pre-release on the version but the constraint isn't looking
412-
// for them assume that pre-releases are not compatible. See issue 21 for
413-
// more details.
414-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
417+
// The existence of prereleases is checked at the group level and passed in.
418+
// Exit early if the version has a prerelease but those are to be ignored.
419+
if v.Prerelease() != "" && !includePre {
415420
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
416421
}
417422

@@ -422,11 +427,10 @@ func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) {
422427
return false, fmt.Errorf("%s is less than %s", v, c.orig)
423428
}
424429

425-
func constraintLessThanEqual(v *Version, c *constraint) (bool, error) {
426-
// If there is a pre-release on the version but the constraint isn't looking
427-
// for them assume that pre-releases are not compatible. See issue 21 for
428-
// more details.
429-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
430+
func constraintLessThanEqual(v *Version, c *constraint, includePre bool) (bool, error) {
431+
// The existence of prereleases is checked at the group level and passed in.
432+
// Exit early if the version has a prerelease but those are to be ignored.
433+
if v.Prerelease() != "" && !includePre {
430434
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
431435
}
432436

@@ -455,11 +459,10 @@ func constraintLessThanEqual(v *Version, c *constraint) (bool, error) {
455459
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
456460
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
457461
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
458-
func constraintTilde(v *Version, c *constraint) (bool, error) {
459-
// If there is a pre-release on the version but the constraint isn't looking
460-
// for them assume that pre-releases are not compatible. See issue 21 for
461-
// more details.
462-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
462+
func constraintTilde(v *Version, c *constraint, includePre bool) (bool, error) {
463+
// The existence of prereleases is checked at the group level and passed in.
464+
// Exit early if the version has a prerelease but those are to be ignored.
465+
if v.Prerelease() != "" && !includePre {
463466
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
464467
}
465468

@@ -487,16 +490,15 @@ func constraintTilde(v *Version, c *constraint) (bool, error) {
487490

488491
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
489492
// it's a straight =
490-
func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
491-
// If there is a pre-release on the version but the constraint isn't looking
492-
// for them assume that pre-releases are not compatible. See issue 21 for
493-
// more details.
494-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
493+
func constraintTildeOrEqual(v *Version, c *constraint, includePre bool) (bool, error) {
494+
// The existence of prereleases is checked at the group level and passed in.
495+
// Exit early if the version has a prerelease but those are to be ignored.
496+
if v.Prerelease() != "" && !includePre {
495497
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
496498
}
497499

498500
if c.dirty {
499-
return constraintTilde(v, c)
501+
return constraintTilde(v, c, includePre)
500502
}
501503

502504
eq := v.Equal(c.con)
@@ -516,11 +518,10 @@ func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
516518
// ^0.0.3 --> >=0.0.3 <0.0.4
517519
// ^0.0 --> >=0.0.0 <0.1.0
518520
// ^0 --> >=0.0.0 <1.0.0
519-
func constraintCaret(v *Version, c *constraint) (bool, error) {
520-
// If there is a pre-release on the version but the constraint isn't looking
521-
// for them assume that pre-releases are not compatible. See issue 21 for
522-
// more details.
523-
if v.Prerelease() != "" && c.con.Prerelease() == "" {
521+
func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) {
522+
// The existence of prereleases is checked at the group level and passed in.
523+
// Exit early if the version has a prerelease but those are to be ignored.
524+
if v.Prerelease() != "" && !includePre {
524525
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
525526
}
526527

constraints_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func TestConstraintCheck(t *testing.T) {
7171
{"!=4.1", "4.1.0", false},
7272
{"!=4.1", "4.1.1", false},
7373
{"!=4.1", "5.1.0-alpha.1", false},
74+
{"!=4.1.0", "5.1.0-alpha.1", false},
7475
{"!=4.1-alpha", "4.1.0", true},
7576
{"!=4.1", "5.1.0", true},
7677
{"<11", "0.1.0", true},
@@ -189,6 +190,7 @@ func TestConstraintCheck(t *testing.T) {
189190
{"^0.2.3-beta.2", "0.2.3-beta.2", true},
190191
}
191192

193+
var hasPre bool
192194
for _, tc := range tests {
193195
c, err := parseConstraint(tc.constraint)
194196
if err != nil {
@@ -202,7 +204,12 @@ func TestConstraintCheck(t *testing.T) {
202204
continue
203205
}
204206

205-
a, _ := c.check(v)
207+
hasPre = false
208+
if c.con.pre != "" {
209+
hasPre = true
210+
}
211+
212+
a, _ := c.check(v, hasPre)
206213
if a != tc.check {
207214
t.Errorf("Constraint %q failing with %q", tc.constraint, tc.version)
208215
}
@@ -366,6 +373,14 @@ func TestConstraintsCheck(t *testing.T) {
366373
{">= 1.1 <2 != 1.2.3 || >= 3", "3.0.0", true},
367374
{">= 1.1 < 2 !=1.2.3 || > 3", "3.0.0", false},
368375
{">=1.1 < 2 !=1.2.3 || > 3", "1.2.3", false},
376+
{">= 1.0.0 <= 2.0.0-beta", "1.0.1-beta", true},
377+
{">= 1.0.0 <= 2.0.0-beta", "1.0.1", true},
378+
{">= 1.0.0 <= 2.0.0-beta", "3.0.0", false},
379+
{">= 1.0.0 <= 2.0.0-beta || > 3", "1.0.1-beta", true},
380+
{">= 1.0.0 <= 2.0.0-beta || > 3", "3.0.1-beta", false},
381+
{">= 1.0.0 <= 2.0.0-beta != 1.0.1 || > 3", "1.0.1-beta", true},
382+
{">= 1.0.0 <= 2.0.0-beta != 1.0.1-beta || > 3", "1.0.1-beta", false},
383+
{">= 1.0.0-0 <= 2.0.0", "1.0.1-beta", true},
369384
{"1.1 - 2", "1.1.1", true},
370385
{"1.5.0 - 4.5", "3.7.0", true},
371386
{"1.1-3", "4.3.2", false},

0 commit comments

Comments
 (0)