From 34e5b5d50fde9b6629f4d7e31ca3300e1ac2c5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 26 Aug 2016 10:18:35 +0200 Subject: [PATCH] SWU: Sort github releases by version, not publication date We can't rely on the publication date anymore with the introduction of prerelease channels, since e.g. a current devel prerelease might have been published earlier than the latest stable but should still be considered the latest release for this release channel --- .../version_checks/github_release.py | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/src/octoprint/plugins/softwareupdate/version_checks/github_release.py b/src/octoprint/plugins/softwareupdate/version_checks/github_release.py index a16f89b6..f9872d50 100644 --- a/src/octoprint/plugins/softwareupdate/version_checks/github_release.py +++ b/src/octoprint/plugins/softwareupdate/version_checks/github_release.py @@ -14,7 +14,10 @@ RELEASE_URL = "https://api.github.com/repos/{user}/{repo}/releases" logger = logging.getLogger("octoprint.plugins.softwareupdate.version_checks.github_release") -def _filter_out_latest(releases, include_prerelease=False, prerelease_channel=None): +def _filter_out_latest(releases, + sort_key=None, + include_prerelease=False, + prerelease_channel=None): """ Filters out the newest of all matching releases. @@ -45,6 +48,9 @@ def _filter_out_latest(releases, include_prerelease=False, prerelease_channel=No nothing = None, None, None + if sort_key is None: + sort_key = lambda release: release.get("published_at", None) + # filter out prereleases and drafts filter_function = lambda rel: not rel["prerelease"] and not rel["draft"] if include_prerelease: @@ -58,8 +64,8 @@ def _filter_out_latest(releases, include_prerelease=False, prerelease_channel=No if not releases: return nothing - # sort by date - releases = sorted(releases, key=lambda release: release.get("published_at", None)) + # sort by sort_key + releases = sorted(releases, key=sort_key) # latest release = last in list latest = releases[-1] @@ -67,7 +73,10 @@ def _filter_out_latest(releases, include_prerelease=False, prerelease_channel=No return latest["name"], latest["tag_name"], latest.get("html_url", None) -def _get_latest_release(user, repo, include_prerelease=False, prerelease_channel=None): +def _get_latest_release(user, repo, compare_type, + include_prerelease=False, + prerelease_channel=None, + force_base=True): nothing = None, None, None r = requests.get(RELEASE_URL.format(user=user, repo=repo)) @@ -84,7 +93,14 @@ def _get_latest_release(user, repo, include_prerelease=False, prerelease_channel releases = filter(lambda rel: set(rel.keys()) & required_fields == required_fields, releases) - return _filter_out_latest(releases, include_prerelease=include_prerelease, prerelease_channel=prerelease_channel) + comparable_factory = _get_comparable_factory(compare_type, + force_base=force_base) + sort_key = lambda release: comparable_factory(_get_sanitized_version(release["tag_name"])) + + return _filter_out_latest(releases, + sort_key=sort_key, + include_prerelease=include_prerelease, + prerelease_channel=prerelease_channel) def _get_sanitized_version(version_string): @@ -150,6 +166,32 @@ def _get_comparable_version_semantic(version_string, force_base=True): return version +def _get_sanitized_compare_type(compare_type, custom=None): + if not compare_type in ("python", "python_unequal", + "semantic", "semantic_unequal", + "unequal", "custom") or compare_type == "custom" and custom is None: + compare_type = "python" + return compare_type + + +def _get_comparable_factory(compare_type, force_base=True): + if compare_type in ("python", "python_unequal"): + return lambda version: _get_comparable_version_pkg_resources(version, force_base=force_base) + elif compare_type in ("semantic", "semantic_unequal"): + return lambda version: _get_comparable_version_semantic(version, force_base=force_base) + else: + return lambda version: version + + +def _get_comparator(compare_type, custom=None): + if compare_type in ("python", "semantic"): + return lambda a, b: a >= b + elif compare_type == "custom": + return custom + else: + return lambda a, b: a == b + + def _is_current(release_information, compare_type, custom=None, force_base=True): """ Checks if the provided release information indicates the version being the most current one. @@ -174,40 +216,16 @@ def _is_current(release_information, compare_type, custom=None, force_base=True) if release_information["remote"]["value"] is None: return True - if not compare_type in ("python", "python_unequal", - "semantic", "semantic_unequal", - "unequal", "custom") or compare_type == "custom" and custom is None: - compare_type = "python" + compare_type = _get_sanitized_compare_type(compare_type, custom=custom) + comparable_factory = _get_comparable_factory(compare_type, force_base=force_base) + comparator = _get_comparator(compare_type, custom=custom) sanitized_local = _get_sanitized_version(release_information["local"]["value"]) sanitized_remote = _get_sanitized_version(release_information["remote"]["value"]) try: - if compare_type == "python": - local_version = _get_comparable_version_pkg_resources(sanitized_local, force_base=force_base) - remote_version = _get_comparable_version_pkg_resources(sanitized_remote, force_base=force_base) - return local_version >= remote_version - - elif compare_type == "python_unequal": - local_version = _get_comparable_version_pkg_resources(sanitized_local, force_base=force_base) - remote_version = _get_comparable_version_pkg_resources(sanitized_remote, force_base=force_base) - return local_version == remote_version - - elif compare_type == "semantic": - local_version = _get_comparable_version_semantic(sanitized_local, force_base=force_base) - remote_version = _get_comparable_version_semantic(sanitized_remote, force_base=force_base) - return local_version >= remote_version - - elif compare_type == "semantic_unequal": - local_version = _get_comparable_version_semantic(sanitized_local, force_base=force_base) - remote_version = _get_comparable_version_semantic(sanitized_remote, force_base=force_base) - return local_version == remote_version - - elif compare_type == "custom": - return custom(sanitized_local, sanitized_remote) - - else: - return sanitized_local == sanitized_remote + return comparator(comparable_factory(sanitized_local), + comparable_factory(sanitized_remote)) except: logger.exception("Could not check if version is current due to an error, assuming it is") return True @@ -221,12 +239,15 @@ def get_latest(target, check, custom_compare=None): include_prerelease = check.get("prerelease", False) prerelease_channel = check.get("prerelease_channel", None) force_base = check.get("force_base", True) + compare_type = _get_sanitized_compare_type(check.get("release_compare", "python"), + custom=custom_compare) remote_name, remote_tag, release_notes = _get_latest_release(check["user"], check["repo"], + compare_type, include_prerelease=include_prerelease, - prerelease_channel=prerelease_channel) - compare_type = check.get("release_compare", "python") + prerelease_channel=prerelease_channel, + force_base=force_base) information =dict( local=dict(name=current, value=current),