Skip to content
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

Support multiple file versions #1247

Merged
merged 44 commits into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1c927e9
Change filter to support multiple version file outputs
mahesh-panchal Aug 2, 2021
e147cc9
Add collectFile to combine different versions
mahesh-panchal Aug 2, 2021
2942f83
Change scrape versions to comma separate multiple versions
mahesh-panchal Aug 2, 2021
8266610
Update CHANGELOG.md
mahesh-panchal Aug 2, 2021
deaf0c1
black lint update
mahesh-panchal Aug 2, 2021
e3c5fe0
Software versions as YAML output
mahesh-panchal Aug 3, 2021
7a894d6
Add comment to add version for each tool
mahesh-panchal Aug 3, 2021
d9267e9
Update output
mahesh-panchal Aug 3, 2021
71d20e1
Collect YAML files together
mahesh-panchal Aug 3, 2021
221e492
Add workflow data to software versions
mahesh-panchal Aug 3, 2021
89d2918
Replace software versions tsv with yml
mahesh-panchal Aug 3, 2021
3945ee8
Update GET_SOFTWARE_VERSION channel names
mahesh-panchal Aug 3, 2021
e6bc9ff
Update nf_core/pipeline-template/modules/local/get_software_versions.nf
mahesh-panchal Aug 4, 2021
85839d3
Move sed for readability
mahesh-panchal Aug 4, 2021
e99da1e
Update nf_core/module-template/modules/main.nf
mahesh-panchal Aug 4, 2021
5ce54a4
Remove Dash from YAML
mahesh-panchal Aug 10, 2021
fd93ebb
Port changes from prototype https://github.com/nf-core/rnaseq/pull/689
mahesh-panchal Aug 12, 2021
3a10d60
Update comment
mahesh-panchal Aug 12, 2021
1a7993d
Update process output
mahesh-panchal Aug 12, 2021
d18174d
Add getProcessName to modules functions_nf lint
mahesh-panchal Aug 12, 2021
edae68b
Update file_exists lint to remove scrape_versions.py
mahesh-panchal Aug 12, 2021
8ae12e3
Remove scrape software version from files unchanged.
mahesh-panchal Aug 12, 2021
074f1c4
Fix YAML output
mahesh-panchal Aug 12, 2021
c21dabd
Update nf_core/module-template/modules/main.nf
mahesh-panchal Aug 12, 2021
70d14aa
Remove getSoftwareName function
mahesh-panchal Aug 12, 2021
1ee14e9
remove software var
mahesh-panchal Aug 12, 2021
a45c66d
Rename publish_dir directory
mahesh-panchal Aug 12, 2021
6682211
Update nf_core/module-template/modules/main.nf
mahesh-panchal Aug 12, 2021
30bbc8a
Update version.txt reference to versions.yml
mahesh-panchal Aug 12, 2021
a98588b
Update lint check for software
mahesh-panchal Aug 12, 2021
c46e219
Make publish_dirs lowercase
mahesh-panchal Aug 12, 2021
77a89e2
Revert "Make publish_dirs lowercase"
mahesh-panchal Aug 13, 2021
bd394ba
Revert "Update lint check for software"
mahesh-panchal Aug 13, 2021
610585c
Revert "Rename publish_dir directory"
mahesh-panchal Aug 13, 2021
2198376
Revert "remove software var"
mahesh-panchal Aug 13, 2021
4240459
Revert "Remove getSoftwareName function"
mahesh-panchal Aug 13, 2021
3bf1b7f
Remove def software line
mahesh-panchal Aug 13, 2021
d965d62
Remove reference to $software
mahesh-panchal Aug 13, 2021
8635612
Update output docs (software_versions.tsv ->software_versions.yml)
mahesh-panchal Sep 14, 2021
2af8709
Do not update nf-core modules in pipeline template yet
grst Sep 23, 2021
6c52385
Update functions.nf in module template and in local pipeline modules
grst Sep 23, 2021
da8dadb
Show linting messages when linting tests failed
grst Sep 23, 2021
9a0dfd9
Print linting error message
grst Sep 23, 2021
82ce4b4
Update nf_core/pipeline-template/docs/output.md
grst Sep 23, 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Template

* Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.

### General

* Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
Expand Down
2 changes: 0 additions & 2 deletions nf_core/lint/files_exist.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def files_exist(self):
assets/email_template.txt
assets/nf-core-PIPELINE_logo.png
assets/sendmail_template.txt
bin/scrape_software_versions.py
conf/modules.config
conf/test.config
conf/test_full.config
Expand Down Expand Up @@ -121,7 +120,6 @@ def files_exist(self):
[os.path.join("assets", "email_template.txt")],
[os.path.join("assets", "sendmail_template.txt")],
[os.path.join("assets", f"nf-core-{short_name}_logo.png")],
[os.path.join("bin", "scrape_software_versions.py")],
[os.path.join("conf", "modules.config")],
[os.path.join("conf", "test.config")],
[os.path.join("conf", "test_full.config")],
Expand Down
1 change: 0 additions & 1 deletion nf_core/lint/files_unchanged.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ def files_unchanged(self):
[os.path.join("assets", "email_template.txt")],
[os.path.join("assets", "sendmail_template.txt")],
[os.path.join("assets", f"nf-core-{short_name}_logo.png")],
[os.path.join("bin", "scrape_software_versions.py")],
[os.path.join("docs", "images", f"nf-core-{short_name}_logo.png")],
[os.path.join("docs", "README.md")],
[os.path.join("lib", "nfcore_external_java_deps.jar")],
Expand Down
54 changes: 32 additions & 22 deletions nf_core/module-template/modules/functions.nf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ def getSoftwareName(task_process) {
return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()
mahesh-panchal marked this conversation as resolved.
Show resolved Hide resolved
}

//
// Extract name of module from process name using $task.process
//
def getProcessName(task_process) {
return task_process.tokenize(':')[-1]
}

//
// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules
//
Expand Down Expand Up @@ -37,32 +44,35 @@ def getPathFromList(path_list) {
// Function to save/publish module results
//
def saveFiles(Map args) {
if (!args.filename.endsWith('.version.txt')) {
def ioptions = initOptions(args.options)
def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
if (ioptions.publish_by_meta) {
def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta
for (key in key_list) {
if (args.meta && key instanceof String) {
def path = key
if (args.meta.containsKey(key)) {
path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key]
}
path = path instanceof String ? path : ''
path_list.add(path)
def ioptions = initOptions(args.options)
def path_list = [ ioptions.publish_dir ?: args.publish_dir ]

// Do not publish versions.yml unless running from pytest workflow
if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) {
return null
}
if (ioptions.publish_by_meta) {
def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta
for (key in key_list) {
if (args.meta && key instanceof String) {
def path = key
if (args.meta.containsKey(key)) {
path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key]
}
path = path instanceof String ? path : ''
path_list.add(path)
}
}
if (ioptions.publish_files instanceof Map) {
for (ext in ioptions.publish_files) {
if (args.filename.endsWith(ext.key)) {
def ext_list = path_list.collect()
ext_list.add(ext.value)
return "${getPathFromList(ext_list)}/$args.filename"
}
}
if (ioptions.publish_files instanceof Map) {
for (ext in ioptions.publish_files) {
if (args.filename.endsWith(ext.key)) {
def ext_list = path_list.collect()
ext_list.add(ext.value)
return "${getPathFromList(ext_list)}/$args.filename"
}
} else if (ioptions.publish_files == null) {
return "${getPathFromList(path_list)}/$args.filename"
}
} else if (ioptions.publish_files == null) {
return "${getPathFromList(path_list)}/$args.filename"
}
}
11 changes: 7 additions & 4 deletions nf_core/module-template/modules/main.nf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Import generic module functions
include { initOptions; saveFiles; getSoftwareName } from './functions'
include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'

// TODO nf-core: If in doubt look at other nf-core/modules to see how we are doing things! :)
// https://github.com/nf-core/modules/tree/master/software
Expand Down Expand Up @@ -52,16 +52,16 @@ process {{ tool_name_underscore|upper }} {
// TODO nf-core: Named file extensions MUST be emitted for ALL output channels
{{ '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
path "versions.yml" , emit: version

script:
def software = getSoftwareName(task.process)
{% if has_meta -%}
def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
{%- 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
// Each software used MUST provide the software name and version number in the YAML version file (versions.yml)
// TODO nf-core: It MUST be possible to pass additional parameters to the tool as a command-line string via the "$options.args" variable
// TODO nf-core: If the tool supports multi-threading then you MUST provide the appropriate parameter
// using the Nextflow "task" variable e.g. "--threads $task.cpus"
Expand All @@ -78,6 +78,9 @@ process {{ tool_name_underscore|upper }} {
{%- endif %}
$bam

echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt
cat <<-END_VERSIONS > versions.yml
${getProcessName(task.process)}:
samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$//' )
END_VERSIONS
"""
}
2 changes: 1 addition & 1 deletion nf_core/module-template/modules/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ output:
- version:
type: file
description: File containing software version
pattern: "*.{version.txt}"
pattern: "versions.yml"
## TODO nf-core: Delete / customise this example output
- bam:
type: file
Expand Down
2 changes: 1 addition & 1 deletion nf_core/modules/lint/functions_nf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def functions_nf(module_lint_object, module):
return

# Test whether all required functions are present
required_functions = ["getSoftwareName", "initOptions", "getPathFromList", "saveFiles"]
required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
lines = "\n".join(lines)
contains_all_functions = True
for f in required_functions:
Expand Down
6 changes: 0 additions & 6 deletions nf_core/modules/lint/main_nf.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,6 @@ def check_script_section(self, lines):
"""
script = "".join(lines)

# check for software
if re.search("\s*def\s*software\s*=\s*getSoftwareName", script):
drpatelh marked this conversation as resolved.
Show resolved Hide resolved
self.passed.append(("main_nf_version_script", "Software version specified in script section", self.main_nf))
else:
self.warned.append(("main_nf_version_script", "Software version unspecified in script section", self.main_nf))

# check for prefix (only if module has a meta map as input)
if self.has_meta:
if re.search("\s*prefix\s*=\s*options.suffix", script):
Expand Down
36 changes: 0 additions & 36 deletions nf_core/pipeline-template/bin/scrape_software_versions.py

This file was deleted.

2 changes: 1 addition & 1 deletion nf_core/pipeline-template/docs/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQ

* `pipeline_info/`
* Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`.
* Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.tsv`.
* Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline.
* Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`.

</details>
Expand Down
54 changes: 32 additions & 22 deletions nf_core/pipeline-template/modules/local/functions.nf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ def getSoftwareName(task_process) {
return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()
}

//
// Extract name of module from process name using $task.process
//
def getProcessName(task_process) {
return task_process.tokenize(':')[-1]
}

//
// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules
//
Expand Down Expand Up @@ -37,32 +44,35 @@ def getPathFromList(path_list) {
// Function to save/publish module results
//
def saveFiles(Map args) {
if (!args.filename.endsWith('.version.txt')) {
def ioptions = initOptions(args.options)
def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
if (ioptions.publish_by_meta) {
def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta
for (key in key_list) {
if (args.meta && key instanceof String) {
def path = key
if (args.meta.containsKey(key)) {
path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key]
}
path = path instanceof String ? path : ''
path_list.add(path)
def ioptions = initOptions(args.options)
def path_list = [ ioptions.publish_dir ?: args.publish_dir ]

// Do not publish versions.yml unless running from pytest workflow
if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) {
return null
}
if (ioptions.publish_by_meta) {
def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta
for (key in key_list) {
if (args.meta && key instanceof String) {
def path = key
if (args.meta.containsKey(key)) {
path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key]
}
path = path instanceof String ? path : ''
path_list.add(path)
}
}
if (ioptions.publish_files instanceof Map) {
for (ext in ioptions.publish_files) {
if (args.filename.endsWith(ext.key)) {
def ext_list = path_list.collect()
ext_list.add(ext.value)
return "${getPathFromList(ext_list)}/$args.filename"
}
}
if (ioptions.publish_files instanceof Map) {
for (ext in ioptions.publish_files) {
if (args.filename.endsWith(ext.key)) {
def ext_list = path_list.collect()
ext_list.add(ext.value)
return "${getPathFromList(ext_list)}/$args.filename"
}
} else if (ioptions.publish_files == null) {
return "${getPathFromList(path_list)}/$args.filename"
}
} else if (ioptions.publish_files == null) {
return "${getPathFromList(path_list)}/$args.filename"
}
}
80 changes: 71 additions & 9 deletions nf_core/pipeline-template/modules/local/get_software_versions.nf
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ process GET_SOFTWARE_VERSIONS {
mode: params.publish_dir_mode,
saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) }

conda (params.enable_conda ? "conda-forge::python=3.8.3" : null)
// This module only requires the PyYAML library, but rather than create a new container on biocontainers we reuse the multiqc container.
conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null)
if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
container "https://depot.galaxyproject.org/singularity/python:3.8.3"
container "https://depot.galaxyproject.org/singularity/multiqc:1.10.1--pyhdfd78af_1"
} else {
container "quay.io/biocontainers/python:3.8.3"
container "quay.io/biocontainers/multiqc:1.10.1--pyhdfd78af_1"
}

cache false
Expand All @@ -21,13 +22,74 @@ process GET_SOFTWARE_VERSIONS {
path versions

output:
path "software_versions.tsv" , emit: tsv
path 'software_versions_mqc.yaml', emit: yaml
path "software_versions.yml" , emit: yml
path "software_versions_mqc.yml" , emit: mqc_yml

script: // This script is bundled with the pipeline, in {{ name }}/bin/
script:
"""
echo $workflow.manifest.version > pipeline.version.txt
echo $workflow.nextflow.version > nextflow.version.txt
scrape_software_versions.py &> software_versions_mqc.yaml
#!/usr/bin/env python

import yaml
from textwrap import dedent

def _make_versions_html(versions):
html = [
dedent(
'''\\
<style>
#nf-core-versions tbody:nth-child(even) {
background-color: #f2f2f2;
}
</style>
<table class="table" style="width:100%" id="nf-core-versions">
<thead>
<tr>
<th> Process Name </th>
<th> Software </th>
<th> Version </th>
</tr>
</thead>
'''
)
]
for process, tmp_versions in sorted(versions.items()):
html.append("<tbody>")
for i, (tool, version) in enumerate(sorted(tmp_versions.items())):
html.append(
dedent(
f'''\\
<tr>
<td><samp>{process if (i == 0) else ''}</samp></td>
<td><samp>{tool}</samp></td>
<td><samp>{version}</samp></td>
</tr>
'''
)
)
html.append("</tbody>")
html.append("</table>")
return "\\n".join(html)

with open("$versions") as f:
versions = yaml.safe_load(f)

versions["Workflow"] = {
"Nextflow": "$workflow.nextflow.version",
"$workflow.manifest.name": "$workflow.manifest.version"
}

versions_mqc = {
'id': 'software_versions',
'section_name': '${workflow.manifest.name} Software Versions',
'section_href': 'https://github.com/${workflow.manifest.name}',
'plot_type': 'html',
'description': 'are collected at run time from the software output.',
'data': _make_versions_html(versions)
}

with open("software_versions.yml", 'w') as f:
yaml.dump(versions, f, default_flow_style=False)
with open("software_versions_mqc.yml", 'w') as f:
yaml.dump(versions_mqc, f, default_flow_style=False)
"""
}
Loading