Merge branch 'stable-1.2.2' into mrbeam
This commit is contained in:
commit
3516e8a8e7
12 changed files with 389 additions and 67 deletions
|
|
@ -12,6 +12,7 @@ import flask
|
|||
import os
|
||||
import threading
|
||||
import time
|
||||
import hashlib
|
||||
|
||||
from . import version_checks, updaters, exceptions, util
|
||||
|
||||
|
|
@ -83,8 +84,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
|
||||
|
|
@ -102,24 +107,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
|
||||
|
||||
|
|
@ -127,61 +126,113 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
|
|||
return {
|
||||
"checks": {
|
||||
"octoprint": {
|
||||
"update_folder": "/home/pi/OctoPrint",
|
||||
"checkout_folder": "/home/pi/OctoPrint",
|
||||
"type": "github_commit",
|
||||
"repo": "OctoPrint",
|
||||
"user": "mrbeam",
|
||||
"branch": "stable-1.2.2",
|
||||
"update_script": "{{python}} \"{update_script}\" --python=\"{{python}}\" \"{{folder}}\" {{target}}".format(update_script=os.path.join(self._basefolder, "scripts", "update-octoprint.py")),
|
||||
"restart": "octoprint"
|
||||
"restart": "octoprint",
|
||||
"current": "Unknown"
|
||||
},
|
||||
"svgtogcode": {
|
||||
"update_folder": "/home/pi/mrbeam-inkscape-ext",
|
||||
"checkout_folder": "/home/pi/mrbeam-inkscape-ext",
|
||||
"type": "github_commit",
|
||||
"repo": "mrbeam-inkscape-ext",
|
||||
"user": "mrbeam",
|
||||
"branch": "mrbeam-stable",
|
||||
"branch": "stable-1.2.2",
|
||||
"update_script": "{{python}} \"{update_script}\" \"{{folder}}\" {{target}}".format(update_script=os.path.join(self._basefolder, "scripts", "git-pull.py")),
|
||||
"restart": "octoprint",
|
||||
"current": "Unknown"
|
||||
},
|
||||
"lcd": {
|
||||
"update_folder": "/home/pi/lcd",
|
||||
"checkout_folder": "/home/pi/lcd",
|
||||
"type": "github_commit",
|
||||
"repo": "lcd",
|
||||
"user": "mrbeam",
|
||||
"branch": "mrbeam-stable",
|
||||
"branch": "stable-1.2.2",
|
||||
"update_script": "{{python}} \"{update_script}\" \"{{folder}}\" {{target}}".format(update_script=os.path.join(self._basefolder, "scripts", "git-pull.py")),
|
||||
"restart": "environment",
|
||||
"current": "Unknown"
|
||||
},
|
||||
"netconnectd": {
|
||||
"update_folder": "/home/pi/netconnectd",
|
||||
"checkout_folder": "/home/pi/netconnectd",
|
||||
"type": "github_commit",
|
||||
"repo": "netconnectd",
|
||||
"user": "mrbeam",
|
||||
"branch": "mrbeam-stable",
|
||||
"branch": "stable-1.2.2",
|
||||
"update_script": "{{python}} \"{update_script}\" \"{{folder}}\" {{target}}".format(update_script=os.path.join(self._basefolder, "scripts", "git-pull.py")),
|
||||
"restart": "environment",
|
||||
"current": "Unknown"
|
||||
},
|
||||
},
|
||||
|
||||
"octoprint_restart_command": "sudo service octoprint restart",
|
||||
"environment_restart_command": "sudo shutdown -r now",
|
||||
"pip_command": None,
|
||||
"cache_ttl": 60,
|
||||
"cache_ttl": 12 * 60,
|
||||
}
|
||||
|
||||
def on_settings_load(self):
|
||||
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 3
|
||||
|
||||
|
|
@ -363,7 +414,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
|
||||
|
|
@ -382,22 +433,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
|
||||
|
|
@ -421,7 +479,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
|
||||
|
||||
|
|
@ -631,13 +693,6 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
|
|||
if not "type" in check:
|
||||
raise exceptions.ConfigurationInvalid("no check type defined")
|
||||
|
||||
if target == "octoprint":
|
||||
from octoprint._version import get_versions
|
||||
from flask.ext.babel import gettext
|
||||
check["displayName"] = gettext("OctoPrint")
|
||||
check["displayVersion"] = "{octoprint_version}"
|
||||
check["current"] = get_versions()["version"]
|
||||
|
||||
check_type = check["type"]
|
||||
if check_type == "github_release":
|
||||
return version_checks.github_release
|
||||
|
|
|
|||
180
src/octoprint/plugins/softwareupdate/scripts/git-pull.py
Normal file
180
src/octoprint/plugins/softwareupdate/scripts/git-pull.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
#!/bin/env python
|
||||
from __future__ import absolute_import
|
||||
|
||||
__author__ = "Gina Haeussge <osd@foosel.net>"
|
||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
||||
|
||||
|
||||
import errno
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _get_git_executables():
|
||||
GITS = ["git"]
|
||||
if sys.platform == "win32":
|
||||
GITS = ["git.cmd", "git.exe"]
|
||||
return GITS
|
||||
|
||||
|
||||
def _git(args, cwd, hide_stderr=False, verbose=False, git_executable=None):
|
||||
if git_executable is not None:
|
||||
commands = [git_executable]
|
||||
else:
|
||||
commands = _get_git_executables()
|
||||
|
||||
for c in commands:
|
||||
try:
|
||||
p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
|
||||
stderr=(subprocess.PIPE if hide_stderr
|
||||
else None))
|
||||
break
|
||||
except EnvironmentError:
|
||||
e = sys.exc_info()[1]
|
||||
if e.errno == errno.ENOENT:
|
||||
continue
|
||||
if verbose:
|
||||
print("unable to run %s" % args[0])
|
||||
print(e)
|
||||
return None, None
|
||||
else:
|
||||
if verbose:
|
||||
print("unable to find command, tried %s" % (commands,))
|
||||
return None, None
|
||||
|
||||
stdout = p.communicate()[0].strip()
|
||||
if sys.version >= '3':
|
||||
stdout = stdout.decode()
|
||||
|
||||
if p.returncode != 0:
|
||||
if verbose:
|
||||
print("unable to run %s (error)" % args[0])
|
||||
|
||||
return p.returncode, stdout
|
||||
|
||||
|
||||
def _python(args, cwd, python_executable, sudo=False):
|
||||
command = [python_executable] + args
|
||||
if sudo:
|
||||
command = ["sudo"] + command
|
||||
try:
|
||||
p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
except:
|
||||
return None, None
|
||||
|
||||
stdout = p.communicate()[0].strip()
|
||||
if sys.version >= "3":
|
||||
stdout = stdout.decode()
|
||||
|
||||
return p.returncode, stdout
|
||||
|
||||
|
||||
def update_source(git_executable, folder, target, force=False):
|
||||
print(">>> Running: git diff --shortstat")
|
||||
returncode, stdout = _git(["diff", "--shortstat"], folder, git_executable=git_executable)
|
||||
if returncode != 0:
|
||||
raise RuntimeError("Could not update, \"git diff\" failed with returncode %d: %s" % (returncode, stdout))
|
||||
if stdout and stdout.strip():
|
||||
# we got changes in the working tree, maybe from the user, so we'll now rescue those into a patch
|
||||
import time
|
||||
import os
|
||||
timestamp = time.strftime("%Y%m%d%H%M")
|
||||
patch = os.path.join(folder, "%s-preupdate.patch" % timestamp)
|
||||
|
||||
print(">>> Running: git diff and saving output to %s" % timestamp)
|
||||
returncode, stdout = _git(["diff"], folder, git_executable=git_executable)
|
||||
if returncode != 0:
|
||||
raise RuntimeError("Could not update, installation directory was dirty and state could not be persisted as a patch to %s" % patch)
|
||||
|
||||
with open(patch, "wb") as f:
|
||||
f.write(stdout)
|
||||
|
||||
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 pull")
|
||||
returncode, stdout = _git(["pull"], folder, git_executable=git_executable)
|
||||
if returncode != 0:
|
||||
raise RuntimeError("Could not update, \"git pull\" failed with returncode %d: %s" % (returncode, stdout))
|
||||
print(stdout)
|
||||
|
||||
if force:
|
||||
reset_command = ["reset"]
|
||||
reset_command += [target]
|
||||
|
||||
print(">>> Running: git %s" % " ".join(reset_command))
|
||||
returncode, stdout = _git(reset_command, folder, git_executable=git_executable)
|
||||
if returncode != 0:
|
||||
raise RuntimeError("Error while updating, \"git %s\" failed with returncode %d: %s" % (" ".join(reset_command), returncode, stdout))
|
||||
print(stdout)
|
||||
|
||||
|
||||
def install_source(python_executable, folder, user=False, sudo=False):
|
||||
print(">>> Running: python setup.py clean")
|
||||
returncode, stdout = _python(["setup.py", "clean"], folder, python_executable)
|
||||
if returncode != 0:
|
||||
print("\"python setup.py clean\" failed with returncode %d: %s" % (returncode, stdout))
|
||||
print("Continuing anyways")
|
||||
print(stdout)
|
||||
|
||||
print(">>> Running: python setup.py install")
|
||||
args = ["setup.py", "install"]
|
||||
if user:
|
||||
args.append("--user")
|
||||
returncode, stdout = _python(args, folder, python_executable, sudo=sudo)
|
||||
if returncode != 0:
|
||||
raise RuntimeError("Could not update, \"python setup.py install\" failed with returncode %d: %s" % (returncode, stdout))
|
||||
print(stdout)
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
import argparse
|
||||
|
||||
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("--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("folder", type=str,
|
||||
help="Specify the base folder of the OctoPrint installation to update")
|
||||
parser.add_argument("target", type=str,
|
||||
help="Specify the commit or tag to which to update")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
def main():
|
||||
args = parse_arguments()
|
||||
|
||||
git_executable = None
|
||||
if args.git_executable:
|
||||
git_executable = args.git_executable
|
||||
|
||||
python_executable = sys.executable
|
||||
if args.python_executable:
|
||||
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)
|
||||
#install_source(python_executable, folder, user=args.user, sudo=args.sudo)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -13,12 +13,20 @@ $(function() {
|
|||
|
||||
self.currentlyBeingUpdated = [];
|
||||
|
||||
self.config_restartCommand = ko.observable();
|
||||
self.config_rebootCommand = ko.observable();
|
||||
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",
|
||||
{
|
||||
|
|
@ -78,19 +86,23 @@ $(function() {
|
|||
var data = {
|
||||
plugins: {
|
||||
softwareupdate: {
|
||||
octoprint_restart_command: self.config_restartCommand(),
|
||||
environment_restart_command: self.config_rebootCommand(),
|
||||
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_restartCommand(self.settings.settings.plugins.softwareupdate.octoprint_restart_command());
|
||||
self.config_rebootCommand(self.settings.settings.plugins.softwareupdate.environment_restart_command());
|
||||
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 +121,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:");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,21 @@
|
|||
<div class="alert" data-bind="visible: octoprintUnconfigured()">{% trans %}
|
||||
Please configure the <strong>checkout folder</strong> of OctoPrint, otherwise
|
||||
this plugin won't be able to update it. Click on the <i class="icon-wrench"></i> button
|
||||
to do this. Also refer to the <a href="https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update" target="_blank">Documentation</a>.
|
||||
{% endtrans %}</div>
|
||||
<div class="alert" data-bind="visible: !octoprintUnconfigured() && octoprintUnreleased()">{% trans %}
|
||||
<p>
|
||||
<strong>You are running a non-release version of OctoPrint but are tracking OctoPrint
|
||||
releases.</strong>
|
||||
</p><p>
|
||||
You probably want OctoPrint to track the matching development version instead.
|
||||
If you have a local OctoPrint checkout folder switched to another branch,
|
||||
<strong>simply switching over to "Commit" tracking</strong> will already
|
||||
take care of that. Otherwise please take a look at the
|
||||
<a href="https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update" target="_blank">Documentation</a>.
|
||||
</p>
|
||||
{% endtrans %}</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-small" data-bind="click: function() { $root.showPluginSettings(); }" title="{{ _('Plugin Configuration') }}"><i class="icon-wrench"></i></button>
|
||||
</div>
|
||||
|
|
@ -11,7 +29,7 @@
|
|||
<span data-bind="invisible: !updateAvailable"><i class="icon-bell" title="{{ _('Update available') }}"></i></span>
|
||||
</td>
|
||||
<td class="settings_plugin_softwareupdate_column_information">
|
||||
<strong data-bind="text: displayName"></strong>: <span data-bind="text: displayVersion"></span><br>
|
||||
<strong data-bind="text: displayName"></strong>: <span data-bind="text: displayVersion"></span> <span data-bind="invisible: updatePossible"><i class="icon-exclamation-sign" title="{{ _('Update not possible, configuration ok?') }}"></i></span><br>
|
||||
<small class="muted">
|
||||
{{ _('Installed:') }} <span data-bind="text: information.local.name"></span><br>
|
||||
{{ _('Available:') }} <span data-bind="text: information.remote.name"></span>
|
||||
|
|
@ -44,7 +62,7 @@
|
|||
<div><small><a href="#" class="muted" onclick="$(this).children().toggleClass('icon-caret-right icon-caret-down').parent().parent().parent().next().slideToggle('fast')"><i class="icon-caret-right"></i> {{ _('Advanced options') }}</a></small></div>
|
||||
<div class="hide">
|
||||
<button class="btn btn-block" data-bind="click: function() { $root.performCheck(true, true, true); }">{{ _('Force check for update (overrides cache used for update checks)') }}</button>
|
||||
<button class="btn btn-block btn-danger" data-bind="click: function() { $root.update(true); }">{{ _('Force update now (even if no new versions are available)') }}</button>
|
||||
<button class="btn btn-block btn-danger" data-bind="visible: CONFIG_DEBUG, click: function() { $root.update(true); }">{{ _('Force update now (even if no new versions are available)') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -56,15 +74,15 @@
|
|||
<div class="modal-body">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Restart Command') }}</label>
|
||||
<label class="control-label">{{ _('OctoPrint checkout folder') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" class="input-block-level" data-bind="value: config_restartCommand">
|
||||
<input type="text" class="input-block-level" data-bind="value: config_checkoutFolder">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Reboot Command') }}</label>
|
||||
<label class="control-label">{{ _('OctoPrint version tracking') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" class="input-block-level" data-bind="value: config_rebootCommand">
|
||||
<select data-bind="value: config_checkType, options: config_availableCheckTypes, optionsText: 'name', optionsValue: 'key'"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
|
|
|
|||
|
|
@ -13,7 +13,17 @@ from ..util import execute
|
|||
|
||||
|
||||
def can_perform_update(target, check):
|
||||
return "update_script" in check and ("checkout_folder" in check or "update_folder" in check)
|
||||
import os
|
||||
script_configured = bool("update_script" in check and check["update_script"])
|
||||
|
||||
folder = None
|
||||
if "update_folder" in check:
|
||||
folder = check["update_folder"]
|
||||
elif "checkout_folder" in check:
|
||||
folder = check["checkout_folder"]
|
||||
folder_configured = bool(folder and os.path.isdir(folder))
|
||||
|
||||
return script_configured and folder_configured
|
||||
|
||||
|
||||
def perform_update(target, check, target_version):
|
||||
|
|
|
|||
|
|
@ -329,12 +329,14 @@ class PrinterProfileManager(object):
|
|||
if os.path.exists(path) and not allow_overwrite:
|
||||
raise SaveError("Profile %s already exists and not allowed to overwrite" % profile["id"])
|
||||
|
||||
from octoprint.util import atomic_write
|
||||
import yaml
|
||||
with open(path, "wb") as f:
|
||||
try:
|
||||
try:
|
||||
with atomic_write(path, "wb") as f:
|
||||
yaml.safe_dump(profile, f, default_flow_style=False, indent=" ", allow_unicode=True)
|
||||
except Exception as e:
|
||||
raise SaveError("Cannot save profile %s: %s" % (profile["id"], str(e)))
|
||||
except Exception as e:
|
||||
self._logger.exception("Error while trying to save profile %s" % profile["id"])
|
||||
raise SaveError("Cannot save profile %s: %s" % (profile["id"], str(e)))
|
||||
|
||||
def _remove_from_path(self, path):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,13 @@ class PrinterStateConnection(sockjs.tornado.SockJSConnection, octoprint.printer.
|
|||
plugin_hash.update(",".join(ui_plugins))
|
||||
|
||||
# connected => update the API key, might be necessary if the client was left open while the server restarted
|
||||
self._emit("connected", {"apikey": octoprint.server.UI_API_KEY, "version": octoprint.server.VERSION, "display_version": octoprint.server.DISPLAY_VERSION, "plugin_hash": plugin_hash.hexdigest()})
|
||||
self._emit("connected", {
|
||||
"apikey": octoprint.server.UI_API_KEY,
|
||||
"version": octoprint.server.VERSION,
|
||||
"display_version": octoprint.server.DISPLAY_VERSION,
|
||||
"branch": octoprint.server.BRANCH,
|
||||
"plugin_hash": plugin_hash.hexdigest()
|
||||
})
|
||||
|
||||
self._printer.register_callback(self)
|
||||
self._fileManager.register_slicingprogress_callback(self)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ from flask import request, g, url_for, make_response, render_template, send_from
|
|||
|
||||
import octoprint.plugin
|
||||
|
||||
from octoprint.server import app, userManager, pluginManager, gettext, debug, LOCALES, VERSION, DISPLAY_VERSION, UI_API_KEY
|
||||
from octoprint.server import app, userManager, pluginManager, gettext, \
|
||||
debug, LOCALES, VERSION, DISPLAY_VERSION, UI_API_KEY, BRANCH
|
||||
from octoprint.settings import settings
|
||||
|
||||
from . import util
|
||||
|
|
@ -252,6 +253,7 @@ def index():
|
|||
debug=debug,
|
||||
version=VERSION,
|
||||
display_version=DISPLAY_VERSION,
|
||||
branch=BRANCH,
|
||||
gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
|
||||
gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
|
||||
uiApiKey=UI_API_KEY,
|
||||
|
|
|
|||
|
|
@ -784,11 +784,17 @@ class Settings(object):
|
|||
if not self._dirty and not force:
|
||||
return False
|
||||
|
||||
with open(self._configfile, "wb") as configFile:
|
||||
yaml.safe_dump(self._config, configFile, default_flow_style=False, indent=" ", allow_unicode=True)
|
||||
self._dirty = False
|
||||
self.load()
|
||||
return True
|
||||
from octoprint.util import atomic_write
|
||||
try:
|
||||
with atomic_write(self._configfile, "wb", prefix="octoprint-config-", suffix=".yaml") as configFile:
|
||||
yaml.safe_dump(self._config, configFile, default_flow_style=False, indent=" ", allow_unicode=True)
|
||||
self._dirty = False
|
||||
except:
|
||||
self._logger.exception("Error while saving config.yaml!")
|
||||
raise
|
||||
else:
|
||||
self.load()
|
||||
return True
|
||||
|
||||
@property
|
||||
def last_modified(self):
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ function DataUpdater(allViewModels) {
|
|||
var oldVersion = VERSION;
|
||||
VERSION = data["version"];
|
||||
DISPLAY_VERSION = data["display_version"];
|
||||
BRANCH = data["branch"];
|
||||
$("span.version").text(DISPLAY_VERSION);
|
||||
|
||||
var oldPluginHash = self._pluginHash;
|
||||
|
|
|
|||
|
|
@ -21,11 +21,12 @@
|
|||
var SOCKJS_URI = "{{ url_for('index') }}" + "sockjs";
|
||||
var SOCKJS_DEBUG = CONFIG_DEBUG;
|
||||
// sockjs should define CLOSE_NORMAL for us, but they don't (from ws spec)
|
||||
var SOCKJS_CLOSE_NORMAL = 1000
|
||||
var SOCKJS_CLOSE_NORMAL = 1000;
|
||||
|
||||
var UI_API_KEY = "{{ uiApiKey }}";
|
||||
var VERSION = "{{ version }}";
|
||||
var DISPLAY_VERSION = "{{ display_version }}";
|
||||
var BRANCH = "{{ branch }}";
|
||||
var LOCALE = "{{ g.locale }}";
|
||||
var AVAILABLE_LOCALES = {{ locales|tojson }};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import shutil
|
|||
import threading
|
||||
from functools import wraps
|
||||
import warnings
|
||||
import contextlib
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -513,6 +514,15 @@ def address_for_client(host, port):
|
|||
except:
|
||||
continue
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def atomic_write(filename, mode="w+b", prefix="tmp", suffix=""):
|
||||
temp_config = tempfile.NamedTemporaryFile(mode=mode, prefix=prefix, suffix=suffix, delete=False)
|
||||
yield temp_config
|
||||
temp_config.close()
|
||||
shutil.move(temp_config.name, filename)
|
||||
|
||||
|
||||
class RepeatedTimer(threading.Thread):
|
||||
"""
|
||||
This class represents an action that should be run repeatedly in an interval. It is similar to python's
|
||||
|
|
|
|||
Loading…
Reference in a new issue