Skip to content

Commit 36ac6d8

Browse files
committed
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.
1 parent 1346ba0 commit 36ac6d8

File tree

2 files changed

+74
-8
lines changed

2 files changed

+74
-8
lines changed

planemo/shed.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def create_repository(ctx, tsi, path, **kwds):
128128

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

140+
if type is None and name.startswith("package_"):
141+
type = "tool_dependency_definition"
142+
elif type is None and name.startswith("suite_"):
143+
type = "repository_suite_definition"
144+
elif type is None:
145+
type = "unrestricted"
146+
140147
# description is required, as is name.
141148
if description is None:
142149
message = "description required for automatic creation of repositories"

planemo/shed_lint.py

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import yaml
34
from galaxy.tools.lint import LintContext
45
from planemo.lint import lint_xsd
@@ -17,6 +18,16 @@
1718
TOOL_DEPENDENCIES_XSD = os.path.join(XSDS_PATH, "tool_dependencies.xsd")
1819
REPO_DEPENDENCIES_XSD = os.path.join(XSDS_PATH, "repository_dependencies.xsd")
1920

21+
# TODO: sync this with tool shed impl someday
22+
VALID_REPOSITORYNAME_RE = re.compile("^[a-z0-9\_]+$")
23+
VALID_PUBLICNAME_RE = re.compile("^[a-z0-9\-]+$")
24+
25+
VALID_REPOSITORY_TYPES = [
26+
"unrestricted",
27+
"tool_dependency_definition",
28+
"repository_suite_definition",
29+
]
30+
2031

2132
def lint_repository(ctx, path, **kwds):
2233
info("Linting repository %s" % path)
@@ -76,12 +87,60 @@ def lint_shed_yaml(path, lint_ctx):
7687
shed_contents = yaml.load(open(shed_yaml, "r"))
7788
except Exception as e:
7889
lint_ctx.warn("Failed to parse .shed.yml file [%s]" % str(e))
90+
lint_ctx.info(".shed.yml found and appears to be valid YAML.")
91+
_lint_shed_contents(lint_ctx, path, shed_contents)
92+
93+
94+
def _lint_shed_contents(lint_ctx, path, shed_contents):
95+
def _lint_if_present(key, func, *args):
96+
value = shed_contents.get(key, None)
97+
if value is not None:
98+
msg = func(value, *args)
99+
if msg:
100+
lint_ctx.warn(msg)
101+
102+
_lint_if_present("owner", _validate_repo_owner)
103+
_lint_if_present("name", _validate_repo_name)
104+
effective_name = shed_contents.get("name", None) or os.path.basename(path)
105+
_lint_if_present("type", _validate_repo_type, effective_name)
106+
107+
108+
def _validate_repo_type(repo_type, name):
109+
if repo_type not in VALID_REPOSITORY_TYPES:
110+
return "Invalid repository type specified [%s]" % repo_type
111+
112+
is_dep = repo_type == "tool_dependency_definition"
113+
is_suite = repo_type == "repository_suite_definition"
114+
if is_dep and not name.startswith("package_"):
115+
return ("Tool dependency definition repositories should have names "
116+
"starting with package_")
117+
if is_suite and not name.startswith("suite_"):
118+
return ("Repository suite definition repositories should have names "
119+
"starting with suite_")
120+
if name.startswith("package_") or name.startswith("suite_"):
121+
if repo_type == "unrestricted":
122+
return ("Repository name indicated specialized repository type "
123+
"but repository is listed as unrestricted.")
124+
125+
126+
def _validate_repo_name(name):
127+
msg = None
128+
if len(name) < 2:
129+
msg = "Repository names must be at least 2 characters in length."
130+
if len(name) > 80:
131+
msg = "Repository names cannot be more than 80 characters in length."
132+
if not(VALID_REPOSITORYNAME_RE.match(name)):
133+
msg = ("Repository names must contain only lower-case letters, "
134+
"numbers and underscore.")
135+
return msg
79136

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

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

0 commit comments

Comments
 (0)