Skip to content

Commit

Permalink
Merge branch 'master' into improv-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
gentlegiantJGC committed Aug 8, 2021
2 parents 6df281e + 6724045 commit 4cc8f05
Show file tree
Hide file tree
Showing 5 changed files with 506 additions and 40 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -103,6 +103,9 @@ venv.bak/
# mypy
.mypy_cache/

# macOS files
.DS_Store

/minecraft_model_reader
/world_temp
/resource_packs
Expand Down
148 changes: 108 additions & 40 deletions amulet_map_editor/api/framework/update_check.py
@@ -1,50 +1,125 @@
from __future__ import annotations

from typing import Tuple
import urllib.request
import threading
import json
import re
import webbrowser
from dataclasses import dataclass
from enum import IntEnum

import wx

from amulet_map_editor import lang

URL = "http://api.github.com/repos/Amulet-Team/Amulet-Map-Editor/releases"

"""
VERSION_REGEX Documentation
This is not a documentation of regular expression syntax, just documentation on the subsequences that this regular expression looks for
For an in-depth guide on regular expressions, see here: https://www.debuggex.com/cheatsheet/regex/python
Our version scheme allows for an optional "v" prefix, so that subsequence is tested by "v?"
Since we use the Semantic Versioning scheme, our version numbers follow this format: <major>.<minor>.<patch>
with the <patch> number being optional (only if it's value would be 0 for that version)
That is tested by "(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))?"
- For the major and minor numbers, "(?P<major>\d+)" will match any non-zero number of digits and group them under the
"major" group (in this instance)
- "\." will then match the separating "." character
- Since the patch number is optional, "(\.(?P<patch>\d+))?" is used instead, the second "." is included in the
optional group, but other wise the capturing group is the same as the major/minor numbers
We then use an optional release type flag to denote whether the release is a beta or alpha release and it's number:
"((a(?P<alpha>\d+))|(b(?P<beta>\d+)))?"
For our nightly builds, we also denote a unique build number with the prefix of ".dev": "(\.dev(?P<devnum>\d{12}))?"
Finally, our version scheme also denotes when Amulet is being ran from source, while this information isn't
necessarily used for update checking, we use it to determine if the update dialog should be shown. If a commit
hash/count is found in the version string of the current Amulet instance, the dialog is not shown. Our regular
expression uses the following to detect that subsequence:
"(\+(?P<commit_count>\d+)\.g(?P<commit_hash>[a-z\d]+)(\.dirty)?)?"
- "commit_count" group signifies the number of commits since the last release
- "commit_hash" group is the current commit hash
- ".dirty" denotes whether uncommitted changes have been made
"""
VERSION_REGEX = re.compile(
r"^v?(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))?(\.(?P<bugfix>\d+))?(b(?P<beta>\d+))?$"
r"^v?(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))?((a(?P<alpha>\d+))|(b(?P<beta>\d+)))?(\.dev(?P<devnum>\d{12}))?(\+(?P<commit_count>\d+)\.g(?P<commit_hash>[a-z\d]+)(\.dirty)?)?$"
)

_EVT_UPDATE_CHECK = wx.NewEventType()
EVT_UPDATE_CHECK = wx.PyEventBinder(_EVT_UPDATE_CHECK, 1)


def get_version(version_string: str) -> Tuple[bool, Tuple[int, ...]]:
class Release(IntEnum):
FULL = 0
BETA = -1
ALPHA = -2


@dataclass
class Version:
release_stage: int
major: int
minor: int
patch: int
alpha_number: int = -1
beta_number: int = -1
nightly_timestamp: int = -1
has_commit_hash: bool = False

def __gt__(self, other: Version):
if self.version_tuple > other.version_tuple:
return True
if self.release_stage > other.release_stage:
return True
if self.release_stage == other.release_stage:
if self.release_stage == Release.ALPHA:
return self.alpha_number > other.alpha_number
elif self.release_stage == Release.BETA:
if self.beta_number > other.beta_number:
return True
elif self.beta_number == other.beta_number and self.beta_number != -1:
return self.nightly_timestamp > other.nightly_timestamp
return False

@property
def version_tuple(self):
return self.major, self.minor, self.patch


def get_version(version_string: str) -> Version:
"""Parse the version into a more usable format
:param version_string: The version string. Eg 1.2 or 1.2.3.4 or 1.2.3.4b0
:return: (bool(full_version), (major, minor, patch, bugfix, <beta>)) beta will not exist if it is not a beta
:return: A Version object from the parsed version string
"""
version_match = VERSION_REGEX.match(version_string)
if version_match:
v = version_match.groupdict()
version = (
int(v["major"]),
int(v["minor"]),
int(
v["patch"] or 0,
),
int(
v["bugfix"] or 0,
),
)
if v["beta"] is None:
# full release
return (True, version)
major, minor = int(v["major"]), int(v["minor"])
if v["patch"] is None:
patch = 0
else:
# beta release
return (False, version + (int(v["beta"]),))
patch = int(v["patch"])
version = Version(Release.FULL, major, minor, patch)

if v.get("alpha") is not None:
version.release_stage = Release.ALPHA
version.alpha_number = int(v["alpha"])
elif v.get("beta") is not None:
version.release_stage = Release.BETA
version.beta_number = int(v["beta"])
if v.get("devnum") is not None:
version.nightly_timestamp = int(v["devnum"])

if v.get("commit_hash") is not None:
version.has_commit_hash = True
return version

raise Exception(f"Invalid version string {version_string}")

Expand Down Expand Up @@ -76,27 +151,17 @@ def run(self):
except Exception:
return

for release in data:
try:
release_version = get_version(release["tag_name"])
except Exception:
continue

if current_version[0] == release_version[0]:
# if they are both full releases or betas
if release_version[1] > current_version[1]:
self._new_version = release["tag_name"]
break
elif not current_version[0]:
# if current is beta
if release_version[0]:
release_version = (
release_version[0],
release_version[1] + (float("inf"),),
)
if release_version[1] > current_version[1]:
self._new_version = release["tag_name"]
break
for release_version, release_data in sorted(
map(lambda d: (get_version(d["tag_name"]), d), data),
key=lambda t: t[0],
reverse=True,
):
if (
release_version > current_version
and release_version.release_stage >= current_version.release_stage
):
self._new_version = release_data["tag_name"]
break

if self._new_version:
evt = UpdateEvent(_EVT_UPDATE_CHECK, -1, self._new_version)
Expand Down Expand Up @@ -153,6 +218,9 @@ def goto_download_page(new_version, _):


def show_update_window(parent, current_version: str, evt: UpdateEvent):
if get_version(current_version).has_commit_hash:
print("Running from source, not showing update dialog")
return
UpdateDialog(parent, current_version, evt.GetVersion()).ShowModal()


Expand Down

0 comments on commit 4cc8f05

Please sign in to comment.