Permalink
Browse files

Improved shed linting and type default.

No .shed.yml fields are required anymore, but when present more are validated more carefully.

 - Validate owner string matches validation inforced by Tool Shed.
 - Validate name string matches validation inforced by Tool Shed.
 - Validate type (if specified) is valid.
 - Validate name matches type conventions.

Use new validation to more confidently select a default type based on name during creation.
  • Loading branch information...
jmchilton committed Apr 22, 2015
1 parent 1346ba0 commit 36ac6d8e6eaad03bbb32e6de94e629733c0ec074
Showing with 74 additions and 8 deletions.
  1. +8 −1 planemo/shed.py
  2. +66 −7 planemo/shed_lint.py
@@ -128,7 +128,7 @@ def create_repository(ctx, tsi, path, **kwds):

description = repo_config.get("description", None)
long_description = repo_config.get("long_description", None)
type = repo_config.get("type", "unrestricted")
type = repo_config.get("type", None)
remote_repository_url = repo_config.get("remote_repository_url", None)
homepage_url = repo_config.get("homepage_url", None)
categories = repo_config.get("categories", [])
@@ -137,6 +137,13 @@ def create_repository(ctx, tsi, path, **kwds):
if name is None:
name = os.path.basename(os.path.abspath(path))

if type is None and name.startswith("package_"):
type = "tool_dependency_definition"
elif type is None and name.startswith("suite_"):
type = "repository_suite_definition"
elif type is None:
type = "unrestricted"

# description is required, as is name.
if description is None:
message = "description required for automatic creation of repositories"
@@ -1,4 +1,5 @@
import os
import re
import yaml
from galaxy.tools.lint import LintContext
from planemo.lint import lint_xsd
@@ -17,6 +18,16 @@
TOOL_DEPENDENCIES_XSD = os.path.join(XSDS_PATH, "tool_dependencies.xsd")
REPO_DEPENDENCIES_XSD = os.path.join(XSDS_PATH, "repository_dependencies.xsd")

# TODO: sync this with tool shed impl someday
VALID_REPOSITORYNAME_RE = re.compile("^[a-z0-9\_]+$")
VALID_PUBLICNAME_RE = re.compile("^[a-z0-9\-]+$")

VALID_REPOSITORY_TYPES = [
"unrestricted",
"tool_dependency_definition",
"repository_suite_definition",
]


def lint_repository(ctx, path, **kwds):
info("Linting repository %s" % path)
@@ -76,12 +87,60 @@ def lint_shed_yaml(path, lint_ctx):
shed_contents = yaml.load(open(shed_yaml, "r"))
except Exception as e:
lint_ctx.warn("Failed to parse .shed.yml file [%s]" % str(e))
lint_ctx.info(".shed.yml found and appears to be valid YAML.")
_lint_shed_contents(lint_ctx, path, shed_contents)


def _lint_shed_contents(lint_ctx, path, shed_contents):
def _lint_if_present(key, func, *args):
value = shed_contents.get(key, None)
if value is not None:
msg = func(value, *args)
if msg:
lint_ctx.warn(msg)

_lint_if_present("owner", _validate_repo_owner)
_lint_if_present("name", _validate_repo_name)
effective_name = shed_contents.get("name", None) or os.path.basename(path)
_lint_if_present("type", _validate_repo_type, effective_name)


def _validate_repo_type(repo_type, name):
if repo_type not in VALID_REPOSITORY_TYPES:
return "Invalid repository type specified [%s]" % repo_type

is_dep = repo_type == "tool_dependency_definition"
is_suite = repo_type == "repository_suite_definition"
if is_dep and not name.startswith("package_"):
return ("Tool dependency definition repositories should have names "
"starting with package_")
if is_suite and not name.startswith("suite_"):
return ("Repository suite definition repositories should have names "
"starting with suite_")
if name.startswith("package_") or name.startswith("suite_"):
if repo_type == "unrestricted":
return ("Repository name indicated specialized repository type "
"but repository is listed as unrestricted.")


def _validate_repo_name(name):
msg = None
if len(name) < 2:
msg = "Repository names must be at least 2 characters in length."
if len(name) > 80:
msg = "Repository names cannot be more than 80 characters in length."
if not(VALID_REPOSITORYNAME_RE.match(name)):
msg = ("Repository names must contain only lower-case letters, "
"numbers and underscore.")
return msg

warned = False
for required_key in ["owner", "name"]:
if required_key not in shed_contents:
lint_ctx.warn(".shed.yml did not contain key [%s]" % required_key)
warned = True

if not warned:
lint_ctx.info(".shed.yml found and appears to be valid YAML.")
def _validate_repo_owner(owner):
msg = None
if len(owner) < 3:
msg = "Owner must be at least 3 characters in length"
if len(owner) > 255:
msg = "Owner cannot be more than 255 characters in length"
if not(VALID_PUBLICNAME_RE.match(owner)):
msg = "Owner must contain only lower-case letters, numbers and '-'"
return msg

0 comments on commit 36ac6d8

Please sign in to comment.