Prefer plain pip over git for updating OctoPrint

At least unless we've selected git based tracking over release tracking.

This should greatly reduce the likelihood of not being able to update
due to some git repository corruption issues, as has happened to some
people in the past.
This commit is contained in:
Gina Häußge 2017-11-02 11:03:12 +01:00
parent c30e54d75c
commit 79581c390a
12 changed files with 192 additions and 132 deletions

View file

@ -14,14 +14,14 @@ First Steps
Out of the box the Software Update Plugin will be able to notify you of any
updates that might be available for your OctoPrint installation or any plugins
that registered themselves with it. In order to also be able to update
your OctoPrint installation, you'll need to configure
at least OctoPrint's checkout folder, and you also should
configure the restart commands for OctoPrint and the whole server.
that registered themselves with it. In order for automatic restarts after updates
to work, you should configure the restart commands for OctoPrint and the whole server.
For configuring the plugin you'll need to go into OctoPrint's Settings Dialog, navigate to the
Software Upda.. _section therein and once you are there click on the little wrench icon in the
upper right corner.
Out of the box the plugin should already be ready to update your OctoPrint installation to current
stable release versions, but you can also switch to one of the available release candidate channels
or outright git commit tracking via the plugin's configuration dialog. To open this dialog, fire up OctoPrint's
Settings Dialog, navigate to the Software Update section therein and once you are there click on the little
wrench icon in the upper right corner.
.. _fig-bundledplugins-softwareupdate-plugin-configuration:
.. figure:: ../images/bundledplugins-softwareupdate-plugin-configuration.png
@ -32,31 +32,16 @@ upper right corner.
There you can adjust the following settings:
* **OctoPrint checkout folder**: This should be the path to OctoPrint's git checkout folder (``/home/pi/OctoPrint``
for OctoPi or `manual installs following the Raspberry Pi setup guide <https://github.com/foosel/OctoPrint/wiki/Setup-on-a-Raspberry-Pi-running-Raspbian>`_).
This must be set to allow updating from within OctoPrint
.. note::
OctoPi releases 0.12.0 and later ship with this already setup for you.
.. note::
**OctoPi 0.11.0 users**: Please also take a look at
`the note at the very end of this FAQ entry <https://github.com/foosel/OctoPrint/wiki/FAQ#how-can-i-update-the-octoprint-installation-on-my-octopi-image>`_.
Due to a little issue in that OctoPi release 0.11.0 you might have to fix
the URL your OctoPrint checkout is using for updating. This can easily be
done by SSHing into your OctoPi instance and doing this::
cd ~/OctoPrint
git remote set-url origin https://github.com/foosel/OctoPrint.git
* **OctoPrint version tracking**: Whether you want to track OctoPrint *releases* or every *commit*. Usually you want to
select "Release" here which is also the default, unless you are a developer.
* **OctoPrint Release Channel**: The release channel of OctoPrint to track for updates. If you only want stable versions,
* **OctoPrint Release Channel** (if tracking releases): The release channel of OctoPrint to track for updates. If you only want stable versions,
select "Stable" here which is also the default. "Maintenance RCs" will also allow you to update to maintenance release
candidates, "Devel RCs" will also allow you to update to development release candidates. If in doubt, leave it at
"Stable". `Read more about Release Channels here <https://github.com/foosel/OctoPrint/wiki/Using-Release-Channels>`_.
* **OctoPrint checkout folder** (if tracking git commits): This must be the path to OctoPrint's git checkout folder
(``/home/pi/OctoPrint`` for OctoPi or `manual installs following the Raspberry Pi setup guide <https://github.com/foosel/OctoPrint/wiki/Setup-on-a-Raspberry-Pi-running-Raspbian>`_).
Note that since OctoPrint 1.3.6 you will no longer need to set this to be able to update to releases, only if you
want to be able to update against some bleeding edge git branch.
* **Version cache TTL**: The "time to live" of the cache OctoPrint will use to temporarily persist the version information
for the various components registered with the plugin, so that they don't have to be queried from the internet every time
you load the page. Defaults to 24h, you usually shouldn't need to change that value.
@ -142,14 +127,13 @@ Configuring the Plugin
# "octoprint" is reserved for OctoPrint
octoprint:
# this defines an version check that will check against releases
# published on OctoPrint's Github repository and an update method
# utilizing an (included) update script that will be run on
# OctoPrint's checkout folder
# published on OctoPrint's Github repository and pip as update method
# against the release archives on Github - this is the default
type: github_release
user: foosel
repo: OctoPrint
update_script: '{python} "/path/to/octoprint-update.py" --python="{python}" "{folder}" "{target}"'
update_folder: /path/to/octoprint/checkout/folder
method: pip
pip: 'https://github.com/foosel/OctoPrint/archive/{target_version}.zip'
# further checks may be define here
@ -249,6 +233,32 @@ Update methods
:ref:`hook <sec-bundledplugins-softwareupdate-hooks>`. A python callable
which performs the update, see below for details.
.. note::
To allow default configurations for multiple update methods, if more than one of
the above update method specific settings is set the one to use can be selected
by setting the property ``method`` to the method specific setting in question.
**Example**
The following example defines both ``pip`` and ``update_script``. By setting to
``method`` to ``pip``, the Software Update plugin is instructed to use that as
update method.
.. code-block::
plugins:
softwareupdate:
checks:
octoprint:
type: github_release
user: foosel
repo: OctoPrint
method: pip
pip: 'https://github.com/foosel/OctoPrint/archive/{target_version}.zip'
update_script: '{python} "/path/to/octoprint-update.py" --python="{python}" "{folder}" "{target}"'
checkout_folder: /path/to/octoprint/checkout/folder
.. _sec-bundledplugins-softwareupdate-configuration-patterns:
Common configuration patterns
@ -268,6 +278,7 @@ plugin itself):
user: foosel
repo: OctoPrint
branch: devel
method: update_script
update_folder: /home/pi/OctoPrint
Plugin installed via pip and hosted on Github under

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -41,6 +41,8 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
octoprint.plugin.EventHandlerPlugin):
COMMIT_TRACKING_TYPES = ("github_commit", "bitbucket_commit")
DATA_FORMAT_VERSION = "v2"
def __init__(self):
self._update_in_progress = False
@ -216,13 +218,18 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
def get_settings_defaults(self):
update_script = os.path.join(self._basefolder, "scripts", "update-octoprint.py")
default_update_script = "{{python}} \"{update_script}\" --branch={{branch}} " \
"--force={{force}} \"{{folder}}\" {{target}}".format(update_script=update_script)
return {
"checks": {
"octoprint": {
"type": "github_release",
"user": "foosel",
"repo": "OctoPrint",
"update_script": "{{python}} \"{update_script}\" --branch={{branch}} --force={{force}} \"{{folder}}\" {{target}}".format(update_script=update_script),
"method": "pip",
"pip": "https://github.com/foosel/OctoPrint/archive/{target_version}.zip",
"update_script": default_update_script,
"restart": "octoprint",
"stable_branch": dict(branch="master", commitish=["master"], name="Stable"),
"prerelease_branches": [dict(branch="rc/maintenance",
@ -302,12 +309,13 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
if "octoprint" in checks:
check = checks["octoprint"]
update_type = check.get("type", None)
update_method = self._get_update_method("octoprint", check)
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
update_type = update_method = checkout_folder = update_folder = prerelease_channel = None
prerelease = False
defaults = dict(
@ -315,6 +323,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
checks=dict(
octoprint=dict(
type=update_type,
method=update_method,
checkout_folder=checkout_folder,
update_folder=update_folder,
prerelease=prerelease,
@ -332,10 +341,30 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
self._settings.set(["checks", "octoprint", "update_folder"], None, defaults=defaults, force=True)
updated_octoprint_check_config = 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)
updated_octoprint_check_config = True
if "octoprint_type" in data:
octoprint_type = data["octoprint_type"]
if octoprint_type == "github_release":
self._settings.set(["checks", "octoprint", "type"], octoprint_type, defaults=defaults, force=True)
self._settings.set(["checks", "octoprint", "method"], "pip", defaults=defaults, force=True)
updated_octoprint_check_config = True
elif octoprint_type == "git_commit":
self._settings.set(["checks", "octoprint", "type"], octoprint_type, defaults=defaults, force=True)
self._settings.set(["checks", "octoprint", "method"], "update_script", defaults=defaults, force=True)
updated_octoprint_check_config = True
if "octoprint_release_channel" in data:
prerelease_branches = self._settings.get(["checks", "octoprint", "prerelease_branches"])
if prerelease_branches and data["octoprint_release_channel"] in [x["branch"] for x in 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)
else:
self._settings.set(["checks", "octoprint", "prerelease"], False, defaults=defaults, force=True)
self._settings.set(["checks", "octoprint", "prerelease_channel"], None, defaults=defaults, force=True)
updated_octoprint_check_config = True
if updated_octoprint_check_config:
self._refresh_configured_checks = True
try:
@ -344,22 +373,23 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
pass
self._version_cache_dirty = True
if "octoprint_release_channel" in data:
prerelease_branches = self._settings.get(["checks", "octoprint", "prerelease_branches"])
if prerelease_branches and data["octoprint_release_channel"] in [x["branch"] for x in 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 5
return 6
def on_settings_migrate(self, target, current=None):
if current is None or current < 6:
# up until & including config version 5 we didn't set the method parameter for the octoprint check
# configuration
configured_checks = self._settings.get(["checks"], incl_defaults=False)
if configured_checks is not None and "octoprint" in configured_checks:
octoprint_check = dict(configured_checks["octoprint"])
if not "method" in octoprint_check and octoprint_check.get("type") == "git_commit":
defaults = dict(plugins=dict(softwareupdate=dict(checks=dict(octoprint=dict(method="pip")))))
self._settings.set(["checks", "octoprint", "method"], "update_script", defaults=defaults)
if current == 4:
# config version 4 didn't correctly remove the old settings for octoprint_restart_command
# and environment_restart_command
@ -504,6 +534,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
hash.update(",".join(targets))
hash.update(str(self._version_cache_timestamp))
hash.update(str(self._connectivity_checker.online))
hash.update(self.DATA_FORMAT_VERSION)
return hash.hexdigest()
def condition():
@ -563,7 +594,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
checks = self._get_configured_checks()
check = checks.get("octoprint", None)
checkout_folder = self._get_octoprint_checkout_folder(checks=checks)
return check and "update_script" in check and not checkout_folder
return check and "method" in check and check["method"] == "update_script" and not checkout_folder
##~~ EventHandlerPlugin API
@ -667,6 +698,9 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
releaseNotes=release_notes,
online=target_online,
error=target_error)
if target == "octoprint" and "released_version" in populated_check:
information[target]["released_version"] = populated_check["released_version"]
if self._version_cache_dirty:
self._save_version_cache()
@ -989,24 +1023,22 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
# we compare versions fully, not just the base so that we see a difference
# between RCs + stable for the same version release
result["force_base"] = False
if check.get("prerelease", None):
# we are tracking prereleases => we want to be on the correct prerelease channel/branch
channel = check.get("prerelease_channel", None)
if channel:
# if we have a release channel, we also set our update_branch here to our release channel
# in case it's not already set
result["update_branch"] = check.get("update_branch", channel)
else:
# we are not tracking prereleases, but aren't on the stable branch either => switch back
# to stable branch on update
result["update_branch"] = check.get("update_branch", stable_branch)
if check.get("update_script", None):
# if we are using the update_script, we need to set our update_branch
if check.get("prerelease", None):
# we are tracking prereleases => we want to be on the correct prerelease channel/branch
channel = check.get("prerelease_channel", None)
if channel:
# if we have a release channel, we also set our update_branch here to our release channel
# in case it's not already set
result["update_branch"] = check.get("update_branch", channel)
else:
# we are not tracking prereleases, but aren't on the stable branch either => switch back
# to stable branch on update
result["update_branch"] = check.get("update_branch", stable_branch)
# we also force an exact version
# we force an exact version
result["force_exact_version"] = True
if BRANCH != result.get("prerelease_channel"):
@ -1017,6 +1049,10 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin,
# branch of the release channel - unequality means we might have to handle
# a downgrade
result["release_compare"] = "python_unequal"
elif check.get("pip", None):
# we force python unequality check for pip installs, to be able to downgrade
result["release_compare"] = "python_unequal"
else:
result["displayName"] = to_unicode(check.get("displayName"), errors="replace")

View file

@ -108,8 +108,15 @@ $(function() {
self.checking = ko.observable(false);
self.octoprintUnconfigured = ko.observable();
self.octoprintUnreleased = ko.observable();
self.octoprintReleasedVersion = ko.observable();
self.octoprintUnconfigured = ko.pureComputed(function() {
return self.error_checkoutFolder();
});
self.octoprintUnreleased = ko.pureComputed(function() {
return self.settings.settings.plugins.softwareupdate.octoprint_type() === "github_release"
&& !self.octoprintReleasedVersion();
});
self.cacheTimestamp = ko.observable();
self.cacheTimestampText = ko.pureComputed(function() {
@ -120,9 +127,18 @@ $(function() {
self.config_notifyUsers = ko.observable();
self.config_checkoutFolder = ko.observable();
self.config_checkType = ko.observable();
self.config_updateMethod = ko.observable();
self.config_releaseChannel = ko.observable();
self.error_checkoutFolder = ko.pureComputed(function() {
return self.config_checkType() === "git_commit"
&& (!self.config_checkoutFolder() || self.config_checkoutFolder().trim() === '');
});
self.enable_configSave = ko.pureComputed(function() {
return self.config_checkType() === "github_release"
|| (self.config_checkType() === "git_commit" && !self.error_checkoutFolder());
});
self.configurationDialog = undefined;
self.confirmationDialog = undefined;
self._updateClicked = false;
@ -137,8 +153,8 @@ $(function() {
{
"name": function(a, b) {
// sorts ascending, puts octoprint first
if (a.key.toLocaleLowerCase() == "octoprint") return -1;
if (b.key.toLocaleLowerCase() == "octoprint") return 1;
if (a.key.toLocaleLowerCase() === "octoprint") return -1;
if (b.key.toLocaleLowerCase() === "octoprint") return 1;
if (a.displayName.toLocaleLowerCase() < b.displayName.toLocaleLowerCase()) return -1;
if (a.displayName.toLocaleLowerCase() > b.displayName.toLocaleLowerCase()) return 1;
@ -211,9 +227,9 @@ $(function() {
softwareupdate: {
cache_ttl: parseInt(self.config_cacheTtl()),
notify_users: self.config_notifyUsers(),
octoprint_checkout_folder: self.config_checkoutFolder(),
octoprint_type: self.config_checkType(),
octoprint_release_channel: self.config_releaseChannel()
octoprint_release_channel: self.config_releaseChannel(),
octoprint_checkout_folder: self.config_checkoutFolder()
}
}
};
@ -231,15 +247,8 @@ $(function() {
};
self._copyConfig = function() {
var updateMethod = self.settings.settings.plugins.softwareupdate.octoprint_method();
var availableCheckTypes = [];
if (updateMethod == "update_script" || updateMethod == "python") {
availableCheckTypes = [{"key": "github_release", "name": gettext("Release")},
var availableCheckTypes = [{"key": "github_release", "name": gettext("Release")},
{"key": "git_commit", "name": gettext("Commit")}];
} else {
availableCheckTypes = [];
}
self.config_availableCheckTypes(availableCheckTypes);
var availableReleaseChannels = [];
@ -248,12 +257,12 @@ $(function() {
});
self.config_availableReleaseChannels(availableReleaseChannels);
self.config_updateMethod(updateMethod);
self.config_cacheTtl(self.settings.settings.plugins.softwareupdate.cache_ttl());
self.config_notifyUsers(self.settings.settings.plugins.softwareupdate.notify_users());
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());
self.config_checkoutFolder(self.settings.settings.plugins.softwareupdate.octoprint_checkout_folder());
};
self._copyConfigBack = function() {
@ -268,13 +277,13 @@ $(function() {
_.each(data.information, function(value, key) {
value["key"] = key;
if (!value.hasOwnProperty("displayName") || value.displayName == "") {
if (!value.hasOwnProperty("displayName") || value.displayName === "") {
value.displayName = value.key;
}
if (!value.hasOwnProperty("displayVersion") || value.displayVersion == "") {
if (!value.hasOwnProperty("displayVersion") || value.displayVersion === "") {
value.displayVersion = value.information.local.name;
}
if (!value.hasOwnProperty("releaseNotes") || value.releaseNotes == "") {
if (!value.hasOwnProperty("releaseNotes") || value.releaseNotes === "") {
value.releaseNotes = undefined;
}
@ -292,27 +301,11 @@ $(function() {
self.versions.updateItems(versions);
var octoprint = data.information["octoprint"];
if (octoprint && octoprint.hasOwnProperty("check")) {
var check = octoprint.check;
if (check["released_version"] === false && check["type"] == "github_release") {
self.octoprintUnreleased(true);
} else {
self.octoprintUnreleased(false);
}
var checkoutFolder = (check["checkout_folder"] || "").trim();
var updateFolder = (check["update_folder"] || "").trim();
var needsFolder = check["update_script"] || false;
if (needsFolder && checkoutFolder == "" && updateFolder == "") {
self.octoprintUnconfigured(true);
} else {
self.octoprintUnconfigured(false);
}
}
self.octoprintReleasedVersion(!octoprint || octoprint.released_version);
if (!self.loginState.isAdmin() && !self.settings.settings.plugins.softwareupdate.notify_users()) return;
if (data.status == "updateAvailable" || data.status == "updatePossible") {
if (data.status === "updateAvailable" || data.status === "updatePossible") {
var text = "<div class='softwareupdate_notification'>" + gettext("There are updates available for the following components:");
text += "<ul class='fa-ul'>";
@ -343,7 +336,7 @@ $(function() {
var eventListeners = {};
var singleButtonNotify = false;
if (data.status == "updatePossible" && self.loginState.isAdmin()) {
if (data.status === "updatePossible" && self.loginState.isAdmin()) {
// if update is possible and user is admin, add action buttons for ignore and update
options["confirm"] = {
confirm: true,
@ -391,7 +384,7 @@ $(function() {
if ((ignoreSeen || !self._hasNotificationBeenSeen(data.information)) && !OctoPrint.coreui.wizardOpen) {
self._showPopup(options, eventListeners, singleButtonNotify);
}
} else if (data.status == "current") {
} else if (data.status === "current") {
if (showIfNothingNew) {
self._showPopup({
title: gettext("Everything is up-to-date"),
@ -473,7 +466,7 @@ $(function() {
if (!Modernizr.localstorage)
return false;
if (localStorage["plugin.softwareupdate.seen_information"] == undefined)
if (localStorage["plugin.softwareupdate.seen_information"] === undefined)
return false;
var knownData = JSON.parse(localStorage["plugin.softwareupdate.seen_information"]);
@ -489,7 +482,7 @@ $(function() {
var hasBeenSeen = true;
_.each(freshData, function(value, key) {
if (!_.has(userData, key) || userData[key] != freshData[key]) {
if (!_.has(userData, key) || userData[key] !== freshData[key]) {
hasBeenSeen = false;
}
});
@ -610,10 +603,10 @@ $(function() {
};
self.onBeforeWizardTabChange = function(next, current) {
if (next && _.startsWith(next, "wizard_plugin_softwareupdate")) {
if (next && next === "#wizard_plugin_softwareupdate") {
// switching to the plugin wizard tab
self._copyConfig();
} else if (current && _.startsWith(current, "wizard_plugin_softwareupdate")) {
} else if (current && current === "#wizard_plugin_softwareupdate") {
// switching away from the plugin wizard tab
self._copyConfigBack();
}
@ -658,7 +651,7 @@ $(function() {
};
self.onDataUpdaterPluginMessage = function(plugin, data) {
if (plugin != "softwareupdate") {
if (plugin !== "softwareupdate") {
return;
}
@ -749,7 +742,7 @@ $(function() {
restartType = messageData.restart_type;
text = gettext("The update finished successfully, please restart OctoPrint now.");
if (restartType == "environment") {
if (restartType === "environment") {
text = gettext("The update finished successfully, please reboot the server now.");
}
@ -770,7 +763,7 @@ $(function() {
case "restart_failed": {
restartType = messageData.restart_type;
text = gettext("Restarting OctoPrint failed, please restart it manually. You might also want to consult the log file on what went wrong here.");
if (restartType == "environment") {
if (restartType === "environment") {
text = gettext("Rebooting the server failed, please reboot it manually. You might also want to consult the log file on what went wrong here.");
}
@ -827,7 +820,7 @@ $(function() {
}
}
if (options != undefined) {
if (options !== undefined) {
self._showPopup(options);
}
};
@ -837,7 +830,7 @@ $(function() {
"'.*?' does not exist -- can't clean it"];
self._forcedStdoutLine = new RegExp(self._forcedStdoutPatterns.join("|"));
self._preprocessLine = function(line) {
if (line.stream == "stderr" && line.line.match(self._forcedStdoutLine)) {
if (line.stream === "stderr" && line.line.match(self._forcedStdoutLine)) {
line.stream = "stdout";
}
return line;

View file

@ -1,6 +1,7 @@
<div class="control-group" data-bind="visible: config_updateMethod() == 'update_script' || config_updateMethod() == 'python_updater'">
<div class="control-group" data-bind="visible: config_checkType() === 'git_commit', css: {error: error_checkoutFolder}">
<label class="control-label">{{ _('OctoPrint checkout folder') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: config_checkoutFolder">
<span class="help-block" data-bind="visible: error_checkoutFolder">{{ _('This needs to be set if you select commit based version tracking.') }}</span>
</div>
</div>

View file

@ -1,4 +1,4 @@
<div class="control-group" data-bind="visible: config_availableCheckTypes().length > 0 && config_checkType() == 'github_release'">
<div class="control-group" data-bind="visible: config_availableReleaseChannels().length && 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>

View file

@ -1,4 +1,4 @@
<div class="control-group" data-bind="visible: config_availableCheckTypes().length > 0">
<div class="control-group" data-bind="visible: config_availableCheckTypes().length > 1">
<label class="control-label">{{ _('OctoPrint version tracking') }}</label>
<div class="controls">
<select data-bind="value: config_checkType, options: config_availableCheckTypes, optionsText: 'name', optionsValue: 'key'"></select>

View file

@ -76,8 +76,8 @@
</div>
<div class="modal-body">
<form class="form-horizontal">
{% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %}
{% include "snippets/plugins/softwareupdate/versionTracking.jinja2" %}
{% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %}
{% include "snippets/plugins/softwareupdate/releaseChannel.jinja2" %}
<div class="control-group">
<label class="control-label">{{ _('Version cache TTL') }}</label>
@ -100,7 +100,7 @@
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Cancel') }}</button>
<button class="btn btn-primary" data-bind="click: savePluginSettings" aria-hidden="true">{{ _('Save') }}</button>
<button class="btn btn-primary" data-bind="enable: enable_configSave, css: {disabled: !enable_configSave()}, click: savePluginSettings" aria-hidden="true">{{ _('Save') }}</button>
</div>
</div>

View file

@ -1,13 +1,14 @@
<h3>{{ _('Software Update') }}</h3>
{% trans %}<p>
OctoPrint can update itself via <code>git</code>, but it needs to know its checkout folder and the way
it should track available updates in order to be able to do that. You can configure that here.
By default, OctoPrint will update itself via <code>pip</code> to published releases. OctoPrint can
also update itself via <code>git</code> to arbitrary development branches you need to check out manually.
If you want to do that though it needs to know its checkout folder. You can configure that here.
</p>{% endtrans %}
<form class="form-horizontal">
{% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %}
{% include "snippets/plugins/softwareupdate/versionTracking.jinja2" %}
{% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %}
</form>
{% trans %}<p>

View file

@ -15,6 +15,8 @@ from .. import exceptions
logger = logging.getLogger("octoprint.plugins.softwareupdate.updaters.pip")
console_logger = logging.getLogger("octoprint.plugins.softwareupdate.updaters.pip.console")
_ALREADY_INSTALLED = "Requirement already satisfied (use --upgrade to upgrade)"
_pip_callers = dict()
_pip_version_dependency_links = pkg_resources.parse_version("1.5")
@ -35,7 +37,7 @@ def _get_pip_caller(command=None):
return _pip_callers[key]
def perform_update(target, check, target_version, log_cb=None, online=True):
def perform_update(target, check, target_version, log_cb=None, online=True, force=False):
pip_command = None
if "pip_command" in check:
pip_command = check["pip_command"]
@ -66,10 +68,10 @@ def perform_update(target, check, target_version, log_cb=None, online=True):
pip_caller.on_log_stdout = _log_stdout
pip_caller.on_log_stderr = _log_stderr
install_arg = check["pip"].format(target_version=target_version)
install_arg = check["pip"].format(target_version=target_version, target=target_version)
logger.debug(u"Target: %s, executing pip install %s" % (target, install_arg))
pip_args = ["install", check["pip"].format(target_version=target_version, target=target_version)]
pip_args = ["install", install_arg]
if "dependency_links" in check and check["dependency_links"]:
pip_args += ["--process-dependency-links"]
@ -77,12 +79,17 @@ def perform_update(target, check, target_version, log_cb=None, online=True):
returncode, stdout, stderr = pip_caller.execute(*pip_args)
if returncode != 0:
raise exceptions.UpdateError("Error while executing pip install", (stdout, stderr))
if not force and any(map(lambda x: x.strip().startswith(_ALREADY_INSTALLED) and (install_arg in x or install_arg in x.lower()), stdout)):
logger.debug(u"Looks like we were already installed in this version. Forcing a reinstall.")
force = True
logger.debug(u"Target: %s, executing pip install %s --ignore-reinstalled --force-reinstall --no-deps" % (target, install_arg))
pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
returncode, stdout, stderr = pip_caller.execute(*pip_args)
if returncode != 0:
raise exceptions.UpdateError("Error while executing pip install --force-reinstall", (stdout, stderr))
if force:
logger.debug(u"Target: %s, executing pip install %s --ignore-reinstalled --force-reinstall --no-deps" % (target, install_arg))
pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
returncode, stdout, stderr = pip_caller.execute(*pip_args)
if returncode != 0:
raise exceptions.UpdateError("Error while executing pip install --force-reinstall", (stdout, stderr))
return "ok"

View file

@ -34,6 +34,9 @@ $(function() {
}
callViewModels(self.allViewModels, "onWizardShow");
callViewModels(self.allViewModels, "onBeforeWizardTabChange", [OCTOPRINT_INITIAL_WIZARD, undefined]);
callViewModels(self.allViewModels, "onAfterWizardTabChange", [OCTOPRINT_INITIAL_WIZARD]);
});
};

View file

@ -44,4 +44,12 @@
{% else %}
var OCTOPRINT_INITIAL_TAB = undefined;
{% endif %}
{% if templates.wizard and templates.wizard.order %}
{% set first_tab = templates.wizard.order[0] %}
{% set entry, data = templates.wizard.entries[first_tab] %}
var OCTOPRINT_INITIAL_WIZARD = "#{{ data._div }}";
{% else %}
var OCTOPRINT_INITIAL_WIZARD = undefined;
{% endif %}
</script>