diff --git a/src/octoprint/plugins/softwareupdate/version_checks/github_release.py b/src/octoprint/plugins/softwareupdate/version_checks/github_release.py index 0d2abf69..5acddc16 100644 --- a/src/octoprint/plugins/softwareupdate/version_checks/github_release.py +++ b/src/octoprint/plugins/softwareupdate/version_checks/github_release.py @@ -44,35 +44,71 @@ def _get_latest_release(user, repo, include_prerelease=False): return latest["name"], latest["tag_name"] -def _is_current(release_information, compare_type, custom=None): +def _get_sanitized_version(version_string): + if "-" in version_string: + version_string = version_string[:version_string.find("-")] + return version_string + + +def _get_comparable_version_pkg_resources(version_string, force_base=True): + import pkg_resources + + version = pkg_resources.parse_version(version_string) + + if force_base: + if isinstance(version, tuple): + # old setuptools + base_version = [] + for part in version: + if part.startswith("*"): + break + base_version.append(part) + version = tuple(base_version) + else: + # new setuptools + version = pkg_resources.parse_version(version.base_version) + + return version + + +def _get_comparable_version_semantic(version_string, force_base=True): + import semantic_version + + version = semantic_version.Version.coerce(version_string, partial=False) + + if force_base: + version_string = "{}.{}.{}".format(version.major, version.minor, version.patch) + version = semantic_version.Version.coerce(version_string, partial=False) + + return version + + +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", "semantic", "unequal", "custom") or compare_type == "custom" and custom is None: compare_type = "python" + sanitized_local = _get_sanitized_version(release_information["local"]["value"]) + sanitized_remote = _get_sanitized_version(release_information["remote"]["value"]) + try: if compare_type == "python": - import pkg_resources - - local_version = pkg_resources.parse_version(release_information["local"]["value"]) - remote_version = pkg_resources.parse_version(release_information["remote"]["value"]) - + 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": - import semantic_version - - local_version = semantic_version.Version(release_information["local"]["value"]) - remote_version = semantic_version.Version(release_information["remote"]["value"]) - + 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(release_information["local"], release_information["remote"]) + return custom(sanitized_local, sanitized_remote) else: - return release_information["local"]["value"] == release_information["remote"]["value"] + return sanitized_local == sanitized_remote except: logger.exception("Could not check if version is current due to an error, assuming it is") return True @@ -82,11 +118,13 @@ def get_latest(target, check, custom_compare=None): if not "user" in check or not "repo" in check: raise ConfigurationInvalid("github_release update configuration for %s needs user and repo set" % target) - current = None - if "current" in check: - current = check["current"] + current = check.get("current", None) + include_prerelease = check.get("prerelease", False) + force_base = check.get("force_base", True) - remote_name, remote_tag = _get_latest_release(check["user"], check["repo"], include_prerelease=check["prerelease"] == True if "prerelease" in check else False) + remote_name, remote_tag = _get_latest_release(check["user"], + check["repo"], + include_prerelease=include_prerelease) compare_type = check["release_compare"] if "release_compare" in check else "python" information =dict( @@ -96,4 +134,7 @@ def get_latest(target, check, custom_compare=None): logger.debug("Target: %s, local: %s, remote: %s" % (target, current, remote_tag)) - return information, _is_current(information, compare_type, custom=custom_compare) + return information, _is_current(information, + compare_type, + custom=custom_compare, + force_base=force_base)