Support sudo for installing plugins, but warn about it
This commit is contained in:
parent
2f24e73fcb
commit
1178fe9e95
5 changed files with 78 additions and 9 deletions
16
README.md
16
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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,25 @@
|
|||
|
||||
{% macro pluginmanager_nopip() %}
|
||||
<div class="alert" data-bind="visible: !pipAvailable()">{% trans %}
|
||||
The <code>pip</code> command could not be detected automatically,
|
||||
please configure it manually. No installation and uninstallation of plugin
|
||||
The <code>pip</code> command could not be found.
|
||||
Please configure it manually. No installation and uninstallation of plugin
|
||||
packages is possible while <code>pip</code> is unavailable.
|
||||
{% endtrans %}</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro pluginmanager_sudopip() %}
|
||||
<div class="alert alert-error" data-bind="visible: pipUseSudo()">{% trans %}
|
||||
The <code>pip</code> command is configured to use <code>sudo</code>. This
|
||||
is <strong>not</strong> recommended due to security reasons. It is <strong>strongly</strong>
|
||||
suggested you install OctoPrint under a
|
||||
<a href="https://github.com/foosel/OctoPrint/#installation">user-owned virtual environment</a>
|
||||
so that the use of <code>sudo</code> is not needed for plugin management.
|
||||
{% endtrans %}</div>
|
||||
{% endmacro %}
|
||||
|
||||
{{ pluginmanager_printing() }}
|
||||
{{ pluginmanager_nopip() }}
|
||||
{{ pluginmanager_sudopip() }}
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-small" data-bind="click: function() { $root.showPluginSettings(); }" title="{{ _('Plugin Configuration') }}"><i class="icon-wrench"></i></button>
|
||||
|
|
@ -61,7 +72,7 @@
|
|||
<button class="btn btn-block" data-bind="click: $root.showRepository">{{ _('Get More...') }}</button>
|
||||
|
||||
<p class="muted" data-bind="visible: pipAvailable()">
|
||||
<small>Using pip at "<span data-bind="text: pipCommand"></span>" (Version <span data-bind="text: pipVersion"></span>)</small>
|
||||
<small>Using pip at "<span data-bind="text: pipCommand"></span>" (Version <span data-bind="text: pipVersion"></span><span data-bind="visible: pipAdditionalArgs">, additional arguments: <span data-bind="text: pipAdditionalArgs"></span></span>)</small>
|
||||
</p>
|
||||
|
||||
<div id="settings_plugin_pluginmanager_workingdialog" class="modal hide fade">
|
||||
|
|
@ -85,6 +96,7 @@
|
|||
<div class="modal-body">
|
||||
{{ pluginmanager_printing() }}
|
||||
{{ pluginmanager_nopip() }}
|
||||
{{ pluginmanager_sudopip() }}
|
||||
<h4 style="position: relative">
|
||||
{{ _('... from the <a href="%(url)s" target="_blank">Plugin Repository</a>', url='http://plugins.octoprint.org') }}
|
||||
<a class="dropdown-toggle pull-right" data-toggle="dropdown" href="#">
|
||||
|
|
@ -200,6 +212,12 @@
|
|||
<span class="help-inline">{{ _('<strong>Only</strong> set this if OctoPrint cannot autodetect the path to <code>pip</code> to use for managing plugins.') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" title="{{ _('Additional arguments for pip command. You should normally not have to change this.') }}">
|
||||
<label class="control-label">{{ _('Additional pip arguments') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" class="input-block-level" data-bind="value: config_pipAdditionalArgs">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" title="{{ _('URL of the Plugin Repository to use. You should normally not have to change this.') }}">
|
||||
<label class="control-label">{{ _('Repository URL') }}</label>
|
||||
<div class="controls">
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue