diff --git a/docs/api/_src/api/nf_core_yml.md b/docs/api/_src/api/nf_core_yml.md new file mode 100644 index 0000000000..48d2fac750 --- /dev/null +++ b/docs/api/_src/api/nf_core_yml.md @@ -0,0 +1,67 @@ +# .nf-core.yml configuration + +The `.nf-core.yml` file at the root of any nf-core repository controls how nf-core +tools behaves for that repository. It is read by `load_tools_config()` and validated +against the `NFCoreYamlConfig` Pydantic model. + +## Minimal examples + +Pipeline repository: + +```yaml +repository_type: pipeline +nf_core_version: "3.2.0" +``` + +Modules repository: + +```yaml +repository_type: modules +nf_core_version: "3.2.0" +container-registry: + - community.wave.seqera.io/library/ +``` + +## Schema + +### Top-level `.nf-core.yml` schema (`NFCoreYamlConfig`) + +```{eval-rst} +.. autopydantic_model:: nf_core.utils.NFCoreYamlConfig + :members: + :undoc-members: + :show-inheritance: + :model-show-json: false + :model-show-config-summary: false + :model-show-validator-members: false + :model-show-field-summary: true + :field-show-alias: true +``` + +### `lint` block (`NFCoreYamlLintConfig`) + +```{eval-rst} +.. autopydantic_model:: nf_core.utils.NFCoreYamlLintConfig + :members: + :undoc-members: + :show-inheritance: + :model-show-json: false + :model-show-config-summary: false + :model-show-validator-members: false + :model-show-field-summary: true + :field-show-alias: true +``` + +### `template` block (`NFCoreTemplateConfig`) + +```{eval-rst} +.. autopydantic_model:: nf_core.utils.NFCoreTemplateConfig + :members: + :undoc-members: + :show-inheritance: + :model-show-json: false + :model-show-config-summary: false + :model-show-validator-members: false + :model-show-field-summary: true + :field-show-alias: true +``` diff --git a/docs/api/_src/index.md b/docs/api/_src/index.md index e752b3b28b..8ebd988b1b 100644 --- a/docs/api/_src/index.md +++ b/docs/api/_src/index.md @@ -8,3 +8,4 @@ This API documentation is for the [`nf-core/tools`](https://github.com/nf-core/t - [Module code lint tests](./module_lint_tests/environment_yml.md) (run by `nf-core modules lint`) - [Subworkflow code lint tests](./subworkflow_lint_tests/main_nf.md) (run by `nf-core subworkflows lint`) - [nf-core/tools Python package API reference](./api/utils.md) +- [`.nf-core.yml` configuration reference](./api/nf_core_yml.md) diff --git a/nf_core/__main__.py b/nf_core/__main__.py index f115bf2bfa..7707a918fd 100644 --- a/nf_core/__main__.py +++ b/nf_core/__main__.py @@ -1316,8 +1316,8 @@ def command_modules_test(ctx, tool, directory, no_prompts, update, once, profile "--registry", type=str, metavar="", - default=None, - help="Registry to use for containers. If not specified it will use docker.registry value in the nextflow.config file", + default="quay.io,community.wave.seqera.io/library/", + help="Comma-separated list of allowed container registry prefixes.", ) @click.option( "-k", @@ -1608,8 +1608,8 @@ def command_subworkflows_list_local(ctx, keywords, json, directory): # pylint: "--registry", type=str, metavar="", - default=None, - help="Registry to use for containers. If not specified it will use docker.registry value in the nextflow.config file", + default="quay.io,community.wave.seqera.io/library/", + help="Comma-separated list of allowed container registry prefixes.", ) @click.option( "-k", diff --git a/nf_core/commands_modules.py b/nf_core/commands_modules.py index 7f5b991745..6f06b24550 100644 --- a/nf_core/commands_modules.py +++ b/nf_core/commands_modules.py @@ -262,7 +262,6 @@ def modules_lint( ) module_lint.lint( module=tool, - registry=registry, key=key, all_modules=all_modules, print_results=True, diff --git a/nf_core/commands_subworkflows.py b/nf_core/commands_subworkflows.py index 193e976401..79ce0f7523 100644 --- a/nf_core/commands_subworkflows.py +++ b/nf_core/commands_subworkflows.py @@ -130,7 +130,6 @@ def subworkflows_lint( ) subworkflow_lint.lint( subworkflow=subworkflow, - registry=registry, key=key, all_subworkflows=all_subworkflows, print_results=True, diff --git a/nf_core/components/lint/__init__.py b/nf_core/components/lint/__init__.py index efcdc11d90..0cbe9decb9 100644 --- a/nf_core/components/lint/__init__.py +++ b/nf_core/components/lint/__init__.py @@ -62,7 +62,7 @@ def __init__( remote_url: str | None = None, branch: str | None = None, no_pull: bool = False, - registry: str | None = None, + registry: str = "quay.io,community.wave.seqera.io/library/", hide_progress: bool = False, ): super().__init__( @@ -156,12 +156,11 @@ def __init__( def __repr__(self) -> str: return f"ComponentLint({self.component_type}, {self.directory})" - def _set_registry(self, registry) -> None: - if registry is None: - self.registry = self.config.get("docker.registry", "quay.io") - else: - self.registry = registry - log.debug(f"Registry set to {self.registry}") + def _set_registry(self, registry: str) -> None: + _, tools_config = nf_core.utils.load_tools_config(self.directory) + user_registries: list[str] = (tools_config.container_registry or []) if tools_config else [] + self.registry: tuple[str, ...] = (*registry.split(","), *user_registries) + log.debug(f"Registries set to {self.registry}") @property def local_module_exclude_tests(self): diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py index f2472db0d3..9821dc0c7d 100644 --- a/nf_core/modules/lint/__init__.py +++ b/nf_core/modules/lint/__init__.py @@ -69,7 +69,7 @@ def __init__( remote_url: str | None = None, branch: str | None = None, no_pull: bool = False, - registry: str | None = None, + registry: str = "quay.io,community.wave.seqera.io/library/", hide_progress: bool = False, ): super().__init__( @@ -88,7 +88,6 @@ def __init__( def lint( self, module=None, - registry="quay.io", key=(), all_modules=False, print_results=True, @@ -173,25 +172,22 @@ def lint( # Lint local modules if local and len(local_modules) > 0: - self.lint_modules(local_modules, registry=registry, local=True, fix_version=fix_version) + self.lint_modules(local_modules, local=True, fix_version=fix_version) # Lint nf-core modules if not local and len(remote_modules) > 0: - self.lint_modules(remote_modules, registry=registry, local=False, fix_version=fix_version) + self.lint_modules(remote_modules, local=False, fix_version=fix_version) if print_results: self._print_results(show_passed=show_passed, sort_by=sort_by, plain_text=plain_text) self.print_summary(plain_text=plain_text) - def lint_modules( - self, modules: list[NFCoreComponent], registry: str = "quay.io", local: bool = False, fix_version: bool = False - ) -> None: + def lint_modules(self, modules: list[NFCoreComponent], local: bool = False, fix_version: bool = False) -> None: """ Lint a list of modules Args: modules ([NFCoreComponent]): A list of module objects - registry (str): The container registry to use. Should be quay.io in most situations. local (boolean): Whether the list consist of local or nf-core modules fix_version (boolean): Fix the module version if a newer version is available """ diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py index b589bc3f0c..0b8df9c046 100644 --- a/nf_core/modules/lint/main_nf.py +++ b/nf_core/modules/lint/main_nf.py @@ -20,7 +20,7 @@ def main_nf( - module_lint_object, module: NFCoreComponent, fix_version: bool, registry: str, progress_bar: Progress + module_lint_object, module: NFCoreComponent, fix_version: bool, registry: tuple[str, ...], progress_bar: Progress ) -> tuple[list[str], list[str]]: """Lint a ``main.nf`` module file @@ -359,15 +359,17 @@ def check_when_section(self, lines): self.passed.append(("main_nf", "when_condition", "when: condition is unchanged", self.main_nf)) -def check_process_section(self, lines, registry, fix_version, progress_bar): +def check_process_section( + self, lines: list[str], registry: tuple[str, ...], fix_version: bool, progress_bar: Progress | None +): """Lint the section of a module between the process definition and the 'input:' definition Specifically checks for correct software versions and containers Args: - lines (List[str]): Content of process. - registry (str): Base Docker registry for containers. Typically quay.io. + lines (list[str]): Content of process. + registry (tuple[str, ...]): Allowed container registry prefixes. fix_version (bool): Fix software version progress_bar (ProgressBar): Progress bar to update. @@ -384,6 +386,7 @@ def check_process_section(self, lines, registry, fix_version, progress_bar): singularity_tag = None docker_tag = None bioconda_packages = [] + allowed_registries = registry # Process name should be all capital letters if all(x.upper() for x in self.process_name): @@ -450,7 +453,7 @@ def check_process_section(self, lines, registry, fix_version, progress_bar): else: self.failed.append(("main_nf", "docker_tag", "Unable to parse docker tag", self.main_nf)) docker_tag = None - if line.startswith((registry, "community.wave.seqera.io/library/")): + if line.startswith(allowed_registries): l_stripped = re.sub(r"\W+$", "", line) self.passed.append( ( @@ -465,15 +468,11 @@ def check_process_section(self, lines, registry, fix_version, progress_bar): ( "main_nf", "container_links", - "Container prefix is not correct. Please add the registry prefix (e.g. 'quay.io/')", + f"Container prefix is not correct. Please add one of the allowed registry prefixes: {', '.join(f'{r}' for r in allowed_registries)}", self.main_nf, ) ) - # Guess if container name is simple one (e.g. nfcore/ubuntu:20.04) - # If so, add quay.io as default container prefix - if line.count("/") == 1 and line.count(":") == 1: - line = "/".join([registry, line]).replace("//", "/") url = urlparse(line.split("'")[0]) if line.startswith("container") or _container_type(line) == "docker" or _container_type(line) == "singularity": @@ -564,7 +563,10 @@ def check_process_section(self, lines, registry, fix_version, progress_bar): log.debug(f"Unable to update package {package} due to error: {e}") else: if fixed: - progress_bar.print(f"[blue]INFO[/blue]\t Updating package '{package}' {ver} -> {last_ver}") + if progress_bar is not None: + progress_bar.print( + f"[blue]INFO[/blue]\t Updating package '{package}' {ver} -> {last_ver}" + ) log.debug(f"Updating package {package} {ver} -> {last_ver}") self.passed.append( ( @@ -575,9 +577,10 @@ def check_process_section(self, lines, registry, fix_version, progress_bar): ) ) else: - progress_bar.print( - f"[blue]INFO[/blue]\t Tried to update package. Unable to update package '{package}' {ver} -> {last_ver}" - ) + if progress_bar is not None: + progress_bar.print( + f"[blue]INFO[/blue]\t Tried to update package. Unable to update package '{package}' {ver} -> {last_ver}" + ) log.debug(f"Unable to update package {package} {ver} -> {last_ver}") self.warned.append( ( diff --git a/nf_core/pipelines/lint/nfcore_yml.py b/nf_core/pipelines/lint/nfcore_yml.py index f202adfd96..ff28a77129 100644 --- a/nf_core/pipelines/lint/nfcore_yml.py +++ b/nf_core/pipelines/lint/nfcore_yml.py @@ -12,11 +12,11 @@ def nfcore_yml(self) -> dict[str, list[str]]: The ``.nf-core.yml`` contains metadata for nf-core tools to correctly apply its features. - * repository type: + * ``repository_type``: Check that the repository type is set. - * nf core version: + * ``nf_core_version``: Check if the nf-core version is set to the latest version. diff --git a/nf_core/subworkflows/lint/__init__.py b/nf_core/subworkflows/lint/__init__.py index 7d5fbccdae..aad0b1b066 100644 --- a/nf_core/subworkflows/lint/__init__.py +++ b/nf_core/subworkflows/lint/__init__.py @@ -51,7 +51,7 @@ def __init__( remote_url=None, branch=None, no_pull=False, - registry=None, + registry: str = "quay.io,community.wave.seqera.io/library/", hide_progress=False, ): super().__init__( @@ -69,7 +69,6 @@ def __init__( def lint( self, subworkflow=None, - registry="quay.io", key=(), all_subworkflows=False, print_results=True, @@ -152,23 +151,22 @@ def lint( # Lint local subworkflows if local and len(local_subworkflows) > 0: - self.lint_subworkflows(local_subworkflows, registry=registry, local=True) + self.lint_subworkflows(local_subworkflows, local=True) # Lint nf-core subworkflows if not local and len(remote_subworkflows) > 0: - self.lint_subworkflows(remote_subworkflows, registry=registry, local=False) + self.lint_subworkflows(remote_subworkflows, local=False) if print_results: self._print_results(show_passed=show_passed, sort_by=sort_by, plain_text=plain_text) self.print_summary(plain_text=plain_text) - def lint_subworkflows(self, subworkflows, registry="quay.io", local=False): + def lint_subworkflows(self, subworkflows, local=False): """ Lint a list of subworkflows Args: subworkflows ([NFCoreComponent]): A list of subworkflow objects - registry (str): The container registry to use. Should be quay.io in most situations. local (boolean): Whether the list consist of local or nf-core subworkflows """ # TODO: consider unifying modules and subworkflows lint_subworkflows() function and add it to the ComponentLint class @@ -189,9 +187,9 @@ def lint_subworkflows(self, subworkflows, registry="quay.io", local=False): for swf in subworkflows: progress_bar.update(lint_progress, advance=1, test_name=swf.component_name) - self.lint_subworkflow(swf, progress_bar, registry=registry, local=local) + self.lint_subworkflow(swf, progress_bar, local=local) - def lint_subworkflow(self, swf, progress_bar, registry, local=False): + def lint_subworkflow(self, swf, progress_bar, local=False): """ Perform linting on one subworkflow diff --git a/nf_core/utils.py b/nf_core/utils.py index d376559e53..d4aca9b0e6 100644 --- a/nf_core/utils.py +++ b/nf_core/utils.py @@ -33,7 +33,7 @@ import rich.markup import yaml from packaging.version import Version -from pydantic import BaseModel, ValidationError, field_validator +from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator from rich.live import Live from rich.spinner import Spinner @@ -1388,6 +1388,8 @@ def __setitem__(self, item: str, value: Any) -> None: class NFCoreYamlConfig(BaseModel): """.nf-core.yml configuration file schema""" + model_config = ConfigDict(populate_by_name=True) + repository_type: Literal["pipeline", "modules"] | None = None """ Type of repository """ nf_core_version: str | None = None @@ -1402,6 +1404,8 @@ class NFCoreYamlConfig(BaseModel): """ Disable bumping of the version for a module/subworkflow (when repository_type is modules). See https://nf-co.re/docs/nf-core-tools/modules/bump-versions for more information. """ update: dict[str, str | bool | dict[str, str | dict[str, str | bool]]] | None = None """ Disable updating specific modules/subworkflows (when repository_type is pipeline). See https://nf-co.re/docs/nf-core-tools/modules/update for more information. """ + container_registry: list[str] | None = Field(default=None, alias="container-registry") + """ Additional container registry prefixes allowed when linting container directives. """ def __getitem__(self, item: str) -> Any: return getattr(self, item) diff --git a/tests/modules/lint/test_main_nf.py b/tests/modules/lint/test_main_nf.py index f6c3848f71..1eddede4d9 100644 --- a/tests/modules/lint/test_main_nf.py +++ b/tests/modules/lint/test_main_nf.py @@ -1,3 +1,6 @@ +from pathlib import Path +from unittest.mock import MagicMock, patch + import pytest import nf_core.modules.lint @@ -6,6 +9,7 @@ _parse_output_topics, check_container_link_line, check_process_labels, + check_process_section, check_script_section, ) @@ -101,6 +105,64 @@ def test_container_links(content, passed, warned, failed): assert len(mock_lint.failed) == failed +@pytest.mark.parametrize( + "container_line,additional_registries,should_pass", + [ + # Known additional registry passes when listed + ( + "container 'community.wave.seqera.io/library/samtools:1.17--h87f3376_0'", + ["community.wave.seqera.io/library/"], + True, + ), + # Same registry fails when not in additional_registries + ( + "container 'community.wave.seqera.io/library/samtools:1.17--h87f3376_0'", + [], + False, + ), + # Default registry still passes when additional_registries is populated + ( + "container 'quay.io/biocontainers/samtools:1.17--h87f3376_0'", + ["community.wave.seqera.io/library/"], + True, + ), + # Multiple additional registries: second one matches + ( + "container 'ghcr.io/org/tool:1.0--abc123'", + ["community.wave.seqera.io/library/", "ghcr.io/"], + True, + ), + ], +) +def test_check_process_section_additional_registries(container_line, additional_registries, should_pass, tmp_path): + """Test that container_links passes/fails based on additional_registries from .nf-core.yml.""" + mock_lint = MockModuleLint() + mock_lint.process_name = "TOOL_SUBTOOL" + mock_lint.component_dir = tmp_path + + with patch("requests.head") as mock_head: + mock_head.return_value = MagicMock(status_code=200) + check_process_section( + mock_lint, + [container_line], + ("quay.io", *additional_registries), + False, + None, + ) + + prefix_passed = any(r[1] == "container_links" and "Container prefix is correct" in r[2] for r in mock_lint.passed) + prefix_failed = any( + r[1] == "container_links" and "Container prefix is not correct" in r[2] for r in mock_lint.failed + ) + + if should_pass: + assert prefix_passed, f"Expected container prefix to pass; passed={mock_lint.passed}, failed={mock_lint.failed}" + assert not prefix_failed, f"Container prefix should not fail; failed={mock_lint.failed}" + else: + assert prefix_failed, f"Expected container prefix to fail; passed={mock_lint.passed}, failed={mock_lint.failed}" + assert not prefix_passed, f"Container prefix should not pass; passed={mock_lint.passed}" + + class TestMainNfLinting(TestModules): """ Test main.nf linting functionality. @@ -140,6 +202,68 @@ def test_main_nf_lint_with_alternative_registry(self): assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}" assert len(module_lint.passed) > 0 + def test_additional_registry_from_nf_core_yml_passes_container_link(self): + """Test that a container using a registry listed in .nf-core.yml container-registry passes linting. + + Creates a local module whose container URL uses a custom registry (myreg.io/library/). + When that registry is listed under ``container-registry`` in ``.nf-core.yml``, the + ``container_links`` check must pass. Without it, the same container must produce a + ``container_links`` warning (local-module failures are demoted to warnings). + """ + local_mod_dir = Path(self.pipeline_dir) / "modules" / "local" / "mock_tool" + local_mod_dir.mkdir(parents=True, exist_ok=True) + (local_mod_dir / "main.nf").write_text( + "process MOCK_TOOL {\n" + " container 'myreg.io/library/tool:1.0--abc123'\n" + " input:\n" + " val(x)\n" + " output:\n" + " path('out.txt'), emit: out\n" + " script:\n" + " 'echo hello > out.txt'\n" + "}\n" + ) + + nf_core_yml_path = Path(self.pipeline_dir) / ".nf-core.yml" + from ruamel.yaml import YAML + + yaml_parser = YAML() + nf_core_yml = yaml_parser.load(nf_core_yml_path) + + # --- positive: registry listed in .nf-core.yml --- + nf_core_yml["container-registry"] = ["myreg.io/library/"] + yaml_parser.dump(nf_core_yml, nf_core_yml_path) + + with patch("requests.head") as mock_head: + mock_head.return_value = MagicMock(status_code=200) + module_lint = nf_core.modules.lint.ModuleLint(directory=self.pipeline_dir) + module_lint.lint(local=True, print_results=False) + + container_link_warns = [ + r for r in module_lint.warned if r.lint_test == "container_links" and "mock_tool" in r.component_name + ] + assert not any("Container prefix is not correct" in r.message for r in container_link_warns), ( + f"Expected no container prefix failure for myreg.io when listed in .nf-core.yml; " + f"warned={[r.message for r in container_link_warns]}" + ) + + # --- negative: same container fails when registry is absent --- + del nf_core_yml["container-registry"] + yaml_parser.dump(nf_core_yml, nf_core_yml_path) + + with patch("requests.head") as mock_head: + mock_head.return_value = MagicMock(status_code=200) + module_lint2 = nf_core.modules.lint.ModuleLint(directory=self.pipeline_dir) + module_lint2.lint(local=True, print_results=False) + + container_link_warns2 = [ + r for r in module_lint2.warned if r.lint_test == "container_links" and "mock_tool" in r.component_name + ] + assert any("Container prefix is not correct" in r.message for r in container_link_warns2), ( + f"Expected container prefix failure for myreg.io when not in .nf-core.yml; " + f"warned={[r.message for r in container_link_warns2]}" + ) + def test_topics_and_emits_version_check(self): """Test that main_nf version emit and topics check works correctly""" diff --git a/tests/modules/lint/test_patch.py b/tests/modules/lint/test_patch.py index e42b0b84a6..2c93f70b24 100644 --- a/tests/modules/lint/test_patch.py +++ b/tests/modules/lint/test_patch.py @@ -55,6 +55,6 @@ def test_modules_lint_patched_modules(self): all_modules=True, ) - assert len(module_lint.failed) == 3, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}" + assert len(module_lint.failed) == 1, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}" assert len(module_lint.passed) > 0 assert len(module_lint.warned) >= 0 diff --git a/tests/modules/test_patch.py b/tests/modules/test_patch.py index a77701b8ca..9f1e9bff71 100644 --- a/tests/modules/test_patch.py +++ b/tests/modules/test_patch.py @@ -6,6 +6,7 @@ import nf_core.components.components_command import nf_core.components.patch +import nf_core.modules.install import nf_core.modules.modules_json import nf_core.modules.patch import nf_core.modules.update @@ -20,10 +21,10 @@ testing if the update commands works correctly with patch files """ -ORG_SHA = "3dc7c14d29af40f1a0871a675364e437559d97a8" -CORRECT_SHA = "63e780200600e340365b669f9c673b670764c569" -SUCCEED_SHA = "0d0515c3f11266e1314e129bec3e308f804c8dc7" -FAIL_SHA = "cb64a5c1ef85619b89ab99dec2e9097fe84e1dc8" +ORG_SHA = "262178fbd015cd9be248630eec267f27e6048b03" +CORRECT_SHA = "262178fbd015cd9be248630eec267f27e6048b03" +SUCCEED_SHA = "b300b7e53f080a174a717d8e394cca1a82a5ba54" +FAIL_SHA = "04d59325d44d309fb195f43d8b672674ee73d309" BISMARK_ALIGN = "bismark/align" REPO_NAME = "nf-core-test" PATCH_BRANCH = "patch-tester" @@ -50,14 +51,16 @@ def modify_main_nf(path): lines = fh.readlines() # We want a patch file that looks something like: # - tuple val(meta), path(reads) - # - path index + # - tuple val(meta3), path(index) # + tuple val(meta), path(reads), path(index) + to_pop = None for line_index in range(len(lines)): if lines[line_index] == " tuple val(meta), path(reads)\n": lines[line_index] = " tuple val(meta), path(reads), path(index)\n" - elif lines[line_index] == " path index\n": + elif lines[line_index] == " tuple val(meta3), path(index)\n": to_pop = line_index - lines.pop(to_pop) + if to_pop: + lines.pop(to_pop) with open(path, "w") as fh: fh.writelines(lines) @@ -108,7 +111,7 @@ def test_create_patch_change(self): assert f"--- {module_relpath / 'main.nf'}\n" in patch_lines, module_relpath / "main.nf" assert f"+++ {module_relpath / 'main.nf'}\n" in patch_lines assert "- tuple val(meta), path(reads)\n" in patch_lines - assert "- path index\n" in patch_lines + assert "- tuple val(meta3), path(index)\n" in patch_lines assert "+ tuple val(meta), path(reads), path(index)\n" in patch_lines def test_create_patch_try_apply_successful(self): @@ -167,7 +170,7 @@ def test_create_patch_try_apply_successful(self): assert f"--- {module_relpath / 'main.nf'}\n" in patch_lines assert f"+++ {module_relpath / 'main.nf'}\n" in patch_lines assert "- tuple val(meta), path(reads)\n" in patch_lines - assert "- path index\n" in patch_lines + assert "- tuple val(meta3), path(index)\n" in patch_lines assert "+ tuple val(meta), path(reads), path(index)\n" in patch_lines # Check that 'main.nf' is updated correctly @@ -175,7 +178,7 @@ def test_create_patch_try_apply_successful(self): main_nf_lines = fh.readlines() # These lines should have been removed by the patch assert " tuple val(meta), path(reads)\n" not in main_nf_lines - assert " path index\n" not in main_nf_lines + assert " tuple val(meta3), path(index)\n" not in main_nf_lines # This line should have been added assert " tuple val(meta), path(reads), path(index)\n" in main_nf_lines @@ -268,7 +271,7 @@ def test_create_patch_update_success(self): assert f"--- {module_relpath / 'main.nf'}\n" in patch_lines assert f"+++ {module_relpath / 'main.nf'}\n" in patch_lines assert "- tuple val(meta), path(reads)\n" in patch_lines - assert "- path index\n" in patch_lines + assert "- tuple val(meta3), path(index)\n" in patch_lines assert "+ tuple val(meta), path(reads), path(index)\n" in patch_lines # Check that 'main.nf' is updated correctly @@ -276,7 +279,7 @@ def test_create_patch_update_success(self): main_nf_lines = fh.readlines() # These lines should have been removed by the patch assert " tuple val(meta), path(reads)\n" not in main_nf_lines - assert " path index\n" not in main_nf_lines + assert " tuple val(meta3), path(index)\n" not in main_nf_lines # This line should have been added assert " tuple val(meta), path(reads), path(index)\n" in main_nf_lines @@ -323,8 +326,8 @@ def test_create_patch_update_fail(self): ).install_component_files(BISMARK_ALIGN, FAIL_SHA, update_obj.modules_repo, temp_dir) temp_module_dir = temp_dir / BISMARK_ALIGN - temp_files = {f.name for f in temp_module_dir.iterdir()} - module_files = {f.name for f in module_path.iterdir()} + temp_files = {f.name for f in temp_module_dir.iterdir() if f.is_file()} + module_files = {f.name for f in module_path.iterdir() if f.is_file()} for file_name in temp_files: assert file_name in module_files with open(module_path / file_name) as fh: diff --git a/tests/pipelines/lint/test_nfcore_yml.py b/tests/pipelines/lint/test_nfcore_yml.py index e888a1d6a1..a2c1babd6a 100644 --- a/tests/pipelines/lint/test_nfcore_yml.py +++ b/tests/pipelines/lint/test_nfcore_yml.py @@ -48,6 +48,15 @@ def test_nfcore_yml_fail_nfcore_version(self): assert len(results.get("passed", [])) >= 0 assert len(results.get("ignored", [])) == 0 + def test_nfcore_yml_container_registry_accepted(self): + """Lint test: nfcore_yml - PASS - container-registry field is accepted without errors""" + self.nf_core_yml["container-registry"] = ["community.wave.seqera.io/library/", "ghcr.io/"] + self.yaml.dump(self.nf_core_yml, self.nf_core_yml_path) + + assert self.lint_obj._load() + results = self.lint_obj.nfcore_yml() + assert len(results.get("failed", [])) == 0 + def test_nfcore_yml_nested_lint_config(self) -> None: """Lint test: nfcore_yml with nested lint config - PASS""" valid_yaml = """