Skip to content
Closed
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
10 changes: 7 additions & 3 deletions nf_core/components/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,13 @@ def install(self, component: str | dict[str, str], silent: bool = False) -> bool
# Install included modules and subworkflows
self.install_included_components(component_dir)

# Regenerate container configuration files for the pipeline when modules are installed
if self.component_type == "modules":
try_generate_container_configs(self.directory, component_dir, component)
# Regenerate container configs once per top-level invocation
# (recursive installs pass silent=True).
if not silent:
if self.component_type == "modules":
try_generate_container_configs(self.directory, component_dir, component)
else:
try_generate_container_configs(self.directory)

if not silent:
modules_json.load()
Expand Down
8 changes: 5 additions & 3 deletions nf_core/components/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,6 @@ def update(self, component=None, silent=False, updated=None, check_diff_exist=Tr
self.modules_json.update(self.component_type, modules_repo, component, version, installed_by=None)
updated.append(component)

# Regenerate container configuration files for the pipeline when modules are updated
if self.component_type == "modules":
try_generate_container_configs(self.directory)
recursive_update = True
modules_to_update, subworkflows_to_update = self.get_components_to_update(component)
if not silent and len(modules_to_update + subworkflows_to_update) > 0 and not self.update_all:
Expand Down Expand Up @@ -356,6 +353,11 @@ def update(self, component=None, silent=False, updated=None, check_diff_exist=Tr
self.modules_json.load()
self.modules_json.dump(run_prettier=True)

# Regenerate container configs once per top-level invocation
# (recursive updates pass silent=True; skip in --save-diff dry runs).
if not silent and updated and not self.save_diff_fn:
try_generate_container_configs(self.directory)

return exit_value

def get_single_component_info(self, component):
Expand Down
7 changes: 7 additions & 0 deletions tests/modules/test_install.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from unittest import mock

import pytest

Expand Down Expand Up @@ -44,6 +45,12 @@ def test_modules_install_trimgalore_twice(self):
self.mods_install.install("trimgalore")
assert self.mods_install.install("trimgalore") is True

@mock.patch("nf_core.components.install.try_generate_container_configs")
def test_modules_install_regenerates_container_configs_once(self, mock_gen):
"""Container config regen runs exactly once for a top-level module install."""
assert self.mods_install.install("trimgalore") is not False
assert mock_gen.call_count == 1

def test_modules_install_from_gitlab(self):
"""Test installing a module from GitLab"""
assert self.mods_install_gitlab.install("fastqc") is True
Expand Down
7 changes: 7 additions & 0 deletions tests/subworkflows/test_install.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from unittest import mock

import pytest

Expand Down Expand Up @@ -191,6 +192,12 @@ def test_subworkflows_install_tracking_added_super_subworkflow(self):
]["installed_by"]
) == sorted(["subworkflows", "bam_sort_stats_samtools"])

@mock.patch("nf_core.components.install.try_generate_container_configs")
def test_subworkflows_install_regenerates_container_configs_once(self, mock_gen):
"""Container config regen runs once for a top-level subworkflow install with module deps."""
assert self.subworkflow_install.install("bam_sort_stats_samtools") is not False
assert mock_gen.call_count == 1

def test_subworkflows_install_alternate_remote(self):
"""Test installing a module from a different remote with the same organization path"""
install_obj = SubworkflowInstall(
Expand Down
19 changes: 19 additions & 0 deletions tests/subworkflows/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ def test_install_and_update(self):
assert update_obj.update("bam_stats_samtools") is True
assert cmp_component(tmpdir, sw_path) is True

@mock.patch("nf_core.components.update.try_generate_container_configs")
def test_subworkflow_update_regenerates_container_configs_once(self, mock_gen):
"""Container config regen runs once for a top-level update, not per linked component."""
assert self.subworkflow_install_old.install("fastq_align_bowtie2")
mock_gen.reset_mock()
update_obj = SubworkflowUpdate(self.pipeline_dir, show_diff=False, update_deps=True)
assert update_obj.update("fastq_align_bowtie2") is True
assert mock_gen.call_count == 1

@mock.patch("nf_core.components.update.try_generate_container_configs")
def test_subworkflow_update_save_diff_skips_container_configs(self, mock_gen):
"""``--save-diff`` is a dry run; container configs must not be regenerated."""
assert self.subworkflow_install_old.install("fastq_align_bowtie2")
mock_gen.reset_mock()
patch_path = Path(self.pipeline_dir, "fastq_align_bowtie2.patch")
update_obj = SubworkflowUpdate(self.pipeline_dir, save_diff_fn=patch_path, update_deps=True)
assert update_obj.update("fastq_align_bowtie2") is True
assert mock_gen.call_count == 0

def test_install_at_hash_and_update(self):
"""Installs an old version of a subworkflow in the pipeline and updates it"""
assert self.subworkflow_install_old.install("fastq_align_bowtie2")
Expand Down
Loading