Skip to content

Commit 8154e8b

Browse files
committed
fix!: prereleases should not match constraints for previous versions.
Signed-off-by: i4k <[email protected]>
1 parent 7867235 commit 8154e8b

File tree

2 files changed

+86
-4
lines changed

2 files changed

+86
-4
lines changed

versions/versions.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,35 @@ func Match(version, constraint string, allowPrereleases bool) (bool, error) {
4646
return false, errors.E(ErrCheck, "invalid constraint", err)
4747
}
4848

49-
allowed := versions.MeetingConstraintsExact(spec)
50-
return allowed.Has(semver), nil
49+
// Prereleases for an upcoming breaking change MUST NOT match the previous release.
50+
// In other words, Semantic Version defines the order below:
51+
// 0.9.9 < 1.0.0-alpha < 1.0.0 < 1.0.1
52+
//
53+
// But we want the behavior below:
54+
//
55+
// The constraint `~> 0.5.0` must not match `0.6.0-rc1` release.
56+
// The reasoning is that v0.6.0-rc1 could already introduce some (or all) of
57+
// the v0.6.0 release and then the loose `~> 0.5.0` could put users at risk.
58+
59+
var plainConstraints, rcConstraints constraints.IntersectionSpec
60+
for _, sel := range spec {
61+
if sel.Boundary.Prerelease != "" {
62+
rcConstraints = append(rcConstraints, sel)
63+
} else {
64+
plainConstraints = append(plainConstraints, sel)
65+
}
66+
}
67+
68+
plainAllowed := versions.MeetingConstraintsExact(plainConstraints)
69+
copied := semver
70+
copied.Prerelease = ""
71+
has := plainAllowed.Has(copied)
72+
if !has {
73+
return false, nil
74+
}
75+
76+
rcAllowed := versions.MeetingConstraintsExact(rcConstraints)
77+
return rcAllowed.Has(semver), nil
5178
}
5279

5380
spec, err := hclversion.NewConstraint(constraint)

versions/versions_test.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ func TestTerramateVersionConstraints(t *testing.T) {
192192
constraint: "> 1.2.2, < 1.2.3",
193193
want: errors.E(versions.ErrCheck),
194194
},
195+
{
196+
version: "1.2.3-alpha",
197+
constraint: "> 1.2.2, < 1.2.3",
198+
prereleases: true,
199+
want: errors.E(versions.ErrCheck),
200+
},
201+
{
202+
version: "1.2.3-alpha",
203+
constraint: "~> 1.2.2",
204+
prereleases: true,
205+
//want: errors.E(versions.ErrCheck),
206+
},
195207
{
196208
version: "1.2.3-dev",
197209
constraint: ">= 1.2.3",
@@ -201,12 +213,12 @@ func TestTerramateVersionConstraints(t *testing.T) {
201213
version: "1.2.3-dev",
202214
constraint: ">= 1.2.3",
203215
prereleases: true,
204-
want: errors.E(versions.ErrCheck),
205216
},
206217
{
207218
version: "1.2.3-dev",
208219
constraint: "< 1.2.3",
209220
prereleases: true,
221+
want: errors.E(versions.ErrCheck),
210222
},
211223
{
212224
version: "1.2.3-dev",
@@ -308,11 +320,54 @@ func TestTerramateVersionConstraints(t *testing.T) {
308320
constraint: "< 1.2.3-dev2",
309321
prereleases: true,
310322
},
323+
{
324+
version: "0.6.0-rc1",
325+
constraint: "~> 0.5.0",
326+
want: errors.E(versions.ErrCheck),
327+
},
328+
{
329+
version: "0.6.0-rc1",
330+
constraint: "~> 0.5.0",
331+
prereleases: true,
332+
want: errors.E(versions.ErrCheck),
333+
},
334+
{
335+
version: "0.6.0-rc1",
336+
constraint: "~> 0.6.0-rc1",
337+
},
338+
{
339+
version: "0.6.0-rc1",
340+
constraint: "~> 0.6.0-rc1",
341+
prereleases: true,
342+
},
343+
{
344+
version: "0.6.0-rc1",
345+
constraint: "~> 0.5.0",
346+
want: errors.E(versions.ErrCheck),
347+
},
348+
{
349+
version: "2.0.0-alpha",
350+
constraint: "~> 1",
351+
prereleases: true,
352+
want: errors.E(versions.ErrCheck),
353+
},
354+
{
355+
version: "1.0.0-alpha",
356+
constraint: "< 2",
357+
prereleases: true,
358+
},
359+
// TODO(i4k): review this with Marius.
360+
// looks broken.
361+
{
362+
version: "1.0.0-alpha",
363+
constraint: "< 1.0.0",
364+
prereleases: true,
365+
want: errors.E(versions.ErrCheck),
366+
},
311367
} {
312368
tc := tc
313369
name := fmt.Sprintf("CheckVersionFor(%q,%q, %t)", tc.version, tc.constraint, tc.prereleases)
314370
t.Run(name, func(t *testing.T) {
315-
t.Parallel()
316371
err := versions.Check(tc.version, tc.constraint, tc.prereleases)
317372
errtest.Assert(t, err, tc.want, "error mismatch")
318373
})

0 commit comments

Comments
 (0)