Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- do not check pytest_modules.yml file, deprecating ([#3748](https://github.com/nf-core/tools/pull/3748))
- Use the org from the .nf-core.yml when linting manifest name and homePage. ([#3767](https://github.com/nf-core/tools/pull/3767))
- Use the org from .nf-core.yml when linting multiqc_config report_comment ([#3800](https://github.com/nf-core/tools/pull/3800))
- Linting of patched subworkflows ([#3755](https://github.com/nf-core/tools/pull/3755))
- Add link to modules and subworkflows linting error docs ([#3818](https://github.com/nf-core/tools/pull/3818))

### Modules

Expand All @@ -32,7 +34,6 @@

### Subworkflows

- Linting of patched subworkflows ([#3755](https://github.com/nf-core/tools/pull/3755))
- Update the utils_nfschema_plugin subworkflow to the latest version ([#3814](https://github.com/nf-core/tools/pull/3814))

### General
Expand Down
23 changes: 16 additions & 7 deletions nf_core/components/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
from typing import Optional, Union

import rich.box
import rich.console
import rich.panel
import rich.repr
from rich.markdown import Markdown
from rich.table import Table

import nf_core.modules.modules_utils
import nf_core.utils
from nf_core import __version__
from nf_core.components.components_command import ComponentCommand
from nf_core.components.nfcore_component import NFCoreComponent
from nf_core.modules.modules_json import ModulesJson
Expand All @@ -38,9 +38,12 @@ class LintExceptionError(Exception):
class LintResult:
"""An object to hold the results of a lint test"""

def __init__(self, component: NFCoreComponent, lint_test: str, message: str, file_path: Path):
def __init__(
self, component: NFCoreComponent, parent_lint_test: str, lint_test: str, message: str, file_path: Path
):
self.component = component
self.lint_test = lint_test
self.parent_lint_test = parent_lint_test
self.message = message
self.file_path = file_path
self.component_name: str = component.component_name
Expand Down Expand Up @@ -251,14 +254,14 @@ def _print_results(self, show_passed=False, sort_by="test"):
pass

# Helper function to format test links nicely
def format_result(test_results, table):
def format_result(test_results: list[LintResult], table: Table) -> Table:
"""
Given an list of error message IDs and the message texts, return a nicely formatted
Given a LintResult object, return a nicely formatted
string for the terminal with appropriate ASCII colours.
"""
# TODO: Row styles don't work current as table-level style overrides.
# Leaving it here in case there is a future fix
last_modname = False
last_modname = ""
even_row = False
for lint_result in test_results:
if last_modname and lint_result.component_name != last_modname:
Expand All @@ -276,10 +279,16 @@ def format_result(test_results, table):
file_path = os.path.relpath(lint_result.file_path, self.directory)
file_path_link = f"[link=vscode://file/{os.path.abspath(file_path)}]{file_path}[/link]"

# Add link to the test documentation
tools_version = __version__
if "dev" in __version__:
tools_version = "dev"
test_link_message = f"[{lint_result.lint_test}](https://nf-co.re/docs/nf-core-tools/api_reference/{tools_version}/{self.component_type[:-1]}_lint_tests/{lint_result.parent_lint_test}): {lint_result.message}"

table.add_row(
module_name,
file_path_link,
Markdown(f"{lint_result.message}"),
Markdown(test_link_message),
style="dim" if even_row else None,
)
return table
Expand Down Expand Up @@ -343,7 +352,7 @@ def format_result(test_results, table):
)
)

def print_summary(self):
def print_summary(self) -> None:
"""Print a summary table to the console."""
table = Table(box=rich.box.ROUNDED)
table.add_column("[bold green]LINT RESULTS SUMMARY", no_wrap=True)
Expand Down
6 changes: 3 additions & 3 deletions nf_core/components/nfcore_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def __init__(
self.component_dir = component_dir
self.repo_type = repo_type
self.base_dir = base_dir
self.passed: list[tuple[str, str, Path]] = []
self.warned: list[tuple[str, str, Path]] = []
self.failed: list[tuple[str, str, Path]] = []
self.passed: list[tuple[str, str, str, Path]] = []
self.warned: list[tuple[str, str, str, Path]] = []
self.failed: list[tuple[str, str, str, Path]] = []
self.inputs: list[list[dict[str, dict[str, str]]]] = []
self.outputs: list[str] = []
self.has_meta: bool = False
Expand Down
2 changes: 0 additions & 2 deletions nf_core/modules/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import rich.progress
import ruamel.yaml

import nf_core.components
import nf_core.components.nfcore_component
import nf_core.modules.modules_utils
import nf_core.utils
from nf_core.components.components_utils import get_biotools_id, get_biotools_response, yaml
Expand Down
40 changes: 30 additions & 10 deletions nf_core/modules/lint/environment_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import ruamel.yaml
from jsonschema import exceptions, validators

from nf_core.components.lint import ComponentLint, LintExceptionError, LintResult
from nf_core.components.lint import ComponentLint, LintExceptionError
from nf_core.components.nfcore_component import NFCoreComponent

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -38,6 +38,7 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
if allow_missing:
module.warned.append(
(
"environment_yml",
"environment_yml_exists",
"Module's `environment.yml` does not exist",
Path(module.component_dir, "environment.yml"),
Expand All @@ -64,19 +65,32 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
if env_yml is None:
raise ruamel.yaml.scanner.ScannerError("Empty YAML file")

module.passed.append(("environment_yml_exists", "Module's `environment.yml` exists", module.environment_yml))
module.passed.append(
(
"environment_yml",
"environment_yml_exists",
"Module's `environment.yml` exists",
module.environment_yml,
)
)

except FileNotFoundError:
# check if the module's main.nf requires a conda environment
with open(Path(module.component_dir, "main.nf")) as fh:
main_nf = fh.read()
if 'conda "${moduleDir}/environment.yml"' in main_nf:
module.failed.append(
("environment_yml_exists", "Module's `environment.yml` does not exist", module.environment_yml)
(
"environment_yml",
"environment_yml_exists",
"Module's `environment.yml` does not exist",
module.environment_yml,
)
)
else:
module.passed.append(
(
"environment_yml",
"environment_yml_exists",
"Module's `environment.yml` does not exist, but it is also not included in the main.nf",
module.environment_yml,
Expand All @@ -91,7 +105,12 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
schema = json.load(fh)
validators.validate(instance=env_yml, schema=schema)
module.passed.append(
("environment_yml_valid", "Module's `environment.yml` is valid", module.environment_yml)
(
"environment_yml",
"environment_yml_valid",
"Module's `environment.yml` is valid",
module.environment_yml,
)
)
valid_env_yml = True
except exceptions.ValidationError as e:
Expand All @@ -102,6 +121,7 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
e.message = e.schema["message"]
module.failed.append(
(
"environment_yml",
"environment_yml_valid",
f"The `environment.yml` of the module {module.component_name} is not valid: {e.message}.{hint}",
module.environment_yml,
Expand Down Expand Up @@ -155,9 +175,9 @@ def sort_key(x):
is_sorted = True

if is_sorted:
module_lint_object.passed.append(
LintResult(
module,
module.passed.append(
(
"environment_yml",
"environment_yml_sorted",
"The dependencies in the module's `environment.yml` are sorted correctly",
module.environment_yml,
Expand Down Expand Up @@ -189,9 +209,9 @@ def sort_key(x):
# Then dump the sorted YAML
yaml.dump(env_yml, fh)

module_lint_object.passed.append(
LintResult(
module,
module.passed.append(
(
"environment_yml",
"environment_yml_sorted",
"The dependencies in the module's `environment.yml` have been sorted",
module.environment_yml,
Expand Down
Loading