From d7a1e3487e8d0b1128a828e81d462cc26d1acf22 Mon Sep 17 00:00:00 2001 From: nf-core-bot <core@nf-co.re> Date: Tue, 14 Dec 2021 16:42:35 +0000 Subject: [PATCH] Template update for nf-core/tools version 2.2 --- .gitattributes | 2 + .github/CONTRIBUTING.md | 38 ++------ .github/ISSUE_TEMPLATE/bug_report.md | 63 ------------- .github/ISSUE_TEMPLATE/bug_report.yml | 52 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.md | 32 ------- .github/ISSUE_TEMPLATE/feature_request.yml | 11 +++ .github/workflows/awsfulltest.yml | 8 +- .github/workflows/awstest.yml | 10 +- .github/workflows/ci.yml | 23 +++-- .github/workflows/linting_comment.yml | 1 + CHANGELOG.md | 2 +- CITATIONS.md | 2 +- README.md | 13 ++- assets/multiqc_config.yaml | 2 +- assets/nf-core-hic_logo.png | Bin 14014 -> 0 bytes assets/nf-core-hic_logo_light.png | Bin 0 -> 8338 bytes assets/sendmail_template.txt | 4 +- bin/scrape_software_versions.py | 36 ------- conf/base.config | 3 + conf/modules.config | 55 ++++++----- conf/test.config | 4 +- docs/images/nf-core-hic_logo.png | Bin 27797 -> 0 bytes docs/images/nf-core-hic_logo_dark.png | 9 ++ docs/images/nf-core-hic_logo_light.png | 13 +++ docs/output.md | 2 +- docs/usage.md | 36 ------- lib/NfcoreSchema.groovy | 26 +++-- lib/NfcoreTemplate.groovy | 30 ++---- lib/Utils.groovy | 7 -- lib/WorkflowMain.groovy | 6 +- modules.json | 9 +- modules/local/functions.nf | 68 ------------- modules/local/get_software_versions.nf | 33 ------- modules/local/samplesheet_check.nf | 24 ++--- .../custom/dumpsoftwareversions/main.nf | 21 +++++ .../custom/dumpsoftwareversions/meta.yml | 34 +++++++ .../templates/dumpsoftwareversions.py | 89 ++++++++++++++++++ modules/nf-core/modules/fastqc/functions.nf | 68 ------------- modules/nf-core/modules/fastqc/main.nf | 39 ++++---- modules/nf-core/modules/fastqc/meta.yml | 7 +- modules/nf-core/modules/multiqc/functions.nf | 68 ------------- modules/nf-core/modules/multiqc/main.nf | 31 +++--- modules/nf-core/modules/multiqc/meta.yml | 7 +- nextflow.config | 33 +++---- nextflow_schema.json | 29 ------ subworkflows/local/input_check.nf | 8 +- workflows/hic.nf | 44 +++------ 48 files changed, 432 insertions(+), 671 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 assets/nf-core-hic_logo.png create mode 100644 assets/nf-core-hic_logo_light.png delete mode 100755 bin/scrape_software_versions.py delete mode 100644 docs/images/nf-core-hic_logo.png create mode 100644 docs/images/nf-core-hic_logo_dark.png create mode 100644 docs/images/nf-core-hic_logo_light.png delete mode 100644 modules/local/functions.nf delete mode 100644 modules/local/get_software_versions.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/main.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py delete mode 100644 modules/nf-core/modules/fastqc/functions.nf delete mode 100644 modules/nf-core/modules/multiqc/functions.nf diff --git a/.gitattributes b/.gitattributes index 7fe5500..050bb12 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ *.config linguist-language=nextflow +modules/nf-core/** linguist-generated +subworkflows/nf-core/** linguist-generated diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4b432d2..383bc0e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -68,16 +68,13 @@ If you wish to contribute a new step, please use the following coding standards: 1. Define the corresponding input channel into your new process from the expected previous process channel 2. Write the process block (see below). 3. Define the output channel if needed (see below). -4. Add any new flags/options to `nextflow.config` with a default (see below). -5. Add any new flags/options to `nextflow_schema.json` with help text (with `nf-core schema build`). -6. Add any new flags/options to the help message (for integer/text parameters, print to help the corresponding `nextflow.config` parameter). -7. Add sanity checks for all relevant parameters. -8. Add any new software to the `scrape_software_versions.py` script in `bin/` and the version command to the `scrape_software_versions` process in `main.nf`. -9. Do local tests that the new code works properly and as expected. -10. Add a new test command in `.github/workflow/ci.yml`. -11. If applicable add a [MultiQC](https://https://multiqc.info/) module. -12. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, name clean up, General Statistics Table column order, and module figures are in the right order. -13. Optional: Add any descriptions of MultiQC report sections and output files to `docs/output.md`. +4. Add any new parameters to `nextflow.config` with a default (see below). +5. Add any new parameters to `nextflow_schema.json` with help text (via the `nf-core schema build` tool). +6. Add sanity checks and validation for all relevant parameters. +7. Perform local tests to validate that the new code works as expected. +8. If applicable, add a new test command in `.github/workflow/ci.yml`. +9. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, file name clean up and module plots are in the appropriate order. If applicable, add a [MultiQC](https://https://multiqc.info/) module. +10. Add a description of the output files and if relevant any appropriate images from the MultiQC report to `docs/output.md`. ### Default values @@ -102,27 +99,6 @@ Please use the following naming schemes, to make it easy to understand what is g If you are using a new feature from core Nextflow, you may bump the minimum required version of nextflow in the pipeline with: `nf-core bump-version --nextflow . [min-nf-version]` -### Software version reporting - -If you add a new tool to the pipeline, please ensure you add the information of the tool to the `get_software_version` process. - -Add to the script block of the process, something like the following: - -```bash -<YOUR_TOOL> --version &> v_<YOUR_TOOL>.txt 2>&1 || true -``` - -or - -```bash -<YOUR_TOOL> --help | head -n 1 &> v_<YOUR_TOOL>.txt 2>&1 || true -``` - -You then need to edit the script `bin/scrape_software_versions.py` to: - -1. Add a Python regex for your tool's `--version` output (as in stored in the `v_<YOUR_TOOL>.txt` file), to ensure the version is reported as a `v` and the version number e.g. `v2.1.1` -2. Add a HTML entry to the `OrderedDict` for formatting in MultiQC. - ### Images and figures For overview images and other documents we follow the nf-core [style guidelines and examples](https://nf-co.re/developers/design_guidelines). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index bc2507d..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Bug report -about: Report something that is broken or incorrect -labels: bug ---- - -<!-- -# nf-core/hic bug report - -Hi there! - -Thanks for telling us about a problem with the pipeline. -Please delete this text and anything that's not relevant from the template below: ---> - -## Check Documentation - -I have checked the following places for your error: - -- [ ] [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) -- [ ] [nf-core/hic pipeline documentation](https://nf-co.re/hic/usage) - -## Description of the bug - -<!-- A clear and concise description of what the bug is. --> - -## Steps to reproduce - -Steps to reproduce the behaviour: - -1. Command line: <!-- [e.g. `nextflow run ...`] --> -2. See error: <!-- [Please provide your error message] --> - -## Expected behaviour - -<!-- A clear and concise description of what you expected to happen. --> - -## Log files - -Have you provided the following extra information/files: - -- [ ] The command used to run the pipeline -- [ ] The `.nextflow.log` file <!-- this is a hidden file in the directory where you launched the pipeline --> - -## System - -- Hardware: <!-- [e.g. HPC, Desktop, Cloud...] --> -- Executor: <!-- [e.g. slurm, local, awsbatch...] --> -- OS: <!-- [e.g. CentOS Linux, macOS, Linux Mint...] --> -- Version <!-- [e.g. 7, 10.13.6, 18.3...] --> - -## Nextflow Installation - -- Version: <!-- [e.g. 21.04.0] --> - -## Container engine - -- Engine: <!-- [e.g. Conda, Docker, Singularity, Podman, Shifter or Charliecloud] --> -- version: <!-- [e.g. 1.0.0] --> - -## Additional context - -<!-- Add any other context about the problem here. --> diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..3505b2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ + +name: Bug report +description: Report something that is broken or incorrect +labels: bug +body: + + - type: markdown + attributes: + value: | + Before you post this issue, please check the documentation: + + - [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) + - [nf-core/hic pipeline documentation](https://nf-co.re/hic/usage) + + - type: textarea + id: description + attributes: + label: Description of the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: command_used + attributes: + label: Command used and terminal output + description: Steps to reproduce the behaviour. Please paste the command you used to launch the pipeline and the output from your terminal. + render: console + placeholder: | + $ nextflow run ... + + Some output where something broke + + - type: textarea + id: files + attributes: + label: Relevant files + description: | + Please drag and drop the relevant files here. Create a `.zip` archive if the extension is not allowed. + Your verbose log file `.nextflow.log` is often useful _(this is a hidden file in the directory where you launched the pipeline)_ as well as custom Nextflow configuration files. + + - type: textarea + id: system + attributes: + label: System information + description: | + * Nextflow version _(eg. 21.10.3)_ + * Hardware _(eg. HPC, Desktop, Cloud)_ + * Executor _(eg. slurm, local, awsbatch)_ + * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter or Charliecloud)_ + * OS _(eg. CentOS Linux, macOS, Linux Mint)_ + * Version of nf-core/hic _(eg. 1.1, 1.5, 1.8.2)_ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 887f045..379c60a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,3 @@ -blank_issues_enabled: false contact_links: - name: Join nf-core url: https://nf-co.re/join diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 2cec9b3..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for the nf-core/hic pipeline -labels: enhancement ---- - -<!-- -# nf-core/hic feature request - -Hi there! - -Thanks for suggesting a new feature for the pipeline! -Please delete this text and anything that's not relevant from the template below: ---> - -## Is your feature request related to a problem? Please describe - -<!-- A clear and concise description of what the problem is. --> - -<!-- e.g. [I'm always frustrated when ...] --> - -## Describe the solution you'd like - -<!-- A clear and concise description of what you want to happen. --> - -## Describe alternatives you've considered - -<!-- A clear and concise description of any alternative solutions or features you've considered. --> - -## Additional context - -<!-- Add any other context about the feature request here. --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d411b18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,11 @@ +name: Feature request +description: Suggest an idea for the nf-core/hic pipeline +labels: enhancement +body: + - type: textarea + id: description + attributes: + label: Description of feature + description: Please describe your suggestion for a new feature. It might help to describe a problem or use case, plus any alternatives that you have considered. + validations: + required: true diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index caa6a1c..dd00a78 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -14,14 +14,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master + uses: nf-core/tower-action@v2 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) # on the `test_full.config` test runs with only one set of parameters with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} @@ -30,5 +30,5 @@ jobs: { "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/hic/results-${{ github.sha }}" } - profiles: '[ "test_full", "aws_tower" ]' - + profiles: test_full,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 654e7b6..cb0c5ee 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -11,18 +11,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master + uses: nf-core/tower-action@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/hic/work-${{ github.sha }} parameters: | { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/hic/results-${{ github.sha }}" + "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/hic/results-test-${{ github.sha }}" } - profiles: '[ "test", "aws_tower" ]' - + profiles: test,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c0b4e6..8ffa52b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,9 @@ on: release: types: [published] -# Uncomment if we need an edge release of Nextflow again -# env: NXF_EDGE: 1 +env: + NXF_ANSI_LOG: false + CAPSULE_LOG: none jobs: test: @@ -17,20 +18,26 @@ jobs: # Only run on push if this is the nf-core dev branch (merged PRs) if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/hic') }} runs-on: ubuntu-latest - env: - NXF_VER: ${{ matrix.nxf_ver }} - NXF_ANSI_LOG: false strategy: matrix: - # Nextflow versions: check pipeline minimum and current latest - nxf_ver: ['21.04.0', ''] + # Nextflow versions + include: + # Test pipeline minimum Nextflow version + - NXF_VER: '21.10.3' + NXF_EDGE: '' + # Test latest edge release of Nextflow + - NXF_VER: '' + NXF_EDGE: '1' steps: - name: Check out pipeline code uses: actions/checkout@v2 - name: Install Nextflow env: - CAPSULE_LOG: none + NXF_VER: ${{ matrix.NXF_VER }} + # Uncomment only if the edge release is more recent than the latest stable release + # See https://github.com/nextflow-io/nextflow/issues/2467 + # NXF_EDGE: ${{ matrix.NXF_EDGE }} run: | wget -qO- get.nextflow.io | bash sudo mv nextflow /usr/local/bin/ diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml index 90f03c6..44d7299 100644 --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -15,6 +15,7 @@ jobs: uses: dawidd6/action-download-artifact@v2 with: workflow: linting.yml + workflow_conclusion: completed - name: Get PR number id: pr_number diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf522d..87c2402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v1.3.0 - [date] +## v1.3.1 - [date] Initial release of nf-core/hic, created with the [nf-core](https://nf-co.re/) template. diff --git a/CITATIONS.md b/CITATIONS.md index da253d2..557e1f8 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -12,7 +12,7 @@ * [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) -* [MultiQC](https://www.ncbi.nlm.nih.gov/pubmed/27312411/) +* [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. ## Software packaging/containerisation tools diff --git a/README.md b/README.md index 3dd2b4b..35b1385 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -#  +#   [](https://github.com/nf-core/hic/actions?query=workflow%3A%22nf-core+CI%22) [](https://github.com/nf-core/hic/actions?query=workflow%3A%22nf-core+linting%22) [](https://nf-co.re/hic/results) [](https://doi.org/10.5281/zenodo.XXXXXXX) -[](https://www.nextflow.io/) +[](https://www.nextflow.io/) [](https://docs.conda.io/en/latest/) [](https://www.docker.com/) [](https://sylabs.io/docs/) @@ -33,18 +33,21 @@ On release, automated continuous integration tests run the pipeline on a full-si ## Quick Start -1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.04.0`) +1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.10.3`) 2. Install any of [`Docker`](https://docs.docker.com/engine/installation/), [`Singularity`](https://www.sylabs.io/guides/3.0/user-guide/), [`Podman`](https://podman.io/), [`Shifter`](https://nersc.gitlab.io/development/shifter/how-to-use/) or [`Charliecloud`](https://hpc.github.io/charliecloud/) for full pipeline reproducibility _(please only use [`Conda`](https://conda.io/miniconda.html) as a last resort; see [docs](https://nf-co.re/usage/configuration#basic-configuration-profiles))_ 3. Download the pipeline and test it on a minimal dataset with a single command: ```console - nextflow run nf-core/hic -profile test,<docker/singularity/podman/shifter/charliecloud/conda/institute> + nextflow run nf-core/hic -profile test,YOURPROFILE ``` + Note that some form of configuration will be needed so that Nextflow knows how to fetch the required software. This is usually done in the form of a config profile (`YOURPROFILE` in the example command above). You can chain multiple config profiles in a comma-separated string. + + > * The pipeline comes with config profiles called `docker`, `singularity`, `podman`, `shifter`, `charliecloud` and `conda` which instruct the pipeline to use the named tool for software management. For example, `-profile test,docker`. > * Please check [nf-core/configs](https://github.com/nf-core/configs#documentation) to see if a custom config file to run nf-core pipelines already exists for your Institute. If so, you can simply use `-profile <institute>` in your command. This will enable either `docker` or `singularity` and set the appropriate execution settings for your local compute environment. - > * If you are using `singularity` then the pipeline will auto-detect this and attempt to download the Singularity images directly as opposed to performing a conversion from Docker images. If you are persistently observing issues downloading Singularity images directly due to timeout or network issues then please use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, it is highly recommended to use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to pre-download all of the required containers before running the pipeline and to set the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options to be able to store and re-use the images from a central location for future pipeline runs. + > * If you are using `singularity` and are persistently observing issues downloading Singularity images directly due to timeout or network issues, then you can use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, you can use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to download images first, before running the pipeline. Setting the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options enables you to store and re-use the images from a central location for future pipeline runs. > * If you are using `conda`, it is highly recommended to use the [`NXF_CONDA_CACHEDIR` or `conda.cacheDir`](https://www.nextflow.io/docs/latest/conda.html) settings to store the environments in a central location for future pipeline runs. 4. Start running your own analysis! diff --git a/assets/multiqc_config.yaml b/assets/multiqc_config.yaml index 41468ca..c05ad14 100644 --- a/assets/multiqc_config.yaml +++ b/assets/multiqc_config.yaml @@ -1,7 +1,7 @@ report_comment: > This report has been generated by the <a href="https://github.com/nf-core/hic" target="_blank">nf-core/hic</a> analysis pipeline. For information about how to interpret these results, please see the - <a href="https://github.com/nf-core/hic" target="_blank">documentation</a>. + <a href="https://nf-co.re/hic" target="_blank">documentation</a>. report_section_order: software_versions: order: -1000 diff --git a/assets/nf-core-hic_logo.png b/assets/nf-core-hic_logo.png deleted file mode 100644 index 37461d9a32ae1f73d9090a3a2387cf8997c9a0ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14014 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 TqUXSdG>E*k^6PR*qoDr>{mh74 diff --git a/assets/nf-core-hic_logo_light.png b/assets/nf-core-hic_logo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..553c19d982fcf9e1e0c9a5f325a8ff290fc087f9 GIT binary patch literal 8338 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 C>s|-| literal 0 HcmV?d00001 diff --git a/assets/sendmail_template.txt b/assets/sendmail_template.txt index 6213286..3c7f723 100644 --- a/assets/sendmail_template.txt +++ b/assets/sendmail_template.txt @@ -12,9 +12,9 @@ $email_html Content-Type: image/png;name="nf-core-hic_logo.png" Content-Transfer-Encoding: base64 Content-ID: <nfcorepipelinelogo> -Content-Disposition: inline; filename="nf-core-hic_logo.png" +Content-Disposition: inline; filename="nf-core-hic_logo_light.png" -<% out << new File("$projectDir/assets/nf-core-hic_logo.png"). +<% out << new File("$projectDir/assets/nf-core-hic_logo_light.png"). bytes. encodeBase64(). toString(). diff --git a/bin/scrape_software_versions.py b/bin/scrape_software_versions.py deleted file mode 100755 index 322c97c..0000000 --- a/bin/scrape_software_versions.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -import os - -results = {} -version_files = [x for x in os.listdir(".") if x.endswith(".version.txt")] -for version_file in version_files: - - software = version_file.replace(".version.txt", "") - if software == "pipeline": - software = "nf-core/hic" - - with open(version_file) as fin: - version = fin.read().strip() - results[software] = version - -# Dump to YAML -print( - """ -id: 'software_versions' -section_name: 'nf-core/hic Software Versions' -section_href: 'https://github.com/nf-core/hic' -plot_type: 'html' -description: 'are collected at run time from the software output.' -data: | - <dl class="dl-horizontal"> -""" -) -for k, v in sorted(results.items()): - print(" <dt>{}</dt><dd><samp>{}</samp></dd>".format(k, v)) -print(" </dl>") - -# Write out as tsv file: -with open("software_versions.tsv", "w") as f: - for k, v in sorted(results.items()): - f.write("{}\t{}\n".format(k, v)) diff --git a/conf/base.config b/conf/base.config index 2e7db0c..42a37e9 100644 --- a/conf/base.config +++ b/conf/base.config @@ -54,4 +54,7 @@ process { errorStrategy = 'retry' maxRetries = 2 } + withName:CUSTOM_DUMPSOFTWAREVERSIONS { + cache = false + } } diff --git a/conf/modules.config b/conf/modules.config index 0b1bfde..a0506a4 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -1,32 +1,41 @@ /* ======================================================================================== - Config file for defining DSL2 per module options + Config file for defining DSL2 per module options and publishing paths ======================================================================================== Available keys to override module options: - args = Additional arguments appended to command in module. - args2 = Second set of arguments appended to command in module (multi-tool modules). - args3 = Third set of arguments appended to command in module (multi-tool modules). - publish_dir = Directory to publish results. - publish_by_meta = Groovy list of keys available in meta map to append as directories to "publish_dir" path - If publish_by_meta = true - Value of ${meta['id']} is appended as a directory to "publish_dir" path - If publish_by_meta = ['id', 'custompath'] - If "id" is in meta map and "custompath" isn't then "${meta['id']}/custompath/" - is appended as a directory to "publish_dir" path - If publish_by_meta = false / null - No directories are appended to "publish_dir" path - publish_files = Groovy map where key = "file_ext" and value = "directory" to publish results for that file extension - The value of "directory" is appended to the standard "publish_dir" path as defined above. - If publish_files = null (unspecified) - All files are published. - If publish_files = false - No files are published. - suffix = File name suffix for output files. + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. ---------------------------------------------------------------------------------------- */ -params { - modules { - 'fastqc' { - args = "--quiet" - } - 'multiqc' { - args = "" - } +process { + + publishDir = [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + + withName: SAMPLESHEET_CHECK { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: FASTQC { + ext.args = '--quiet' } + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + pattern: '*_versions.yml' + ] + } + } diff --git a/conf/test.config b/conf/test.config index d117e41..5582c1d 100644 --- a/conf/test.config +++ b/conf/test.config @@ -16,8 +16,8 @@ params { // Limit resources so that this can run on GitHub Actions max_cpus = 2 - max_memory = 6.GB - max_time = 6.h + max_memory = '6.GB' + max_time = '6.h' // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets diff --git a/docs/images/nf-core-hic_logo.png b/docs/images/nf-core-hic_logo.png deleted file mode 100644 index 274eb3dc3f3db879c7f3cbc3fd8f49a705a9a3fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27797 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 BA*%oY diff --git a/docs/images/nf-core-hic_logo_dark.png b/docs/images/nf-core-hic_logo_dark.png new file mode 100644 index 0000000..dee3db8 --- /dev/null +++ b/docs/images/nf-core-hic_logo_dark.png @@ -0,0 +1,9 @@ + +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> +<HTML><HEAD> +<TITLE> 508 Resource Limit Is Reached</TITLE> +</HEAD><BODY> +<H1>Resource Limit Is Reached</H1> +The website is temporarily unable to service your request as it exceeded resource limit. +Please try again later. +</BODY></HTML> diff --git a/docs/images/nf-core-hic_logo_light.png b/docs/images/nf-core-hic_logo_light.png new file mode 100644 index 0000000..1993002 --- /dev/null +++ b/docs/images/nf-core-hic_logo_light.png @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> +<html><head> +<title>507 Insufficient Storage</title> +</head><body> +<h1>Insufficient Storage</h1> +<p>The method could not be performed on the resource +because the server is unable to store the +representation needed to successfully complete the +request. There is insufficient free space left in +your storage allocation.</p> +<p>Additionally, a 507 Insufficient Storage +error was encountered while trying to use an ErrorDocument to handle the request.</p> +</body></html> diff --git a/docs/output.md b/docs/output.md index e2d35a1..57dce62 100644 --- a/docs/output.md +++ b/docs/output.md @@ -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> diff --git a/docs/usage.md b/docs/usage.md index 79b33ec..d063428 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -181,42 +181,6 @@ process { > **NB:** We specify just the process name i.e. `STAR_ALIGN` in the config file and not the full task name string that is printed to screen in the error message or on the terminal whilst the pipeline is running i.e. `RNASEQ:ALIGN_STAR:STAR_ALIGN`. You may get a warning suggesting that the process selector isn't recognised but you can ignore that if the process name has been specified correctly. This is something that needs to be fixed upstream in core Nextflow. -### Tool-specific options - -For the ultimate flexibility, we have implemented and are using Nextflow DSL2 modules in a way where it is possible for both developers and users to change tool-specific command-line arguments (e.g. providing an additional command-line argument to the `STAR_ALIGN` process) as well as publishing options (e.g. saving files produced by the `STAR_ALIGN` process that aren't saved by default by the pipeline). In the majority of instances, as a user you won't have to change the default options set by the pipeline developer(s), however, there may be edge cases where creating a simple custom config file can improve the behaviour of the pipeline if for example it is failing due to a weird error that requires setting a tool-specific parameter to deal with smaller / larger genomes. - -The command-line arguments passed to STAR in the `STAR_ALIGN` module are a combination of: - -* Mandatory arguments or those that need to be evaluated within the scope of the module, as supplied in the [`script`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L49-L55) section of the module file. - -* An [`options.args`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L56) string of non-mandatory parameters that is set to be empty by default in the module but can be overwritten when including the module in the sub-workflow / workflow context via the `addParams` Nextflow option. - -The nf-core/rnaseq pipeline has a sub-workflow (see [terminology](https://github.com/nf-core/modules#terminology)) specifically to align reads with STAR and to sort, index and generate some basic stats on the resulting BAM files using SAMtools. At the top of this file we import the `STAR_ALIGN` module via the Nextflow [`include`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L10) keyword and by default the options passed to the module via the `addParams` option are set as an empty Groovy map [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L5); this in turn means `options.args` will be set to empty by default in the module file too. This is an intentional design choice and allows us to implement well-written sub-workflows composed of a chain of tools that by default run with the bare minimum parameter set for any given tool in order to make it much easier to share across pipelines and to provide the flexibility for users and developers to customise any non-mandatory arguments. - -When including the sub-workflow above in the main pipeline workflow we use the same `include` statement, however, we now have the ability to overwrite options for each of the tools in the sub-workflow including the [`align_options`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L225) variable that will be used specifically to overwrite the optional arguments passed to the `STAR_ALIGN` module. In this case, the options to be provided to `STAR_ALIGN` have been assigned sensible defaults by the developer(s) in the pipeline's [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L70-L74) and can be accessed and customised in the [workflow context](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L201-L204) too before eventually passing them to the sub-workflow as a Groovy map called `star_align_options`. These options will then be propagated from `workflow -> sub-workflow -> module`. - -As mentioned at the beginning of this section it may also be necessary for users to overwrite the options passed to modules to be able to customise specific aspects of the way in which a particular tool is executed by the pipeline. Given that all of the default module options are stored in the pipeline's `modules.config` as a [`params` variable](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L24-L25) it is also possible to overwrite any of these options via a custom config file. - -Say for example we want to append an additional, non-mandatory parameter (i.e. `--outFilterMismatchNmax 16`) to the arguments passed to the `STAR_ALIGN` module. Firstly, we need to copy across the default `args` specified in the [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L71) and create a custom config file that is a composite of the default `args` as well as the additional options you would like to provide. This is very important because Nextflow will overwrite the default value of `args` that you provide via the custom config. - -As you will see in the example below, we have: - -* appended `--outFilterMismatchNmax 16` to the default `args` used by the module. -* changed the default `publish_dir` value to where the files will eventually be published in the main results directory. -* appended `'bam':''` to the default value of `publish_files` so that the BAM files generated by the process will also be saved in the top-level results directory for the module. Note: `'out':'log'` means any file/directory ending in `out` will now be saved in a separate directory called `my_star_directory/log/`. - -```nextflow -params { - modules { - 'star_align' { - args = "--quantMode TranscriptomeSAM --twopassMode Basic --outSAMtype BAM Unsorted --readFilesCommand zcat --runRNGseed 0 --outFilterMultimapNmax 20 --alignSJDBoverhangMin 1 --outSAMattributes NH HI AS NM MD --quantTranscriptomeBan Singleend --outFilterMismatchNmax 16" - publish_dir = "my_star_directory" - publish_files = ['out':'log', 'tab':'log', 'bam':''] - } - } -} -``` - ### Updating containers The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. If for some reason you need to use a different version of a particular tool with the pipeline then you just need to identify the `process` name and override the Nextflow `container` definition for that process using the `withName` declaration. For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a tool called [Pangolin](https://github.com/cov-lineages/pangolin) has been used during the COVID-19 pandemic to assign lineages to SARS-CoV-2 genome sequenced samples. Given that the lineage assignments change quite frequently it doesn't make sense to re-release the nf-core/viralrecon everytime a new version of Pangolin has been released. However, you can override the default container used by the pipeline by creating a custom config file and passing it as a command-line argument via `-c custom.config`. diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index 8d6920d..40ab65f 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -105,9 +105,13 @@ class NfcoreSchema { // Collect expected parameters from the schema def expectedParams = [] + def enums = [:] for (group in schemaParams) { for (p in group.value['properties']) { expectedParams.push(p.key) + if (group.value['properties'][p.key].containsKey('enum')) { + enums[p.key] = group.value['properties'][p.key]['enum'] + } } } @@ -155,7 +159,7 @@ class NfcoreSchema { println '' log.error 'ERROR: Validation of pipeline parameters failed!' JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log) + printExceptions(exceptionJSON, params_json, log, enums) println '' has_error = true } @@ -202,7 +206,7 @@ class NfcoreSchema { } def type = '[' + group_params.get(param).type + ']' def description = group_params.get(param).description - def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : '' + def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' def description_default = description + colors.dim + defaultValue + colors.reset // Wrap long description texts // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap @@ -260,13 +264,12 @@ class NfcoreSchema { // Get pipeline parameters defined in JSON Schema def Map params_summary = [:] - def blacklist = ['hostnames'] def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) for (group in params_map.keySet()) { def sub_params = new LinkedHashMap() def group_params = params_map.get(group) // This gets the parameters of that particular group for (param in group_params.keySet()) { - if (params.containsKey(param) && !blacklist.contains(param)) { + if (params.containsKey(param)) { def params_value = params.get(param) def schema_value = group_params.get(param).default def param_type = group_params.get(param).type @@ -330,7 +333,7 @@ class NfcoreSchema { // // Loop over nested exceptions and print the causingException // - private static void printExceptions(ex_json, params_json, log) { + private static void printExceptions(ex_json, params_json, log, enums, limit=5) { def causingExceptions = ex_json['causingExceptions'] if (causingExceptions.length() == 0) { def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ @@ -346,11 +349,20 @@ class NfcoreSchema { else { def param = ex_json['pointerToViolation'] - ~/^#\// def param_val = params_json[param].toString() - log.error "* --${param}: ${ex_json['message']} (${param_val})" + if (enums.containsKey(param)) { + def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" + if (enums[param].size() > limit) { + log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" + } else { + log.error "${error_msg}: ${enums[param].join(', ')})" + } + } else { + log.error "* --${param}: ${ex_json['message']} (${param_val})" + } } } for (ex in causingExceptions) { - printExceptions(ex, params_json, log) + printExceptions(ex, params_json, log, enums) } } diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 44551e0..2fc0a9b 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -19,27 +19,16 @@ class NfcoreTemplate { } // - // Check params.hostnames + // Warn if a -profile or Nextflow config has not been provided to run the pipeline // - public static void hostName(workflow, params, log) { - Map colors = logColours(params.monochrome_logs) - if (params.hostnames) { - try { - def hostname = "hostname".execute().text.trim() - params.hostnames.each { prof, hnames -> - hnames.each { hname -> - if (hostname.contains(hname) && !workflow.profile.contains(prof)) { - log.info "=${colors.yellow}====================================================${colors.reset}=\n" + - "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + - " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + - " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + - "=${colors.yellow}====================================================${colors.reset}=" - } - } - } - } catch (Exception e) { - log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}." - } + public static void checkConfigProvided(workflow, log) { + if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { + log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + + " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + + " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + + " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + + "Please refer to the quick start section and usage docs for the pipeline.\n " } } @@ -168,7 +157,6 @@ class NfcoreTemplate { log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" } } else { - hostName(workflow, params, log) log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" } } diff --git a/lib/Utils.groovy b/lib/Utils.groovy index 18173e9..1b88aec 100755 --- a/lib/Utils.groovy +++ b/lib/Utils.groovy @@ -37,11 +37,4 @@ class Utils { "===================================================================================" } } - - // - // Join module args with appropriate spacing - // - public static String joinModuleArgs(args_list) { - return ' ' + args_list.join(' ') - } } diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index 92eb176..a86a891 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -61,6 +61,9 @@ class WorkflowMain { // Print parameter summary log to screen log.info paramsSummaryLog(workflow, params, log) + // Check that a -profile or Nextflow config has been provided to run the pipeline + NfcoreTemplate.checkConfigProvided(workflow, log) + // Check that conda channels are set-up correctly if (params.enable_conda) { Utils.checkCondaChannels(log) @@ -69,9 +72,6 @@ class WorkflowMain { // Check AWS batch settings NfcoreTemplate.awsBatch(workflow, params) - // Check the hostnames against configured profiles - NfcoreTemplate.hostName(workflow, params, log) - // Check input has been provided if (!params.input) { log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'" diff --git a/modules.json b/modules.json index a68b1c1..2233ec4 100644 --- a/modules.json +++ b/modules.json @@ -3,12 +3,15 @@ "homePage": "https://github.com/nf-core/hic", "repos": { "nf-core/modules": { + "custom/dumpsoftwareversions": { + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, "fastqc": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "multiqc": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" } } } -} +} \ No newline at end of file diff --git a/modules/local/functions.nf b/modules/local/functions.nf deleted file mode 100644 index da9da09..0000000 --- a/modules/local/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// 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) - } - } - } - 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" - } - } -} diff --git a/modules/local/get_software_versions.nf b/modules/local/get_software_versions.nf deleted file mode 100644 index 9dc5249..0000000 --- a/modules/local/get_software_versions.nf +++ /dev/null @@ -1,33 +0,0 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - -process GET_SOFTWARE_VERSIONS { - publishDir "${params.outdir}", - 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) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } - - cache false - - input: - path versions - - output: - path "software_versions.tsv" , emit: tsv - path 'software_versions_mqc.yaml', emit: yaml - - script: // This script is bundled with the pipeline, in nf-core/hic/bin/ - """ - echo $workflow.manifest.version > pipeline.version.txt - echo $workflow.nextflow.version > nextflow.version.txt - scrape_software_versions.py &> software_versions_mqc.yaml - """ -} diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index 760a426..31f62fa 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -1,31 +1,27 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - process SAMPLESHEET_CHECK { tag "$samplesheet" - publishDir "${params.outdir}", - 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) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.8.3' : + 'quay.io/biocontainers/python:3.8.3' }" input: path samplesheet output: - path '*.csv' + path '*.csv' , emit: csv + path "versions.yml", emit: versions script: // This script is bundled with the pipeline, in nf-core/hic/bin/ """ check_samplesheet.py \\ $samplesheet \\ samplesheet.valid.csv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf new file mode 100644 index 0000000..934bb46 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf @@ -0,0 +1,21 @@ +process CUSTOM_DUMPSOFTWAREVERSIONS { + label 'process_low' + + // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container + conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" + + input: + path versions + + output: + path "software_versions.yml" , emit: yml + path "software_versions_mqc.yml", emit: mqc_yml + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + template 'dumpsoftwareversions.py' +} diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml new file mode 100644 index 0000000..5b5b8a6 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml @@ -0,0 +1,34 @@ +name: custom_dumpsoftwareversions +description: Custom module used to dump software versions within the nf-core pipeline template +keywords: + - custom + - version +tools: + - custom: + description: Custom module used to dump software versions within the nf-core pipeline template + homepage: https://github.com/nf-core/tools + documentation: https://github.com/nf-core/tools + licence: ['MIT'] +input: + - versions: + type: file + description: YML file containing software versions + pattern: "*.yml" + +output: + - yml: + type: file + description: Standard YML file containing software versions + pattern: "software_versions.yml" + - mqc_yml: + type: file + description: MultiQC custom content YML file containing software versions + pattern: "software_versions_mqc.yml" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100644 index 0000000..d139039 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import yaml +import platform +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) + + +versions_this_module = {} +versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, +} + +with open("$versions") as f: + versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + +# aggregate versions by the module name (derived from fully-qualified process name) +versions_by_module = {} +for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + assert versions_by_module[module] == process_versions, ( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + +versions_by_module["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_by_module), +} + +with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) +with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + +with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) diff --git a/modules/nf-core/modules/fastqc/functions.nf b/modules/nf-core/modules/fastqc/functions.nf deleted file mode 100644 index da9da09..0000000 --- a/modules/nf-core/modules/fastqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// 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) - } - } - } - 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" - } - } -} diff --git a/modules/nf-core/modules/fastqc/main.nf b/modules/nf-core/modules/fastqc/main.nf index 39c327b..d250eca 100644 --- a/modules/nf-core/modules/fastqc/main.nf +++ b/modules/nf-core/modules/fastqc/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process FASTQC { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::fastqc=0.11.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0" - } else { - container "quay.io/biocontainers/fastqc:0.11.9--0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : + 'quay.io/biocontainers/fastqc:0.11.9--0' }" input: tuple val(meta), path(reads) @@ -24,24 +13,32 @@ process FASTQC { output: tuple val(meta), path("*.html"), emit: html tuple val(meta), path("*.zip") , emit: zip - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' // Add soft-links to original FastQs for consistent naming in pipeline - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" if (meta.single_end) { """ [ ! -f ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } else { """ [ ! -f ${prefix}_1.fastq.gz ] && ln -s ${reads[0]} ${prefix}_1.fastq.gz [ ! -f ${prefix}_2.fastq.gz ] && ln -s ${reads[1]} ${prefix}_2.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/fastqc/meta.yml b/modules/nf-core/modules/fastqc/meta.yml index 8eb9953..b09553a 100644 --- a/modules/nf-core/modules/fastqc/meta.yml +++ b/modules/nf-core/modules/fastqc/meta.yml @@ -15,6 +15,7 @@ tools: overrepresented sequences. homepage: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/ documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/ + licence: ['GPL-2.0-only'] input: - meta: type: map @@ -40,10 +41,10 @@ output: type: file description: FastQC report archive pattern: "*_{fastqc.zip}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@grst" diff --git a/modules/nf-core/modules/multiqc/functions.nf b/modules/nf-core/modules/multiqc/functions.nf deleted file mode 100644 index da9da09..0000000 --- a/modules/nf-core/modules/multiqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// 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) - } - } - } - 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" - } - } -} diff --git a/modules/nf-core/modules/multiqc/main.nf b/modules/nf-core/modules/multiqc/main.nf index da78080..3dceb16 100644 --- a/modules/nf-core/modules/multiqc/main.nf +++ b/modules/nf-core/modules/multiqc/main.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MULTIQC { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - 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/multiqc:1.10.1--py_0" - } else { - container "quay.io/biocontainers/multiqc:1.10.1--py_0" - } + conda (params.enable_conda ? 'bioconda::multiqc=1.11' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path multiqc_files @@ -24,12 +13,16 @@ process MULTIQC { path "*multiqc_report.html", emit: report path "*_data" , emit: data path "*_plots" , optional:true, emit: plots - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ - multiqc -f $options.args . - multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt + multiqc -f $args . + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/multiqc/meta.yml b/modules/nf-core/modules/multiqc/meta.yml index 532a8bb..63c75a4 100644 --- a/modules/nf-core/modules/multiqc/meta.yml +++ b/modules/nf-core/modules/multiqc/meta.yml @@ -11,6 +11,7 @@ tools: It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. homepage: https://multiqc.info/ documentation: https://multiqc.info/docs/ + licence: ['GPL-3.0-or-later'] input: - multiqc_files: type: file @@ -29,10 +30,10 @@ output: type: file description: Plots created by MultiQC pattern: "*_data" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@abhi18av" - "@bunop" diff --git a/nextflow.config b/nextflow.config index 8c5d7ac..d3ba246 100644 --- a/nextflow.config +++ b/nextflow.config @@ -26,7 +26,6 @@ params { // Boilerplate options outdir = './results' tracedir = "${params.outdir}/pipeline_info" - publish_dir_mode = 'copy' email = null email_on_fail = null plaintext_email = false @@ -34,14 +33,12 @@ params { help = false validate_params = true show_hidden_params = false - schema_ignore_params = 'genomes,modules' + schema_ignore_params = 'genomes' enable_conda = false - singularity_pull_docker_container = false // Config options custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" - hostnames = [:] config_profile_description = null config_profile_contact = null config_profile_url = null @@ -58,9 +55,6 @@ params { // Load base.config by default for all pipelines includeConfig 'conf/base.config' -// Load modules.config for DSL2 module specific options -includeConfig 'conf/modules.config' - // Load nf-core custom profiles from different Institutions try { includeConfig "${params.custom_config_base}/nfcore_custom.config" @@ -68,13 +62,6 @@ try { System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") } -// Load igenomes.config if required -if (!params.igenomes_ignore) { - includeConfig 'conf/igenomes.config' -} else { - params.genomes = [:] -} - profiles { debug { process.beforeScript = 'echo $HOSTNAME' } conda { @@ -126,11 +113,22 @@ profiles { test_full { includeConfig 'conf/test_full.config' } } +// Load igenomes.config if required +if (!params.igenomes_ignore) { + includeConfig 'conf/igenomes.config' +} else { + params.genomes = [:] +} + // Export these variables to prevent local Python/R libraries from conflicting with those in the container +// The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. +// See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. + env { PYTHONNOUSERSITE = 1 R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" + JULIA_DEPOT_PATH = "/usr/local/share/julia" } // Capture exit codes from upstream processes when piping @@ -160,10 +158,13 @@ manifest { homePage = 'https://github.com/nf-core/hic' description = 'Analysis of Chromosome Conformation Capture data (Hi-C)' mainScript = 'main.nf' - nextflowVersion = '!>=21.04.0' - version = '1.3.0' + nextflowVersion = '!>=21.10.3' + version = '1.3.1' } +// Load modules.config for DSL2 module specific options +includeConfig 'conf/modules.config' + // Function to ensure that resource requirements don't go beyond // a maximum limit def check_max(obj, type) { diff --git a/nextflow_schema.json b/nextflow_schema.json index f8154bd..e3bebce 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -104,12 +104,6 @@ "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", "fa_icon": "fas fa-users-cog" }, - "hostnames": { - "type": "string", - "description": "Institutional configs hostname.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, "config_profile_name": { "type": "string", "description": "Institutional config name.", @@ -184,22 +178,6 @@ "fa_icon": "fas fa-question-circle", "hidden": true }, - "publish_dir_mode": { - "type": "string", - "default": "copy", - "description": "Method used to save pipeline results to output directory.", - "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", - "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], - "hidden": true - }, "email_on_fail": { "type": "string", "description": "Email address for completion summary, only when pipeline fails.", @@ -260,13 +238,6 @@ "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", "hidden": true, "fa_icon": "fas fa-bacon" - }, - "singularity_pull_docker_container": { - "type": "boolean", - "description": "Instead of directly downloading Singularity images for use with Singularity, force the workflow to pull and convert Docker containers instead.", - "hidden": true, - "fa_icon": "fas fa-toolbox", - "help_text": "This may be useful for example if you are unable to directly pull Singularity containers to run the pipeline due to http/https proxy issues." } } } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index b664bc8..cddcbb3 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,9 +2,7 @@ // Check input samplesheet and get read channels // -params.options = [:] - -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' addParams( options: params.options ) +include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' workflow INPUT_CHECK { take: @@ -12,12 +10,14 @@ workflow INPUT_CHECK { main: SAMPLESHEET_CHECK ( samplesheet ) + .csv .splitCsv ( header:true, sep:',' ) .map { create_fastq_channels(it) } .set { reads } emit: - reads // channel: [ val(meta), [ reads ] ] + reads // channel: [ val(meta), [ reads ] ] + versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] diff --git a/workflows/hic.nf b/workflows/hic.nf index bf53d9a..f29d026 100644 --- a/workflows/hic.nf +++ b/workflows/hic.nf @@ -32,18 +32,10 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi ======================================================================================== */ -// Don't overwrite global params.modules, create a copy instead and use that within the main script. -def modules = params.modules.clone() - -// -// MODULE: Local to the pipeline -// -include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files : ['tsv':'']] ) - // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) +include { INPUT_CHECK } from '../subworkflows/local/input_check' /* ======================================================================================== @@ -51,14 +43,12 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( opti ======================================================================================== */ -def multiqc_options = modules['multiqc'] -multiqc_options.args += params.multiqc_title ? Utils.joinModuleArgs(["--title \"$params.multiqc_title\""]) : '' - // // MODULE: Installed directly from nf-core/modules // -include { FASTQC } from '../modules/nf-core/modules/fastqc/main' addParams( options: modules['fastqc'] ) -include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' addParams( options: multiqc_options ) +include { FASTQC } from '../modules/nf-core/modules/fastqc/main' +include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' /* ======================================================================================== @@ -71,7 +61,7 @@ def multiqc_report = [] workflow HIC { - ch_software_versions = Channel.empty() + ch_versions = Channel.empty() // // SUBWORKFLOW: Read in samplesheet, validate and stage input files @@ -79,6 +69,7 @@ workflow HIC { INPUT_CHECK ( ch_input ) + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) // // MODULE: Run FastQC @@ -86,21 +77,10 @@ workflow HIC { FASTQC ( INPUT_CHECK.out.reads ) - ch_software_versions = ch_software_versions.mix(FASTQC.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(FASTQC.out.versions.first()) - // - // MODULE: Pipeline reporting - // - ch_software_versions - .map { it -> if (it) [ it.baseName, it ] } - .groupTuple() - .map { it[1][0] } - .flatten() - .collect() - .set { ch_software_versions } - - GET_SOFTWARE_VERSIONS ( - ch_software_versions.map { it }.collect() + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') ) // @@ -113,14 +93,14 @@ workflow HIC { ch_multiqc_files = ch_multiqc_files.mix(Channel.from(ch_multiqc_config)) ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_custom_config.collect().ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(GET_SOFTWARE_VERSIONS.out.yaml.collect()) + ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) MULTIQC ( ch_multiqc_files.collect() ) - multiqc_report = MULTIQC.out.report.toList() - ch_software_versions = ch_software_versions.mix(MULTIQC.out.version.ifEmpty(null)) + multiqc_report = MULTIQC.out.report.toList() + ch_versions = ch_versions.mix(MULTIQC.out.versions) } /* -- GitLab