96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package updater
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
type Phase string
|
||
|
|
|
||
|
|
const (
|
||
|
|
PhaseIdle Phase = "idle"
|
||
|
|
PhaseScheduled Phase = "scheduled"
|
||
|
|
PhaseRunning Phase = "running"
|
||
|
|
PhaseSuccess Phase = "success"
|
||
|
|
PhaseFailed Phase = "failed"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Status 是主程序与 fonu-updater sidecar 共享的更新状态。
|
||
|
|
type Status struct {
|
||
|
|
Enabled bool `json:"enabled"`
|
||
|
|
Mode string `json:"mode"`
|
||
|
|
CurrentVersion string `json:"current_version,omitempty"`
|
||
|
|
LatestVersion string `json:"latest_version,omitempty"`
|
||
|
|
UpdateAvailable bool `json:"update_available"`
|
||
|
|
Busy bool `json:"busy"`
|
||
|
|
Phase Phase `json:"phase"`
|
||
|
|
Message string `json:"message"`
|
||
|
|
Log []string `json:"log,omitempty"`
|
||
|
|
CheckedAt string `json:"checked_at,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func busyPhase(phase Phase) bool {
|
||
|
|
return phase == PhaseScheduled || phase == PhaseRunning
|
||
|
|
}
|
||
|
|
|
||
|
|
// CompareVersions 比较形如 1.2.3、v1.2.3 的版本号;返回值大于 0 表示 a 更新。
|
||
|
|
func CompareVersions(a, b string) int {
|
||
|
|
left := parseVersion(a)
|
||
|
|
right := parseVersion(b)
|
||
|
|
length := len(left)
|
||
|
|
if len(right) > length {
|
||
|
|
length = len(right)
|
||
|
|
}
|
||
|
|
for index := 0; index < length; index++ {
|
||
|
|
var lv, rv int
|
||
|
|
if index < len(left) {
|
||
|
|
lv = left[index]
|
||
|
|
}
|
||
|
|
if index < len(right) {
|
||
|
|
rv = right[index]
|
||
|
|
}
|
||
|
|
if lv != rv {
|
||
|
|
if lv > rv {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
return -1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
func parseVersion(value string) []int {
|
||
|
|
clean := strings.TrimSpace(strings.TrimPrefix(value, "v"))
|
||
|
|
if index := strings.IndexAny(clean, "-+"); index >= 0 {
|
||
|
|
clean = clean[:index]
|
||
|
|
}
|
||
|
|
parts := strings.Split(clean, ".")
|
||
|
|
out := make([]int, 0, len(parts))
|
||
|
|
for _, part := range parts {
|
||
|
|
number, err := strconv.Atoi(part)
|
||
|
|
if err != nil {
|
||
|
|
out = append(out, 0)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
out = append(out, number)
|
||
|
|
}
|
||
|
|
return out
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateSemver(value string) error {
|
||
|
|
if value == "" {
|
||
|
|
return fmt.Errorf("版本号为空")
|
||
|
|
}
|
||
|
|
parts := strings.Split(strings.TrimPrefix(value, "v"), ".")
|
||
|
|
if len(parts) != 3 {
|
||
|
|
return fmt.Errorf("版本号格式无效: %s", value)
|
||
|
|
}
|
||
|
|
for _, part := range parts {
|
||
|
|
if _, err := strconv.Atoi(part); err != nil {
|
||
|
|
return fmt.Errorf("版本号格式无效: %s", value)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|