From 1105d9312a8ac9fefe4fcf18fac1661fb1832244 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sun, 11 Jul 2021 12:10:59 -0700 Subject: [PATCH] Updater: Better SemVer version checks for updates * Instead of a simple "cur. ver != latest ver" check, parse the Major, Minor and Patch components and do a detailed check. * So a x.x.1 release could be made for a specific platform that had a bad build, and it won't mind when it sees the latest version is the older x.x.0 build that other platforms had working fine. --- pkg/branding/branding.go | 2 +- pkg/main_scene.go | 2 +- pkg/scripting/scripting.go | 2 - pkg/uix/canvas_actors.go | 1 - pkg/updater/updater.go | 73 +++++++++++++++++++++++++++++++++++++ pkg/updater/updater_test.go | 72 ++++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 pkg/updater/updater_test.go diff --git a/pkg/branding/branding.go b/pkg/branding/branding.go index 09dce04..8fe8f73 100644 --- a/pkg/branding/branding.go +++ b/pkg/branding/branding.go @@ -4,7 +4,7 @@ package branding const ( AppName = "Sketchy Maze" Summary = "A drawing-based maze game" - Version = "0.7.0" + Version = "0.7.1" Website = "https://www.sketchymaze.com" Copyright = "2021 Noah Petherbridge" diff --git a/pkg/main_scene.go b/pkg/main_scene.go index 584aee9..04ea028 100644 --- a/pkg/main_scene.go +++ b/pkg/main_scene.go @@ -218,7 +218,7 @@ func (s *MainScene) checkUpdate() { return } - if info.LatestVersion != branding.Version { + if info.IsNewerVersionThan(branding.Version) { s.updateInfo = info s.updateButton.Show() } diff --git a/pkg/scripting/scripting.go b/pkg/scripting/scripting.go index ae019d8..90fa1b8 100644 --- a/pkg/scripting/scripting.go +++ b/pkg/scripting/scripting.go @@ -69,8 +69,6 @@ func (s *Supervisor) InstallScripts(level *level.Level) error { // AddLevelScript adds a script to the supervisor with level hooks. func (s *Supervisor) AddLevelScript(id string) error { - log.Debug("InstallScripts: load script from Actor %s", id) - if _, ok := s.scripts[id]; ok { return fmt.Errorf("duplicate actor ID %s in level", id) } diff --git a/pkg/uix/canvas_actors.go b/pkg/uix/canvas_actors.go index 2c91aa3..8d99216 100644 --- a/pkg/uix/canvas_actors.go +++ b/pkg/uix/canvas_actors.go @@ -78,7 +78,6 @@ func (w *Canvas) InstallScripts() error { } // Call the main() function. - log.Debug("Calling Main() for %s", actor.ID()) if err := vm.Main(); err != nil { log.Error("main() for actor %s errored: %s", actor.ID(), err) } diff --git a/pkg/updater/updater.go b/pkg/updater/updater.go index 298deb6..4fe2a7d 100644 --- a/pkg/updater/updater.go +++ b/pkg/updater/updater.go @@ -6,6 +6,8 @@ import ( "fmt" "io/ioutil" "net/http" + "regexp" + "strconv" "time" "git.kirsle.net/apps/doodle/pkg/branding" @@ -18,6 +20,42 @@ type VersionInfo struct { DownloadURL string `json:"downloadUrl"` } +// IsNewerVersionThan checks if the version.json houses a newer +// game version than the currently running game's version string. +func (i VersionInfo) IsNewerVersionThan(versionString string) bool { + // Parse the two semantic versions. + curSemver, err := ParseSemver(versionString) + if err != nil { + log.Error("VersionInfo.IsNewerVersionThan: didn't parse semver: %s", err) + return false + } + + latestSemver, err := ParseSemver(i.LatestVersion) + if err != nil { + log.Error("VersionInfo.IsNewerVersionThan: didn't parse latest semver: %s", err) + return false + } + + // Check if there's a newer version. + if latestSemver[Major] > curSemver[Major] { + // We're a major version behind, like 0.x.x < 1.x.x + return true + } else if latestSemver[Major] == curSemver[Major] { + // We're on the same major version, like 0.x.x + // Check the minor version. + if latestSemver[Minor] > curSemver[Minor] { + return true + } else if latestSemver[Minor] == curSemver[Minor] { + // Same minor version, check patch version. + if latestSemver[Patch] > curSemver[Patch] { + return true + } + } + } + + return false +} + // Last result of the update check, until forced to re-check. var lastUpdate VersionInfo @@ -61,3 +99,38 @@ func CheckNow() (VersionInfo, error) { lastUpdate = VersionInfo{} return Check() } + +// ParseSemver parses a Project: Doodle semantic version number into its +// three integer parts. Historical versions of the game looked like +// either "0.1.0-alpha" or "0.7.0" and the given version string is +// expected to match that pattern. +func ParseSemver(versionString string) ([3]int, error) { + var numbers [3]int + result := semverRegexp.FindStringSubmatch(versionString) + if result == nil { + return numbers, fmt.Errorf("%s: didn't parse as a valid semver string", versionString) + } + + // string to int helper + var atoi = func(v string) int { + a, _ := strconv.Atoi(v) + return a + } + + numbers = [3]int{ + atoi(result[1]), + atoi(result[2]), + atoi(result[3]), + } + return numbers, nil +} + +var semverRegexp = regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+).*`) + +// Some semantic version indexing constants, to parse the result of +// ParseSemver with more semantic code. +const ( + Major int = iota + Minor + Patch +) diff --git a/pkg/updater/updater_test.go b/pkg/updater/updater_test.go new file mode 100644 index 0000000..0fe3c34 --- /dev/null +++ b/pkg/updater/updater_test.go @@ -0,0 +1,72 @@ +package updater_test + +import ( + "testing" + + "git.kirsle.net/apps/doodle/pkg/updater" +) + +// Test the semver logic. +func TestParseSemver(t *testing.T) { + var tests = []struct { + A string // version strings + B string + Expect bool // expect B is newer version than A + }{ + { + "0.1.0-alpha", + "0.1.0-alpha", + false, + }, + { + "0.1.0-alpha", + "0.2.0-alpha", + true, + }, + { + "0.2.0-alpha", + "0.2.0-alpha", + false, + }, + { + "0.2.0-alpha", + "0.1.0-alpha", + false, + }, + { + "0.2.0-alpha", + "0.2.1-alpha", + true, + }, + { + "0.1.0-alpha", + "0.2.1-alpha", + true, + }, + { + "0.2.1-alpha", + "0.3.0", + true, + }, + { + "0.3.0", + "0.4.1", + true, + }, + { + "0.1", + "0.2.0", + false, + }, + } + for i, test := range tests { + result := updater.VersionInfo{ + LatestVersion: test.B, + }.IsNewerVersionThan(test.A) + if result != test.Expect { + t.Errorf("Test %d: %s <> %s: expected %+v but got %+v", + i, test.A, test.B, test.Expect, result, + ) + } + } +}