Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Flexible linting for actionsci and multiqc_config #1461

Merged
merged 15 commits into from
Mar 17, 2022
Merged
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ New features should also come with new tests, to keep the test-coverage high (we
You can try running the tests locally before pushing code using the following command:

```bash
pytest --color=yes tests/
pytest --color=yes
jpfeuffer marked this conversation as resolved.
Show resolved Hide resolved
```

### Lint Tests
Expand Down
5 changes: 5 additions & 0 deletions docs/api/_src/pipeline_lint_tests/multiqc_config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# multiqc_config

```{eval-rst}
.. automethod:: nf_core.lint.PipelineLint.multiqc_config
```
2 changes: 2 additions & 0 deletions nf_core/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class PipelineLint(nf_core.utils.Pipeline):
from .files_unchanged import files_unchanged
from .merge_markers import merge_markers
from .modules_json import modules_json
from .multiqc_config import multiqc_config
from .nextflow_config import nextflow_config
from .pipeline_name_conventions import pipeline_name_conventions
from .pipeline_todos import pipeline_todos
Expand Down Expand Up @@ -205,6 +206,7 @@ def _get_all_lint_tests(release_mode):
"actions_schema_validation",
"merge_markers",
"modules_json",
"multiqc_config",
] + (["version_consistency"] if release_mode else [])

def _load(self):
Expand Down
10 changes: 8 additions & 2 deletions nf_core/lint/actions_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,15 @@ def actions_ci(self):

# Check that the action is turned on for the correct events
try:
expected = {"push": {"branches": ["dev"]}, "pull_request": None, "release": {"types": ["published"]}}
# NB: YAML dict key 'on' is evaluated to a Python dict key True
assert ciwf[True] == expected
assert "dev" in ciwf[True]["push"]["branches"]
pr_subtree = ciwf[True]["pull_request"]
assert (
pr_subtree == None
or ("branches" in pr_subtree and "dev" in pr_subtree["branches"])
or ("ignore_branches" in pr_subtree and not "dev" in pr_subtree["ignore_branches"])
)
assert "published" in ciwf[True]["release"]["types"]
jpfeuffer marked this conversation as resolved.
Show resolved Hide resolved
except (AssertionError, KeyError, TypeError):
failed.append("'.github/workflows/ci.yml' is not triggered on expected events")
else:
Expand Down
3 changes: 0 additions & 3 deletions nf_core/lint/files_unchanged.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def files_unchanged(self):
Files that can have additional content but must include the template contents::

.gitignore
assets/multiqc_config.yaml

.. tip:: You can configure the ``nf-core lint`` tests to ignore any of these checks by setting
the ``files_unchanged`` key as follows in your linting config file. For example:
Expand All @@ -54,7 +53,6 @@ def files_unchanged(self):

files_unchanged:
- .github/workflows/branch.yml
- assets/multiqc_config.yaml

"""

Expand Down Expand Up @@ -102,7 +100,6 @@ def files_unchanged(self):
]
files_partial = [
[".gitignore", "foo"],
[os.path.join("assets", "multiqc_config.yaml")],
]

# Only show error messages from pipeline creation
Expand Down
91 changes: 91 additions & 0 deletions nf_core/lint/multiqc_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env python

import os
import yaml


def multiqc_config(self):
"""Make sure basic multiQC plugins are installed and plots are exported
Basic template:

.. code-block:: yaml
report_comment: >
This report has been generated by the <a href="https://github.com/nf-core/quantms" target="_blank">nf-core/quantms</a>
analysis pipeline. For information about how to interpret these results, please see the
<a href="https://nf-co.re/quantms" target="_blank">documentation</a>.
report_section_order:
software_versions:
order: -1000
nf-core-quantms-summary:
order: -1001

export_plots: true
"""
passed = []
warned = []
failed = []

fn = os.path.join(self.wf_path, "assets", "multiqc_config.yaml")

# Return a failed status if we can't find the file
if not os.path.isfile(fn):
return {"ignored": ["'assets/multiqc_config.yaml' not found"]}

try:
with open(fn, "r") as fh:
mqc_yml = yaml.safe_load(fh)
except Exception as e:
return {"failed": ["Could not parse yaml file: {}, {}".format(fn, e)]}

# Check that the report_comment exists and matches
try:
assert "report_section_order" in mqc_yml
orders = dict()
summary_plugin_name = f"nf-core-{self.pipeline_name}-summary"
min_plugins = ["software_versions", summary_plugin_name]
for plugin in min_plugins:
assert plugin in mqc_yml["report_section_order"], f"Section {plugin} missing in report_section_order"
assert "order" in mqc_yml["report_section_order"][plugin], f"Section {plugin} 'order' missing. Must be < 0"
plugin_order = mqc_yml["report_section_order"][plugin]["order"]
assert plugin_order < 0, f"Section {plugin} 'order' must be < 0"

for plugin in mqc_yml["report_section_order"]:
if "order" in mqc_yml["report_section_order"][plugin]:
orders[plugin] = mqc_yml["report_section_order"][plugin]["order"]

assert orders[summary_plugin_name] == min(
orders.values()
), f"Section {summary_plugin_name} should have the lowest order"
orders.pop(summary_plugin_name)
assert orders["software_versions"] == min(
orders.values()
), f"Section software_versions should have the second lowest order"
except (AssertionError, KeyError, TypeError) as e:
failed.append(f"'assets/multiqc_config.yaml' does not meet requirements: {e}")
else:
passed.append("'assets/multiqc_config.yaml' follows the ordering scheme of the minimally required plugins.")

# Check that the minimum plugins exist and are coming first in the summary
try:
assert "report_comment" in mqc_yml
assert (
mqc_yml["report_comment"].strip()
== f'This report has been generated by the <a href="https://github.com/nf-core/{self.pipeline_name}" target="_blank">nf-core/{self.pipeline_name}</a> '
"analysis pipeline. For information about how to interpret these results, please see the "
'<a href="https://nf-co.re/{self.pipeline_name}" target="_blank">documentation</a>.'
)
except (AssertionError, KeyError, TypeError):
warned.append("'assets/multiqc_config.yaml' does not contain a matching report_comment.")
else:
passed.append("'assets/multiqc_config.yaml' contains a matching report_comment.")

# Check that export_plots is activated
try:
assert "export_plots" in mqc_yml
assert mqc_yml["export_plots"] == True
except (AssertionError, KeyError, TypeError):
failed.append("'assets/multiqc_config.yaml' does not contain 'export_plots: true'.")
else:
passed.append("'assets/multiqc_config.yaml' contains 'export_plots: true'.")

return {"passed": passed, "warned": warned, "failed": failed}
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[pytest]
filterwarnings =
ignore::pytest.PytestRemovedIn8Warning:_pytest.nodes:140
testpaths =
tests
jpfeuffer marked this conversation as resolved.
Show resolved Hide resolved