Skip to content

Commit 64d3fd5

Browse files
authored
internal/nix: fix version comparison for prerelease versions (#2131)
1 parent 8382061 commit 64d3fd5

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

internal/nix/nix.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,21 @@ const (
180180
Version2_19 = "2.19.0"
181181
Version2_20 = "2.20.0"
182182
Version2_21 = "2.21.0"
183+
Version2_22 = "2.22.0"
183184

184185
MinVersion = Version2_12
185186
)
186187

187188
// versionRegexp matches the first line of "nix --version" output.
188-
// Semantic component sourced from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
189-
var versionRegexp = regexp.MustCompile(`^(.+) \(.+\) ((?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)$`)
189+
//
190+
// The semantic component is sourced from <https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string>.
191+
// It's been modified to tolerate Nix prerelease versions, which don't have a
192+
// hyphen before the prerelease component and contain underscores.
193+
var versionRegexp = regexp.MustCompile(`^(.+) \(.+\) ((?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:(?:-|pre)(?P<prerelease>(?:0|[1-9]\d*|\d*[_a-zA-Z-][_0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[_a-zA-Z-][_0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)$`)
194+
195+
// preReleaseRegexp matches Nix prerelease version strings, which are not valid
196+
// semvers.
197+
var preReleaseRegexp = regexp.MustCompile(`pre(?P<date>[0-9]+)_(?P<commit>[a-f0-9]{4,40})$`)
190198

191199
// VersionInfo contains information about a Nix installation.
192200
type VersionInfo struct {
@@ -294,7 +302,15 @@ func (v VersionInfo) AtLeast(version string) bool {
294302
if !semver.IsValid(version) {
295303
panic(fmt.Sprintf("nix.atLeast: invalid version %q", version[1:]))
296304
}
297-
return semver.Compare("v"+v.Version, version) >= 0
305+
if semver.IsValid("v" + v.Version) {
306+
return semver.Compare("v"+v.Version, version) >= 0
307+
}
308+
309+
// If the version isn't a valid semver, check to see if it's a
310+
// prerelease (e.g., 2.23.0pre20240526_7de033d6) and coerce it to a
311+
// valid version (2.23.0-pre.20240526+7de033d6) so we can compare it.
312+
prerelease := preReleaseRegexp.ReplaceAllString(v.Version, "-pre.$date+$commit")
313+
return semver.Compare("v"+prerelease, version) >= 0
298314
}
299315

300316
// version is the cached output of `nix --version --debug`.

internal/nix/nix_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func TestParseVersionInfoShort(t *testing.T) {
174174
version string
175175
}{
176176
{"nix (Nix) 2.21.2", "nix", "2.21.2"},
177+
{"nix (Nix) 2.23.0pre20240526_7de033d6", "nix", "2.23.0pre20240526_7de033d6"},
177178
{"command (Nix) name (Nix) 2.21.2", "command (Nix) name", "2.21.2"},
178179
{"nix (Lix, like Nix) 2.90.0-beta.1", "nix", "2.90.0-beta.1"},
179180
}
@@ -232,6 +233,24 @@ func TestVersionInfoAtLeast(t *testing.T) {
232233
t.Errorf("got %s >= %s", info.Version, Version2_14)
233234
}
234235

236+
// https://github.com/jetify-com/devbox/issues/2128
237+
info.Version = "2.23.0pre20240526_7de033d6"
238+
if !info.AtLeast(Version2_12) {
239+
t.Errorf("got %s < %s", info.Version, Version2_12)
240+
}
241+
if info.AtLeast("2.23.0") {
242+
t.Errorf("got %s > %s", info.Version, "2.23.0")
243+
}
244+
if info.AtLeast("2.24.0") {
245+
t.Errorf("got %s > %s", info.Version, "2.24.0")
246+
}
247+
if info.AtLeast("2.23.0-pre.99999999") {
248+
t.Errorf("got %s > %s", info.Version, "2.23.0-pre.99999999")
249+
}
250+
if !info.AtLeast("2.23.0-pre.1") {
251+
t.Errorf("got %s < %s", info.Version, "2.23.0-pre.1")
252+
}
253+
235254
t.Run("ArgEmptyPanic", func(t *testing.T) {
236255
defer func() {
237256
if r := recover(); r == nil {

0 commit comments

Comments
 (0)