From 79581c390a6372aa437d2cee88f2b5c821fd409a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 2 Nov 2017 11:03:12 +0100 Subject: [PATCH] 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. --- docs/bundledplugins/softwareupdate.rst | 75 +++++++------ ...ns-softwareupdate-plugin-configuration.png | Bin 29121 -> 24456 bytes .../plugins/softwareupdate/__init__.py | 104 ++++++++++++------ .../static/js/softwareupdate.js | 91 +++++++-------- .../softwareupdate/checkoutFolder.jinja2 | 3 +- .../softwareupdate/releaseChannel.jinja2 | 2 +- .../softwareupdate/versionTracking.jinja2 | 2 +- .../templates/softwareupdate_settings.jinja2 | 4 +- .../templates/softwareupdate_wizard.jinja2 | 7 +- .../plugins/softwareupdate/updaters/pip.py | 25 +++-- .../static/js/app/viewmodels/wizard.js | 3 + src/octoprint/templates/initscript.jinja2 | 8 ++ 12 files changed, 192 insertions(+), 132 deletions(-) diff --git a/docs/bundledplugins/softwareupdate.rst b/docs/bundledplugins/softwareupdate.rst index 763a0de5..904a3922 100644 --- a/docs/bundledplugins/softwareupdate.rst +++ b/docs/bundledplugins/softwareupdate.rst @@ -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 `_). - 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 `_. - 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 `_. + * **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 `_). + 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 `. 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 diff --git a/docs/images/bundledplugins-softwareupdate-plugin-configuration.png b/docs/images/bundledplugins-softwareupdate-plugin-configuration.png index 7d5a672cb3606f9ed8473a67860af9f78a972f27..ba13a99738d4c8449c8194a7a1e4c2e656406251 100644 GIT binary patch literal 24456 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 Kp~lWX`M&`z9wq_+ literal 29121 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 z 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") diff --git a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js index 9b6b1f3e..8b5a3c87 100644 --- a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js +++ b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js @@ -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 = "
" + gettext("There are updates available for the following components:"); text += "
    "; @@ -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; diff --git a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/checkoutFolder.jinja2 b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/checkoutFolder.jinja2 index b45c8655..906cc227 100644 --- a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/checkoutFolder.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/checkoutFolder.jinja2 @@ -1,6 +1,7 @@ -
    +
    + {{ _('This needs to be set if you select commit based version tracking.') }}
    diff --git a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 index 6133e9b5..57ec4151 100644 --- a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/releaseChannel.jinja2 @@ -1,4 +1,4 @@ -
    +
    diff --git a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2 b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2 index d53d21a6..a57a81a7 100644 --- a/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/snippets/plugins/softwareupdate/versionTracking.jinja2 @@ -1,4 +1,4 @@ -
    +
    diff --git a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 index aa6c9e50..a8a60fed 100644 --- a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2 @@ -76,8 +76,8 @@
    diff --git a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2 b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2 index d840a292..eabb1cef 100644 --- a/src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2 +++ b/src/octoprint/plugins/softwareupdate/templates/softwareupdate_wizard.jinja2 @@ -1,13 +1,14 @@

    {{ _('Software Update') }}

    {% trans %}

    - OctoPrint can update itself via git, 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 pip to published releases. OctoPrint can + also update itself via git 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.

    {% endtrans %} - {% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %} {% include "snippets/plugins/softwareupdate/versionTracking.jinja2" %} + {% include "snippets/plugins/softwareupdate/checkoutFolder.jinja2" %} {% trans %}

    diff --git a/src/octoprint/plugins/softwareupdate/updaters/pip.py b/src/octoprint/plugins/softwareupdate/updaters/pip.py index 3c0e9022..8605e2b3 100644 --- a/src/octoprint/plugins/softwareupdate/updaters/pip.py +++ b/src/octoprint/plugins/softwareupdate/updaters/pip.py @@ -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" diff --git a/src/octoprint/static/js/app/viewmodels/wizard.js b/src/octoprint/static/js/app/viewmodels/wizard.js index fbc2e37b..72c965ed 100644 --- a/src/octoprint/static/js/app/viewmodels/wizard.js +++ b/src/octoprint/static/js/app/viewmodels/wizard.js @@ -34,6 +34,9 @@ $(function() { } callViewModels(self.allViewModels, "onWizardShow"); + + callViewModels(self.allViewModels, "onBeforeWizardTabChange", [OCTOPRINT_INITIAL_WIZARD, undefined]); + callViewModels(self.allViewModels, "onAfterWizardTabChange", [OCTOPRINT_INITIAL_WIZARD]); }); }; diff --git a/src/octoprint/templates/initscript.jinja2 b/src/octoprint/templates/initscript.jinja2 index daacfde4..722830bf 100644 --- a/src/octoprint/templates/initscript.jinja2 +++ b/src/octoprint/templates/initscript.jinja2 @@ -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 %}