26f897c1ec
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
154 lines
3.1 KiB
Go
154 lines
3.1 KiB
Go
package update
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type semVersion struct {
|
|
major int
|
|
minor int
|
|
patch int
|
|
prerelease []string
|
|
}
|
|
|
|
func compareVersions(a, b string) (int, error) {
|
|
va, err := parseVersion(a)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
vb, err := parseVersion(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return va.compare(vb), nil
|
|
}
|
|
|
|
func parseVersion(raw string) (semVersion, error) {
|
|
trimmed := strings.TrimSpace(raw)
|
|
trimmed = strings.TrimPrefix(trimmed, "v")
|
|
if trimmed == "" {
|
|
return semVersion{}, fmt.Errorf("parse version %q: empty", raw)
|
|
}
|
|
|
|
trimmed, _, _ = strings.Cut(trimmed, "+")
|
|
core := trimmed
|
|
pre := ""
|
|
if before, after, ok := strings.Cut(trimmed, "-"); ok {
|
|
core = before
|
|
pre = after
|
|
}
|
|
|
|
parts := strings.Split(core, ".")
|
|
if len(parts) == 0 || len(parts) > 3 {
|
|
return semVersion{}, fmt.Errorf("parse version %q: invalid core", raw)
|
|
}
|
|
|
|
v := semVersion{}
|
|
for len(parts) < 3 {
|
|
parts = append(parts, "0")
|
|
}
|
|
ints := []*int{&v.major, &v.minor, &v.patch}
|
|
for i, part := range parts {
|
|
if part == "" {
|
|
return semVersion{}, fmt.Errorf("parse version %q: empty numeric segment", raw)
|
|
}
|
|
n, err := strconv.Atoi(part)
|
|
if err != nil || n < 0 {
|
|
return semVersion{}, fmt.Errorf("parse version %q: invalid numeric segment %q", raw, part)
|
|
}
|
|
*ints[i] = n
|
|
}
|
|
|
|
if pre != "" {
|
|
idents := strings.Split(pre, ".")
|
|
for _, ident := range idents {
|
|
if ident == "" {
|
|
return semVersion{}, fmt.Errorf("parse version %q: empty prerelease segment", raw)
|
|
}
|
|
v.prerelease = append(v.prerelease, ident)
|
|
}
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
func isDevVersion(version string) bool {
|
|
if version == "" || version == "dev" {
|
|
return true
|
|
}
|
|
_, err := parseVersion(version)
|
|
return err != nil
|
|
}
|
|
|
|
func (v semVersion) compare(other semVersion) int {
|
|
if diff := cmpInt(v.major, other.major); diff != 0 {
|
|
return diff
|
|
}
|
|
if diff := cmpInt(v.minor, other.minor); diff != 0 {
|
|
return diff
|
|
}
|
|
if diff := cmpInt(v.patch, other.patch); diff != 0 {
|
|
return diff
|
|
}
|
|
|
|
if len(v.prerelease) == 0 && len(other.prerelease) == 0 {
|
|
return 0
|
|
}
|
|
if len(v.prerelease) == 0 {
|
|
return 1
|
|
}
|
|
if len(other.prerelease) == 0 {
|
|
return -1
|
|
}
|
|
|
|
for i := 0; i < len(v.prerelease) && i < len(other.prerelease); i++ {
|
|
if diff := comparePrereleaseIdentifier(v.prerelease[i], other.prerelease[i]); diff != 0 {
|
|
return diff
|
|
}
|
|
}
|
|
return cmpInt(len(v.prerelease), len(other.prerelease))
|
|
}
|
|
|
|
func comparePrereleaseIdentifier(a, b string) int {
|
|
ai, aerr := strconv.Atoi(a)
|
|
bi, berr := strconv.Atoi(b)
|
|
switch {
|
|
case aerr == nil && berr == nil:
|
|
return cmpInt(ai, bi)
|
|
case aerr == nil:
|
|
return -1
|
|
case berr == nil:
|
|
return 1
|
|
default:
|
|
return cmpInt(strings.Compare(a, b), 0)
|
|
}
|
|
}
|
|
|
|
func cmpInt(a, b int) int {
|
|
switch {
|
|
case a < b:
|
|
return -1
|
|
case a > b:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func releaseArchiveName(app, version string, platform platformSpec) string {
|
|
ext := ".tar.gz"
|
|
if platform.GOOS == "windows" {
|
|
ext = ".zip"
|
|
}
|
|
return fmt.Sprintf("%s-%s-%s-%s%s", app, version, platform.GOOS, platform.GOARCH, ext)
|
|
}
|
|
|
|
func binaryName(app string, platform platformSpec) string {
|
|
if platform.GOOS == "windows" {
|
|
return app + ".exe"
|
|
}
|
|
return app
|
|
}
|