Skip to content

added support for blacklist #1162

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

Merged
merged 11 commits into from
Jul 8, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* Refactored passing of command line arguments to `nf-core` commands and subcommands ([#1139](https://github.com/nf-core/tools/issues/1139), [#1140](https://github.com/nf-core/tools/issues/1140))
* Check for `modules.json` for entries of modules that are not actually installed in the pipeline [[#1141](https://github.com/nf-core/tools/issues/1141)]
* Added `<keywords>` argument to `nf-core modules list` for filtering the listed modules. ([#1139](https://github.com/nf-core/tools/issues/1139)
* Added support for a `bump-versions` configuratoin file [[#1142](https://github.com/nf-core/tools/issues/1142)]
* Fixed `nf-core modules create-test-yml` so it doesn't break when the output directory is supplied [[#1148](https://github.com/nf-core/tools/issues/1148)]
* Updated `nf-core modules lint` to work with new directory structure [[#1159](https://github.com/nf-core/tools/issues/1159)]
* Updated `nf-core modules install` and `modules.json` to work with new directory structure ([#1159](https://github.com/nf-core/tools/issues/1159))
Expand Down
19 changes: 4 additions & 15 deletions nf_core/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,13 @@ def _load(self):
def _load_lint_config(self):
"""Parse a pipeline lint config file.

Look for a file called either `.nf-core-lint.yml` or
`.nf-core-lint.yaml` in the pipeline root directory and parse it.
(`.yml` takes precedence).
Load the '.nf-core.yml' config file and extract
the lint config from it

Add parsed config to the `self.lint_config` class attribute.
"""
config_fn = os.path.join(self.wf_path, ".nf-core-lint.yml")

# Pick up the file if it's .yaml instead of .yml
if not os.path.isfile(config_fn):
config_fn = os.path.join(self.wf_path, ".nf-core-lint.yaml")

# Load the YAML
try:
with open(config_fn, "r") as fh:
self.lint_config = yaml.safe_load(fh)
except FileNotFoundError:
log.debug("No lint config file found: {}".format(config_fn))
tools_config = nf_core.utils.load_tools_config(self.wf_path)
self.lint_config = tools_config.get("lint", {})

# Check if we have any keys that don't match lint test names
for k in self.lint_config:
Expand Down
157 changes: 89 additions & 68 deletions nf_core/modules/bump_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import print_function
import logging
import questionary
import os
import re
import rich
from rich.console import Console
Expand All @@ -15,6 +16,7 @@
import rich
from nf_core.utils import rich_force_colors
import sys
import yaml

import nf_core.utils
import nf_core.modules.module_utils
Expand All @@ -32,6 +34,7 @@ def __init__(self, pipeline_dir):
self.updated = None
self.failed = None
self.show_up_to_date = None
self.tools_config = {}

def bump_versions(self, module=None, all_modules=False, show_uptodate=False):
"""
Expand Down Expand Up @@ -61,6 +64,9 @@ def bump_versions(self, module=None, all_modules=False, show_uptodate=False):
# Get list of all modules
_, nfcore_modules = nf_core.modules.module_utils.get_installed_modules(self.dir)

# Load the .nf-core-tools.config
self.tools_config = nf_core.utils.load_tools_config(self.dir)

# Prompt for module or all
if module is None and not all_modules:
question = {
Expand Down Expand Up @@ -112,6 +118,7 @@ def bump_module_version(self, module: NFCoreModule):
Args:
module: NFCoreModule
"""
config_version = None
# Extract bioconda version from `main.nf`
bioconda_packages = self.get_bioconda_version(module)

Expand All @@ -120,88 +127,102 @@ def bump_module_version(self, module: NFCoreModule):
self.failed.append((f"Ignoring mulled container", module.module_name))
return False

# Check for correct version and newer versions
# Don't update if blocked in blacklist
self.bump_versions_config = self.tools_config.get("bump-versions", {})
if module.module_name in self.bump_versions_config.keys():
config_version = self.bump_versions_config[module.module_name]
if not config_version:
self.up_to_date.append((f"Omitting module due to config: {module.module_name}", module.module_name))
return False

# check for correct version and newer versions
bioconda_tool_name = bioconda_packages[0].split("=")[0].replace("bioconda::", "").strip("'").strip('"')
bp = bioconda_packages[0]
bp = bp.strip("'").strip('"')
try:
bioconda_version = bp.split("=")[1]
response = nf_core.utils.anaconda_package(bp)
except LookupError as e:
self.failed.append((f"Conda version not specified correctly: {module.main_nf}", module.module_name))
return False
except ValueError as e:
self.failed.append((f"Conda version not specified correctly: {module.main_nf}", module.module_name))
return False
else:
bioconda_version = bp.split("=")[1]

if not config_version:
try:
response = nf_core.utils.anaconda_package(bp)
except LookupError as e:
self.failed.append((f"Conda version not specified correctly: {module.main_nf}", module.module_name))
return False
except ValueError as e:
self.failed.append((f"Conda version not specified correctly: {module.main_nf}", module.module_name))
return False

# Check that required version is available at all
if bioconda_version not in response.get("versions"):
self.failed.append((f"Conda package had unknown version: `{module.main_nf}`", module.module_name))
return False

# Check version is latest available
last_ver = response.get("latest_version")
if last_ver is not None and last_ver != bioconda_version:
log.debug(f"Updating version for {module.module_name}")
# Get docker and singularity container links
try:
docker_img, singularity_img = nf_core.utils.get_biocontainer_tag(bioconda_tool_name, last_ver)
except LookupError as e:
self.failed.append((f"Could not download container tags: {e}", module.module_name))
return False
else:
last_ver = config_version

patterns = [
(bioconda_packages[0], f"'bioconda::{bioconda_tool_name}={last_ver}'"),
(r"quay.io/biocontainers/{}:[^'\"\s]+".format(bioconda_tool_name), docker_img),
(
r"https://depot.galaxyproject.org/singularity/{}:[^'\"\s]+".format(bioconda_tool_name),
singularity_img,
),
]

with open(module.main_nf, "r") as fh:
content = fh.read()

# Go over file content of main.nf and find replacements
for pattern in patterns:
found_match = False
newcontent = []
for line in content.splitlines():

# Match the pattern
matches_pattern = re.findall("^.*{}.*$".format(pattern[0]), line)
if matches_pattern:
found_match = True

# Replace the match
newline = re.sub(pattern[0], pattern[1], line)
newcontent.append(newline)
# No match, keep line as it is
else:
newcontent.append(line)

if found_match:
content = "\n".join(newcontent)
if last_ver is not None and last_ver != bioconda_version:
log.debug(f"Updating version for {module.module_name}")
# Get docker and singularity container links
try:
docker_img, singularity_img = nf_core.utils.get_biocontainer_tag(bioconda_tool_name, last_ver)
except LookupError as e:
self.failed.append((f"Could not download container tags: {e}", module.module_name))
return False

patterns = [
(bioconda_packages[0], f"'bioconda::{bioconda_tool_name}={last_ver}'"),
(r"quay.io/biocontainers/{}:[^'\"\s]+".format(bioconda_tool_name), docker_img),
(
r"https://depot.galaxyproject.org/singularity/{}:[^'\"\s]+".format(bioconda_tool_name),
singularity_img,
),
]

with open(module.main_nf, "r") as fh:
content = fh.read()

# Go over file content of main.nf and find replacements
for pattern in patterns:
found_match = False
newcontent = []
for line in content.splitlines():

# Match the pattern
matches_pattern = re.findall("^.*{}.*$".format(pattern[0]), line)
if matches_pattern:
found_match = True

# Replace the match
newline = re.sub(pattern[0], pattern[1], line)
newcontent.append(newline)
# No match, keep line as it is
else:
self.failed.append(
(f"Did not find pattern {pattern[0]} in module {module.module_name}", module.module_name)
)
return False

# Write new content to the file
with open(module.main_nf, "w") as fh:
fh.write(content)

self.updated.append(
(
f"Module updated: {bioconda_version} --> {last_ver}",
module.module_name,
newcontent.append(line)

if found_match:
content = "\n".join(newcontent)
else:
self.failed.append(
(f"Did not find pattern {pattern[0]} in module {module.module_name}", module.module_name)
)
return False

# Write new content to the file
with open(module.main_nf, "w") as fh:
fh.write(content)

self.updated.append(
(
f"Module updated: {bioconda_version} --> {last_ver}",
module.module_name,
)
return True
)
return True

else:
self.up_to_date.append((f"Module version up to date: {module.module_name}", module.module_name))
return True
else:
self.up_to_date.append((f"Module version up to date: {module.module_name}", module.module_name))
return True

def get_bioconda_version(self, module):
"""
Expand Down
5 changes: 4 additions & 1 deletion nf_core/modules/nfcore_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ def __init__(self, module_dir, repo_type, base_dir, nf_core_module=True):
if self.repo_type == "pipeline":
self.module_name = module_dir.split("nf-core/modules" + os.sep)[1]
else:
self.module_name = module_dir.split("modules/modules" + os.sep)[1]
if "modules/modules" in module_dir:
self.module_name = module_dir.split("modules/modules" + os.sep)[1]
else:
self.module_name = module_dir.split("modules" + os.sep)[1]

self.test_dir = os.path.join(self.base_dir, "tests", "modules", self.module_name)
self.test_yml = os.path.join(self.test_dir, "test.yml")
Expand Down
38 changes: 38 additions & 0 deletions nf_core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,3 +735,41 @@ def get_repo_releases_branches(pipeline, wfs):

# Return pipeline again in case we added the nf-core/ prefix
return pipeline, wf_releases, wf_branches


def load_tools_config(dir="."):
"""
Parse the nf-core.yml configuration file

Look for a file called either `.nf-core.yml` or `.nf-core.yaml`

Also looks for the deprecated file `.nf-core-lint.yml/yaml` and issues
a warning that this file will be deprecated in the future

Returns the loaded config dict or False, if the file couldn't be loaded
"""
tools_config = {}
config_fn = os.path.join(dir, ".nf-core.yml")

# Check if old config file is used
old_config_fn_yml = os.path.join(dir, ".nf-core-lint.yml")
old_config_fn_yaml = os.path.join(dir, ".nf-core-lint.yaml")

if os.path.isfile(old_config_fn_yml) or os.path.isfile(old_config_fn_yaml):
log.error(
f"Deprecated `nf-core-lint.yml` file found! The file will not be loaded. Please rename the file to `.nf-core.yml`."
)
return {}

if not os.path.isfile(config_fn):
config_fn = os.path.join(dir, ".nf-core.yaml")

# Load the YAML
try:
with open(config_fn, "r") as fh:
tools_config = yaml.safe_load(fh)
except FileNotFoundError:
log.debug(f"No tools config file found: {config_fn}")
return {}

return tools_config
4 changes: 2 additions & 2 deletions tests/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def test_load_lint_config_ignore_all_tests(self):
lint_obj = nf_core.lint.PipelineLint(new_pipeline)

# Make a config file listing all test names
config_dict = {test_name: False for test_name in lint_obj.lint_tests}
with open(os.path.join(new_pipeline, ".nf-core-lint.yml"), "w") as fh:
config_dict = {"lint": {test_name: False for test_name in lint_obj.lint_tests}}
with open(os.path.join(new_pipeline, ".nf-core.yml"), "w") as fh:
yaml.dump(config_dict, fh)

# Load the new lint config file and check
Expand Down