Files
ttstd-infinite-canvas/backend/internal/hostupdate/version.go
ddCat 52913939d0 fix(deploy): 系统更新 - 非正式镜像标签可在线更新到正式版
sha-* 等非正式标签不再被字典序判成比 vX.Y.Z 更新;健康检查对非正式目标版本只确认服务就绪;管理后台更新页并排展示当前运行与最新发布版本并标注非正式版本
2026-09-18 08:50:18 +08:00

96 lines
2.2 KiB
Go

package hostupdate
import (
"strconv"
"strings"
)
type versionPart struct {
major, minor, patch int
pre []string
}
func IsReleaseVersion(raw string) bool {
_, ok := parseVersion(raw)
return ok
}
func CompareVersions(left, right string) int {
a, aok := parseVersion(left)
b, bok := parseVersion(right)
if aok && bok {
return compareParsedVersions(a, b)
}
// sha-5509b17、latest 等非正式标签不能按字典序和 v1.5.0 比,否则会被当成更新而不让升级。
if !aok && bok {
return -1
}
if aok && !bok {
return 1
}
return strings.Compare(strings.TrimSpace(left), strings.TrimSpace(right))
}
func compareParsedVersions(a, b versionPart) int {
for _, pair := range [][2]int{{a.major, b.major}, {a.minor, b.minor}, {a.patch, b.patch}} {
if pair[0] < pair[1] {
return -1
}
if pair[0] > pair[1] {
return 1
}
}
if len(a.pre) == 0 && len(b.pre) > 0 {
return 1
}
if len(a.pre) > 0 && len(b.pre) == 0 {
return -1
}
for index := 0; index < len(a.pre) || index < len(b.pre); index++ {
if index >= len(a.pre) {
return -1
}
if index >= len(b.pre) {
return 1
}
an, aerr := strconv.Atoi(a.pre[index])
bn, berr := strconv.Atoi(b.pre[index])
switch {
case aerr == nil && berr == nil && an < bn:
return -1
case aerr == nil && berr == nil && an > bn:
return 1
case aerr == nil && berr != nil:
return -1
case aerr != nil && berr == nil:
return 1
case a.pre[index] < b.pre[index]:
return -1
case a.pre[index] > b.pre[index]:
return 1
}
}
return 0
}
func parseVersion(raw string) (versionPart, bool) {
value := strings.TrimPrefix(strings.TrimSpace(raw), "v")
value = strings.SplitN(value, "+", 2)[0]
parts := strings.SplitN(value, "-", 2)
core := strings.Split(parts[0], ".")
if len(core) != 3 {
return versionPart{}, false
}
major, err1 := strconv.Atoi(core[0])
minor, err2 := strconv.Atoi(core[1])
patch, err3 := strconv.Atoi(core[2])
if err1 != nil || err2 != nil || err3 != nil {
return versionPart{}, false
}
parsed := versionPart{major: major, minor: minor, patch: patch}
if len(parts) == 2 && parts[1] != "" {
parsed.pre = strings.FieldsFunc(parts[1], func(r rune) bool { return r == '.' || r == '-' })
}
return parsed, true
}