diff --git a/src/octoprint/plugins/softwareupdate/__init__.py b/src/octoprint/plugins/softwareupdate/__init__.py index 32fb4ab1..6ac86bdf 100644 --- a/src/octoprint/plugins/softwareupdate/__init__.py +++ b/src/octoprint/plugins/softwareupdate/__init__.py @@ -14,6 +14,7 @@ import threading import time import logging import logging.handlers +import hashlib from . import version_checks, updaters, exceptions, util @@ -99,8 +100,12 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, self._logger.exception("Error while loading version cache from disk") else: try: - if "octoprint" in data and len(data["octoprint"]) == 4 and "local" in data["octoprint"][1] and "value" in data["octoprint"][1]["local"]: - data_version = data["octoprint"][1]["local"]["value"] + if not isinstance(data, dict): + self._logger.info("Version cache was created in a different format, not using it") + return + + if "__version" in data: + data_version = data["__version"] else: self._logger.info("Can't determine version of OctoPrint version cache was created for, not using it") return @@ -118,24 +123,18 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, self._logger.exception("Error parsing in version cache data") def _save_version_cache(self): - import tempfile import yaml - import shutil + from octoprint.util import atomic_write + from octoprint._version import get_versions - file_obj = tempfile.NamedTemporaryFile(delete=False) - try: + octoprint_version = get_versions()["version"] + self._version_cache["__version"] = octoprint_version + + with atomic_write(self._version_cache_path) as file_obj: yaml.safe_dump(self._version_cache, stream=file_obj, default_flow_style=False, indent=" ", allow_unicode=True) - file_obj.close() - shutil.move(file_obj.name, self._version_cache_path) - self._version_cache_dirty = False - self._logger.info("Saved version cache to disk") - finally: - try: - if os.path.exists(file_obj.name): - os.remove(file_obj.name) - except Exception as e: - self._logger.warn("Could not delete file {}: {}".format(file_obj.name, str(e))) + self._version_cache_dirty = False + self._logger.info("Saved version cache to disk") #~~ SettingsPlugin API @@ -159,20 +158,62 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, data = dict(octoprint.plugin.SettingsPlugin.on_settings_load(self)) if "checks" in data: del data["checks"] + + checks = self._get_configured_checks() + if "octoprint" in checks: + if "checkout_folder" in checks["octoprint"]: + data["octoprint_checkout_folder"] = checks["octoprint"]["checkout_folder"] + elif "update_folder" in checks["octoprint"]: + data["octoprint_checkout_folder"] = checks["octoprint"]["update_folder"] + else: + data["octoprint_checkout_folder"] = None + data["octoprint_type"] = checks["octoprint"].get("type", None) + else: + data["octoprint_checkout_folder"] = None + data["octoprint_type"] = None + return data def on_settings_save(self, data): for key in self.get_settings_defaults(): - if key == "checks" or key == "cache_ttl": + if key == "checks" or key == "cache_ttl" or key == "octoprint_checkout_folder" or key == "octoprint_type": continue if key in data: self._settings.set([key], data[key]) if "cache_ttl" in data: self._settings.set_int(["cache_ttl"], data["cache_ttl"]) - self._version_cache_ttl = self._settings.get_int(["cache_ttl"]) * 60 + checks = self._get_configured_checks() + if "octoprint" in checks: + check = checks["octoprint"] + update_type = check.get("type", None) + checkout_folder = check.get("checkout_folder", None) + update_folder = check.get("update_folder", None) + + defaults = dict( + plugins=dict(softwareupdate=dict( + checks=dict( + octoprint=dict( + type=update_type, + checkout_folder=checkout_folder, + update_folder=update_folder + ) + ) + )) + ) + + if "octoprint_checkout_folder" in data: + self._settings.set(["checks", "octoprint", "checkout_folder"], data["octoprint_checkout_folder"], defaults=defaults, force=True) + if update_folder and data["octoprint_checkout_folder"]: + self._settings.set(["checks", "octoprint", "update_folder"], None, defaults=defaults, force=True) + self._refresh_configured_checks = True + + if "octoprint_type" in data and data["octoprint_type"] in ("github_release", "git_commit"): + self._settings.set(["checks", "octoprint", "type"], data["octoprint_type"], defaults=defaults, force=True) + self._refresh_configured_checks = True + def get_settings_version(self): return 4 @@ -380,7 +421,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, try: target_information, target_update_available, target_update_possible = self._get_current_version(target, populated_check, force=force) if target_information is None: - continue + target_information = dict() except exceptions.UnknownCheckType: self._logger.warn("Unknown update check type for %s" % target) continue @@ -399,22 +440,29 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, updatePossible=target_update_possible, information=target_information, displayName=populated_check["displayName"], - displayVersion=populated_check["displayVersion"].format(octoprint_version=octoprint_version, local_name=local_name, local_value=local_value)) + displayVersion=populated_check["displayVersion"].format(octoprint_version=octoprint_version, local_name=local_name, local_value=local_value), + check=populated_check) if self._version_cache_dirty: self._save_version_cache() return information, update_available, update_possible + def _get_check_hash(self, check): + hash = hashlib.md5() + hash.update(repr(check)) + return hash.hexdigest() + def _get_current_version(self, target, check, force=False): """ Determines the current version information for one target based on its check configuration. """ + current_hash = self._get_check_hash(check) if target in self._version_cache and not force: - timestamp, information, update_available, update_possible = self._version_cache[target] - if timestamp + self._version_cache_ttl >= time.time() > timestamp: + data = self._version_cache[target] + if data["hash"] == current_hash and data["timestamp"] + self._version_cache_ttl >= time.time() > data["timestamp"]: # we also check that timestamp < now to not get confused too much by clock changes - return information, update_available, update_possible + return data["information"], data["available"], data["possible"] information = dict() update_available = False @@ -437,7 +485,11 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, except: update_possible = False - self._version_cache[target] = (time.time(), information, update_available, update_possible) + self._version_cache[target] = dict(timestamp=time.time(), + hash=current_hash, + information=information, + available=update_available, + possible=update_possible) self._version_cache_dirty = True return information, update_available, update_possible diff --git a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js index f9044536..a018f688 100644 --- a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js +++ b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js @@ -19,10 +19,20 @@ $(function() { self.workingOutput = undefined; self.loglines = ko.observableArray([]); + self.octoprintUnconfigured = ko.observable(); + self.octoprintUnreleased = ko.observable(); + self.config_cacheTtl = ko.observable(); + self.config_checkoutFolder = ko.observable(); + self.config_checkType = ko.observable(); self.configurationDialog = $("#settings_plugin_softwareupdate_configurationdialog"); + self.config_availableCheckTypes = [ + {"key": "github_release", "name": gettext("Release")}, + {"key": "git_commit", "name": gettext("Commit")} + ]; + self.versions = new ItemListHelper( "plugin.softwareupdate.versions", { @@ -82,15 +92,23 @@ $(function() { var data = { plugins: { softwareupdate: { - cache_ttl: parseInt(self.config_cacheTtl()) + cache_ttl: parseInt(self.config_cacheTtl()), + octoprint_checkout_folder: self.config_checkoutFolder(), + octoprint_type: self.config_checkType() } } }; - self.settings.saveData(data, function() { self.configurationDialog.modal("hide"); self._copyConfig(); }); + self.settings.saveData(data, function() { + self.configurationDialog.modal("hide"); + self._copyConfig(); + self.performCheck(); + }); }; self._copyConfig = 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.fromCheckResponse = function(data, ignoreSeen, showIfNothingNew) { @@ -109,6 +127,25 @@ $(function() { }); self.versions.updateItems(versions); + var octoprint = data.information["octoprint"]; + if (octoprint && octoprint.hasOwnProperty("check")) { + var check = octoprint.check; + if (BRANCH != "master" && check["type"] == "github_release") { + self.octoprintUnreleased(true); + } else { + self.octoprintUnreleased(false); + } + + 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 == "") { + self.octoprintUnconfigured(true); + } else { + self.octoprintUnconfigured(false); + } + } + if (data.status == "updateAvailable" || data.status == "updatePossible") { var text = gettext("There are updates available for the following components:"); diff --git a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 index d649099d..5f56ba6f 100644 --- a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 @@ -1,3 +1,21 @@ +
{% trans %} + Please configure the checkout folder of OctoPrint, otherwise + this plugin won't be able to update it. Click on the button + to do this. Also refer to the Documentation. +{% endtrans %}
+
{% trans %} +

+ You are running a non-release version of OctoPrint but are tracking OctoPrint + releases. +

+ You probably want OctoPrint to track the matching development version instead. + If you have a local OctoPrint checkout folder switched to another branch, + simply switching over to "Commit" tracking will already + take care of that. Otherwise please take a look at the + Documentation. +

+{% endtrans %}
+
@@ -11,7 +29,7 @@ - :
+ :
{{ _('Installed:') }}
{{ _('Available:') }} @@ -44,7 +62,7 @@
{{ _('Advanced options') }}
- +
@@ -55,6 +73,18 @@