Procházet zdrojové kódy

Implement almost full semver comparison (fixes #436)

Jakob Borg před 11 roky
rodič
revize
4afe02cb21
2 změnil soubory, kde provedl 111 přidání a 7 odebrání
  1. 103 7
      cmd/syncthing/upgrade_common.go
  2. 8 0
      cmd/syncthing/upgrade_test.go

+ 103 - 7
cmd/syncthing/upgrade_common.go

@@ -1,7 +1,6 @@
 package main
 package main
 
 
 import (
 import (
-	"bytes"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
@@ -17,17 +16,114 @@ type githubAsset struct {
 	Name string `json:"name"`
 	Name string `json:"name"`
 }
 }
 
 
+// Returns 1 if a>b, -1 if a<b and 0 if they are equal
 func compareVersions(a, b string) int {
 func compareVersions(a, b string) int {
-	return bytes.Compare(versionParts(a), versionParts(b))
+	arel, apre := versionParts(a)
+	brel, bpre := versionParts(b)
+
+	minlen := len(arel)
+	if l := len(brel); l < minlen {
+		minlen = l
+	}
+
+	// First compare major-minor-patch versions
+	for i := 0; i < minlen; i++ {
+		if arel[i] < brel[i] {
+			return -1
+		}
+		if arel[i] > brel[i] {
+			return 1
+		}
+	}
+
+	// Longer version is newer, when the preceding parts are equal
+	if len(arel) < len(brel) {
+		return -1
+	}
+	if len(arel) > len(brel) {
+		return 1
+	}
+
+	// Prerelease versions are older, if the versions are the same
+	if len(apre) == 0 && len(bpre) > 0 {
+		return 1
+	}
+	if len(apre) > 0 && len(bpre) == 0 {
+		return -1
+	}
+
+	minlen = len(apre)
+	if l := len(bpre); l < minlen {
+		minlen = l
+	}
+
+	// Compare prerelease strings
+	for i := 0; i < minlen; i++ {
+		switch av := apre[i].(type) {
+		case int:
+			switch bv := bpre[i].(type) {
+			case int:
+				if av < bv {
+					return -1
+				}
+				if av > bv {
+					return 1
+				}
+			case string:
+				return -1
+			}
+		case string:
+			switch bv := bpre[i].(type) {
+			case int:
+				return 1
+			case string:
+				if av < bv {
+					return -1
+				}
+				if av > bv {
+					return 1
+				}
+			}
+		}
+	}
+
+	// If all else is equal, longer prerelease string is newer
+	if len(apre) < len(bpre) {
+		return -1
+	}
+	if len(apre) > len(bpre) {
+		return 1
+	}
+
+	// Looks like they're actually the same
+	return 0
 }
 }
 
 
-func versionParts(v string) []byte {
-	parts := strings.Split(v, "-")
+// Split a version into parts.
+// "1.2.3-beta.2" -> []int{1, 2, 3}, []interface{}{"beta", 2}
+func versionParts(v string) ([]int, []interface{}) {
+	parts := strings.SplitN(v, "-", 2)
 	fields := strings.Split(parts[0], ".")
 	fields := strings.Split(parts[0], ".")
-	res := make([]byte, len(fields))
+
+	release := make([]int, len(fields))
 	for i, s := range fields {
 	for i, s := range fields {
 		v, _ := strconv.Atoi(s)
 		v, _ := strconv.Atoi(s)
-		res[i] = byte(v)
+		release[i] = v
 	}
 	}
-	return res
+
+	var prerelease []interface{}
+	if len(parts) > 1 {
+		fields = strings.Split(parts[1], ".")
+		prerelease = make([]interface{}, len(fields))
+		for i, s := range fields {
+			v, err := strconv.Atoi(s)
+			if err == nil {
+				prerelease[i] = v
+			} else {
+				prerelease[i] = s
+			}
+		}
+	}
+
+	return release, prerelease
 }
 }

+ 8 - 0
cmd/syncthing/upgrade_test.go

@@ -20,6 +20,14 @@ var testcases = []struct {
 	{"0.1.10", "0.1.9", 1},
 	{"0.1.10", "0.1.9", 1},
 	{"0.10.0", "0.2.0", 1},
 	{"0.10.0", "0.2.0", 1},
 	{"30.10.0", "4.9.0", 1},
 	{"30.10.0", "4.9.0", 1},
+	{"0.9.0-beta7", "0.9.0-beta6", 1},
+	{"1.0.0-alpha", "1.0.0-alpha.1", -1},
+	{"1.0.0-alpha.1", "1.0.0-alpha.beta", -1},
+	{"1.0.0-alpha.beta", "1.0.0-beta", -1},
+	{"1.0.0-beta", "1.0.0-beta.2", -1},
+	{"1.0.0-beta.2", "1.0.0-beta.11", -1},
+	{"1.0.0-beta.11", "1.0.0-rc.1", -1},
+	{"1.0.0-rc.1", "1.0.0", -1},
 }
 }
 
 
 func TestCompareVersions(t *testing.T) {
 func TestCompareVersions(t *testing.T) {