Skip to content

Commit

Permalink
PMGR: Support VCS URLs to install from
Browse files Browse the repository at this point in the history
Implements #2104
  • Loading branch information
foosel committed Oct 2, 2017
1 parent 0f8f457 commit c5e7212
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
10 changes: 10 additions & 0 deletions src/octoprint/plugins/pluginmanager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
octoprint.plugin.EventHandlerPlugin):

ARCHIVE_EXTENSIONS = (".zip", ".tar.gz", ".tgz", ".tar")

# valid pip install URL schemes according to https://pip.pypa.io/en/stable/reference/pip_install/
URL_SCHEMES = ("http", "https", "git",
"git+http", "git+https", "git+ssh", "git+git",
"hg+http", "hg+https", "hg+static-http", "hg+ssh",
"svn", "svn+svn", "svn+http", "svn+https", "svn+ssh",
"bzr+http", "bzr+https", "bzr+ssh", "bzr+sftp", "bzr+ftp", "bzr+lp")

OPERATING_SYSTEMS = dict(windows=["win32"],
linux=lambda x: x.startswith("linux"),
Expand Down Expand Up @@ -299,6 +306,9 @@ def on_api_command(self, command, data):

def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False):
if url is not None:
if not any(map(lambda scheme: url.starts_with(scheme + "://"), self.URL_SCHEMES)):
raise ValueError("Invalid URL to pip install from")

source = url
source_type = "url"
already_installed_check = lambda line: url in line
Expand Down
42 changes: 38 additions & 4 deletions src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,23 +239,57 @@ $(function() {
};

self.invalidUrl = ko.pureComputed(function() {
// supported pip install URL schemes, according to https://pip.pypa.io/en/stable/reference/pip_install/
var allowedUrlSchemes = ["http", "https",
"git", "git+http", "git+https", "git+ssh", "git+git",
"hg+http", "hg+https", "hg+static-http", "hg+ssh",
"svn", "svn+svn", "svn+http", "svn+https", "svn+ssh",
"bzr+http", "bzr+https", "bzr+ssh", "bzr+sftp", "brz+ftp", "bzr+lp"];

var url = self.installUrl();
return url !== undefined && url.trim() != "" && !(_.startsWith(url.toLocaleLowerCase(), "http://") || _.startsWith(url.toLocaleLowerCase(), "https://"));
var lowerUrl = url !== undefined ? url.toLocaleLowerCase() : undefined;

var lowerUrlStartsWithScheme = function(scheme) {
return _.startsWith(lowerUrl, scheme + "://");
};

return url !== undefined && url.trim() !== ""
&& !(_.any(allowedUrlSchemes, lowerUrlStartsWithScheme));
});

self.enableUrlInstall = ko.pureComputed(function() {
var url = self.installUrl();
return self.enableManagement() && self.pipAvailable() && !self.safeMode() && self.online() && url !== undefined && url.trim() != "" && !self.invalidUrl();
return self.enableManagement()
&& self.pipAvailable()
&& !self.safeMode()
&& self.online()
&& url !== undefined
&& url.trim() !== ""
&& !self.invalidUrl();
});

self.invalidArchive = ko.pureComputed(function() {
var allowedArchiveExtensions = [".zip", ".tar.gz", ".tgz", ".tar"];

var name = self.uploadFilename();
return name !== undefined && !(_.endsWith(name.toLocaleLowerCase(), ".zip") || _.endsWith(name.toLocaleLowerCase(), ".tar.gz") || _.endsWith(name.toLocaleLowerCase(), ".tgz") || _.endsWith(name.toLocaleLowerCase(), ".tar"));
var lowerName = name !== undefined ? name.toLocaleLowerCase() : undefined;

var lowerNameHasExtension = function(extension) {
return _.endsWith(lowerName, extension);
};

return name !== undefined
&& !(_.any(allowedArchiveExtensions, lowerNameHasExtension));
});

self.enableArchiveInstall = ko.pureComputed(function() {
var name = self.uploadFilename();
return self.enableManagement() && self.pipAvailable() && !self.safeMode() && name !== undefined && name.trim() != "" && !self.invalidArchive();
return self.enableManagement()
&& self.pipAvailable()
&& !self.safeMode()
&& name !== undefined
&& name.trim() !== ""
&& !self.invalidArchive();
});

self.uploadElement.fileupload({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
</div>
<button class="btn btn-primary span3" data-bind="enable: enableUrlInstall, css: {disabled: !enableUrlInstall()}, click: function() { if (enableUrlInstall()) { $root.installPlugin(); } }">{{ _('Install') }}</button>
</div>
<span class="help-block" data-bind="visible: invalidUrl">{{ _('This does not look like a valid "http://" or "https://" URL.') }}</span>
<span class="help-block" data-bind="visible: invalidUrl">{{ _('This does not look like a valid URL. Expected http, https or any of the <a href="%(url)s" target="_blank">supported VCS URLs</a>.', url='https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support') }}</span>
</form>

<h4>{{ _('... from an uploaded archive') }}</h4>
Expand Down

0 comments on commit c5e7212

Please sign in to comment.