Skip to content

Commit 7af69f1

Browse files
authored
Merge pull request nf-core#3608 from nf-core/feat/bump-versions-for-sub-modules
2 parents 2dfcb1e + 9a4f113 commit 7af69f1

File tree

5 files changed

+101
-15
lines changed

5 files changed

+101
-15
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Modules
1010

11+
- feat: nf-core modules bump-version supports specifying the toolkit ([#3608](https://github.com/nf-core/tools/pull/3608))
12+
1113
### Subworkflows
1214

1315
### General

nf_core/__main__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,7 +1398,7 @@ def command_modules_info(ctx, tool, directory):
13981398
type=str,
13991399
callback=normalize_case,
14001400
required=False,
1401-
metavar="<tool> or <tool/subtool>",
1401+
metavar="<tool> or <tool/subtool>. Module to bump versions for. If <tool> is provided and <tool/subtool> exists, all subtools will be bumped.",
14021402
shell_complete=autocomplete_modules,
14031403
)
14041404
@click.option(
@@ -1411,12 +1411,13 @@ def command_modules_info(ctx, tool, directory):
14111411
)
14121412
@click.option("-a", "--all", is_flag=True, help="Run on all modules")
14131413
@click.option("-s", "--show-all", is_flag=True, help="Show up-to-date modules in results too")
1414-
def command_modules_bump_versions(ctx, tool, directory, all, show_all):
1414+
@click.option("-r", "--dry-run", is_flag=True, help="Dry run the command")
1415+
def command_modules_bump_versions(ctx, tool, directory, all, show_all, dry_run):
14151416
"""
14161417
Bump versions for one or more modules in a clone of
14171418
the nf-core/modules repo.
14181419
"""
1419-
modules_bump_versions(ctx, tool, directory, all, show_all)
1420+
modules_bump_versions(ctx, tool, directory, all, show_all, dry_run)
14201421

14211422

14221423
# nf-core subworkflows click command

nf_core/commands_modules.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ def modules_info(ctx, tool, directory):
334334
sys.exit(1)
335335

336336

337-
def modules_bump_versions(ctx, tool, directory, all, show_all):
337+
def modules_bump_versions(ctx, tool, directory, all, show_all, dry_run):
338338
"""
339339
Bump versions for one or more modules in a clone of
340340
the nf-core/modules repo.
@@ -349,7 +349,7 @@ def modules_bump_versions(ctx, tool, directory, all, show_all):
349349
ctx.obj["modules_repo_branch"],
350350
ctx.obj["modules_repo_no_pull"],
351351
)
352-
version_bumper.bump_versions(module=tool, all_modules=all, show_uptodate=show_all)
352+
version_bumper.bump_versions(module=tool, all_modules=all, show_up_to_date=show_all, dry_run=dry_run)
353353
except ModuleExceptionError as e:
354354
log.error(e)
355355
sys.exit(1)

nf_core/modules/bump_versions.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,16 @@ def __init__(
4646
self.tools_config: Optional[NFCoreYamlConfig]
4747

4848
def bump_versions(
49-
self, module: Union[str, None] = None, all_modules: bool = False, show_uptodate: bool = False
50-
) -> None:
49+
self,
50+
module: Optional[str] = None,
51+
all_modules: bool = False,
52+
show_up_to_date: bool = False,
53+
dry_run: bool = False,
54+
) -> list[NFCoreComponent]:
5155
"""
52-
Bump the container and conda version of single module or all modules
56+
Bump the container and conda version of single module or all modules.
57+
58+
If module is the name of a directory in the modules directory, all modules in that directory will be bumped.
5359
5460
Looks for a bioconda tool version in the `main.nf` file of the module and checks whether
5561
are more recent version is available. If yes, then tries to get docker/singularity
@@ -59,12 +65,17 @@ def bump_versions(
5965
Args:
6066
module: a specific module to update
6167
all_modules: whether to bump versions for all modules
68+
show_up_to_date: whether to show up-to-date modules as well
69+
dry_run: whether to dry run the command
70+
71+
Returns:
72+
list[NFCoreComponent]: the updated modules
6273
"""
6374
self.up_to_date = []
6475
self.updated = []
6576
self.failed = []
6677
self.ignored = []
67-
self.show_up_to_date = show_uptodate
78+
self.show_up_to_date = show_up_to_date
6879

6980
# Check modules directory structure
7081
self.check_modules_structure()
@@ -105,12 +116,23 @@ def bump_versions(
105116
raise nf_core.modules.modules_utils.ModuleExceptionError(
106117
"You cannot specify a tool and request all tools to be bumped."
107118
)
108-
nfcore_modules = [m for m in nfcore_modules if m.component_name == module]
119+
# First try to find an exact match
120+
exact_matches = [m for m in nfcore_modules if m.component_name == module]
121+
if exact_matches:
122+
nfcore_modules = exact_matches
123+
else:
124+
# If no exact match, look for modules that start with the given name (subtools)
125+
nfcore_modules = [m for m in nfcore_modules if m.component_name.startswith(module + "/")]
126+
109127
if len(nfcore_modules) == 0:
110128
raise nf_core.modules.modules_utils.ModuleExceptionError(
111129
f"Could not find the specified module: '{module}'"
112130
)
113131

132+
# mainly used for testing, return the list of nfcore_modules selected
133+
if dry_run:
134+
return nfcore_modules
135+
114136
progress_bar = Progress(
115137
"[bold blue]{task.description}",
116138
BarColumn(bar_width=None),
@@ -130,6 +152,8 @@ def bump_versions(
130152

131153
self._print_results()
132154

155+
return nfcore_modules
156+
133157
def bump_module_version(self, module: NFCoreComponent) -> bool:
134158
"""
135159
Bump the bioconda and container version of a single NFCoreComponent
@@ -160,9 +184,9 @@ def bump_module_version(self, module: NFCoreComponent) -> bool:
160184
return False
161185

162186
# Don't update if blocked in blacklist
163-
self.bump_versions_config = getattr(self.tools_config, "bump-versions", {}) or {}
164-
if module.component_name in self.bump_versions_config:
165-
config_version = self.bump_versions_config[module.component_name]
187+
bump_versions_config: dict[str, str] = getattr(self.tools_config, "bump-versions", {}) or {}
188+
if module.component_name in bump_versions_config:
189+
config_version = bump_versions_config[module.component_name]
166190
if not config_version:
167191
self.ignored.append(("Omitting module due to config.", module.component_name))
168192
return False

tests/modules/test_bump_versions.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import os
22
import re
3+
import tempfile
4+
from pathlib import Path
35

46
import pytest
7+
import ruamel.yaml
58

69
import nf_core.modules.bump_versions
10+
from nf_core import __version__
711
from nf_core.modules.modules_utils import ModuleExceptionError
12+
from nf_core.utils import NFCoreYamlConfig
813

914
from ..test_modules import TestModules
1015

@@ -20,14 +25,68 @@ def test_modules_bump_versions_single_module(self):
2025
with open(env_yml_path, "w") as fh:
2126
fh.write(new_content)
2227
version_bumper = nf_core.modules.bump_versions.ModuleVersionBumper(pipeline_dir=self.nfcore_modules)
23-
version_bumper.bump_versions(module="bpipe/test")
28+
modules = version_bumper.bump_versions(module="bpipe/test")
2429
assert len(version_bumper.failed) == 0
30+
assert [m.component_name for m in modules] == ["bpipe/test"]
2531

2632
def test_modules_bump_versions_all_modules(self):
2733
"""Test updating all modules"""
2834
version_bumper = nf_core.modules.bump_versions.ModuleVersionBumper(pipeline_dir=self.nfcore_modules)
29-
version_bumper.bump_versions(all_modules=True)
35+
modules = version_bumper.bump_versions(all_modules=True)
3036
assert len(version_bumper.failed) == 0
37+
assert [m.component_name for m in modules] == ["bpipe/test"]
38+
39+
@staticmethod
40+
def _mock_nf_core_yml(root_dir: Path) -> None:
41+
"""Mock the .nf_core.yml"""
42+
yaml = ruamel.yaml.YAML()
43+
yaml.preserve_quotes = True
44+
yaml.indent(mapping=2, sequence=2, offset=0)
45+
nf_core_yml = NFCoreYamlConfig(nf_core_version=__version__, repository_type="modules", org_path="nf-core")
46+
with open(Path(root_dir, ".nf-core.yml"), "w") as fh:
47+
yaml.dump(nf_core_yml.model_dump(), fh)
48+
49+
@staticmethod
50+
def _mock_modules(root_dir: Path, modules: list[str]) -> None:
51+
"""Mock the directory for a given module (or sub-module) for use with `dry_run`"""
52+
nf_core_dir = root_dir / "modules" / "nf-core"
53+
for module in modules:
54+
if "/" in module:
55+
module, sub_module = module.split("/")
56+
module_dir = nf_core_dir / module / sub_module
57+
else:
58+
module_dir = nf_core_dir / module
59+
module_dir.mkdir(parents=True)
60+
module_main = module_dir / "main.nf"
61+
with module_main.open("w"):
62+
pass
63+
64+
def test_modules_bump_versions_multiple_modules(self):
65+
"""Test updating all modules when multiple modules are present"""
66+
# mock the fgbio directory
67+
root_dir = Path(tempfile.TemporaryDirectory().name)
68+
self._mock_modules(root_dir=root_dir, modules=["fqgrep", "fqtk"])
69+
# mock the ".nf-core.yml"
70+
self._mock_nf_core_yml(root_dir=root_dir)
71+
72+
# run it with dryrun to return the modules that it found
73+
version_bumper = nf_core.modules.bump_versions.ModuleVersionBumper(pipeline_dir=root_dir)
74+
modules = version_bumper.bump_versions(all_modules=True, dry_run=True)
75+
assert sorted([m.component_name for m in modules]) == sorted(["fqgrep", "fqtk"])
76+
77+
def test_modules_bump_versions_submodules(self):
78+
"""Test updating a submodules"""
79+
# mock the fgbio directory
80+
root_dir = Path(tempfile.TemporaryDirectory().name)
81+
in_modules = ["fgbio/callduplexconsensusreads", "fgbio/groupreadsbyumi"]
82+
self._mock_modules(root_dir=root_dir, modules=in_modules)
83+
# mock the ".nf-core.yml"
84+
self._mock_nf_core_yml(root_dir=root_dir)
85+
86+
# run it with dryrun to return the modules that it found
87+
version_bumper = nf_core.modules.bump_versions.ModuleVersionBumper(pipeline_dir=root_dir)
88+
out_modules = version_bumper.bump_versions(module="fgbio", dry_run=True)
89+
assert sorted([m.component_name for m in out_modules]) == sorted(in_modules)
3190

3291
def test_modules_bump_versions_fail(self):
3392
"""Fail updating a module with wrong name"""

0 commit comments

Comments
 (0)