Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f38b5d0
Remove nf-core modules check
ewels Mar 15, 2021
910e139
Comment out 'nf-core modules update' cli command for now
ewels Mar 15, 2021
7d527e6
Modules install + remove - exit if an invalid directory
ewels Mar 15, 2021
bcd6f62
Create - rename .github/filters.yml to tests/pytest_include.yml
ewels Mar 15, 2021
2c64007
Add awesome fuzzy-finder for nf-core modules install
ewels Mar 15, 2021
91a82a3
modules list - output rich table or JSON
ewels Mar 15, 2021
210282d
nf-core modules list - print local installed modules
ewels Mar 15, 2021
9c7a491
Fix pytest_include.yml path
ewels Mar 15, 2021
df24e78
Blacken
ewels Mar 15, 2021
0d96227
Fix pytests
ewels Mar 15, 2021
37e34ab
Fix merge conflicts, use pytest_software.yml
ewels Mar 15, 2021
f493287
Cut down cli help text length for nf-core modules create
ewels Mar 15, 2021
336de7d
Drop 'process' directory when creating a new local module
ewels Mar 15, 2021
ab5c1e2
Remove linebreaks from module template around prefix
ewels Mar 15, 2021
cf4a78f
Update main.nf
drpatelh Mar 15, 2021
e212eba
Create - fix bug when conda package not found for meta info.
ewels Mar 15, 2021
55fe76d
Modules template - remove example command flags
ewels Mar 15, 2021
4fb0895
Module create - strip cookiecutter template structure back
ewels Mar 15, 2021
fef60f5
modules create - switch to jinja2 instead of cookiecutter
ewels Mar 15, 2021
7b09297
Use |upper filter instead of variable specially for uppercase
ewels Mar 15, 2021
94c614b
Just throw all object attributes at the jinja template
ewels Mar 15, 2021
50b3be5
Linebreaks in test main.nf
ewels Mar 15, 2021
464260a
Added more help text explaining links
ewels Mar 15, 2021
fc1e45f
Update example test data path
ewels Mar 15, 2021
17db9a5
Log about PROFILE, fix tests bug with import path for no subtool
ewels Mar 15, 2021
b08dbb0
Test yml builder - fix minor yaml output issues
ewels Mar 15, 2021
19da126
Update pytest with new path
ewels Mar 15, 2021
494477f
Add back example flags if using meta
ewels Mar 15, 2021
5e82962
modules create-test-yml - prompt to set profile to docker, singularit…
ewels Mar 15, 2021
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
148 changes: 57 additions & 91 deletions nf_core/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,53 +354,65 @@ def modules(ctx, repository, branch):

@modules.command(help_priority=1)
@click.pass_context
def list(ctx):
@click.argument("pipeline_dir", type=click.Path(exists=True), required=False, metavar="(<pipeline directory>)")
@click.option("-j", "--json", is_flag=True, help="Print as JSON to stdout")
def list(ctx, pipeline_dir, json):
"""
List available software modules.

Lists all currently available software wrappers in the nf-core/modules repository.
If a pipeline directory is given, lists all modules installed locally.

If no pipeline directory is given, lists all currently available
software wrappers in the nf-core/modules repository.
"""
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
print(mods.list_modules())
print(mods.list_modules(pipeline_dir, json))


@modules.command(help_priority=2)
@click.pass_context
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.argument("tool", type=str, required=True, metavar="<tool name>")
@click.argument("tool", type=str, required=False, metavar="(<tool name>)")
def install(ctx, pipeline_dir, tool):
"""
Add a DSL2 software wrapper module to a pipeline.

Given a software name, finds the relevant files in nf-core/modules
and copies to the pipeline along with associated metadata.
"""
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.pipeline_dir = pipeline_dir
mods.install(tool)


@modules.command(help_priority=3)
@click.pass_context
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.argument("tool", type=str, metavar="<tool name>")
@click.option("-f", "--force", is_flag=True, default=False, help="Force overwrite of files")
def update(ctx, tool, pipeline_dir, force):
If <tool name> is not supplied on the command line, an interactive fuzzy-finder
tool is given with available nf-core module names.
"""
Update one or all software wrapper modules.
try:
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.pipeline_dir = pipeline_dir
mods.install(tool)
except UserWarning as e:
log.critical(e)
sys.exit(1)

Compares a currently installed module against what is available in nf-core/modules.
Fetchs files and updates all relevant files for that software wrapper.

If no module name is specified, loops through all currently installed modules.
If no version is specified, looks for the latest available version on nf-core/modules.
"""
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.pipeline_dir = pipeline_dir
mods.update(tool, force=force)
# TODO: Not yet implemented
# @modules.command(help_priority=3)
# @click.pass_context
# @click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
# @click.argument("tool", type=str, metavar="<tool name>")
# def update(ctx, tool, pipeline_dir):
# """
# Update one or all software wrapper modules.
#
# Compares a currently installed module against what is available in nf-core/modules.
# Fetchs files and updates all relevant files for that software wrapper.
#
# If no module name is specified, loops through all currently installed modules.
# If no version is specified, looks for the latest available version on nf-core/modules.
# """
# mods = nf_core.modules.PipelineModules()
# mods.modules_repo = ctx.obj["modules_repo_obj"]
# mods.pipeline_dir = pipeline_dir
# mods.update(tool)


@modules.command(help_priority=4)
Expand All @@ -411,56 +423,24 @@ def remove(ctx, pipeline_dir, tool):
"""
Remove a software wrapper from a pipeline.
"""
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.pipeline_dir = pipeline_dir
mods.remove(tool)


@modules.command(help_priority=5)
@click.pass_context
def check(ctx):
"""
Check that imported module code has not been modified.

Compares a software module against the copy on nf-core/modules.
If any local modifications are found, the command logs an error
and exits with a non-zero exit code.

Use by the lint tests and automated CI to check that centralised
software wrapper code is only modified in the central repository.
"""
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.check_modules()
try:
mods = nf_core.modules.PipelineModules()
mods.modules_repo = ctx.obj["modules_repo_obj"]
mods.pipeline_dir = pipeline_dir
mods.remove(tool)
except UserWarning as e:
log.critical(e)
sys.exit(1)


@modules.command("create", help_priority=6)
@modules.command("create", help_priority=5)
@click.pass_context
@click.argument("directory", type=click.Path(exists=True), required=True, metavar="<directory>")
@click.argument("tool", type=str, required=True, metavar="<tool/subtool>")
@click.option("-a", "--author", type=str, metavar="<author>", help="GitHub username")
@click.option(
"-l",
"--label",
type=str,
metavar="<process label>",
help="Standard resource label for process i.e. 'process_low', 'process_medium' or 'process_high'",
)
@click.option(
"-m",
"--meta",
is_flag=True,
default=False,
help="Sample information will be provided to module via a 'meta' Groovy map",
)
@click.option(
"-n",
"--no-meta",
is_flag=True,
default=False,
help="Sample information will not be provided to module via a 'meta' Groovy map",
)
@click.option("-a", "--author", type=str, metavar="<author>", help="Module author's GitHub username")
@click.option("-l", "--label", type=str, metavar="<process label>", help="Standard resource label for process")
@click.option("-m", "--meta", is_flag=True, default=False, help="Use Groovy meta map for sample information")
@click.option("-n", "--no-meta", is_flag=True, default=False, help="Don't use meta map for sample information")
@click.option("-f", "--force", is_flag=True, default=False, help="Overwrite any files if they already exist")
def create_module(ctx, directory, tool, author, label, meta, no_meta, force):
"""
Expand All @@ -470,25 +450,11 @@ def create_module(ctx, directory, tool, author, label, meta, no_meta, force):
Tool should be named just <tool> or <tool/subtool>
e.g fastqc or samtools/sort, respectively.

If <directory> is a pipeline, this function creates a file called:
'<directory>/modules/local/tool.nf'
OR
'<directory>/modules/local/tool_subtool.nf'

If <directory> is a clone of nf-core/modules, it creates or modifies the following files:
If <directory> is a pipeline, this function creates a file called
'modules/local/tool_subtool.nf'

\b
modules/software/tool/subtool/
* main.nf
* meta.yml
* functions.nf
modules/tests/software/tool/subtool/
* main.nf
* test.yml
tests/config/pytest_software.yml

The function will attempt to automatically find a Bioconda package called <tool>
and matching Docker / Singularity images from BioContainers.
If <directory> is a clone of nf-core/modules, it creates or modifies files
in 'modules/software', 'modules/tests' and 'tests/config/pytest_software.yml'
"""
# Combine two bool flags into one variable
has_meta = None
Expand All @@ -508,7 +474,7 @@ def create_module(ctx, directory, tool, author, label, meta, no_meta, force):
sys.exit(1)


@modules.command("create-test-yml", help_priority=7)
@modules.command("create-test-yml", help_priority=6)
@click.pass_context
@click.argument("module", type=str, required=True, metavar="<module name>")
@click.option("-r", "--run-tests", is_flag=True, default=False, help="Run the test workflows")
Expand All @@ -534,7 +500,7 @@ def create_test_yml(ctx, module, run_tests, output, force, no_prompts):


## nf-core schema subcommands
@nf_core_cli.group(cls=CustomHelpOrder, help_priority=8)
@nf_core_cli.group(cls=CustomHelpOrder, help_priority=7)
def schema():
"""
Suite of tools for developers to manage pipeline schema.
Expand Down
29 changes: 4 additions & 25 deletions nf_core/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,15 @@
import json
import logging
import os
import prompt_toolkit
import questionary
import re
import subprocess
import textwrap
import webbrowser

import nf_core.schema, nf_core.utils

log = logging.getLogger(__name__)

# Custom style for questionary
nfcore_question_style = prompt_toolkit.styles.Style(
[
("qmark", "fg:ansiblue bold"), # token in front of the question
("question", "bold"), # question text
("answer", "fg:ansigreen nobold"), # submitted answer text behind the question
("pointer", "fg:ansiyellow bold"), # pointer used in select and checkbox prompts
("highlighted", "fg:ansiblue bold"), # pointed-at choice in select and checkbox prompts
("selected", "fg:ansigreen noreverse"), # style for a selected item of a checkbox
("separator", "fg:ansiblack"), # separator in lists
("instruction", ""), # user instructions for select, rawselect, checkbox
("text", ""), # plain text
("disabled", "fg:gray italic"), # disabled choices for select and checkbox prompts
("choice-default", "fg:ansiblack"),
("choice-default-changed", "fg:ansiyellow"),
("choice-required", "fg:ansired"),
]
)


class Launch(object):
""" Class to hold config option to launch a pipeline """
Expand Down Expand Up @@ -268,7 +247,7 @@ def prompt_web_gui(self):
"choices": ["Web based", "Command line"],
"default": "Web based",
}
answer = questionary.unsafe_prompt([question], style=nfcore_question_style)
answer = questionary.unsafe_prompt([question], style=nf_core.utils.nfcore_question_style)
return answer["use_web_gui"] == "Web based"

def launch_web_gui(self):
Expand Down Expand Up @@ -405,12 +384,12 @@ def prompt_param(self, param_id, param_obj, is_required, answers):

# Print the question
question = self.single_param_to_questionary(param_id, param_obj, answers)
answer = questionary.unsafe_prompt([question], style=nfcore_question_style)
answer = questionary.unsafe_prompt([question], style=nf_core.utils.nfcore_question_style)

# If required and got an empty reponse, ask again
while type(answer[param_id]) is str and answer[param_id].strip() == "" and is_required:
log.error("'–-{}' is required".format(param_id))
answer = questionary.unsafe_prompt([question], style=nfcore_question_style)
answer = questionary.unsafe_prompt([question], style=nf_core.utils.nfcore_question_style)

# Ignore if empty
if answer[param_id] == "":
Expand Down Expand Up @@ -480,7 +459,7 @@ def prompt_group(self, group_id, group_obj):
if len(question["choices"]) == 2:
return {}

answer = questionary.unsafe_prompt([question], style=nfcore_question_style)
answer = questionary.unsafe_prompt([question], style=nf_core.utils.nfcore_question_style)
if answer[group_id] == "Continue >>":
while_break = True
# Check if there are any required parameters that don't have answers
Expand Down
1 change: 0 additions & 1 deletion nf_core/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ def sort_pulled_date(wf):
table.add_row(*rowdata, style="dim")
else:
table.add_row(*rowdata)
t_headers = ["Name", "Latest Release", "Released", "Last Pulled", "Have latest release?"]

# Print summary table
return table
Expand Down
17 changes: 0 additions & 17 deletions nf_core/module-template/cookiecutter.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ include { initOptions; saveFiles; getSoftwareName } from './functions'
params.options = [:]
options = initOptions(params.options)

process {{ cookiecutter.tool_name_upper }} {
{{ 'tag "$meta.id"' if cookiecutter.has_meta else "'$bam'" }}
label '{{ cookiecutter.label }}'
process {{ tool_name|upper }} {
{{ 'tag "$meta.id"' if has_meta else "'$bam'" }}
label '{{ label }}'
publishDir "${params.outdir}",
mode: params.publish_dir_mode,
saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:{{ 'meta.id' if cookiecutter.has_meta else "''" }}) }
saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:{{ 'meta.id' if has_meta else "''" }}) }

// TODO nf-core: List required Conda package(s).
// Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
// For Conda, the build (i.e. "h9402c20_2") must be EXCLUDED to support installation on different operating systems.
// TODO nf-core: See section in main README for further information regarding finding and adding container addresses to the section below.
conda (params.enable_conda ? "{{ cookiecutter.bioconda if cookiecutter.bioconda else 'YOUR-TOOL-HERE' }}" : null)
conda (params.enable_conda ? "{{ bioconda if bioconda else 'YOUR-TOOL-HERE' }}" : null)
if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
container "https://depot.galaxyproject.org/singularity/{{ cookiecutter.container_tag if cookiecutter.container_tag else 'YOUR-TOOL-HERE' }}"
container "https://depot.galaxyproject.org/singularity/{{ container_tag if container_tag else 'YOUR-TOOL-HERE' }}"
} else {
container "quay.io/biocontainers/{{ cookiecutter.container_tag if cookiecutter.container_tag else 'YOUR-TOOL-HERE' }}"
container "quay.io/biocontainers/{{ container_tag if container_tag else 'YOUR-TOOL-HERE' }}"
}

input:
Expand All @@ -45,19 +45,19 @@ process {{ cookiecutter.tool_name_upper }} {
// https://github.com/nf-core/modules/blob/master/software/bwa/index/main.nf
// TODO nf-core: Where applicable please provide/convert compressed files as input/output
// e.g. "*.fastq.gz" and NOT "*.fastq", "*.bam" and NOT "*.sam" etc.
{{ 'tuple val(meta), path(bam)' if cookiecutter.has_meta else 'path bam' }}
{{ 'tuple val(meta), path(bam)' if has_meta else 'path bam' }}

output:
// TODO nf-core: Named file extensions MUST be emitted for ALL output channels
{{ 'tuple val(meta), path("*.bam")' if cookiecutter.has_meta else 'path "*.bam"' }} , emit: bam
{{ 'tuple val(meta), path("*.bam")' if has_meta else 'path "*.bam"' }}, emit: bam
// TODO nf-core: List additional required output channels/values here
path "*.version.txt" , emit: version

script:
def software = getSoftwareName(task.process)
{% if cookiecutter.has_meta %}
{% if has_meta -%}
def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
{% endif %}
{%- endif %}
// TODO nf-core: Where possible, a command MUST be provided to obtain the version number of the software e.g. 1.10
// If the software is unable to output a version number on the command-line then it can be manually specified
// e.g. https://github.com/nf-core/modules/blob/master/software/homer/annotatepeaks/main.nf
Expand All @@ -71,8 +71,10 @@ process {{ cookiecutter.tool_name_upper }} {
sort \\
$options.args \\
-@ $task.cpus \\
{%- if has_meta %}
-o ${prefix}.bam \\
-T $prefix \\
{%- endif %}
$bam

echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt
Expand Down
Loading