Skip to content

Commit df60f0c

Browse files
authored
Merge pull request #2190 from mashehu/add-yaml-schema
add schema based validation for meta.ymls to linting
2 parents 9e7f50e + 5e2de35 commit df60f0c

File tree

8 files changed

+58
-29
lines changed

8 files changed

+58
-29
lines changed

nf_core/module-template/modules/meta.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
---
2+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json
13
name: "{{ component_name_underscore }}"
24
{% if not_empty_template -%}
35
## TODO nf-core: Add a description of the module and list keywords
46
{% endif -%}
57
description: write your description here
68
keywords:
79
- sort
10+
- example
11+
- genomics
812
tools:
913
- "{{ component }}":
1014
{% if not_empty_template -%}
@@ -32,9 +36,9 @@ input:
3236
## TODO nf-core: Delete / customise this example input
3337
{%- endif %}
3438
- {{ 'bam:' if not_empty_template else "input:" }}
35-
type: file
36-
description: {{ 'Sorted BAM/CRAM/SAM file' if not_empty_template else "" }}
37-
pattern: {{ '"*.{bam,cram,sam}"' if not_empty_template else "" }}
39+
type: file
40+
description: {{ 'Sorted BAM/CRAM/SAM file' if not_empty_template else "" }}
41+
pattern: {{ '"*.{bam,cram,sam}"' if not_empty_template else "" }}
3842

3943
{% if not_empty_template -%}
4044
## TODO nf-core: Add a description of all of the variables used as output
@@ -55,9 +59,9 @@ output:
5559
## TODO nf-core: Delete / customise this example output
5660
{%- endif %}
5761
- {{ 'bam:' if not_empty_template else "output:" }}
58-
type: file
59-
description: {{ 'Sorted BAM/CRAM/SAM file' if not_empty_template else "" }}
60-
pattern: {{ '"*.{bam,cram,sam}"' if not_empty_template else "" }}
62+
type: file
63+
description: {{ 'Sorted BAM/CRAM/SAM file' if not_empty_template else "" }}
64+
pattern: {{ '"*.{bam,cram,sam}"' if not_empty_template else "" }}
6165

6266
authors:
6367
- "{{ author }}"

nf_core/modules/lint/meta_yml.py

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import json
12
from pathlib import Path
23

4+
import jsonschema.validators
35
import yaml
46

57
from nf_core.modules.modules_differ import ModulesDiffer
@@ -10,17 +12,15 @@ def meta_yml(module_lint_object, module):
1012
Lint a ``meta.yml`` file
1113
1214
The lint test checks that the module has
13-
a ``meta.yml`` file and that it contains
14-
the required keys: ``name``, input`` and
15-
``output``.
15+
a ``meta.yml`` file and that it follows the
16+
JSON schema defined in the ``modules/yaml-schema.json``
17+
file in the nf-core/modules repository.
1618
1719
In addition it checks that the module name
1820
and module input is consistent between the
1921
``meta.yml`` and the ``main.nf``.
2022
2123
"""
22-
required_keys = ["name", "output"]
23-
required_keys_lists = ["input", "output"]
2424
# Check if we have a patch file, get original file in that case
2525
meta_yaml = None
2626
if module.is_patched:
@@ -42,21 +42,31 @@ def meta_yml(module_lint_object, module):
4242
module.failed.append(("meta_yml_exists", "Module `meta.yml` does not exist", module.meta_yml))
4343
return
4444

45-
# Confirm that all required keys are given
46-
contains_required_keys = True
47-
all_list_children = True
48-
for rk in required_keys:
49-
if rk not in meta_yaml.keys():
50-
module.failed.append(("meta_required_keys", f"`{rk}` not specified in YAML", module.meta_yml))
51-
contains_required_keys = False
52-
elif rk in meta_yaml.keys() and not isinstance(meta_yaml[rk], list) and rk in required_keys_lists:
53-
module.failed.append(("meta_required_keys", f"`{rk}` is not a list", module.meta_yml))
54-
all_list_children = False
55-
if contains_required_keys:
56-
module.passed.append(("meta_required_keys", "`meta.yml` contains all required keys", module.meta_yml))
45+
# Confirm that the meta.yml file is valid according to the JSON schema
46+
valid_meta_yml = True
47+
try:
48+
with open(Path(module_lint_object.modules_repo.local_repo_dir, "modules/yaml-schema.json"), "r") as fh:
49+
schema = json.load(fh)
50+
jsonschema.validators.validate(instance=meta_yaml, schema=schema)
51+
module.passed.append(("meta_yml_valid", "Module `meta.yml` is valid", module.meta_yml))
52+
except jsonschema.exceptions.ValidationError as e:
53+
valid_meta_yml = False
54+
hint = ""
55+
if len(e.path) > 0:
56+
hint = f"\nCheck the entry for `{e.path[0]}`."
57+
if e.message.startswith("None is not of type 'object'") and len(e.path) > 2:
58+
hint = f"\nCheck that the child entries of {e.path[0]+'.'+e.path[2]} are indented correctly."
59+
module.failed.append(
60+
(
61+
"meta_yml_valid",
62+
f"The `meta.yml` of the module {module.module_name} is not valid: {e.message}.{hint}",
63+
module.meta_yml,
64+
)
65+
)
66+
return
5767

5868
# Confirm that all input and output channels are specified
59-
if contains_required_keys and all_list_children:
69+
if valid_meta_yml:
6070
if "input" in meta_yaml:
6171
meta_input = [list(x.keys())[0] for x in meta_yaml["input"]]
6272
for input in module.inputs:

nf_core/pipeline-template/modules/nf-core/custom/dumpsoftwareversions/meta.yml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nf_core/subworkflow-template/subworkflows/meta.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json
12
name: "{{ subworkflow_name }}"
23
## TODO nf-core: Add a description of the subworkflow and list keywords
34
description: Sort SAM/BAM/CRAM file

tests/modules/lint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_modules_lint_empty(self):
4343

4444

4545
def test_modules_lint_new_modules(self):
46-
"""lint all modules in nf-core/modules repo clone"""
46+
"""lint a new module"""
4747
module_lint = nf_core.modules.ModuleLint(dir=self.nfcore_modules)
4848
module_lint.lint(print_results=True, all_modules=True)
4949
assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"

tests/modules/patch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"""
1919

2020
ORG_SHA = "002623ccc88a3b0cb302c7d8f13792a95354d9f2"
21-
CORRECT_SHA = "63fd3cdb1be733041db74c15542a7b5b8f4095ed"
21+
CORRECT_SHA = "0245a9277d51a47c8aa68d264d294cf45312fab8"
2222
SUCCEED_SHA = "ba15c20c032c549d77c5773659f19c2927daf48e"
2323
FAIL_SHA = "67b642d4471c4005220a342cad3818d5ba2b5a73"
2424
BISMARK_ALIGN = "bismark/align"

tests/test_modules.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ def create_modules_repo_dummy(tmp_dir):
4545
with requests_cache.disabled():
4646
module_create.create()
4747

48+
# Remove doi from meta.yml which makes lint fail
49+
meta_yml = os.path.join(root_dir, "modules", "nf-core", "bpipe", "test", "meta.yml")
50+
with open(meta_yml, "r") as fh:
51+
lines = fh.readlines()
52+
for line_index in range(len(lines)):
53+
if "doi" in lines[line_index]:
54+
to_pop = line_index
55+
lines.pop(to_pop)
56+
with open(meta_yml, "w") as fh:
57+
fh.writelines(lines)
58+
4859
return root_dir
4960

5061

tests/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ def mock_anaconda_api_calls(rsps: responses.RequestsMock, module, version):
8080
anaconda_mock = {
8181
"latest_version": version.split("--")[0],
8282
"summary": "",
83-
"doc_url": "",
84-
"dev_url": "",
83+
"doc_url": "http://test",
84+
"dev_url": "http://test",
8585
"files": [{"version": version.split("--")[0]}],
8686
"license": "",
8787
}

0 commit comments

Comments
 (0)