5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-08 01:11:06 +08:00

feat: better version comparison

This commit is contained in:
Lea Anthony 2019-05-10 23:01:50 +10:00
parent 11fd50f78d
commit 71194108de
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
3 changed files with 177 additions and 69 deletions

View File

@ -5,34 +5,24 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"regexp"
"sort" "sort"
"github.com/Masterminds/semver"
) )
// GitHubHelper is a utility class for interacting with GitHub // GitHubHelper is a utility class for interacting with GitHub
type GitHubHelper struct { type GitHubHelper struct {
validPrereleaseRegex *regexp.Regexp
validReleaseRegex *regexp.Regexp
} }
// NewGitHubHelper returns a new GitHub Helper // NewGitHubHelper returns a new GitHub Helper
func NewGitHubHelper() *GitHubHelper { func NewGitHubHelper() *GitHubHelper {
const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` return &GitHubHelper{}
const SemPreVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)`
return &GitHubHelper{
validPrereleaseRegex: regexp.MustCompile(SemPreVerRegex),
validReleaseRegex: regexp.MustCompile(SemVerRegex),
}
} }
// GetVersionTags gets the list of tags on the Wails repo // GetVersionTags gets the list of tags on the Wails repo
// It retuns a list of sorted tags in descending order // It retuns a list of sorted tags in descending order
func (g *GitHubHelper) GetVersionTags() ([]*semver.Version, error) { func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
result := []*SemanticVersion{}
result := []*semver.Version{}
var err error var err error
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags") resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
@ -53,7 +43,7 @@ func (g *GitHubHelper) GetVersionTags() ([]*semver.Version, error) {
// Convert tag data to Version structs // Convert tag data to Version structs
for _, tag := range data { for _, tag := range data {
version := tag["name"].(string) version := tag["name"].(string)
semver, err := semver.NewVersion(version) semver, err := NewSemanticVersion(version)
if err != nil { if err != nil {
return result, err return result, err
} }
@ -61,32 +51,41 @@ func (g *GitHubHelper) GetVersionTags() ([]*semver.Version, error) {
} }
// Reverse Sort // Reverse Sort
sort.Sort(sort.Reverse(semver.Collection(result))) sort.Sort(sort.Reverse(SemverCollection(result)))
return result, err return result, err
} }
func (g *GitHubHelper) isRelease(tag *semver.Version) bool {
return g.validReleaseRegex.MatchString(tag.String())
}
func (g *GitHubHelper) isPreRelease(tag *semver.Version) bool {
return g.validPrereleaseRegex.MatchString(tag.String())
}
// GetLatestStableRelease gets the latest stable release on GitHub // GetLatestStableRelease gets the latest stable release on GitHub
func (g *GitHubHelper) GetLatestStableRelease() (result string, err error) { func (g *GitHubHelper) GetLatestStableRelease() (result *SemanticVersion, err error) {
tags, err := g.GetVersionTags() tags, err := g.GetVersionTags()
if err != nil { if err != nil {
return "", err return nil, err
} }
for _, tag := range tags { for _, tag := range tags {
if g.isRelease(tag) { if tag.IsRelease() {
return "v" + tag.String(), nil return tag, nil
} }
} }
return "", fmt.Errorf("no release tag found") return nil, fmt.Errorf("no release tag found")
}
// GetLatestPreRelease gets the latest prerelease on GitHub
func (g *GitHubHelper) GetLatestPreRelease() (result *SemanticVersion, err error) {
tags, err := g.GetVersionTags()
if err != nil {
return nil, err
}
for _, tag := range tags {
if tag.IsPreRelease() {
return tag, nil
}
}
return nil, fmt.Errorf("no prerelease tag found")
} }

79
cmd/semver.go Normal file
View File

@ -0,0 +1,79 @@
package cmd
import (
"regexp"
"github.com/masterminds/semver"
)
type SemanticVersion struct {
Version *semver.Version
validPrereleaseRegex *regexp.Regexp
validReleaseRegex *regexp.Regexp
}
// NewSemanticVersion creates a new SemanticVersion object with the given version string
func NewSemanticVersion(version string) (*SemanticVersion, error) {
semverVersion, err := semver.NewVersion(version)
if err != nil {
return nil, err
}
const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?`
const SemPreVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)`
return &SemanticVersion{
Version: semverVersion,
validPrereleaseRegex: regexp.MustCompile(SemPreVerRegex),
validReleaseRegex: regexp.MustCompile(SemVerRegex),
}, nil
}
// IsRelease returns true if it's a release version
func (s *SemanticVersion) IsRelease() bool {
return s.validReleaseRegex.MatchString(s.Version.String())
}
// IsPreRelease returns true if it's a prerelease version
func (s *SemanticVersion) IsPreRelease() bool {
return s.validPrereleaseRegex.MatchString(s.Version.String())
}
func (s *SemanticVersion) String() string {
return s.Version.String()
}
func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) {
// Set up new constraint
constraint, err := semver.NewConstraint("> " + version.Version.String())
if err != nil {
return false, err
}
// Check if the desired one is greater than the requested on
success, msgs := constraint.Validate(s.Version)
if !success {
return false, msgs[0]
}
return true, nil
}
// SemverCollection is a collection of SemanticVersion objects
type SemverCollection []*SemanticVersion
// Len returns the length of a collection. The number of Version instances
// on the slice.
func (c SemverCollection) Len() int {
return len(c)
}
// Less is needed for the sort interface to compare two Version objects on the
// slice. If checks if one is less than the other.
func (c SemverCollection) Less(i, j int) bool {
return c[i].Version.LessThan(c[j].Version)
}
// Swap is needed for the sort interface to replace the Version objects
// at two different positions in the slice.
func (c SemverCollection) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}

View File

@ -1,11 +1,8 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"net/http"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
@ -14,13 +11,16 @@ import (
func init() { func init() {
var prereleaseRequired bool
// var forceRebuild = false // var forceRebuild = false
checkSpinner := spinner.NewSpinner() checkSpinner := spinner.NewSpinner()
checkSpinner.SetSpinSpeed(50) checkSpinner.SetSpinSpeed(50)
commandDescription := `This command checks if there are updates to Wails.` commandDescription := `This command allows you to update your version of Wails.`
updateCmd := app.Command("update", "Check for Updates."). updateCmd := app.Command("update", "Check for Updates.").
LongDescription(commandDescription) LongDescription(commandDescription).
BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
updateCmd.Action(func() error { updateCmd.Action(func() error {
@ -30,32 +30,64 @@ func init() {
// Get versions // Get versions
checkSpinner.Start(message) checkSpinner.Start(message)
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
github := cmd.NewGitHubHelper()
var desiredVersion *cmd.SemanticVersion
var err error
if prereleaseRequired {
desiredVersion, err = github.GetLatestPreRelease()
} else {
desiredVersion, err = github.GetLatestStableRelease()
}
if err != nil { if err != nil {
checkSpinner.Error(err.Error()) checkSpinner.Error(err.Error())
return err return err
} }
checkSpinner.Success() checkSpinner.Success()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
checkSpinner.Error(err.Error())
return err
}
data := []map[string]interface{}{}
err = json.Unmarshal(body, &data)
if err != nil {
return err
}
latestVersion := data[0]["name"].(string)
fmt.Println() fmt.Println()
fmt.Println("Current Version: " + cmd.Version)
fmt.Println(" Latest Version: " + latestVersion) fmt.Println(" Current Version : " + cmd.Version)
if latestVersion != cmd.Version { if prereleaseRequired {
fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion)
} else {
fmt.Printf(" Latest Release : v%s\n", desiredVersion)
}
return updateToVersion(desiredVersion)
})
}
func updateToVersion(version *cmd.SemanticVersion) error {
// Early exit
if version.String() == cmd.Version {
logger.Green("Looks like you're up to date!")
return nil
}
compareVersion := cmd.Version
if version.IsPreRelease() {
compareVersion += "-0"
}
currentVersion, err := cmd.NewSemanticVersion(compareVersion)
if err != nil {
return err
}
// Compare
success, err := version.IsGreaterThan(currentVersion)
if !success {
logger.Red("The requested version is lower than the current version. Aborting.")
return nil
}
desiredVersion := "v" + version.String()
fmt.Println()
updateSpinner := spinner.NewSpinner() updateSpinner := spinner.NewSpinner()
updateSpinner.SetSpinSpeed(40) updateSpinner.SetSpinSpeed(40)
updateSpinner.Start("Updating to : " + latestVersion) updateSpinner.Start("Installing Wails " + desiredVersion)
// Run command in non module directory // Run command in non module directory
homeDir, err := homedir.Dir() homeDir, err := homedir.Dir()
@ -63,16 +95,14 @@ func init() {
log.Fatal("Cannot find home directory! Please file a bug report!") log.Fatal("Cannot find home directory! Please file a bug report!")
} }
err = cmd.NewProgramHelper().RunCommandArray([]string{"go", "get", "github.com/wailsapp/wails/.../."}, homeDir) err = cmd.NewProgramHelper().RunCommandArray([]string{"go", "get", "github.com/wailsapp/wails/cmd/wails@" + desiredVersion}, homeDir)
if err != nil { if err != nil {
updateSpinner.Error(err.Error()) updateSpinner.Error(err.Error())
return err return err
} }
updateSpinner.Success() updateSpinner.Success()
logger.Yellow("Wails updated to " + latestVersion) fmt.Println()
} else { logger.Green("Wails updated to " + desiredVersion)
logger.Yellow("Looks like you're up to date!")
}
return nil return nil
})
} }