Skip to content

Commit

Permalink
Use Masterminds/semver package to compare versions
Browse files Browse the repository at this point in the history
  • Loading branch information
pjcdawkins committed Jan 3, 2025
1 parent 278d5ad commit 6aadc93
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 224 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22
toolchain go1.22.4

require (
github.com/Masterminds/semver/v3 v3.3.1
github.com/fatih/color v1.17.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-playground/validator/v10 v10.20.0
Expand All @@ -23,7 +24,6 @@ require (
require (
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1r
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
Expand Down
97 changes: 4 additions & 93 deletions internal/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,106 +6,22 @@ import (
"io"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"

"github.com/mattn/go-isatty"

"github.com/platformsh/cli/internal/config"
"github.com/platformsh/cli/internal/state"
"github.com/platformsh/cli/internal/version"
)

var versionRegex = regexp.MustCompile(`^(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<preRelease>.+))?$`)

// ReleaseInfo stores information about a release
type ReleaseInfo struct {
Version string `json:"tag_name"`
URL string `json:"html_url"`
PublishedAt time.Time `json:"published_at"`
}

// Version contains parsed information about a SemVer version
type Version struct {
VersionParts [3]int
PreReleaseParts []string
}

// CompareVersions and see which version is greater
func CompareVersions(a, b *Version) int {
// Compare Major, Minor and Patch versions
for i := 0; i < 3; i++ {
if a.VersionParts[i] > b.VersionParts[i] {
return 1
}
if a.VersionParts[i] < b.VersionParts[i] {
return -1
}
}

// Start comparing identifiers
for i := 0; ; i++ {
// Check that there are identifiers left
if len(a.PreReleaseParts) <= i && len(b.PreReleaseParts) <= i {
return 0
}

// Shorter takes precedence
if len(b.PreReleaseParts) <= i {
return 1
}
if len(a.PreReleaseParts) <= i {
return -1
}

aPart := a.PreReleaseParts[i]
bPart := b.PreReleaseParts[i]
aInt, aErr := strconv.Atoi(aPart)
bInt, bErr := strconv.Atoi(bPart)

// Try comparing integers first
if aErr == nil && bErr == nil {
if aInt > bInt {
return 1
}
if aInt < bInt {
return -1
}
// Integer wins string
} else if aErr == nil {
return 1
} else if bErr == nil {
return -1
// Compare strings
} else if cmp := strings.Compare(aPart, bPart); cmp != 0 {
return cmp
}
}
}

// ParseVersion from a string, returning a Version or error if it's not SemVer
func ParseVersion(version string) (*Version, error) {
if !versionRegex.MatchString(version) {
return nil, fmt.Errorf("version does not match SemVer: %s", version)
}

result := versionRegex.FindStringSubmatch(version)
major, _ := strconv.Atoi(result[versionRegex.SubexpIndex("major")])
minor, _ := strconv.Atoi(result[versionRegex.SubexpIndex("minor")])
patch, _ := strconv.Atoi(result[versionRegex.SubexpIndex("patch")])
preRelease := result[versionRegex.SubexpIndex("preRelease")]
var preReleaseParts []string
if preRelease != "" {
preReleaseParts = strings.Split(preRelease, ".")
}

return &Version{
VersionParts: [3]int{major, minor, patch},
PreReleaseParts: preReleaseParts,
}, nil
}

// CheckForUpdate checks whether this software has had a newer release on GitHub
func CheckForUpdate(cnf *config.Config, currentVersion string) (*ReleaseInfo, error) {
if !shouldCheckForUpdate(cnf) {
Expand All @@ -130,16 +46,11 @@ func CheckForUpdate(cnf *config.Config, currentVersion string) (*ReleaseInfo, er
return nil, fmt.Errorf("could not determine latest release: %w", err)
}

currentVersionParsed, err := ParseVersion(currentVersion)
cmp, err := version.Compare(releaseInfo.Version, currentVersion)
if err != nil {
return nil, err
}

latestVersionParsed, err := ParseVersion(releaseInfo.Version)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not compare versions: %w", err)
}
if CompareVersions(latestVersionParsed, currentVersionParsed) == 1 {
if cmp > 0 {
return releaseInfo, nil
}

Expand Down
128 changes: 0 additions & 128 deletions internal/update_test.go

This file was deleted.

26 changes: 26 additions & 0 deletions internal/version/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package version

import "github.com/Masterminds/semver/v3"

// Compare parses and compares two semantic version numbers.
// It returns -1, 0 or 1, representing whether v1 is less than, equal to or greater than v2.
func Compare(v1, v2 string) (int, error) {
if v1 == v2 {
return 0, nil
}
version1, err := semver.NewVersion(v1)
if err != nil {
return 0, err
}
version2, err := semver.NewVersion(v2)
if err != nil {
return 0, err
}
return version1.Compare(version2), nil
}

// Validate tests if a version number is valid.
func Validate(v string) bool {
_, err := semver.NewVersion(v)
return err == nil
}
Loading

0 comments on commit 6aadc93

Please sign in to comment.