diff --git a/cmd/github.go b/cmd/github.go index 991077336..b74699165 100644 --- a/cmd/github.go +++ b/cmd/github.go @@ -5,34 +5,24 @@ import ( "fmt" "io/ioutil" "net/http" - "regexp" "sort" - - "github.com/Masterminds/semver" ) // GitHubHelper is a utility class for interacting with GitHub type GitHubHelper struct { - validPrereleaseRegex *regexp.Regexp - validReleaseRegex *regexp.Regexp } // NewGitHubHelper returns a new GitHub Helper func NewGitHubHelper() *GitHubHelper { - 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 &GitHubHelper{ - validPrereleaseRegex: regexp.MustCompile(SemPreVerRegex), - validReleaseRegex: regexp.MustCompile(SemVerRegex), - } + return &GitHubHelper{} } // GetVersionTags gets the list of tags on the Wails repo // 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 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 for _, tag := range data { version := tag["name"].(string) - semver, err := semver.NewVersion(version) + semver, err := NewSemanticVersion(version) if err != nil { return result, err } @@ -61,32 +51,41 @@ func (g *GitHubHelper) GetVersionTags() ([]*semver.Version, error) { } // Reverse Sort - sort.Sort(sort.Reverse(semver.Collection(result))) + sort.Sort(sort.Reverse(SemverCollection(result))) 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 -func (g *GitHubHelper) GetLatestStableRelease() (result string, err error) { +func (g *GitHubHelper) GetLatestStableRelease() (result *SemanticVersion, err error) { tags, err := g.GetVersionTags() if err != nil { - return "", err + return nil, err } for _, tag := range tags { - if g.isRelease(tag) { - return "v" + tag.String(), nil + if tag.IsRelease() { + 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") } diff --git a/cmd/semver.go b/cmd/semver.go new file mode 100644 index 000000000..556e84e34 --- /dev/null +++ b/cmd/semver.go @@ -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] +} diff --git a/cmd/wails/8_update.go b/cmd/wails/8_update.go index 0db44f538..8f8c399ef 100644 --- a/cmd/wails/8_update.go +++ b/cmd/wails/8_update.go @@ -1,11 +1,8 @@ package main import ( - "encoding/json" "fmt" - "io/ioutil" "log" - "net/http" "github.com/leaanthony/spinner" "github.com/mitchellh/go-homedir" @@ -14,13 +11,16 @@ import ( func init() { + var prereleaseRequired bool + // var forceRebuild = false checkSpinner := spinner.NewSpinner() 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."). - LongDescription(commandDescription) + LongDescription(commandDescription). + BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired) updateCmd.Action(func() error { @@ -30,49 +30,79 @@ func init() { // Get versions 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 { checkSpinner.Error(err.Error()) return err } 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("Current Version: " + cmd.Version) - fmt.Println(" Latest Version: " + latestVersion) - if latestVersion != cmd.Version { - updateSpinner := spinner.NewSpinner() - updateSpinner.SetSpinSpeed(40) - updateSpinner.Start("Updating to : " + latestVersion) - // Run command in non module directory - homeDir, err := homedir.Dir() - if err != nil { - log.Fatal("Cannot find home directory! Please file a bug report!") - } - - err = cmd.NewProgramHelper().RunCommandArray([]string{"go", "get", "github.com/wailsapp/wails/.../."}, homeDir) - if err != nil { - updateSpinner.Error(err.Error()) - return err - } - updateSpinner.Success() - logger.Yellow("Wails updated to " + latestVersion) + fmt.Println(" Current Version : " + cmd.Version) + if prereleaseRequired { + fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion) } else { - logger.Yellow("Looks like you're up to date!") + fmt.Printf(" Latest Release : v%s\n", desiredVersion) } - return nil + + 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.SetSpinSpeed(40) + updateSpinner.Start("Installing Wails " + desiredVersion) + + // Run command in non module directory + homeDir, err := homedir.Dir() + if err != nil { + log.Fatal("Cannot find home directory! Please file a bug report!") + } + + err = cmd.NewProgramHelper().RunCommandArray([]string{"go", "get", "github.com/wailsapp/wails/cmd/wails@" + desiredVersion}, homeDir) + if err != nil { + updateSpinner.Error(err.Error()) + return err + } + updateSpinner.Success() + fmt.Println() + logger.Green("Wails updated to " + desiredVersion) + + return nil +}