diff --git a/README.md b/README.md index 06d5b6b0..4d5a9ba0 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,25 @@ For information about how to go about contributions of any kind, please see the Installation ------------ -Installation instructions for installing from source for different operating systems can be found [on the wiki](https://github.com/foosel/OctoPrint/wiki#assorted-guides). +Installation instructions for installing from source for different operating +systems can be found [on the wiki](https://github.com/foosel/OctoPrint/wiki#assorted-guides). If you want to run OctoPrint on a Raspberry Pi you might want to take a look at [OctoPi](https://github.com/guysoft/OctoPi) which is a custom SD card image that includes OctoPrint plus dependencies. +The generic steps that should basically be done regardless of operating system +and runtime environment are the following (as *regular +user*, please keep your hands *off* of the `sudo` command here!) - this assumes +you already have Python 2.7, pip and virtualenv set up: + +1. Checkout OctoPrint: `git clone https://github.com/foosel/OctoPrint.git` +2. Change into the OctoPrint folder: `cd OctoPrint` +3. Create a user-owned virtual environment therein: `virtualenv --system-site-packages venv` +4. Install OctoPrint *into that virtual environment*: `./venv/bin/python setup.py install` + +You may then start the OctoPrint server via `/path/to/OctoPrint/venv/bin/octoprint`, see [Usage](#usage) +for details. + After installation, please make sure you follow the first-run wizard and set up access control as necessary. If you want to not only be notified about new releases but also be able to automatically upgrade to them from within diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 8390cd8e..a92fa91c 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -82,6 +82,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, repository="http://plugins.octoprint.org/plugins.json", repository_ttl=24*60, pip=None, + pip_args=None, dependency_links=False, hidden=[] ) @@ -187,7 +188,9 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, pip=dict( available=self._pip_caller.available, command=self._pip_caller.command, - version=str(self._pip_caller.version) + version=str(self._pip_caller.version), + use_sudo=self._pip_caller.use_sudo, + additional_args=self._settings.get(["pip_args"]) )) def on_api_command(self, command, data): @@ -447,6 +450,10 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, if self._pip_caller < self._pip_version_dependency_links: args.remove("--process-dependency-links") + additional_args = self._settings.get(["pip_args"]) + if additional_args: + args.append(additional_args) + return self._pip_caller.execute(*args) def _log_message(self, *lines): diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index 35a634d8..75c8e2f3 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -9,6 +9,7 @@ $(function() { self.config_repositoryUrl = ko.observable(); self.config_repositoryTtl = ko.observable(); self.config_pipCommand = ko.observable(); + self.config_pipAdditionalArgs = ko.observable(); self.configurationDialog = $("#settings_plugin_pluginmanager_configurationdialog"); @@ -81,6 +82,8 @@ $(function() { self.pipAvailable = ko.observable(false); self.pipCommand = ko.observable(); self.pipVersion = ko.observable(); + self.pipUseSudo = ko.observable(); + self.pipAdditionalArgs = ko.observable(); self.working = ko.observable(false); self.workingTitle = ko.observable(); @@ -208,9 +211,13 @@ $(function() { if (data.available) { self.pipCommand(data.command); self.pipVersion(data.version); + self.pipUseSudo(data.use_sudo); + self.pipAdditionalArgs(data.additional_args); } else { self.pipCommand(undefined); self.pipVersion(undefined); + self.pipUseSudo(undefined); + self.pipAdditionalArgs(undefined); } }; @@ -392,12 +399,18 @@ $(function() { repositoryTtl = null; } + var pipArgs = self.config_pipAdditionalArgs(); + if (pipArgs != undefined && pipArgs.trim() == "") { + pipArgs = null; + } + var data = { plugins: { pluginmanager: { repository: repository, repository_ttl: repositoryTtl, - pip: pipCommand + pip: pipCommand, + pip_args: pipArgs } } }; @@ -412,6 +425,7 @@ $(function() { self.config_repositoryUrl(self.settingsViewModel.settings.plugins.pluginmanager.repository()); self.config_repositoryTtl(self.settingsViewModel.settings.plugins.pluginmanager.repository_ttl()); self.config_pipCommand(self.settingsViewModel.settings.plugins.pluginmanager.pip()); + self.config_pipAdditionalArgs(self.settingsViewModel.settings.plugins.pluginmanager.pip_args()); }; self.installed = function(data) { diff --git a/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 b/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 index 7c00534a..d399b593 100644 --- a/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 +++ b/src/octoprint/plugins/pluginmanager/templates/pluginmanager_settings.jinja2 @@ -6,14 +6,25 @@ {% macro pluginmanager_nopip() %}
{% trans %} - The pip command could not be detected automatically, - please configure it manually. No installation and uninstallation of plugin + The pip command could not be found. + Please configure it manually. No installation and uninstallation of plugin packages is possible while pip is unavailable. {% endtrans %}
{% endmacro %} +{% macro pluginmanager_sudopip() %} +
{% trans %} + The pip command is configured to use sudo. This + is not recommended due to security reasons. It is strongly + suggested you install OctoPrint under a + user-owned virtual environment + so that the use of sudo is not needed for plugin management. +{% endtrans %}
+{% endmacro %} + {{ pluginmanager_printing() }} {{ pluginmanager_nopip() }} +{{ pluginmanager_sudopip() }}
@@ -61,7 +72,7 @@

- Using pip at "" (Version ) + Using pip at "" (Version , additional arguments: )

+
+ +
+ +
+
diff --git a/src/octoprint/util/pip.py b/src/octoprint/util/pip.py index 7fa3dbc3..11c6f118 100644 --- a/src/octoprint/util/pip.py +++ b/src/octoprint/util/pip.py @@ -26,6 +26,7 @@ class PipCaller(object): self._command = None self._version = None + self._use_sudo = False self.trigger_refresh() @@ -53,13 +54,17 @@ class PipCaller(object): def version(self): return self._version + @property + def use_sudo(self): + return self._use_sudo + @property def available(self): return self._command is not None def trigger_refresh(self): try: - self._command, self._version = self._find_pip() + self._command, self._version, self._use_sudo = self._find_pip() except: self._logger.exception("Error while discovering pip command") self._command = None @@ -74,6 +79,8 @@ class PipCaller(object): raise UnknownPip() command = [self._command] + list(args) + if self._use_sudo: + command = ["sudo"] + command joined_command = " ".join(command) self._logger.debug(u"Calling: {}".format(joined_command)) @@ -120,6 +127,11 @@ class PipCaller(object): def _find_pip(self): pip_command = self.configured + if pip_command is not None and pip_command.startswith("sudo "): + pip_command = pip_command[len("sudo "):] + pip_sudo = True + else: + pip_sudo = False pip_version = None if pip_command is None: @@ -152,7 +164,11 @@ class PipCaller(object): if pip_command is not None: self._logger.debug("Found pip at {}, going to figure out its version".format(pip_command)) - p = sarge.run([pip_command, "--version"], stdout=sarge.Capture(), stderr=sarge.Capture()) + + sarge_command = [pip_command, "--version"] + if pip_sudo: + sarge_command = ["sudo"] + sarge_command + p = sarge.run(sarge_command, stdout=sarge.Capture(), stderr=sarge.Capture()) if p.returncode != 0: self._logger.warn("Error while trying to run pip --version: {}".format(p.stderr.text)) @@ -182,7 +198,7 @@ class PipCaller(object): else: self._logger.info("Found pip at {}, version is {}".format(pip_command, version_segment)) - return pip_command, pip_version + return pip_command, pip_version, pip_sudo def _log_stdout(self, *lines): self.on_log_stdout(*lines)