First throw at release channels

We start out with master (Stable), rc/maintenance (Maintenance) and rc/devel (Devel)
This commit is contained in:
Gina Häußge 2016-08-24 19:02:39 +02:00
parent 0a78c92407
commit b5bc03e711
8 changed files with 263 additions and 61 deletions

View file

@ -7,10 +7,10 @@
# The file is processed from top to bottom, the first matching line wins. If <tag> or <reference commit> are left out,
# the lookup table does not apply to the matched branches
# master, prerelease and rc shall not use the lookup table, only tags
# master and rc shall not use the lookup table, only tags
master
rc/.*
prerelease
rc
# neither should disconnected checkouts, e.g. 'git checkout <tag>'
HEAD

View file

@ -299,14 +299,9 @@ There are three main branches in OctoPrint:
* `master`: The master branch always contains the current stable release. It
is *only* updated on new releases. Will have a version number following
the scheme `x.y.z` (e.g. `1.2.9`) or - if it's absolutely necessary to
add a commit after release to this branch - `x.y.z.post<commits since x.y.z>`
the scheme `<x>.<y>.<z>` (e.g. `1.2.9`) or - if it's absolutely necessary to
add a commit after release to this branch - `<x>.<y>.<z>.post<commits since x.y.z>`
(e.g. `1.2.9.post1`).
* `prerelease`: This branch is only used during the short period where a
future release has "graduated" from the `maintenance` branch and is already
tagged, but still marked on Github as a pre-release. This is mostly used for
update testing just before new releases. Version number follows the scheme
`x.y.z` (e.g. `1.2.9`), just like the `master` branch.
* `maintenance`: Improvements and fixes of the current release that make up
the next release go here. More or less continously updated. You can consider
this a preview of the next release version. It should be very stable at all
@ -314,7 +309,7 @@ There are three main branches in OctoPrint:
next stable release, so if you want to help out development, running the
`maintenance` branch and reporting back anything you find is a very good way
to do that. Will usually have a version number following the scheme
`x.y.z+1.dev.<commits since increase of z>` for an OctoPrint version of `x.y.z`
`<x>.<y>.<z+1>.dev<commits since increase of z>` for an OctoPrint version of `<x>.<y>.<z>`
(e.g. `1.2.10.dev12`).
* `devel`: Ongoing development of new features that will go into the next bigger
release (MINOR version number increases) will happen on this branch. Usually
@ -322,8 +317,16 @@ There are three main branches in OctoPrint:
temporarily. Can be considered the "bleeding edge". All PRs should target
*this* branch. Important improvements and fixes from PRs here are backported to
`maintenance` as needed. Will usually have a version number following the
scheme `x.y+1.0.dev<commits since increase of y>` for an OctoPrint version
of `x.y.z` (e.g. `1.3.0.dev123`).
scheme `<x>.<y+1>.0.dev<commits since increase of y>` for a current OctoPrint version
of `<x>.<y>.<z>` (e.g. `1.3.0.dev123`).
* `rc/maintenance`: This branch is reserved for future releases that have graduated from
the `maintenance` branch and are now being pushed on the "Maintenance"
pre release channel for further testing. Version number follows the scheme
`<x>.<y>.<z>rc<n>` (e.g. `1.2.9rc1`).
* `rc/devel`: This branch is reserved for future releases that have graduated from
the `devel` branch and are now being pushed on the "Devel" pre release channel
for further testing. Version number follows the scheme `<x>.<y+1>.0rc<n>` (e.g. `1.3.0rc1`)
for a current stable OctoPrint version of `<x>.<y>.<z>`.
Additionally, from time to time you might see other branches pop up in the repository.
Those usually have one of the following prefixes:
@ -334,9 +337,6 @@ Those usually have one of the following prefixes:
`maintenance` and `devel` branches.
* `dev/...` or `feature/...`: New functionality under development that is to be merged
into the `devel` branch.
* `rc`: A branch similar in nature to the `prerelease` branch, only that it will be
used to provide current release candidates of the next stable version to be derived
from the `devel` branch.
There is also the `gh-pages` branch, which holds OctoPrint's web page, and a couple of
older development branches that are slowly being migrated or deleted.

View file

@ -18,7 +18,7 @@ from . import version_checks, updaters, exceptions, util
from octoprint.server.util.flask import restricted_access
from octoprint.server import admin_permission, VERSION, REVISION
from octoprint.server import admin_permission, VERSION, REVISION, BRANCH
from octoprint.util import dict_merge
import octoprint.settings
@ -138,19 +138,26 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
#~~ SettingsPlugin API
def get_settings_defaults(self):
update_script = os.path.join(self._basefolder, "scripts", "update-octoprint.py")
return {
"checks": {
"octoprint": {
"type": "github_release",
"user": "foosel",
"repo": "OctoPrint",
"update_script": "{{python}} \"{update_script}\" --python=\"{{python}}\" \"{{folder}}\" {{target}}".format(update_script=os.path.join(self._basefolder, "scripts", "update-octoprint.py")),
"update_script": "{{python}} \"{update_script}\" --branch={{branch}} --force={{force}} \"{{folder}}\" {{target}}".format(update_script=update_script),
"restart": "octoprint"
},
},
"pip_command": None,
"check_providers": {},
"octoprint_stable_branch": dict(branch="master", name="Stable"),
"octoprint_prerelease_branches": [
dict(branch="rc/maintenance", name="Maintenance"),
dict(branch="rc/devel", name="Devel")
],
"cache_ttl": 24 * 60,
}
@ -159,6 +166,19 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
if "checks" in data:
del data["checks"]
branch_mappings = []
if "octoprint_stable_branch" in data:
branch_mappings.append(data["octoprint_stable_branch"])
del data["octoprint_stable_branch"]
if "octoprint_prerelease_branches" in data:
for mapping in data["octoprint_prerelease_branches"]:
branch_mappings.append(mapping)
del data["octoprint_prerelease_branches"]
data["octoprint_branch_mappings"] = branch_mappings
if "check_providers" in data:
del data["check_providers"]
checks = self._get_configured_checks()
if "octoprint" in checks:
if "checkout_folder" in checks["octoprint"]:
@ -168,6 +188,12 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
else:
data["octoprint_checkout_folder"] = None
data["octoprint_type"] = checks["octoprint"].get("type", None)
data["octoprint_release_channel"] = self._settings.get(["octoprint_stable_branch", "branch"])
if checks["octoprint"].get("prerelease", False):
channel = checks["octoprint"].get("prerelease_channel", BRANCH)
if channel in [x["branch"] for x in self._settings.get(["octoprint_prerelease_branches"])]:
data["octoprint_release_channel"] = channel
else:
data["octoprint_checkout_folder"] = None
data["octoprint_type"] = None
@ -191,6 +217,11 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
update_type = check.get("type", None)
checkout_folder = check.get("checkout_folder", None)
update_folder = check.get("update_folder", None)
prerelease = check.get("prerelease", False)
prerelease_channel = check.get("prerelease_channel", None)
else:
update_type = checkout_folder = update_folder = prerelease_channel = None
prerelease = False
defaults = dict(
plugins=dict(softwareupdate=dict(
@ -198,7 +229,9 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
octoprint=dict(
type=update_type,
checkout_folder=checkout_folder,
update_folder=update_folder
update_folder=update_folder,
prerelease=prerelease,
prerelease_channel=prerelease_channel
)
)
))
@ -214,6 +247,16 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
self._settings.set(["checks", "octoprint", "type"], data["octoprint_type"], defaults=defaults, force=True)
self._refresh_configured_checks = True
if "octoprint_release_channel" in data:
if data["octoprint_release_channel"] in [x["branch"] for x in self._settings.get(["octoprint_prerelease_branches"])]:
self._settings.set(["checks", "octoprint", "prerelease"], True, defaults=defaults, force=True)
self._settings.set(["checks", "octoprint", "prerelease_channel"], data["octoprint_release_channel"], defaults=defaults, force=True)
self._refresh_configured_checks = True
else:
self._settings.set(["checks", "octoprint", "prerelease"], False, defaults=defaults, force=True)
self._settings.set(["checks", "octoprint", "prerelease_channel"], None, defaults=defaults, force=True)
self._refresh_configured_checks = True
def get_settings_version(self):
return 4
@ -704,10 +747,29 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
result["current"] = REVISION if REVISION else "unknown"
else:
result["current"] = VERSION
if check["type"] == "github_release" and (check["prerelease"] or BRANCH != self._settings.get(["octoprint_stable_branch", "branch"])):
# we force python unequality check here because that will also allow us to
# downgrade on a prerelease channel change
result["release_compare"] = "python_unequal"
# also we compare versions fully, not just the base so that we see a difference
# between RCs for the same version release
result["force_base"] = False
if check.get("update_script", None):
channel = result["prerelease_channel"] = check.get("prerelease_channel", BRANCH)
if channel:
# if we have a release channel, we also set our update_branch here to our release channel
result["update_branch"] = check.get("update_branch", channel)
# we also force our target version in the update
result["force_exact_version"] = True
else:
result["displayName"] = check.get("displayName", target)
result["displayVersion"] = check.get("displayVersion", check.get("current", "unknown"))
if check["type"] in ("github_commit"):
if check["type"] in ("github_commit",):
result["current"] = check.get("current", None)
else:
result["current"] = check.get("current", check.get("displayVersion", None))

View file

@ -71,7 +71,7 @@ def _python(args, cwd, python_executable, sudo=False):
return p.returncode, stdout
def update_source(git_executable, folder, target, force=False):
def _rescue_changes(git_executable, folder):
print(">>> Running: git diff --shortstat")
returncode, stdout = _git(["diff", "--shortstat"], folder, git_executable=git_executable)
if returncode != 0:
@ -91,11 +91,30 @@ def update_source(git_executable, folder, target, force=False):
with open(patch, "wb") as f:
f.write(stdout)
return True
return False
def update_source(git_executable, folder, target, force=False, branch=None):
if _rescue_changes(git_executable, folder):
print(">>> Running: git reset --hard")
returncode, stdout = _git(["reset", "--hard"], folder, git_executable=git_executable)
if returncode != 0:
raise RuntimeError("Could not update, \"git reset --hard\" failed with returncode %d: %s" % (returncode, stdout))
print(">>> Running: git fetch")
returncode, stdout = _git(["fetch"], folder, git_executable=git_executable)
if returncode != 0:
raise RuntimeError("Could not update, \"git fetch\" failed with returncode %d: %s" % (returncode, stdout))
print(stdout)
if branch is not None and branch.strip() != "":
print(">>> Running: git checkout {}".format(branch))
returncode, stdout = _git(["checkout", branch], folder, git_executable=git_executable)
if returncode != 0:
raise RuntimeError("Could not update, \"git checkout\" failed with returncode %d: %s" % (returncode, stdout))
print(">>> Running: git pull")
returncode, stdout = _git(["pull"], folder, git_executable=git_executable)
if returncode != 0:
@ -134,18 +153,24 @@ def install_source(python_executable, folder, user=False, sudo=False):
def parse_arguments():
import argparse
boolean_trues = ["true", "yes", "1"]
boolean_falses = ["false", "no", "0"]
parser = argparse.ArgumentParser(prog="update-octoprint.py")
parser.add_argument("--git", action="store", type=str, dest="git_executable",
help="Specify git executable to use")
parser.add_argument("--python", action="store", type=str, dest="python_executable",
help="Specify python executable to use")
parser.add_argument("--force", action="store_true", dest="force",
help="Set this to force the update to only the specified version (nothing newer)")
parser.add_argument("--force", action="store", type=lambda x: x in boolean_trues,
dest="force", default=False,
help="Set this to true to force the update to only the specified version (nothing newer, nothing older)")
parser.add_argument("--sudo", action="store_true", dest="sudo",
help="Install with sudo")
parser.add_argument("--user", action="store_true", dest="user",
help="Install to the user site directory instead of the general site directory")
parser.add_argument("--branch", action="store", type=str, dest="branch", default=None,
help="Specify the branch to make sure is checked out")
parser.add_argument("folder", type=str,
help="Specify the base folder of the OctoPrint installation to update")
parser.add_argument("target", type=str,
@ -167,13 +192,12 @@ def main():
python_executable = args.python_executable
folder = args.folder
target = args.target
import os
if not os.access(folder, os.W_OK):
raise RuntimeError("Could not update, base folder is not writable")
update_source(git_executable, folder, target, force=args.force)
update_source(git_executable, folder, args.target, force=args.force, branch=args.branch)
install_source(python_executable, folder, user=args.user, sudo=args.sudo)
if __name__ == "__main__":

View file

@ -21,6 +21,7 @@ $(function() {
self.config_cacheTtl = ko.observable();
self.config_checkoutFolder = ko.observable();
self.config_checkType = ko.observable();
self.config_releaseChannel = ko.observable();
self.configurationDialog = $("#settings_plugin_softwareupdate_configurationdialog");
self.confirmationDialog = $("#softwareupdate_confirmation_dialog");
@ -29,6 +30,7 @@ $(function() {
{"key": "github_release", "name": gettext("Release")},
{"key": "git_commit", "name": gettext("Commit")}
];
self.config_availableReleaseChannels = ko.observableArray([]);
self.reloadOverlay = $("#reloadui_overlay");
@ -97,7 +99,8 @@ $(function() {
softwareupdate: {
cache_ttl: parseInt(self.config_cacheTtl()),
octoprint_checkout_folder: self.config_checkoutFolder(),
octoprint_type: self.config_checkType()
octoprint_type: self.config_checkType(),
octoprint_release_channel: self.config_releaseChannel()
}
}
};
@ -112,6 +115,13 @@ $(function() {
self.config_cacheTtl(self.settings.settings.plugins.softwareupdate.cache_ttl());
self.config_checkoutFolder(self.settings.settings.plugins.softwareupdate.octoprint_checkout_folder());
self.config_checkType(self.settings.settings.plugins.softwareupdate.octoprint_type());
self.config_releaseChannel(self.settings.settings.plugins.softwareupdate.octoprint_release_channel());
var availableReleaseChannels = [];
_.each(self.settings.settings.plugins.softwareupdate.octoprint_branch_mappings(), function(mapping) {
availableReleaseChannels.push({"key": mapping.branch, "name": gettext(mapping.name || mapping.branch)});
});
self.config_availableReleaseChannels(availableReleaseChannels);
};
self.fromCheckResponse = function(data, ignoreSeen, showIfNothingNew) {
@ -145,7 +155,7 @@ $(function() {
var octoprint = data.information["octoprint"];
if (octoprint && octoprint.hasOwnProperty("check")) {
var check = octoprint.check;
if (BRANCH != "master" && check["type"] == "github_release") {
if (check["release_branches"] && !_.contains(check["release_branches"], BRANCH) && check["type"] == "github_release") {
self.octoprintUnreleased(true);
} else {
self.octoprintUnreleased(false);
@ -153,8 +163,8 @@ $(function() {
var checkoutFolder = (check["checkout_folder"] || "").trim();
var updateFolder = (check["update_folder"] || "").trim();
var checkType = check["type"] || "";
if ((checkType == "github_release" || checkType == "git_commit") && checkoutFolder == "" && updateFolder == "") {
var needsFolder = check["update_script"] || false;
if (needsFolder && checkoutFolder == "" && updateFolder == "") {
self.octoprintUnconfigured(true);
} else {
self.octoprintUnconfigured(false);

View file

@ -86,6 +86,12 @@
<select data-bind="value: config_checkType, options: config_availableCheckTypes, optionsText: 'name', optionsValue: 'key'"></select>
</div>
</div>
<div class="control-group" data-bind="visible: config_checkType() == 'github_release'">
<label class="control-label">{{ _('OctoPrint Release Channel') }}</label>
<div class="controls">
<select data-bind="value: config_releaseChannel, options: config_availableReleaseChannels, optionsText: 'name', optionsValue: 'key'"></select>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Version cache TTL') }}</label>
<div class="controls">

View file

@ -33,9 +33,11 @@ def perform_update(target, check, target_version):
raise ConfigurationInvalid("checkout_folder and update_folder are missing for update target %s, one is needed" % target)
update_script = check["update_script"]
folder = check["update_folder"] if "update_folder" in check else check["checkout_folder"]
pre_update_script = check["pre_update_script"] if "pre_update_script" in check else None
post_update_script = check["post_update_script"] if "post_update_script" in check else None
update_branch = check.get("update_branch", "")
force_exact_version = check.get("force_exact_version", False)
folder = check.get("update_folder", check["checkout_folder"])
pre_update_script = check.get("pre_update_script", None)
post_update_script = check.get("post_update_script", None)
update_stdout = ""
update_stderr = ""
@ -56,7 +58,11 @@ def perform_update(target, check, target_version):
### update
try:
update_command = update_script.format(python=sys.executable, folder=folder, target=target_version)
update_command = update_script.format(python=sys.executable,
folder=folder,
target=target_version,
branch=update_branch,
force="true" if force_exact_version else "false")
logger.debug("Target %s, running update script: %s" % (target, update_command))
returncode, stdout, stderr = execute(update_command, cwd=folder)

View file

@ -14,7 +14,60 @@ RELEASE_URL = "https://api.github.com/repos/{user}/{repo}/releases"
logger = logging.getLogger("octoprint.plugins.softwareupdate.version_checks.github_release")
def _get_latest_release(user, repo, include_prerelease=False):
def _filter_out_latest(releases, include_prerelease=False, prerelease_channel=None):
"""
Filters out the newest of all matching releases.
Tests:
>>> release_1_2_15 = dict(name="1.2.15", tag_name="1.2.15", html_url="some_url", published_at="2016-07-29T19:53:29Z", prerelease=False, draft=False, target_commitish="prerelease")
>>> release_1_2_16rc1 = dict(name="1.2.16rc1", tag_name="1.2.16rc1", html_url="some_url", published_at="2016-08-29T12:00:00Z", prerelease=True, draft=False, target_commitish="rc/maintenance")
>>> release_1_2_16rc2 = dict(name="1.2.16rc2", tag_name="1.2.16rc2", html_url="some_url", published_at="2016-08-30T12:00:00Z", prerelease=True, draft=False, target_commitish="rc/maintenance")
>>> release_1_2_17rc1 = dict(name="1.2.17rc1", tag_name="1.2.17rc1", html_url="some_url", published_at="2016-08-31T12:00:00Z", prerelease=True, draft=True, target_commitish="rc/maintenance")
>>> release_1_3_0rc1 = dict(name="1.3.0rc1", tag_name="1.3.0rc1", html_url="some_url", published_at="2016-12-12T12:00:00Z", prerelease=True, draft=False, target_commitish="rc/devel")
>>> release_1_4_0rc1 = dict(name="1.4.0rc1", tag_name="1.4.0rc1", html_url="some_url", published_at="2017-12-12T12:00:00Z", prerelease=True, draft=False, target_commitish="rc/future")
>>> releases = [release_1_2_15, release_1_2_16rc1, release_1_2_16rc2, release_1_2_17rc1, release_1_3_0rc1, release_1_4_0rc1]
>>> _filter_out_latest(releases, include_prerelease=False, prerelease_channel=None)
('1.2.15', '1.2.15', 'some_url')
>>> _filter_out_latest(releases, include_prerelease=True, prerelease_channel="rc/maintenance")
('1.2.16rc2', '1.2.16rc2', 'some_url')
>>> _filter_out_latest(releases, include_prerelease=True, prerelease_channel="rc/devel")
('1.3.0rc1', '1.3.0rc1', 'some_url')
>>> _filter_out_latest(releases, include_prerelease=True, prerelease_channel=None)
('1.4.0rc1', '1.4.0rc1', 'some_url')
>>> _filter_out_latest(releases, include_prerelease=True, prerelease_channel="rc/doesntexist")
('1.2.15', '1.2.15', 'some_url')
>>> _filter_out_latest([release_1_2_17rc1])
(None, None, None)
>>> _filter_out_latest([release_1_2_16rc1, release_1_2_16rc2])
(None, None, None)
"""
nothing = None, None, None
# filter out prereleases and drafts
filter_function = lambda rel: not rel["prerelease"] and not rel["draft"]
if include_prerelease:
if prerelease_channel:
filter_function = lambda rel: not rel["draft"] and (
not rel["prerelease"] or rel["target_commitish"] == prerelease_channel)
else:
filter_function = lambda rel: not rel["draft"]
releases = filter(filter_function, releases)
if not releases:
return nothing
# sort by date
releases = sorted(releases, key=lambda release: release.get("published_at", None))
# latest release = last in list
latest = releases[-1]
return latest["name"], latest["tag_name"], latest.get("html_url", None)
def _get_latest_release(user, repo, include_prerelease=False, prerelease_channel=None):
nothing = None, None, None
r = requests.get(RELEASE_URL.format(user=user, repo=repo))
@ -27,36 +80,48 @@ def _get_latest_release(user, repo, include_prerelease=False):
releases = r.json()
# sanitize
required_fields = {"name", "tag_name", "html_url", "draft", "prerelease", "published_at"}
required_fields = {"name", "tag_name", "html_url", "draft", "prerelease", "published_at", "target_commitish"}
releases = filter(lambda rel: set(rel.keys()) & required_fields == required_fields,
releases)
# filter out prereleases and drafts
if include_prerelease:
releases = filter(lambda rel: not rel["draft"], releases)
else:
releases = filter(lambda rel: not rel["prerelease"] and not rel["draft"],
releases)
if not releases:
return nothing
# sort by date
comp = lambda a, b: cmp(a.get("published_at", None), b["published_at"])
releases = sorted(releases, cmp=comp)
# latest release = last in list
latest = releases[-1]
return latest["name"], latest["tag_name"], latest.get("html_url", None)
return _filter_out_latest(releases, include_prerelease=include_prerelease, prerelease_channel=prerelease_channel)
def _get_sanitized_version(version_string):
"""
Removes "-..." prefix from version strings.
Tests:
>>> _get_sanitized_version("1.2.15")
'1.2.15'
>>> _get_sanitized_version("1.2.15-dev12")
'1.2.15'
"""
if "-" in version_string:
version_string = version_string[:version_string.find("-")]
return version_string
def _get_base_from_version_tuple(version_tuple):
"""
Reduces version tuple to base version.
Tests:
>>> _get_base_from_version_tuple(("1", "2", "15"))
('1', '2', '15')
>>> _get_base_from_version_tuple(("1", "2", "15", "*", "dev12"))
('1', '2', '15')
"""
base_version = []
for part in version_tuple:
if part.startswith("*"):
break
base_version.append(part)
return tuple(base_version)
def _get_comparable_version_pkg_resources(version_string, force_base=True):
import pkg_resources
@ -65,12 +130,7 @@ def _get_comparable_version_pkg_resources(version_string, force_base=True):
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)
version = _get_base_from_version_tuple(version)
else:
# new setuptools
version = pkg_resources.parse_version(version.base_version)
@ -91,10 +151,32 @@ def _get_comparable_version_semantic(version_string, force_base=True):
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.
Tests:
>>> _is_current(dict(remote=dict(value=None))
True
>>> _is_current(dict(local=dict(value="1.2.15"), remote=dict(value="1.2.16")))
False
>>> _is_current(dict(local=dict(value="1.2.16dev1"), remote=dict(value="1.2.16dev2")))
True
>>> _is_current(dict(local=dict(value="1.2.16dev1"), remote=dict(value="1.2.16dev2")), force_base=False)
False
>>> _is_current(dict(local=dict(value="1.2.16dev3"), remote=dict(value="1.2.16dev2")), force_base=False)
True
>>> _is_current(dict(local=dict(value="1.2.16dev3"), remote=dict(value="1.2.16dev2")), force_base=False, compare_type="python_unequal")
False
"""
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:
if not compare_type in ("python", "python_unequal",
"semantic", "semantic_unequal",
"unequal", "custom") or compare_type == "custom" and custom is None:
compare_type = "python"
sanitized_local = _get_sanitized_version(release_information["local"]["value"])
@ -106,11 +188,21 @@ def _is_current(release_information, compare_type, custom=None, force_base=True)
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)
@ -127,12 +219,14 @@ def get_latest(target, check, custom_compare=None):
current = check.get("current", None)
include_prerelease = check.get("prerelease", False)
prerelease_channel = check.get("prerelease_channel", None)
force_base = check.get("force_base", True)
remote_name, remote_tag, release_notes = _get_latest_release(check["user"],
check["repo"],
include_prerelease=include_prerelease)
compare_type = check["release_compare"] if "release_compare" in check else "python"
include_prerelease=include_prerelease,
prerelease_channel=prerelease_channel)
compare_type = check.get("release_compare", "python")
information =dict(
local=dict(name=current, value=current),