From 19f19e53c0b75265058c74237c63710cd556e5f5 Mon Sep 17 00:00:00 2001
From: Julianus Pfeuffer <pfeuffer@informatik.uni-tuebingen.de>
Date: Mon, 31 May 2021 16:21:29 +0200
Subject: [PATCH 001/266] [FIX] schema validation with empty default str

---
 nf_core/schema.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/nf_core/schema.py b/nf_core/schema.py
index 17fa6ed783..b41258d95d 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -114,7 +114,8 @@ def sanitise_param_default(self, param):
 
         # For everything else, an empty string is an empty string
         if isinstance(param["default"], str) and param["default"].strip() == "":
-            return ""
+            param["default"] = ""
+            return param
 
         # Integers
         if param["type"] == "integer":

From 90e9204560ea9973186078a33cd0ac9ba10c0fe2 Mon Sep 17 00:00:00 2001
From: Julianus Pfeuffer <pfeuffer@informatik.uni-tuebingen.de>
Date: Mon, 31 May 2021 16:32:56 +0200
Subject: [PATCH 002/266] Try adding test

---
 tests/test_schema.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tests/test_schema.py b/tests/test_schema.py
index 7d37636a0d..360a062dd2 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -127,6 +127,15 @@ def test_validate_params_pass(self):
         self.schema_obj.load_schema()
         self.schema_obj.input_params = {"input": "fubar"}
         assert self.schema_obj.validate_params()
+        
+    def test_validate_params_pass_ext(self):
+        """Try validating an extended set of parameters against a schema"""
+        self.schema_obj.schema = {
+            "properties": {"foo": {"type": "string"}, "bar": {"type": "string", "default": ""}},
+            "required": ["foo"]
+        }
+        self.schema_obj.params
+        assert self.schema_obj.validate_params()
 
     def test_validate_params_fail(self):
         """Check that False is returned if params don't validate against a schema"""

From 4f15fe2852abebbd0e36f49df1f99f86ad36db8b Mon Sep 17 00:00:00 2001
From: Julianus Pfeuffer <pfeuffer@informatik.uni-tuebingen.de>
Date: Mon, 31 May 2021 16:44:12 +0200
Subject: [PATCH 003/266] remove leftover line

---
 tests/test_schema.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tests/test_schema.py b/tests/test_schema.py
index 360a062dd2..b20f54efc3 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -134,7 +134,6 @@ def test_validate_params_pass_ext(self):
             "properties": {"foo": {"type": "string"}, "bar": {"type": "string", "default": ""}},
             "required": ["foo"]
         }
-        self.schema_obj.params
         assert self.schema_obj.validate_params()
 
     def test_validate_params_fail(self):

From b5af037d1ee82ea0ccece99d138dfcb9a8ae9e10 Mon Sep 17 00:00:00 2001
From: kevinmenden <kevin.menden@t-online.de>
Date: Fri, 11 Jun 2021 10:21:14 +0200
Subject: [PATCH 004/266] check for invalid config default params

---
 nf_core/lint/schema_params.py |  7 ++++++
 nf_core/schema.py             | 43 +++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/nf_core/lint/schema_params.py b/nf_core/lint/schema_params.py
index 20c962c226..5cb0c88665 100644
--- a/nf_core/lint/schema_params.py
+++ b/nf_core/lint/schema_params.py
@@ -29,6 +29,9 @@ def schema_params(self):
     # Add schema params found in the config but not the schema
     added_params = self.schema_obj.add_schema_found_configs()
 
+    # Invalid default parameters in nextflow.config
+    invalid_config_default_params = self.schema_obj.invalid_nextflow_config_default_parameters
+
     if len(removed_params) > 0:
         for param in removed_params:
             warned.append("Schema param `{}` not found from nextflow config".format(param))
@@ -40,4 +43,8 @@ def schema_params(self):
     if len(removed_params) == 0 and len(added_params) == 0:
         passed.append("Schema matched params returned from nextflow config")
 
+    if len(invalid_config_default_params) > 0:
+        for param in invalid_config_default_params:
+            warned.append(f"Invalid default value for param `{param}` in nextflow config")
+
     return {"passed": passed, "warned": warned, "failed": failed}
diff --git a/nf_core/schema.py b/nf_core/schema.py
index 17fa6ed783..1b58cc0beb 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -37,6 +37,7 @@ def __init__(self):
         self.schema_params = []
         self.input_params = {}
         self.pipeline_params = {}
+        self.invalid_nextflow_config_default_parameters = []
         self.pipeline_manifest = {}
         self.schema_from_scratch = False
         self.no_prompts = False
@@ -223,6 +224,48 @@ def validate_default_params(self):
             raise AssertionError("Default parameters are invalid: {}".format(e.message))
         log.info("[green][✓] Default parameters look valid")
 
+        # Make sure every default parameter exists in the nextflow.config and is of correc type
+        if self.pipeline_manifest == {}:
+            self.get_wf_params()
+
+        # Go over group keys
+        for group_key, group in schema_no_required["definitions"].items():
+            group_properties = group.get("properties")
+            for param in group_properties:
+                if param in self.pipeline_params:
+                    self.validate_config_default_parameter(param, group_properties[param]["type"], self.pipeline_params[param])
+    
+        # Go over ungrouped params
+        ungrouped_properties = self.schema.get("properties")
+        for param in ungrouped_properties:
+            if param in self.pipeline_params:
+                self.validate_config_default_parameter(param, ungrouped_properties[param]["type"], self.pipeline_params[param])
+            else:
+                self.invalid_nextflow_config_default_parameters.append(param)
+
+    def validate_config_default_parameter(self, param, schema_default_type, config_default):
+        """
+        Assure that default parameters in the nextflow.config are correctly set
+        by comparing them to their type in the schema
+        """
+        if schema_default_type == "string":
+            if config_default in ["false", "true", "''"]:
+                self.invalid_nextflow_config_default_parameters.append(param)
+        if schema_default_type == "boolean":
+            if not config_default in ["false", "true"]:
+                self.invalid_nextflow_config_default_parameters.append(param)
+        if schema_default_type == "integer":
+            try:
+                int(config_default)
+            except ValueError:
+                self.invalid_nextflow_config_default_parameters.append(param)
+        if schema_default_type == "number":
+            try:
+                float(config_default)
+            except ValueError:
+                self.invalid_nextflow_config_default_parameters.append(param)
+
+        
     def validate_schema(self, schema=None):
         """
         Check that the Schema is valid

From f6633f7f312b3ce071c2ffae730aec1d59dd5d27 Mon Sep 17 00:00:00 2001
From: kevinmenden <kevin.menden@t-online.de>
Date: Fri, 11 Jun 2021 13:46:15 +0200
Subject: [PATCH 005/266] add ignore parameters

---
 nf_core/lint/schema_params.py |  2 +-
 nf_core/schema.py             | 38 +++++++++++++++++++++++++++++------
 2 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/nf_core/lint/schema_params.py b/nf_core/lint/schema_params.py
index 5cb0c88665..6ec34f6adb 100644
--- a/nf_core/lint/schema_params.py
+++ b/nf_core/lint/schema_params.py
@@ -45,6 +45,6 @@ def schema_params(self):
 
     if len(invalid_config_default_params) > 0:
         for param in invalid_config_default_params:
-            warned.append(f"Invalid default value for param `{param}` in nextflow config")
+            warned.append(f"Default value for param `{param}` invalid or missing in nextflow config")
 
     return {"passed": passed, "warned": warned, "failed": failed}
diff --git a/nf_core/schema.py b/nf_core/schema.py
index 1b58cc0beb..65771b33b3 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -17,6 +17,7 @@
 import webbrowser
 import yaml
 import copy
+import re
 
 import nf_core.list, nf_core.utils
 
@@ -227,27 +228,52 @@ def validate_default_params(self):
         # Make sure every default parameter exists in the nextflow.config and is of correc type
         if self.pipeline_manifest == {}:
             self.get_wf_params()
+        
+        # Collect parameters to ignore
+        if "schema_ignore_params" in self.pipeline_params:
+            params_ignore = self.pipeline_params.get("schema_ignore_params", "").strip("\"'").split(",")
+        else:
+            params_ignore = []
+
+        # Scrape main.nf for additional parameter declarations and ignore them, too
+        try:
+            main_nf = os.path.join(self.pipeline_dir, "main.nf")
+            with open(main_nf, "r") as fh:
+                for l in fh:
+                    match = re.match(r"^\s*(params\.[a-zA-Z0-9_]+)\s*=", l)
+                    if match:
+                        params_ignore.append(match.group(1).split(".")[1])
+        except FileNotFoundError as e:
+            log.debug("Could not open {} to look for parameter declarations - {}".format(main_nf, e))
 
         # Go over group keys
         for group_key, group in schema_no_required["definitions"].items():
             group_properties = group.get("properties")
             for param in group_properties:
+                if param in params_ignore:
+                    continue
                 if param in self.pipeline_params:
                     self.validate_config_default_parameter(param, group_properties[param]["type"], self.pipeline_params[param])
     
-        # Go over ungrouped params
+        # Go over ungrouped params if any exist
         ungrouped_properties = self.schema.get("properties")
-        for param in ungrouped_properties:
-            if param in self.pipeline_params:
-                self.validate_config_default_parameter(param, ungrouped_properties[param]["type"], self.pipeline_params[param])
-            else:
-                self.invalid_nextflow_config_default_parameters.append(param)
+        if ungrouped_properties:
+            for param in ungrouped_properties:
+                if param in params_ignore:
+                    continue
+                if param in self.pipeline_params:
+                    self.validate_config_default_parameter(param, ungrouped_properties[param]["type"], self.pipeline_params[param])
+                else:
+                    self.invalid_nextflow_config_default_parameters.append(param)
 
     def validate_config_default_parameter(self, param, schema_default_type, config_default):
         """
         Assure that default parameters in the nextflow.config are correctly set
         by comparing them to their type in the schema
         """
+        # if default is null, we're good
+        if config_default == "null":
+            return
         if schema_default_type == "string":
             if config_default in ["false", "true", "''"]:
                 self.invalid_nextflow_config_default_parameters.append(param)

From ba5af90a1ccccb1e8bd8e48a25abf3b813b74ef6 Mon Sep 17 00:00:00 2001
From: kevinmenden <kevin.menden@t-online.de>
Date: Fri, 11 Jun 2021 13:52:50 +0200
Subject: [PATCH 006/266] comments and changelog

---
 CHANGELOG.md      |  1 +
 nf_core/schema.py | 10 ++++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12780b3faf..020c67564a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 * Regular release sync fix - this time it was to do with JSON serialisation [[#1072](https://github.com/nf-core/tools/pull/1072)]
 * Fixed bug in schema validation that ignores upper/lower-case typos in parameters [[#1087](https://github.com/nf-core/tools/issues/1087)]
 * Bugfix: Download should use path relative to workflow for configs
+* Added lint check for valid default parameterse in `nextflow.config` [[#992](https://github.com/nf-core/tools/issues/992)]
 
 ### Modules
 
diff --git a/nf_core/schema.py b/nf_core/schema.py
index 65771b33b3..12dd91f488 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -208,6 +208,9 @@ def validate_default_params(self):
         """
         Check that all default parameters in the schema are valid
         Ignores 'required' flag, as required parameters might have no defaults
+
+        Additional check that all parameters have defaults in nextflow.config and that
+        these are valid and adhere to guidelines
         """
         try:
             assert self.schema is not None
@@ -225,7 +228,7 @@ def validate_default_params(self):
             raise AssertionError("Default parameters are invalid: {}".format(e.message))
         log.info("[green][✓] Default parameters look valid")
 
-        # Make sure every default parameter exists in the nextflow.config and is of correc type
+        # Make sure every default parameter exists in the nextflow.config and is of correct type
         if self.pipeline_manifest == {}:
             self.get_wf_params()
         
@@ -235,7 +238,7 @@ def validate_default_params(self):
         else:
             params_ignore = []
 
-        # Scrape main.nf for additional parameter declarations and ignore them, too
+        # Scrape main.nf for additional parameter declarations and add to params_ignore
         try:
             main_nf = os.path.join(self.pipeline_dir, "main.nf")
             with open(main_nf, "r") as fh:
@@ -254,6 +257,8 @@ def validate_default_params(self):
                     continue
                 if param in self.pipeline_params:
                     self.validate_config_default_parameter(param, group_properties[param]["type"], self.pipeline_params[param])
+                else:
+                    self.invalid_nextflow_config_default_parameters.append(param)
     
         # Go over ungrouped params if any exist
         ungrouped_properties = self.schema.get("properties")
@@ -274,6 +279,7 @@ def validate_config_default_parameter(self, param, schema_default_type, config_d
         # if default is null, we're good
         if config_default == "null":
             return
+        # else check for allowed defaults
         if schema_default_type == "string":
             if config_default in ["false", "true", "''"]:
                 self.invalid_nextflow_config_default_parameters.append(param)

From 6c20bf8f9cbe136924a9d216052c8693be32966f Mon Sep 17 00:00:00 2001
From: kevinmenden <kevin.menden@t-online.de>
Date: Fri, 11 Jun 2021 13:59:23 +0200
Subject: [PATCH 007/266] back in black

---
 nf_core/schema.py | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/nf_core/schema.py b/nf_core/schema.py
index 12dd91f488..f5d092ce92 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -231,7 +231,7 @@ def validate_default_params(self):
         # Make sure every default parameter exists in the nextflow.config and is of correct type
         if self.pipeline_manifest == {}:
             self.get_wf_params()
-        
+
         # Collect parameters to ignore
         if "schema_ignore_params" in self.pipeline_params:
             params_ignore = self.pipeline_params.get("schema_ignore_params", "").strip("\"'").split(",")
@@ -256,10 +256,12 @@ def validate_default_params(self):
                 if param in params_ignore:
                     continue
                 if param in self.pipeline_params:
-                    self.validate_config_default_parameter(param, group_properties[param]["type"], self.pipeline_params[param])
+                    self.validate_config_default_parameter(
+                        param, group_properties[param]["type"], self.pipeline_params[param]
+                    )
                 else:
                     self.invalid_nextflow_config_default_parameters.append(param)
-    
+
         # Go over ungrouped params if any exist
         ungrouped_properties = self.schema.get("properties")
         if ungrouped_properties:
@@ -267,7 +269,9 @@ def validate_default_params(self):
                 if param in params_ignore:
                     continue
                 if param in self.pipeline_params:
-                    self.validate_config_default_parameter(param, ungrouped_properties[param]["type"], self.pipeline_params[param])
+                    self.validate_config_default_parameter(
+                        param, ungrouped_properties[param]["type"], self.pipeline_params[param]
+                    )
                 else:
                     self.invalid_nextflow_config_default_parameters.append(param)
 
@@ -297,7 +301,6 @@ def validate_config_default_parameter(self, param, schema_default_type, config_d
             except ValueError:
                 self.invalid_nextflow_config_default_parameters.append(param)
 
-        
     def validate_schema(self, schema=None):
         """
         Check that the Schema is valid

From f29b7b6e9b0ed8baa2deb730005ae1469ba05a92 Mon Sep 17 00:00:00 2001
From: kevinmenden <kevin.menden@t-online.de>
Date: Fri, 11 Jun 2021 15:07:36 +0200
Subject: [PATCH 008/266] use null instead of false

---
 nf_core/schema.py | 13 +------------
 nf_core/utils.py  |  2 +-
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/nf_core/schema.py b/nf_core/schema.py
index f5d092ce92..4c9f4a8401 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -229,7 +229,7 @@ def validate_default_params(self):
         log.info("[green][✓] Default parameters look valid")
 
         # Make sure every default parameter exists in the nextflow.config and is of correct type
-        if self.pipeline_manifest == {}:
+        if self.pipeline_params == {}:
             self.get_wf_params()
 
         # Collect parameters to ignore
@@ -238,17 +238,6 @@ def validate_default_params(self):
         else:
             params_ignore = []
 
-        # Scrape main.nf for additional parameter declarations and add to params_ignore
-        try:
-            main_nf = os.path.join(self.pipeline_dir, "main.nf")
-            with open(main_nf, "r") as fh:
-                for l in fh:
-                    match = re.match(r"^\s*(params\.[a-zA-Z0-9_]+)\s*=", l)
-                    if match:
-                        params_ignore.append(match.group(1).split(".")[1])
-        except FileNotFoundError as e:
-            log.debug("Could not open {} to look for parameter declarations - {}".format(main_nf, e))
-
         # Go over group keys
         for group_key, group in schema_no_required["definitions"].items():
             group_properties = group.get("properties")
diff --git a/nf_core/utils.py b/nf_core/utils.py
index 7d44a1850f..c335a06aa2 100644
--- a/nf_core/utils.py
+++ b/nf_core/utils.py
@@ -250,7 +250,7 @@ def fetch_wf_config(wf_path):
             for l in fh:
                 match = re.match(r"^\s*(params\.[a-zA-Z0-9_]+)\s*=", l)
                 if match:
-                    config[match.group(1)] = "false"
+                    config[match.group(1)] = "null"
     except FileNotFoundError as e:
         log.debug("Could not open {} to look for parameter declarations - {}".format(main_nf, e))
 

From 31fcebd9352e8e4337bb8fd20bd074ea389a33d5 Mon Sep 17 00:00:00 2001
From: Kevin Menden <kevin.menden@t-online.de>
Date: Mon, 5 Jul 2021 11:15:32 +0200
Subject: [PATCH 009/266] Update CHANGELOG.md

Co-authored-by: Erik Danielsson <53212377+ErikDanielsson@users.noreply.github.com>
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 020c67564a..17198c5fbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@
 * Regular release sync fix - this time it was to do with JSON serialisation [[#1072](https://github.com/nf-core/tools/pull/1072)]
 * Fixed bug in schema validation that ignores upper/lower-case typos in parameters [[#1087](https://github.com/nf-core/tools/issues/1087)]
 * Bugfix: Download should use path relative to workflow for configs
-* Added lint check for valid default parameterse in `nextflow.config` [[#992](https://github.com/nf-core/tools/issues/992)]
+* Added lint check for valid default parameters in `nextflow.config` [[#992](https://github.com/nf-core/tools/issues/992)]
 
 ### Modules
 

From cd103d613168f5cd82a8ed7fbedddba42741202e Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 6 Jul 2021 17:14:25 +0200
Subject: [PATCH 010/266] Converted tools issue templates into new yml form
 syntax

---
 .github/ISSUE_TEMPLATE/bug_report.md       | 45 ----------------------
 .github/ISSUE_TEMPLATE/bug_report.yml      | 44 +++++++++++++++++++++
 .github/ISSUE_TEMPLATE/config.yml          |  1 -
 .github/ISSUE_TEMPLATE/feature_request.md  | 32 ---------------
 .github/ISSUE_TEMPLATE/feature_request.yml | 11 ++++++
 5 files changed, 55 insertions(+), 78 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

diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 8f2a1bccff..0000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-name: Bug report
-about: Report something that is broken or incorrect
-labels: bug
----
-
-<!--
-# nf-core/tools bug report
-
-Hi there!
-
-Thanks for telling us about a problem with the nf-core/tools package.
-Please delete this text and anything that's not relevant from the template below:
--->
-
-## 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. `nf-core lint ...`] -->
-2. See error: <!-- [Please provide your error message] -->
-
-## Expected behaviour
-
-<!-- A clear and concise description of what you expected to happen. -->
-
-## System
-
-- Hardware: <!-- [e.g. HPC, Desktop, Cloud...] -->
-- Executor: <!-- [e.g. slurm, local, awsbatch...] -->
-- OS: <!-- [e.g. CentOS Linux, macOS, Linux Mint...] -->
-- Version of nf-core/tools: <!-- [e.g. 1.1, 1.5, 1.8.2...] -->
-- Python version: <!-- [e.g. 3.7, 3.8...] -->
-
-## Nextflow Installation
-
-- Version: <!-- [e.g. 19.10.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 0000000000..e2aa44a764
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,44 @@
+name: Bug report
+description: Report something that is broken or incorrect
+labels: bug
+body:
+
+  - type: markdown
+    attributes:
+      value: |
+        Hi there!
+
+        Thanks for telling us about a problem with the nf-core/tools package.
+
+  - 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 and output from your terminal.
+      render: console
+      placeholder: |
+        $ nf-core lint ...
+
+        Some output where something broke
+
+  - type: textarea
+    id: system
+    attributes:
+      label: System information
+      description: |
+        A clear and concise description of what the bug is.
+
+        * Nextflow version _(eg. 21.04.01)_
+        * Hardware _(eg. HPC, Desktop, Cloud)_
+        * Executor _(eg. slurm, local, awsbatch)_
+        * OS _(eg. CentOS Linux, macOS, Linux Mint)_
+        * Version of nf-core/tools _(eg. 1.1, 1.5, 1.8.2)_
+        * Python version _(eg. 3.7, 3.8)_
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2a03179137..a6a4b51932 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 8c4e9237fe..0000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for the nf-core website
-labels: enhancement
----
-
-<!--
-# nf-core/tools feature request
-
-Hi there!
-
-Thanks for suggesting a new feature for the nf-core/tools package!
-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 0000000000..5d4d7b17a2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,11 @@
+name: Feature request
+description: Suggest an idea for nf-core/tools
+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

From 174dffa5782122a590e31623b331bfb666d50b1f Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 6 Jul 2021 19:52:32 +0200
Subject: [PATCH 011/266] Use issue template forms for the pipeline template
 too

---
 .github/ISSUE_TEMPLATE/bug_report.yml         |  2 -
 .../.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 ----------
 .../ISSUE_TEMPLATE/feature_request.yml        | 11 ++++
 6 files changed, 63 insertions(+), 98 deletions(-)
 delete mode 100644 nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.md
 create mode 100644 nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
 delete mode 100644 nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.md
 create mode 100644 nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.yml

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index e2aa44a764..1046a3fa20 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -34,8 +34,6 @@ body:
     attributes:
       label: System information
       description: |
-        A clear and concise description of what the bug is.
-
         * Nextflow version _(eg. 21.04.01)_
         * Hardware _(eg. HPC, Desktop, Cloud)_
         * Executor _(eg. slurm, local, awsbatch)_
diff --git a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.md b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 5c5100c97b..0000000000
--- a/nf_core/pipeline-template/.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
----
-
-<!--
-# {{ name }} 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)
-- [ ] [{{ name }} pipeline documentation](https://nf-co.re/{{ short_name }}/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/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000000..9148922314
--- /dev/null
+++ b/nf_core/pipeline-template/.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)
+        - [{{ name }} pipeline documentation](https://nf-co.re/{{ short_name }}/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 upload (drag and drop) and relevant files. Make into a `.zip` file 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.04.01)_
+        * 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 {{ name }} _(eg. 1.1, 1.5, 1.8.2)_
diff --git a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/config.yml b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/config.yml
index a582ac2fb3..19cd8f7c56 100644
--- a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/config.yml
+++ b/nf_core/pipeline-template/.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/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.md b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 1727d53f01..0000000000
--- a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for the {{ name }} pipeline
-labels: enhancement
----
-
-<!--
-# {{ name }} 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/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.yml b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000000..ad3a715de3
--- /dev/null
+++ b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,11 @@
+name: Feature request
+description: Suggest an idea for the {{ name }} 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

From 08008d352e4335cac497292acee073dfc49316d2 Mon Sep 17 00:00:00 2001
From: "Maxime U. Garcia" <max.u.garcia@gmail.com>
Date: Fri, 16 Jul 2021 17:55:22 +0200
Subject: [PATCH 012/266] Update nextflow.config

Load genomes after profiles in case they are changed (ie tests)
(sarek stuff)
---
 nf_core/pipeline-template/nextflow.config | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 3f23f45b34..f275f525d2 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -68,13 +68,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,6 +119,13 @@ 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
 env {
     PYTHONNOUSERSITE = 1

From 5376811f1c910227d00bf6a130eac55fc81b07ff Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Tue, 27 Jul 2021 16:39:13 +0100
Subject: [PATCH 013/266] Bump versions to 2.2dev

---
 CHANGELOG.md | 8 ++++++++
 setup.py     | 2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c49fa0212e..2ce74ef28f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # nf-core/tools: Changelog
 
+## v2.2dev
+
+### Template
+
+### General
+
+### Modules
+
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
 ### Template
diff --git a/setup.py b/setup.py
index 5be47e737a..513f9d9fec 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 from setuptools import setup, find_packages
 
-version = "2.1"
+version = "2.2dev"
 
 with open("README.md") as f:
     readme = f.read()

From 05faa5a6cff4a27cfe72084911718a6927358be4 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Thu, 29 Jul 2021 14:11:16 +0200
Subject: [PATCH 014/266] fix typo

---
 nf_core/modules/module_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 00d32090ef..30d6e4cc08 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -396,7 +396,7 @@ def prompt_module_version_sha(module, modules_repo, installed_sha=None):
         log.warning(e)
         next_page_commits = None
 
-    while git_sha is "":
+    while git_sha == "":
         commits = next_page_commits
         try:
             next_page_commits = get_module_git_log(

From e48f024b85bb06b2e73915cd6197c2e93967ad85 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Thu, 29 Jul 2021 14:13:25 +0200
Subject: [PATCH 015/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ce74ef28f..1643031624 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
 
 ### Modules
 
+* Fixed typo in `module_utils.py`.
+
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
 ### Template

From a07fba28d32b64c185195dbe98f3bafcf1635a51 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Thu, 29 Jul 2021 14:34:05 +0200
Subject: [PATCH 016/266] Make questionary asks unsafe

---
 nf_core/modules/bump_versions.py | 2 +-
 nf_core/modules/create.py        | 2 +-
 nf_core/modules/remove.py        | 4 ++--
 nf_core/modules/update.py        | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/nf_core/modules/bump_versions.py b/nf_core/modules/bump_versions.py
index 9189cbb1f6..4d24faa52b 100644
--- a/nf_core/modules/bump_versions.py
+++ b/nf_core/modules/bump_versions.py
@@ -83,7 +83,7 @@ def bump_versions(self, module=None, all_modules=False, show_uptodate=False):
                     "Tool name:",
                     choices=[m.module_name for m in nfcore_modules],
                     style=nf_core.utils.nfcore_question_style,
-                ).ask()
+                ).unsafe_ask()
 
         if module:
             self.show_up_to_date = True
diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index daeb18eaf9..8473f64b8d 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -204,7 +204,7 @@ def create(self):
                 choices=process_label_defaults,
                 style=nf_core.utils.nfcore_question_style,
                 default="process_low",
-            ).ask()
+            ).unsafe_ask()
 
         if self.has_meta is None:
             log.info(
diff --git a/nf_core/modules/remove.py b/nf_core/modules/remove.py
index f657c361a3..996966e7ee 100644
--- a/nf_core/modules/remove.py
+++ b/nf_core/modules/remove.py
@@ -46,12 +46,12 @@ def remove(self, module):
         else:
             repo_name = questionary.autocomplete(
                 "Repo name:", choices=self.module_names.keys(), style=nf_core.utils.nfcore_question_style
-            ).ask()
+            ).unsafe_ask()
 
         if module is None:
             module = questionary.autocomplete(
                 "Tool name:", choices=self.module_names[repo_name], style=nf_core.utils.nfcore_question_style
-            ).ask()
+            ).unsafe_ask()
 
         # Set the remove folder based on the repository name
         remove_folder = os.path.split(repo_name)
diff --git a/nf_core/modules/update.py b/nf_core/modules/update.py
index 2bddfec763..d41544f1c0 100644
--- a/nf_core/modules/update.py
+++ b/nf_core/modules/update.py
@@ -40,7 +40,7 @@ def update(self, module):
                     "Update all modules or a single named module?",
                     choices=choices,
                     style=nf_core.utils.nfcore_question_style,
-                ).ask()
+                ).unsafe_ask()
                 == "All modules"
             )
 

From b7f69e62fc88f7cf7048475e5b2937d4652aab74 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Thu, 29 Jul 2021 14:38:42 +0200
Subject: [PATCH 017/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ce74ef28f..dbc8656791 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
 
 ### General
 
+* Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
+
 ### Modules
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]

From 05e571dff188deab54d980c168e8107e77744a90 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 2 Aug 2021 11:28:18 +0200
Subject: [PATCH 018/266] Fixed crashing lint test due when missing process
 section

---
 nf_core/modules/lint/__init__.py | 2 +-
 nf_core/modules/lint/main_nf.py  | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index f9de48a304..857e679ad1 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -321,7 +321,7 @@ def lint_module(self, mod, local=False):
         if local:
             self.main_nf(mod)
             self.passed += [LintResult(mod, *m) for m in mod.passed]
-            self.warned += [LintResult(mod, *m) for m in mod.warned]
+            self.warned += [LintResult(mod, *m) for m in (mod.warned + mod.failed)]
 
         # Otherwise run all the lint tests
         else:
diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 018dc99af2..f1b46de054 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -141,6 +141,13 @@ def check_process_section(self, lines):
     Specifically checks for correct software versions
     and containers
     """
+    # Check that we have a process section
+    if len(lines) == 0:
+        self.failed.append(("process_exist", "Process definition does not exist", self.main_nf))
+        return
+    else:
+        self.passed.append(("process_exist", "Process definition exists", self.main_nf))
+
     # Checks that build numbers of bioconda, singularity and docker container are matching
     build_id = "build"
     singularity_tag = "singularity"

From bcb92f85ef6f893f8fd863fd70e73e1115c30482 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 2 Aug 2021 11:29:59 +0200
Subject: [PATCH 019/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9f518293f..de57c84cb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 ### Modules
 
 * Fixed typo in `module_utils.py`.
+* Fixed failing lint test when process section was missing from module. Also added the local failing tests to the warned section of the output table. ([#1235](https://github.com/nf-core/tools/issues/1235))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 

From b933df8beda99edf92868b8d9af7362d83efa7c6 Mon Sep 17 00:00:00 2001
From: "Maxime U. Garcia" <max.u.garcia@gmail.com>
Date: Fri, 6 Aug 2021 14:13:10 +0200
Subject: [PATCH 020/266] Update multiqc_config.yaml

I think the docs should be the nf-core website and not the GitHub repo.
---
 nf_core/pipeline-template/assets/multiqc_config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/assets/multiqc_config.yaml b/nf_core/pipeline-template/assets/multiqc_config.yaml
index e3f940c2e7..bbc9a14e1c 100644
--- a/nf_core/pipeline-template/assets/multiqc_config.yaml
+++ b/nf_core/pipeline-template/assets/multiqc_config.yaml
@@ -1,7 +1,7 @@
 report_comment: >
     This report has been generated by the <a href="https://github.com/{{ name }}" target="_blank">{{ name }}</a>
     analysis pipeline. For information about how to interpret these results, please see the
-    <a href="https://github.com/{{ name }}" target="_blank">documentation</a>.
+    <a href="https://nf-co.re/{{ short_name }}" target="_blank">documentation</a>.
 report_section_order:
     software_versions:
         order: -1000

From 9571df9a03ed61c394cbe581236e8be94a2ddfe5 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Fri, 6 Aug 2021 14:47:19 +0200
Subject: [PATCH 021/266] Add '--diff' flag to 'nf-core modules update'

---
 nf_core/__main__.py                |  9 ++-
 nf_core/modules/install.py         |  6 +-
 nf_core/modules/modules_command.py |  7 ++-
 nf_core/modules/update.py          | 88 ++++++++++++++++++++++++++----
 4 files changed, 92 insertions(+), 18 deletions(-)

diff --git a/nf_core/__main__.py b/nf_core/__main__.py
index 483d709780..4d828fd671 100755
--- a/nf_core/__main__.py
+++ b/nf_core/__main__.py
@@ -449,14 +449,19 @@ def install(ctx, tool, dir, prompt, force, sha):
 @click.option("-p", "--prompt", is_flag=True, default=False, help="Prompt for the version of the module")
 @click.option("-s", "--sha", type=str, metavar="<commit sha>", help="Install module at commit SHA")
 @click.option("-a", "--all", is_flag=True, default=False, help="Update all modules installed in pipeline")
-def update(ctx, tool, dir, force, prompt, sha, all):
+@click.option(
+    "-c", "--diff", is_flag=True, default=False, help="Show differences between module versions before updating"
+)
+def update(ctx, tool, dir, force, prompt, sha, all, diff):
     """
     Update DSL2 modules within a pipeline.
 
     Fetches and updates module files from a remote repo e.g. nf-core/modules.
     """
     try:
-        module_install = nf_core.modules.ModuleUpdate(dir, force=force, prompt=prompt, sha=sha, update_all=all)
+        module_install = nf_core.modules.ModuleUpdate(
+            dir, force=force, prompt=prompt, sha=sha, update_all=all, diff=diff
+        )
         module_install.modules_repo = ctx.obj["modules_repo_obj"]
         exit_status = module_install.update(tool)
         if not exit_status and all:
diff --git a/nf_core/modules/install.py b/nf_core/modules/install.py
index b0d96c4311..c5c33cc2a8 100644
--- a/nf_core/modules/install.py
+++ b/nf_core/modules/install.py
@@ -82,10 +82,10 @@ def install(self, module):
             current_entry = None
 
         # Set the install folder based on the repository name
-        install_folder = [self.modules_repo.owner, self.modules_repo.repo]
+        install_folder = [self.dir, "modules", self.modules_repo.owner, self.modules_repo.repo]
 
         # Compute the module directory
-        module_dir = os.path.join(self.dir, "modules", *install_folder, module)
+        module_dir = os.path.join(*install_folder, module)
 
         # Check that the module is not already installed
         if (current_entry is not None and os.path.exists(module_dir)) and not self.force:
@@ -128,7 +128,7 @@ def install(self, module):
         log.debug(f"Installing module '{module}' at modules hash {version} from {self.modules_repo.name}")
 
         # Download module files
-        if not self.download_module_file(module, version, self.modules_repo, install_folder, module_dir):
+        if not self.download_module_file(module, version, self.modules_repo, install_folder):
             return False
 
         # Update module.json with newly installed module
diff --git a/nf_core/modules/modules_command.py b/nf_core/modules/modules_command.py
index e7ba1a5c05..ba8bba9ef1 100644
--- a/nf_core/modules/modules_command.py
+++ b/nf_core/modules/modules_command.py
@@ -253,19 +253,20 @@ def clear_module_dir(self, module_name, module_dir):
             log.error("Could not remove module: {}".format(e))
             return False
 
-    def download_module_file(self, module_name, module_version, modules_repo, install_folder, module_dir):
+    def download_module_file(self, module_name, module_version, modules_repo, install_folder, dry_run=False):
         """Downloads the files of a module from the remote repo"""
         files = modules_repo.get_module_file_urls(module_name, module_version)
         log.debug("Fetching module files:\n - {}".format("\n - ".join(files.keys())))
         for filename, api_url in files.items():
             split_filename = filename.split("/")
-            dl_filename = os.path.join(self.dir, "modules", *install_folder, *split_filename[1:])
+            dl_filename = os.path.join(*install_folder, *split_filename[1:])
             try:
                 self.modules_repo.download_gh_file(dl_filename, api_url)
             except (SystemError, LookupError) as e:
                 log.error(e)
                 return False
-        log.info("Downloaded {} files to {}".format(len(files), module_dir))
+        if not dry_run:
+            log.info("Downloaded {} files to {}".format(len(files), os.path.join(*install_folder, module_name)))
         return True
 
     def load_modules_json(self):
diff --git a/nf_core/modules/update.py b/nf_core/modules/update.py
index d41544f1c0..8f41b57850 100644
--- a/nf_core/modules/update.py
+++ b/nf_core/modules/update.py
@@ -1,6 +1,11 @@
 import os
+import shutil
 import questionary
 import logging
+import tempfile
+import difflib
+from rich.console import Console
+from rich.syntax import Syntax
 
 import nf_core.utils
 import nf_core.modules.module_utils
@@ -13,12 +18,13 @@
 
 
 class ModuleUpdate(ModuleCommand):
-    def __init__(self, pipeline_dir, force=False, prompt=False, sha=None, update_all=False):
+    def __init__(self, pipeline_dir, force=False, prompt=False, sha=None, update_all=False, diff=False):
         super().__init__(pipeline_dir)
         self.force = force
         self.prompt = prompt
         self.sha = sha
         self.update_all = update_all
+        self.diff = diff
 
     def update(self, module):
         if self.repo_type == "modules":
@@ -173,6 +179,7 @@ def update(self, module):
 
         exit_value = True
         for modules_repo, module, sha in repos_mods_shas:
+            dry_run = self.diff
             if not module_exist_in_repo(module, modules_repo):
                 warn_msg = f"Module '{module}' not found in remote '{modules_repo.name}' ({modules_repo.branch})"
                 if self.update_all:
@@ -187,10 +194,10 @@ def update(self, module):
                 current_entry = None
 
             # Set the install folder based on the repository name
-            install_folder = [modules_repo.owner, modules_repo.repo]
+            install_folder = [self.dir, "modules", modules_repo.owner, modules_repo.repo]
 
             # Compute the module directory
-            module_dir = os.path.join(self.dir, "modules", *install_folder, module)
+            module_dir = os.path.join(*install_folder, module)
 
             if sha:
                 version = sha
@@ -225,17 +232,78 @@ def update(self, module):
                         log.info(f"'{modules_repo.name}/{module}' is already up to date")
                     continue
 
-            log.info(f"Updating '{modules_repo.name}/{module}'")
-            log.debug(f"Updating module '{module}' to {version} from {modules_repo.name}")
+            if not dry_run:
+                log.info(f"Updating '{modules_repo.name}/{module}'")
+                log.debug(f"Updating module '{module}' to {version} from {modules_repo.name}")
 
-            log.debug(f"Removing old version of module '{module}'")
-            self.clear_module_dir(module, module_dir)
+                log.debug(f"Removing old version of module '{module}'")
+                self.clear_module_dir(module, module_dir)
+
+            if dry_run:
+                # Set the install folder to a temporary directory
+                install_folder = ["/tmp", next(tempfile._get_candidate_names())]
 
             # Download module files
-            if not self.download_module_file(module, version, modules_repo, install_folder, module_dir):
+            if not self.download_module_file(module, version, modules_repo, install_folder, dry_run=dry_run):
                 exit_value = False
                 continue
 
-            # Update module.json with newly installed module
-            self.update_modules_json(modules_json, modules_repo.name, module, version)
+            if dry_run:
+                console = Console(force_terminal=nf_core.utils.rich_force_colors())
+                files = os.listdir(os.path.join(*install_folder, module))
+                temp_folder = os.path.join(*install_folder, module)
+                log.info(
+                    f"Changes in module '{module}' between ({current_entry['git_sha'] if current_entry is not None else '?'}) and ({version if version is not None else 'latest'})"
+                )
+
+                for file in files:
+                    temp_path = os.path.join(temp_folder, file)
+                    curr_path = os.path.join(module_dir, file)
+                    if os.path.exists(temp_path) and os.path.exists(curr_path):
+                        with open(temp_path, "r") as fh:
+                            new_lines = fh.readlines()
+                        with open(curr_path, "r") as fh:
+                            old_lines = fh.readlines()
+                        if new_lines == old_lines:
+                            # The files are identical
+                            log.info(f"'{os.path.join(module, file)}' is unchanged")
+                        else:
+                            log.info(f"Changes in '{os.path.join(module, file)}':")
+                            # Compute the diff
+                            diff = difflib.unified_diff(
+                                old_lines,
+                                new_lines,
+                                fromfile=f"{os.path.join(module, file)} (installed)",
+                                tofile=f"{os.path.join(module, file)} (new)",
+                            )
+
+                            # Pretty print the diff using the pygments diff lexer
+                            console.print(Syntax("".join(diff), "diff", theme="ansi_light"))
+
+                    elif os.path.exists(temp_path):
+                        # The file was created between the commits
+                        log.info(f"Created file '{file}'")
+
+                    elif os.path.exists(curr_path):
+                        # The file was removed between the commits
+                        log.info(f"Removed file '{file}'")
+
+                # Ask the user if they want to install the module
+                dry_run = not questionary.confirm("Update module?", default=False).unsafe_ask()
+                if not dry_run:
+                    # The new module files are already installed
+                    # we just need to clear the directory and move the
+                    # new files from the temporary directory
+                    self.clear_module_dir(module, module_dir)
+                    os.mkdir(module_dir)
+                    for file in files:
+                        path = os.path.join(temp_folder, file)
+                        if os.path.exists(path):
+                            shutil.move(path, os.path.join(module_dir, file))
+                    log.info(f"Updating '{modules_repo.name}/{module}'")
+                    log.debug(f"Updating module '{module}' to {version} from {modules_repo.name}")
+
+            if not dry_run:
+                # Update module.json with newly installed module
+                self.update_modules_json(modules_json, modules_repo.name, module, version)
         return exit_value

From 14dec1ec9ff18fc0ff11e1624d57b15503cbb84b Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Fri, 6 Aug 2021 14:50:27 +0200
Subject: [PATCH 022/266] Update docs and CHANGELOG.md

---
 CHANGELOG.md | 1 +
 README.md    | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9f518293f..c757b4ae2b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 ### Modules
 
 * Fixed typo in `module_utils.py`.
+* Added `--diff` flag to `nf-core modules update` which shows the diff between the installed files and the versions
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
diff --git a/README.md b/README.md
index 3a8fbc47ab..c874796049 100644
--- a/README.md
+++ b/README.md
@@ -1019,11 +1019,12 @@ INFO     Downloaded 3 files to ./modules/nf-core/modules/fastqc
 
 You can pass the module name as an optional argument to `nf-core modules update` instead of using the cli prompt, eg: `nf-core modules update fastqc`. You can specify a pipeline directory other than the current working directory by using the `--dir <pipeline dir>`.
 
-There are four additional flags that you can use with this command:
+There are five additional flags that you can use with this command:
 
 * `--force`: Reinstall module even if it appears to be up to date
 * `--prompt`: Select the module version using a cli prompt.
 * `--sha <commit_sha>`: Install the module at a specific commit from the `nf-core/modules` repository.
+* `--diff`: Show the diff between the installed files and the new version before installing.
 * `--all`: Use this flag to run the command on all modules in the pipeline.
 
 If you don't want to update certain modules or want to update them to specific versions, you can make use of the `.nf-core.yml` configuration file. For example, you can prevent the `star/align` module installed from `nf-core/modules` from being updated by adding the following to the `.nf-core.yml` file:

From d959a677d78b06567240754f0fc8a5a21d9a4a40 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Fri, 6 Aug 2021 17:23:22 +0200
Subject: [PATCH 023/266] Print to log that module entry was found in
 '.nf-core.yml'

---
 nf_core/modules/update.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/update.py b/nf_core/modules/update.py
index 8f41b57850..dc69c5b15a 100644
--- a/nf_core/modules/update.py
+++ b/nf_core/modules/update.py
@@ -100,12 +100,15 @@ def update(self, module):
                         log.info("Module's update entry in '.nf-core.yml' is set to False")
                         return False
                     elif isinstance(config_entry, str):
+                        sha = config_entry
                         if self.sha:
                             log.warning(
-                                "Found entry in '.nf-core.yml' for module "
+                                f"Found entry in '.nf-core.yml' for module '{module}' "
                                 "which will override version specified with '--sha'"
                             )
-                        sha = config_entry
+                        else:
+                            log.info(f"Found entry in '.nf-core.yml' for module '{module}'")
+                        log.info(f"Updating module to ({sha})")
                     else:
                         log.error("Module's update entry in '.nf-core.yml' is of wrong type")
                         return False

From a640d64d82151e842c81ec9e2b559e5eb8699f3b Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 11:00:11 +0200
Subject: [PATCH 024/266] Fix bug

---
 nf_core/modules/create.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index 8473f64b8d..b8fc6d30db 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -145,7 +145,9 @@ def create(self):
                 log.info(f"Using Bioconda package: '{self.bioconda}'")
                 break
             except (ValueError, LookupError) as e:
-                log.warning(f"Could not find Conda dependency using the Anaconda API: '{self.tool}'")
+                log.warning(
+                    f"Could not find Conda dependency using the Anaconda API: '{self.tool_conda_name if self.tool_conda_name else self.tool}'"
+                )
                 if rich.prompt.Confirm.ask(f"[violet]Do you want to enter a different Bioconda package name?"):
                     self.tool_conda_name = rich.prompt.Prompt.ask("[violet]Name of Bioconda package").strip()
                     continue

From 43af5384c24c413b1acee566d3be1db48bee7065 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 11:14:30 +0200
Subject: [PATCH 025/266] Update help texts

---
 nf_core/__main__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/__main__.py b/nf_core/__main__.py
index 4d828fd671..4c08e4fc87 100755
--- a/nf_core/__main__.py
+++ b/nf_core/__main__.py
@@ -502,10 +502,10 @@ def create_module(ctx, tool, dir, author, label, meta, no_meta, force, conda_nam
     """
     Create a new DSL2 module from the nf-core template.
 
-    If <directory> is a pipeline, this function creates a file called
+    If the working directory is a pipeline, this function creates a file called
     'modules/local/tool_subtool.nf'
 
-    If <directory> is a clone of nf-core/modules, it creates or modifies files
+    If the working directory is a clone of nf-core/modules, it creates or modifies files
     in 'modules/', 'tests/modules' and 'tests/config/pytest_modules.yml'
     """
     # Combine two bool flags into one variable

From fe480ad8e1b2864015331b1bb2c581d73a52d409 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 11:16:08 +0200
Subject: [PATCH 026/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c757b4ae2b..fb5a6b3465 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 
 * Fixed typo in `module_utils.py`.
 * Added `--diff` flag to `nf-core modules update` which shows the diff between the installed files and the versions
+* Update `nf-core modules create` help texts which were not changed with the introduction of the `--dir` flag
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 

From 22a8ff83f14e1e455b831d8b6ada85d7dd472319 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 11:47:31 +0200
Subject: [PATCH 027/266] Add '-r' option to nextflow command

---
 nf_core/launch.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/launch.py b/nf_core/launch.py
index 36a21b076b..3bf257ff07 100644
--- a/nf_core/launch.py
+++ b/nf_core/launch.py
@@ -209,7 +209,7 @@ def get_pipeline_schema(self):
                     return False
 
                 self.pipeline_revision = nf_core.utils.prompt_pipeline_release_branch(wf_releases, wf_branches)
-                self.nextflow_cmd += " -r {}".format(self.pipeline_revision)
+            self.nextflow_cmd += " -r {}".format(self.pipeline_revision)
 
         # Get schema from name, load it and lint it
         try:

From 4a8740a8ad9ec6de3a0fcaf3e5f023205a45a4d9 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 11:51:30 +0200
Subject: [PATCH 028/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c757b4ae2b..e22884f09c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 ### General
 
 * Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
+* Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
 
 ### Modules
 

From a50d5bd900f48a077d15aec216d393bf76f8fe5a Mon Sep 17 00:00:00 2001
From: Erik Danielsson <53212377+ErikDanielsson@users.noreply.github.com>
Date: Mon, 9 Aug 2021 14:30:10 +0200
Subject: [PATCH 029/266] Apply suggestions from code review

---
 nf_core/__main__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/__main__.py b/nf_core/__main__.py
index 4c08e4fc87..24c575d576 100755
--- a/nf_core/__main__.py
+++ b/nf_core/__main__.py
@@ -502,10 +502,10 @@ def create_module(ctx, tool, dir, author, label, meta, no_meta, force, conda_nam
     """
     Create a new DSL2 module from the nf-core template.
 
-    If the working directory is a pipeline, this function creates a file called
+    If the specified is a pipeline, this function creates a file called
     'modules/local/tool_subtool.nf'
 
-    If the working directory is a clone of nf-core/modules, it creates or modifies files
+    If the specified directory is a clone of nf-core/modules, it creates or modifies files
     in 'modules/', 'tests/modules' and 'tests/config/pytest_modules.yml'
     """
     # Combine two bool flags into one variable

From 8765910ba69a19bd62ebc077aa23fc9e42fab84d Mon Sep 17 00:00:00 2001
From: Erik Danielsson <53212377+ErikDanielsson@users.noreply.github.com>
Date: Mon, 9 Aug 2021 14:30:43 +0200
Subject: [PATCH 030/266] Update nf_core/__main__.py

---
 nf_core/__main__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/__main__.py b/nf_core/__main__.py
index 24c575d576..a9b5f05b68 100755
--- a/nf_core/__main__.py
+++ b/nf_core/__main__.py
@@ -502,7 +502,7 @@ def create_module(ctx, tool, dir, author, label, meta, no_meta, force, conda_nam
     """
     Create a new DSL2 module from the nf-core template.
 
-    If the specified is a pipeline, this function creates a file called
+    If the specified directory is a pipeline, this function creates a file called
     'modules/local/tool_subtool.nf'
 
     If the specified directory is a clone of nf-core/modules, it creates or modifies files

From 3759ea04a6a7511520bc2a58f502d9b125bc2154 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 14:34:24 +0200
Subject: [PATCH 031/266] Update README.md lint regex

---
 nf_core/lint/readme.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/lint/readme.py b/nf_core/lint/readme.py
index 7031bb05fd..f42239a8b5 100644
--- a/nf_core/lint/readme.py
+++ b/nf_core/lint/readme.py
@@ -63,7 +63,7 @@ def readme(self):
 
     # Check that the minimum version mentioned in the quick start section is consistent
     # Looking for: "1. Install [`Nextflow`](https://nf-co.re/usage/installation) (`>=21.04.0`)"
-    nf_version_re = r"1\.\s*Install\s*\[`Nextflow`\]\(https://nf-co.re/usage/installation\)\s*\(`>=(\d*\.\d*\.\d*)`\)"
+    nf_version_re = r"1\.\s*Install\s*\[`Nextflow`\]\(https://www.nextflow.io/docs/latest/getstarted.html#installation\)\s*\(`>=(\d*\.\d*\.\d*)`\)"
     match = re.search(nf_version_re, content)
     if match:
         nf_quickstart_version = match.group(1)

From 30efe6ec68cad67a46b6ab256dd2cbcd6a88e8ea Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 14:38:32 +0200
Subject: [PATCH 032/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e22884f09c..29d5bda6e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 
 * Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
 * Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
+* Update regex in `readme` test of `nf-core lint` to agree with the pipeline template ([#1260](https://github.com/nf-core/tools/issues/1260))
 
 ### Modules
 

From 4a4d821ef7e44cd9f5e4884d44e442d7bddefd35 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 15:36:48 +0200
Subject: [PATCH 033/266] Fix outdated fix message in lint

---
 CHANGELOG.md          | 1 +
 nf_core/lint_utils.py | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e22884f09c..3c968d89c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 
 * Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
 * Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
+* Update 'fix' message in `nf-core lint` to conform to the current command line options. ([#1259](https://github.com/nf-core/tools/issues/1259))
 
 ### Modules
 
diff --git a/nf_core/lint_utils.py b/nf_core/lint_utils.py
index 6e2084922e..ea62dd2b00 100644
--- a/nf_core/lint_utils.py
+++ b/nf_core/lint_utils.py
@@ -38,7 +38,9 @@ def print_fixes(lint_obj, module_lint_obj):
     """Prints available and applied fixes"""
 
     if len(lint_obj.could_fix):
-        fix_cmd = "nf-core lint {} --fix {}".format(lint_obj.wf_path, " --fix ".join(lint_obj.could_fix))
+        fix_cmd = "nf-core lint {}--fix {}".format(
+            "" if lint_obj.wf_path == "." else f"--dir {lint_obj.wf_path}", " --fix ".join(lint_obj.could_fix)
+        )
         console.print(
             f"\nTip: Some of these linting errors can automatically be resolved with the following command:\n\n[blue]    {fix_cmd}\n"
         )

From 66c0c4e8a082231af5ad48e357f9e3d7117241b5 Mon Sep 17 00:00:00 2001
From: Erik Danielsson <danielsson.erik.0@gmail.com>
Date: Mon, 9 Aug 2021 15:50:23 +0200
Subject: [PATCH 034/266] Update link in help text

---
 nf_core/lint/readme.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/lint/readme.py b/nf_core/lint/readme.py
index f42239a8b5..ab240fc267 100644
--- a/nf_core/lint/readme.py
+++ b/nf_core/lint/readme.py
@@ -62,7 +62,7 @@ def readme(self):
         warned.append("README did not have a Nextflow minimum version badge.")
 
     # Check that the minimum version mentioned in the quick start section is consistent
-    # Looking for: "1. Install [`Nextflow`](https://nf-co.re/usage/installation) (`>=21.04.0`)"
+    # Looking for: "1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.04.0`)"
     nf_version_re = r"1\.\s*Install\s*\[`Nextflow`\]\(https://www.nextflow.io/docs/latest/getstarted.html#installation\)\s*\(`>=(\d*\.\d*\.\d*)`\)"
     match = re.search(nf_version_re, content)
     if match:

From 1c927e9481702c9c7d48915592a83a5df04cf1bb Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Mon, 2 Aug 2021 16:16:10 +0200
Subject: [PATCH 035/266] Change filter to support multiple version file
 outputs

---
 nf_core/pipeline-template/workflows/pipeline.nf | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index fe1882b420..c27d4b4df3 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -92,15 +92,12 @@ workflow {{ short_name|upper }} {
     // MODULE: Pipeline reporting
     //
     ch_software_versions
-        .map { it -> if (it) [ it.baseName, it ] }
-        .groupTuple()
-        .map { it[1][0] }
         .flatten()
-        .collect()
+        .unique { it.getName() + it.getText() }
         .set { ch_software_versions }
 
     GET_SOFTWARE_VERSIONS (
-        ch_software_versions.map { it }.collect()
+        ch_software_versions.collect()
     )
 
     //

From e147cc931d004ef7dbd4870bfe63464255e4a952 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Mon, 2 Aug 2021 22:18:29 +0200
Subject: [PATCH 036/266] Add collectFile to combine different versions

---
 nf_core/pipeline-template/workflows/pipeline.nf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index c27d4b4df3..880886fd64 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -94,6 +94,7 @@ workflow {{ short_name|upper }} {
     ch_software_versions
         .flatten()
         .unique { it.getName() + it.getText() }
+        .collectFile(sort:true) { it -> [ it.getName(), it.getText()] } 
         .set { ch_software_versions }
 
     GET_SOFTWARE_VERSIONS (

From 2942f8384d9420ad5a6aa803aaf6968ccdc5f2b9 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Mon, 2 Aug 2021 22:27:29 +0200
Subject: [PATCH 037/266] Change scrape versions to comma separate multiple
 versions

---
 nf_core/pipeline-template/bin/scrape_software_versions.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/bin/scrape_software_versions.py b/nf_core/pipeline-template/bin/scrape_software_versions.py
index 241dc8b7a6..057c99ffd3 100755
--- a/nf_core/pipeline-template/bin/scrape_software_versions.py
+++ b/nf_core/pipeline-template/bin/scrape_software_versions.py
@@ -11,7 +11,7 @@
         software = "{{ name }}"
 
     with open(version_file) as fin:
-        version = fin.read().strip()
+        version = fin.read().strip().replace('\n',', ')
     results[software] = version
 
 # Dump to YAML

From 826661037e95706df18280a2d883bc33ac12c7b6 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Mon, 2 Aug 2021 22:37:15 +0200
Subject: [PATCH 038/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe87bf81dc..dc696843c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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))

From deaf0c12c92215c0051831adac39b4dbbcc8944e Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Mon, 2 Aug 2021 22:47:57 +0200
Subject: [PATCH 039/266] black lint update

---
 nf_core/pipeline-template/bin/scrape_software_versions.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/bin/scrape_software_versions.py b/nf_core/pipeline-template/bin/scrape_software_versions.py
index 057c99ffd3..a554188966 100755
--- a/nf_core/pipeline-template/bin/scrape_software_versions.py
+++ b/nf_core/pipeline-template/bin/scrape_software_versions.py
@@ -11,7 +11,7 @@
         software = "{{ name }}"
 
     with open(version_file) as fin:
-        version = fin.read().strip().replace('\n',', ')
+        version = fin.read().strip().replace("\n", ", ")
     results[software] = version
 
 # Dump to YAML

From e3c5fe0873612527af76b56e831daf0968a5fdc5 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 15:07:41 +0200
Subject: [PATCH 040/266] Software versions as YAML output

---
 nf_core/module-template/modules/main.nf | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 6e4dcde636..c568852139 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -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
+    ${task.process}:
+        - samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
+    END_VERSIONS
     """
 }

From 7a894d668b4429829fea4acd6f8acf1fe50994cd Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 15:23:38 +0200
Subject: [PATCH 041/266] Add comment to add version for each tool

---
 nf_core/module-template/modules/main.nf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index c568852139..741f8b04d0 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -62,6 +62,7 @@ process {{ tool_name_underscore|upper }} {
     // 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 tool MUST provide the tool 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"

From d9267e901bc06ce3d46c3b55780a420dcbea0d89 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 15:23:57 +0200
Subject: [PATCH 042/266] Update output

---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 741f8b04d0..ee3293f445 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -52,7 +52,7 @@ 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)

From 71d20e1c2ea7e1743931c1c26891ce5595087697 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 15:27:19 +0200
Subject: [PATCH 043/266] Collect YAML files together

---
 nf_core/pipeline-template/workflows/pipeline.nf | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 880886fd64..bbd9ae62a4 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -91,14 +91,14 @@ workflow {{ short_name|upper }} {
     //
     // MODULE: Pipeline reporting
     //
-    ch_software_versions
-        .flatten()
-        .unique { it.getName() + it.getText() }
-        .collectFile(sort:true) { it -> [ it.getName(), it.getText()] } 
-        .set { ch_software_versions }
+    // ch_software_versions
+        // .flatten()
+        // .unique { it.getName() + it.getText() }
+        // .collectFile(sort:true) { it -> [ it.getName(), it.getText()] }
+        // .set { ch_software_versions }
 
     GET_SOFTWARE_VERSIONS (
-        ch_software_versions.collect()
+        ch_software_versions.collectFile()
     )
 
     //

From 221e49252509f5aee2e77754be59dd0ff9da62bd Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 15:59:35 +0200
Subject: [PATCH 044/266] Add workflow data to software versions

---
 .../modules/local/get_software_versions.nf            | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index 8af8af1735..b902834c50 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -26,8 +26,13 @@ process GET_SOFTWARE_VERSIONS {
 
     script: // This script is bundled with the pipeline, in {{ name }}/bin/
     """
-    echo $workflow.manifest.version > pipeline.version.txt
-    echo $workflow.nextflow.version > nextflow.version.txt
-    scrape_software_versions.py &> software_versions_mqc.yaml
+    # echo $workflow.manifest.version > pipeline.version.txt
+    # echo $workflow.nextflow.version > nextflow.version.txt
+    # scrape_software_versions.py &> software_versions_mqc.yaml
+    cat - $versions <<-END_WORKFLOW_VERSION > software_versions_mqc.yaml
+    Workflow:
+        - Nextflow: $workflow.nextflow.version
+        - $workflow.manifest.name: $workflow.manifest.version
+    END_WORKFLOW_VERSION
     """
 }

From 89d291863412e3dac601d8781c8175ef0de0e7f3 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 20:17:45 +0200
Subject: [PATCH 045/266] Replace software versions tsv with yml

---
 .../modules/local/get_software_versions.nf      | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index b902834c50..fc7fcbca37 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -21,18 +21,23 @@ 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.yaml', emit: mqc_yaml
 
     script: // This script is bundled with the pipeline, in {{ name }}/bin/
     """
-    # echo $workflow.manifest.version > pipeline.version.txt
-    # echo $workflow.nextflow.version > nextflow.version.txt
-    # scrape_software_versions.py &> software_versions_mqc.yaml
-    cat - $versions <<-END_WORKFLOW_VERSION > software_versions_mqc.yaml
+    cat - $versions <<-END_WORKFLOW_VERSION > software_versions.yml
     Workflow:
         - Nextflow: $workflow.nextflow.version
         - $workflow.manifest.name: $workflow.manifest.version
     END_WORKFLOW_VERSION
+    cat - <( sed 's/^/    /' software_versions.yml ) <<-END_MQC_YAML > software_versions_mqc.yaml
+    id: 'software_versions'
+    section_name: '{{ name }} Software Versions'
+    section_href: 'https://github.com/{{ name }}'
+    plot_type: 'table'
+    description: 'are collected at run time from the software output.'
+    data:
+    END_MQC_YAML
     """
 }

From 3945ee8901217e49ea9c64fe5ab9c708b8fcf973 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 3 Aug 2021 20:21:34 +0200
Subject: [PATCH 046/266] Update GET_SOFTWARE_VERSION channel names

---
 nf_core/pipeline-template/workflows/pipeline.nf | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index bbd9ae62a4..9fb4d0d0f5 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -38,7 +38,7 @@ 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':'']] )
+include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files : ['yml':'']] )
 
 //
 // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules
@@ -111,7 +111,7 @@ workflow {{ short_name|upper }} {
     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(GET_SOFTWARE_VERSIONS.out.mqc_yaml.collect())
     ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([]))
 
     MULTIQC (

From e6bc9ff61e2ca735cce2fe20003867b3715ab1b4 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Wed, 4 Aug 2021 10:34:36 +0200
Subject: [PATCH 047/266] Update
 nf_core/pipeline-template/modules/local/get_software_versions.nf

Co-authored-by: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/pipeline-template/modules/local/get_software_versions.nf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index fc7fcbca37..943adcf5cf 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -31,6 +31,7 @@ process GET_SOFTWARE_VERSIONS {
         - Nextflow: $workflow.nextflow.version
         - $workflow.manifest.name: $workflow.manifest.version
     END_WORKFLOW_VERSION
+    
     cat - <( sed 's/^/    /' software_versions.yml ) <<-END_MQC_YAML > software_versions_mqc.yaml
     id: 'software_versions'
     section_name: '{{ name }} Software Versions'

From 85839d32de84461119b6174c98a20e19b23ca554 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Wed, 4 Aug 2021 10:42:47 +0200
Subject: [PATCH 048/266] Move sed for readability

---
 .../pipeline-template/modules/local/get_software_versions.nf | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index 943adcf5cf..0059db317a 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -31,14 +31,15 @@ process GET_SOFTWARE_VERSIONS {
         - Nextflow: $workflow.nextflow.version
         - $workflow.manifest.name: $workflow.manifest.version
     END_WORKFLOW_VERSION
-    
-    cat - <( sed 's/^/    /' software_versions.yml ) <<-END_MQC_YAML > software_versions_mqc.yaml
+
+    cat - <<-END_MQC_YAML > software_versions_mqc.yaml
     id: 'software_versions'
     section_name: '{{ name }} Software Versions'
     section_href: 'https://github.com/{{ name }}'
     plot_type: 'table'
     description: 'are collected at run time from the software output.'
     data:
+    \$( sed 's/^/    /' software_versions.yml ) 
     END_MQC_YAML
     """
 }

From e99da1e4377bf7b4abe1c2c0525d5333cde93af1 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Wed, 4 Aug 2021 10:55:53 +0200
Subject: [PATCH 049/266] Update nf_core/module-template/modules/main.nf

---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index ee3293f445..9dfb70d706 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -80,7 +80,7 @@ process {{ tool_name_underscore|upper }} {
         $bam
 
     cat <<-END_VERSIONS > versions.yml
-    ${task.process}:
+    ${task.process.tokenize(':')[-1]}:
         - samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
     END_VERSIONS
     """

From 5ce54a41011f752066b4f6349928fb61c78e32b5 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 10 Aug 2021 09:16:45 +0200
Subject: [PATCH 050/266] Remove Dash from YAML

---
 nf_core/module-template/modules/main.nf                     | 2 +-
 .../modules/local/get_software_versions.nf                  | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 9dfb70d706..97d4eb7ccd 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -81,7 +81,7 @@ process {{ tool_name_underscore|upper }} {
 
     cat <<-END_VERSIONS > versions.yml
     ${task.process.tokenize(':')[-1]}:
-        - samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
+        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
     END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index 0059db317a..acfec1f288 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -28,8 +28,8 @@ process GET_SOFTWARE_VERSIONS {
     """
     cat - $versions <<-END_WORKFLOW_VERSION > software_versions.yml
     Workflow:
-        - Nextflow: $workflow.nextflow.version
-        - $workflow.manifest.name: $workflow.manifest.version
+        Nextflow: $workflow.nextflow.version
+        $workflow.manifest.name: $workflow.manifest.version
     END_WORKFLOW_VERSION
 
     cat - <<-END_MQC_YAML > software_versions_mqc.yaml
@@ -39,7 +39,7 @@ process GET_SOFTWARE_VERSIONS {
     plot_type: 'table'
     description: 'are collected at run time from the software output.'
     data:
-    \$( sed 's/^/    /' software_versions.yml ) 
+    \$( sed 's/^/    /' software_versions.yml )
     END_MQC_YAML
     """
 }

From fd93ebbc7000a7e54ddac09ca240a5a5e70c3e41 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:25:40 +0200
Subject: [PATCH 051/266] Port changes from prototype
 https://github.com/nf-core/rnaseq/pull/689

Co-Authored-By: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/module-template/modules/functions.nf  |  7 ++
 nf_core/module-template/modules/main.nf       |  8 +-
 .../bin/scrape_software_versions.py           | 36 --------
 .../modules/local/functions.nf                |  7 ++
 .../modules/local/get_software_versions.nf    | 88 +++++++++++++++----
 .../nf-core/modules/fastqc/functions.nf       |  7 ++
 .../modules/nf-core/modules/fastqc/main.nf    | 14 ++-
 .../nf-core/modules/multiqc/functions.nf      |  7 ++
 .../modules/nf-core/modules/multiqc/main.nf   |  8 +-
 .../pipeline-template/workflows/pipeline.nf   |  9 --
 10 files changed, 118 insertions(+), 73 deletions(-)
 delete mode 100755 nf_core/pipeline-template/bin/scrape_software_versions.py

diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
index da9da093d3..4860a36278 100644
--- a/nf_core/module-template/modules/functions.nf
+++ b/nf_core/module-template/modules/functions.nf
@@ -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
 //
diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 97d4eb7ccd..164329e965 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -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
@@ -62,7 +62,7 @@ process {{ tool_name_underscore|upper }} {
     // 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 tool MUST provide the tool name and version number in the YAML version file (versions.yml)
+    //               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"
@@ -80,8 +80,8 @@ process {{ tool_name_underscore|upper }} {
         $bam
 
     cat <<-END_VERSIONS > versions.yml
-    ${task.process.tokenize(':')[-1]}:
-        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
+    ${getProcessName(task.process)}:
+        $software: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
     END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/bin/scrape_software_versions.py b/nf_core/pipeline-template/bin/scrape_software_versions.py
deleted file mode 100755
index a554188966..0000000000
--- a/nf_core/pipeline-template/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 = "{{ name }}"
-
-    with open(version_file) as fin:
-        version = fin.read().strip().replace("\n", ", ")
-    results[software] = version
-
-# Dump to YAML
-print(
-    """
-id: 'software_versions'
-section_name: '{{ name }} Software Versions'
-section_href: 'https://github.com/{{ name }}'
-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/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
index da9da093d3..4860a36278 100644
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ b/nf_core/pipeline-template/modules/local/functions.nf
@@ -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
 //
diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index acfec1f288..0120a80baf 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -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 requires only 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
@@ -24,22 +25,71 @@ process GET_SOFTWARE_VERSIONS {
     path "software_versions.yml"     , emit: yml
     path 'software_versions_mqc.yaml', emit: mqc_yaml
 
-    script: // This script is bundled with the pipeline, in {{ name }}/bin/
+    script:
     """
-    cat - $versions <<-END_WORKFLOW_VERSION > software_versions.yml
-    Workflow:
-        Nextflow: $workflow.nextflow.version
-        $workflow.manifest.name: $workflow.manifest.version
-    END_WORKFLOW_VERSION
-
-    cat - <<-END_MQC_YAML > software_versions_mqc.yaml
-    id: 'software_versions'
-    section_name: '{{ name }} Software Versions'
-    section_href: 'https://github.com/{{ name }}'
-    plot_type: 'table'
-    description: 'are collected at run time from the software output.'
-    data:
-    \$( sed 's/^/    /' software_versions.yml )
-    END_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)
     """
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index da9da093d3..4860a36278 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -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
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 39c327b261..f90aae1a55 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -34,14 +34,22 @@ process FASTQC {
         """
         [ ! -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
+
+        cat <<-END_VERSIONS > versions.yml
+        ${getProcessName(task.process)}:
+            $software: \$(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
+
+        cat <<-END_VERSIONS > versions.yml
+        ${getProcessName(task.process)}:
+            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
+        END_VERSIONS
         """
     }
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index da9da093d3..4860a36278 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -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
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index da78080024..17a3097671 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -30,6 +30,10 @@ process MULTIQC {
     def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
-    multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt
+
+    cat <<-END_VERSIONS > versions.yml
+    ${getProcessName(task.process)}:
+        $software: \$(multiqc --version | sed -e "s/multiqc, version //g")
+    END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 9fb4d0d0f5..f99538d4c8 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -88,15 +88,6 @@ workflow {{ short_name|upper }} {
     )
     ch_software_versions = ch_software_versions.mix(FASTQC.out.version.first().ifEmpty(null))
 
-    //
-    // MODULE: Pipeline reporting
-    //
-    // ch_software_versions
-        // .flatten()
-        // .unique { it.getName() + it.getText() }
-        // .collectFile(sort:true) { it -> [ it.getName(), it.getText()] }
-        // .set { ch_software_versions }
-
     GET_SOFTWARE_VERSIONS (
         ch_software_versions.collectFile()
     )

From 3a10d60617a3958da9dd36a594b6a0387cf833e5 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:27:16 +0200
Subject: [PATCH 052/266] Update comment

---
 .../pipeline-template/modules/local/get_software_versions.nf    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index 0120a80baf..ede47ed68d 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -8,7 +8,7 @@ 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:[]) }
 
-    // This module requires only the PyYAML library, but rather than create a new container on biocontainers, we reuse the multiqc container.
+    // 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/multiqc:1.10.1--pyhdfd78af_1"

From 1a7993d28a2cb14449aeadf10436085199c4f79f Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:30:27 +0200
Subject: [PATCH 053/266] Update process output

---
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index f90aae1a55..97183b62c9 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -24,7 +24,7 @@ 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: version
 
     script:
     // Add soft-links to original FastQs for consistent naming in pipeline
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 17a3097671..77c8f4361a 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -24,7 +24,7 @@ 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: version
 
     script:
     def software = getSoftwareName(task.process)

From d18174d69a52aa5b56037a9d33e8a5e2ff314869 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:40:59 +0200
Subject: [PATCH 054/266] Add getProcessName to modules functions_nf lint

---
 nf_core/modules/lint/functions_nf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/functions_nf.py b/nf_core/modules/lint/functions_nf.py
index 600a1ae7fd..aef0d115ea 100644
--- a/nf_core/modules/lint/functions_nf.py
+++ b/nf_core/modules/lint/functions_nf.py
@@ -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:

From edae68b5b147712462ea4826523c86b2451178cc Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:44:50 +0200
Subject: [PATCH 055/266] Update file_exists lint to remove scrape_versions.py

---
 nf_core/lint/files_exist.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index 7b97aa3fb5..e1b28bc9a3 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -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
@@ -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")],

From 8ae12e3d6a2e7893ea6b137697023245764904a8 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:47:34 +0200
Subject: [PATCH 056/266] Remove scrape software version from files unchanged.

---
 nf_core/lint/files_unchanged.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/nf_core/lint/files_unchanged.py b/nf_core/lint/files_unchanged.py
index 37728b8b06..262e8c4449 100644
--- a/nf_core/lint/files_unchanged.py
+++ b/nf_core/lint/files_unchanged.py
@@ -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")],

From 074f1c496c6b2427df25b5d0fddd74b12ccbe1ca Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 10:50:25 +0200
Subject: [PATCH 057/266] Fix YAML output

---
 .../pipeline-template/modules/local/get_software_versions.nf    | 2 +-
 nf_core/pipeline-template/workflows/pipeline.nf                 | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/local/get_software_versions.nf
index ede47ed68d..08d58f9c52 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/local/get_software_versions.nf
@@ -23,7 +23,7 @@ process GET_SOFTWARE_VERSIONS {
 
     output:
     path "software_versions.yml"     , emit: yml
-    path 'software_versions_mqc.yaml', emit: mqc_yaml
+    path "software_versions_mqc.yml" , emit: mqc_yml
 
     script:
     """
diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index f99538d4c8..44924b8116 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -102,7 +102,7 @@ workflow {{ short_name|upper }} {
     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.mqc_yaml.collect())
+    ch_multiqc_files = ch_multiqc_files.mix(GET_SOFTWARE_VERSIONS.out.mqc_yml.collect())
     ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([]))
 
     MULTIQC (

From c21dabdd26ee8a46d32bcb78387916216e45a2f3 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 11:33:55 +0200
Subject: [PATCH 058/266] Update nf_core/module-template/modules/main.nf

Co-authored-by: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 164329e965..aa2a96021a 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -81,7 +81,7 @@ process {{ tool_name_underscore|upper }} {
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        $software: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
+        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
     END_VERSIONS
     """
 }

From 70d14aaf950c0e825310e1229017b01ccacc6662 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 11:39:43 +0200
Subject: [PATCH 059/266] Remove getSoftwareName function

---
 nf_core/module-template/modules/functions.nf               | 7 -------
 nf_core/module-template/modules/main.nf                    | 3 +--
 nf_core/modules/lint/functions_nf.py                       | 2 +-
 nf_core/pipeline-template/modules/local/functions.nf       | 7 -------
 .../modules/nf-core/modules/fastqc/functions.nf            | 7 -------
 .../modules/nf-core/modules/fastqc/main.nf                 | 2 +-
 .../modules/nf-core/modules/multiqc/functions.nf           | 7 -------
 .../modules/nf-core/modules/multiqc/main.nf                | 2 +-
 8 files changed, 4 insertions(+), 33 deletions(-)

diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
index 4860a36278..0dfaa3a1ca 100644
--- a/nf_core/module-template/modules/functions.nf
+++ b/nf_core/module-template/modules/functions.nf
@@ -2,13 +2,6 @@
 //  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()
-}
-
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index aa2a96021a..b72659f824 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
+include { initOptions; saveFiles; 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
@@ -55,7 +55,6 @@ process {{ tool_name_underscore|upper }} {
     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 %}
diff --git a/nf_core/modules/lint/functions_nf.py b/nf_core/modules/lint/functions_nf.py
index aef0d115ea..f03f0c39a3 100644
--- a/nf_core/modules/lint/functions_nf.py
+++ b/nf_core/modules/lint/functions_nf.py
@@ -22,7 +22,7 @@ def functions_nf(module_lint_object, module):
         return
 
     # Test whether all required functions are present
-    required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
+    required_functions = ["getProcessName", "initOptions", "getPathFromList", "saveFiles"]
     lines = "\n".join(lines)
     contains_all_functions = True
     for f in required_functions:
diff --git a/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
index 4860a36278..0dfaa3a1ca 100644
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ b/nf_core/pipeline-template/modules/local/functions.nf
@@ -2,13 +2,6 @@
 //  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()
-}
-
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index 4860a36278..0dfaa3a1ca 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -2,13 +2,6 @@
 //  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()
-}
-
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 97183b62c9..8becdcb48d 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
+include { initOptions; saveFiles; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index 4860a36278..0dfaa3a1ca 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -2,13 +2,6 @@
 //  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()
-}
-
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 77c8f4361a..a9ba0fe3a2 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
+include { initOptions; saveFiles; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)

From 1ee14e9a98608a87391d6b0739950fc85efcff4b Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 11:44:03 +0200
Subject: [PATCH 060/266] remove software var

---
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf | 5 ++---
 .../modules/nf-core/modules/multiqc/main.nf                  | 3 +--
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 8becdcb48d..26eb12cdad 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -28,7 +28,6 @@ process FASTQC {
 
     script:
     // 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}"
     if (meta.single_end) {
         """
@@ -37,7 +36,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
+            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     } else {
@@ -48,7 +47,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
+            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index a9ba0fe3a2..65b1e64bb7 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -27,13 +27,12 @@ process MULTIQC {
     path "versions.yml"        , emit: version
 
     script:
-    def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        $software: \$(multiqc --version | sed -e "s/multiqc, version //g")
+        multiqc: \$(multiqc --version | sed -e "s/multiqc, version //g")
     END_VERSIONS
     """
 }

From a45c66d3b3a9dbdbbab26d07d904bb9e1e55c5e1 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 11:52:12 +0200
Subject: [PATCH 061/266] Rename publish_dir directory

---
 nf_core/module-template/modules/main.nf                         | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index b72659f824..21b079ab3e 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -26,7 +26,7 @@ process {{ tool_name_underscore|upper }} {
     label '{{ process_label }}'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
 
     // TODO nf-core: List required Conda package(s).
     //               Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 26eb12cdad..a725ecce8d 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -9,7 +9,7 @@ process FASTQC {
     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']) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 65b1e64bb7..c97ed17c10 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -8,7 +8,7 @@ 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:[]) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(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) {

From 6682211194bb8e923f3a7a1cd603c0c2af997ae3 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 13:13:24 +0200
Subject: [PATCH 062/266] Update nf_core/module-template/modules/main.nf

Co-authored-by: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 21b079ab3e..16d35aa675 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -80,7 +80,7 @@ process {{ tool_name_underscore|upper }} {
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$// )
+        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$//' )
     END_VERSIONS
     """
 }

From 30bbc8aef6ba9f5791d4a8d089af6a3502e9b2d9 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 13:31:24 +0200
Subject: [PATCH 063/266] Update version.txt reference to versions.yml

---
 nf_core/module-template/modules/functions.nf                    | 2 +-
 nf_core/module-template/modules/meta.yml                        | 2 +-
 nf_core/pipeline-template/modules/local/functions.nf            | 2 +-
 .../modules/nf-core/modules/fastqc/functions.nf                 | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/meta.yml   | 2 +-
 .../modules/nf-core/modules/multiqc/functions.nf                | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/meta.yml  | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
index 0dfaa3a1ca..8171d73fa4 100644
--- a/nf_core/module-template/modules/functions.nf
+++ b/nf_core/module-template/modules/functions.nf
@@ -37,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.endsWith('.version.txt')) {
+    if (!args.filename.equals('versions.yml')) {
         def ioptions  = initOptions(args.options)
         def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
         if (ioptions.publish_by_meta) {
diff --git a/nf_core/module-template/modules/meta.yml b/nf_core/module-template/modules/meta.yml
index be6d3e5f93..c42dd613bd 100644
--- a/nf_core/module-template/modules/meta.yml
+++ b/nf_core/module-template/modules/meta.yml
@@ -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
diff --git a/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
index 0dfaa3a1ca..8171d73fa4 100644
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ b/nf_core/pipeline-template/modules/local/functions.nf
@@ -37,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.endsWith('.version.txt')) {
+    if (!args.filename.equals('versions.yml')) {
         def ioptions  = initOptions(args.options)
         def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
         if (ioptions.publish_by_meta) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index 0dfaa3a1ca..8171d73fa4 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -37,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.endsWith('.version.txt')) {
+    if (!args.filename.equals('versions.yml')) {
         def ioptions  = initOptions(args.options)
         def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
         if (ioptions.publish_by_meta) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
index 8eb9953dce..48031356b5 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
@@ -43,7 +43,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "*.{version.txt}"
+        pattern: "versions.yml"
 authors:
     - "@drpatelh"
     - "@grst"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index 0dfaa3a1ca..8171d73fa4 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -37,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.endsWith('.version.txt')) {
+    if (!args.filename.equals('versions.yml')) {
         def ioptions  = initOptions(args.options)
         def path_list = [ ioptions.publish_dir ?: args.publish_dir ]
         if (ioptions.publish_by_meta) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
index 532a8bb1ef..2d99ec0d12 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
@@ -32,7 +32,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "*.{version.txt}"
+        pattern: "versions.yml"
 authors:
     - "@abhi18av"
     - "@bunop"

From a98588b0a25eb98fb76cfdccfedf25699873963b Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 13:34:13 +0200
Subject: [PATCH 064/266] Update lint check for software

---
 nf_core/modules/lint/main_nf.py | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 018dc99af2..ac29e9ff26 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -116,16 +116,10 @@ def main_nf(module_lint_object, module):
 def check_script_section(self, lines):
     """
     Lint the script section
-    Checks whether 'def sotware' and 'def prefix' are defined
+    Checks whether 'def prefix' is defined
     """
     script = "".join(lines)
 
-    # check for software
-    if re.search("\s*def\s*software\s*=\s*getSoftwareName", script):
-        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):

From c46e2194e91f1878b3a714550eec6bf8b7d5c40e Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Thu, 12 Aug 2021 14:28:25 +0200
Subject: [PATCH 065/266] Make publish_dirs lowercase

---
 nf_core/module-template/modules/main.nf                         | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 16d35aa675..4047c0bf04 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -26,7 +26,7 @@ process {{ tool_name_underscore|upper }} {
     label '{{ process_label }}'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
 
     // TODO nf-core: List required Conda package(s).
     //               Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index a725ecce8d..35ed481c9c 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -9,7 +9,7 @@ process FASTQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:meta, publish_by_meta:['id']) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), 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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index c97ed17c10..2fc36ad1f3 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -8,7 +8,7 @@ process MULTIQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:[:], publish_by_meta:[]) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), meta:[:], publish_by_meta:[]) }
 
     conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null)
     if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {

From 77a89e2b6ac18952c2fbf96a9601292587a1160e Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:17:06 +0200
Subject: [PATCH 066/266] Revert "Make publish_dirs lowercase"

This reverts commit c46e2194e91f1878b3a714550eec6bf8b7d5c40e.
---
 nf_core/module-template/modules/main.nf                         | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 4047c0bf04..16d35aa675 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -26,7 +26,7 @@ process {{ tool_name_underscore|upper }} {
     label '{{ process_label }}'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
 
     // TODO nf-core: List required Conda package(s).
     //               Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 35ed481c9c..a725ecce8d 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -9,7 +9,7 @@ process FASTQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), meta:meta, publish_by_meta:['id']) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 2fc36ad1f3..c97ed17c10 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -8,7 +8,7 @@ process MULTIQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process).toLowerCase(), meta:[:], publish_by_meta:[]) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(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) {

From bd394ba3a7f6e6e5f3f99e252c506d83ce92fe82 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:17:28 +0200
Subject: [PATCH 067/266] Revert "Update lint check for software"

This reverts commit a98588b0a25eb98fb76cfdccfedf25699873963b.
---
 nf_core/modules/lint/main_nf.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index ac29e9ff26..018dc99af2 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -116,10 +116,16 @@ def main_nf(module_lint_object, module):
 def check_script_section(self, lines):
     """
     Lint the script section
-    Checks whether 'def prefix' is defined
+    Checks whether 'def sotware' and 'def prefix' are defined
     """
     script = "".join(lines)
 
+    # check for software
+    if re.search("\s*def\s*software\s*=\s*getSoftwareName", script):
+        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):

From 610585cbcdc47390744224e525298be983b96a7f Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:17:44 +0200
Subject: [PATCH 068/266] Revert "Rename publish_dir directory"

This reverts commit a45c66d3b3a9dbdbbab26d07d904bb9e1e55c5e1.
---
 nf_core/module-template/modules/main.nf                         | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 16d35aa675..b91d44307b 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -26,7 +26,7 @@ process {{ tool_name_underscore|upper }} {
     label '{{ process_label }}'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
+        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
 
     // TODO nf-core: List required Conda package(s).
     //               Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index a725ecce8d..26eb12cdad 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -9,7 +9,7 @@ process FASTQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:meta, publish_by_meta:['id']) }
+        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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index c97ed17c10..65b1e64bb7 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -8,7 +8,7 @@ process MULTIQC {
     label 'process_medium'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getProcessName(task.process), meta:[:], publish_by_meta:[]) }
+        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) {

From 21983765f28e432273285eaa8f013dc1ac9dc7df Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:18:01 +0200
Subject: [PATCH 069/266] Revert "remove software var"

This reverts commit 1ee14e9a98608a87391d6b0739950fc85efcff4b.
---
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf | 5 +++--
 .../modules/nf-core/modules/multiqc/main.nf                  | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 26eb12cdad..8becdcb48d 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -28,6 +28,7 @@ process FASTQC {
 
     script:
     // 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}"
     if (meta.single_end) {
         """
@@ -36,7 +37,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
+            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     } else {
@@ -47,7 +48,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
+            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 65b1e64bb7..a9ba0fe3a2 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -27,12 +27,13 @@ process MULTIQC {
     path "versions.yml"        , emit: version
 
     script:
+    def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        multiqc: \$(multiqc --version | sed -e "s/multiqc, version //g")
+        $software: \$(multiqc --version | sed -e "s/multiqc, version //g")
     END_VERSIONS
     """
 }

From 42404591f04cc614a70b225aacbb9c6828c64540 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:18:12 +0200
Subject: [PATCH 070/266] Revert "Remove getSoftwareName function"

This reverts commit 70d14aaf950c0e825310e1229017b01ccacc6662.
---
 nf_core/module-template/modules/functions.nf               | 7 +++++++
 nf_core/module-template/modules/main.nf                    | 3 ++-
 nf_core/modules/lint/functions_nf.py                       | 2 +-
 nf_core/pipeline-template/modules/local/functions.nf       | 7 +++++++
 .../modules/nf-core/modules/fastqc/functions.nf            | 7 +++++++
 .../modules/nf-core/modules/fastqc/main.nf                 | 2 +-
 .../modules/nf-core/modules/multiqc/functions.nf           | 7 +++++++
 .../modules/nf-core/modules/multiqc/main.nf                | 2 +-
 8 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
index 8171d73fa4..9fbf2c4992 100644
--- a/nf_core/module-template/modules/functions.nf
+++ b/nf_core/module-template/modules/functions.nf
@@ -2,6 +2,13 @@
 //  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()
+}
+
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index b91d44307b..460f5f1b76 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getProcessName } 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
@@ -55,6 +55,7 @@ process {{ tool_name_underscore|upper }} {
     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 %}
diff --git a/nf_core/modules/lint/functions_nf.py b/nf_core/modules/lint/functions_nf.py
index f03f0c39a3..aef0d115ea 100644
--- a/nf_core/modules/lint/functions_nf.py
+++ b/nf_core/modules/lint/functions_nf.py
@@ -22,7 +22,7 @@ def functions_nf(module_lint_object, module):
         return
 
     # Test whether all required functions are present
-    required_functions = ["getProcessName", "initOptions", "getPathFromList", "saveFiles"]
+    required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
     lines = "\n".join(lines)
     contains_all_functions = True
     for f in required_functions:
diff --git a/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
index 8171d73fa4..9fbf2c4992 100644
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ b/nf_core/pipeline-template/modules/local/functions.nf
@@ -2,6 +2,13 @@
 //  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()
+}
+
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index 8171d73fa4..9fbf2c4992 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -2,6 +2,13 @@
 //  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()
+}
+
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 8becdcb48d..97183b62c9 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getProcessName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index 8171d73fa4..9fbf2c4992 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -2,6 +2,13 @@
 //  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()
+}
+
 //
 // Extract name of module from process name using $task.process
 //
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index a9ba0fe3a2..77c8f4361a 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getProcessName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)

From 3bf1b7f76230807c3291a9f40f221e7061d10971 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:25:44 +0200
Subject: [PATCH 071/266] Remove def software line

---
 nf_core/module-template/modules/main.nf                     | 1 -
 nf_core/modules/lint/main_nf.py                             | 6 ------
 .../modules/nf-core/modules/fastqc/main.nf                  | 1 -
 .../modules/nf-core/modules/multiqc/main.nf                 | 1 -
 4 files changed, 9 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 460f5f1b76..457f2b39bc 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -55,7 +55,6 @@ process {{ tool_name_underscore|upper }} {
     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 %}
diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 018dc99af2..0393d352eb 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -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):
-        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):
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 97183b62c9..07585128ec 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -28,7 +28,6 @@ process FASTQC {
 
     script:
     // 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}"
     if (meta.single_end) {
         """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 77c8f4361a..a92cca59b0 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -27,7 +27,6 @@ process MULTIQC {
     path "versions.yml"        , emit: version
 
     script:
-    def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
 

From d965d62d5fb894a8bd12460b19dc7a1f2722872d Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Fri, 13 Aug 2021 09:30:14 +0200
Subject: [PATCH 072/266] Remove reference to $software

---
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf  | 4 ++--
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 07585128ec..8173cd8962 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -36,7 +36,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
+            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     } else {
@@ -47,7 +47,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            $software: \$(fastqc --version | sed -e "s/FastQC v//g")
+            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
         END_VERSIONS
         """
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index a92cca59b0..d1e26b6fbe 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -32,7 +32,7 @@ process MULTIQC {
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        $software: \$(multiqc --version | sed -e "s/multiqc, version //g")
+        multiqc: \$(multiqc --version | sed -e "s/multiqc, version //g")
     END_VERSIONS
     """
 }

From a3c49ef39a58b54836f2aeae74f46b70092cbd9e Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Mon, 6 Sep 2021 12:21:06 -0600
Subject: [PATCH 073/266] display enum choices on error

---
 .../pipeline-template/lib/NfcoreSchema.groovy | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
index 8d6920dd64..07a2387a38 100755
--- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy
+++ b/nf_core/pipeline-template/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
         }
@@ -330,7 +334,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,7 +350,16 @@ 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) {

From 12103eb2675937b3d0c64510858f4074a56d3165 Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Mon, 6 Sep 2021 12:27:20 -0600
Subject: [PATCH 074/266] fix typo

---
 nf_core/pipeline-template/lib/NfcoreSchema.groovy | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
index 07a2387a38..4ee0b989b5 100755
--- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
@@ -355,7 +355,7 @@ class NfcoreSchema {
                     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(', ')})"
+                        log.error "${error_msg}: ${enums[param].join(', ')})"
                     }
                 } else {
                     log.error "* --${param}: ${ex_json['message']} (${param_val})"

From 462cb651eca494718075c1c352732b199d8b5b3d Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 7 Sep 2021 09:58:43 +0200
Subject: [PATCH 075/266] Catch AssertionError from schema build

Fixes bug found by @erikrikarddaniel where error from `nextflow config` raised an `AssertionError` that was not handled.
---
 nf_core/__main__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/__main__.py b/nf_core/__main__.py
index a9b5f05b68..a9500509a4 100755
--- a/nf_core/__main__.py
+++ b/nf_core/__main__.py
@@ -668,7 +668,7 @@ def build(dir, no_prompts, web_only, url):
         schema_obj = nf_core.schema.PipelineSchema()
         if schema_obj.build_schema(dir, no_prompts, web_only, url) is False:
             sys.exit(1)
-    except UserWarning as e:
+    except (UserWarning, AssertionError) as e:
         log.error(e)
         sys.exit(1)
 

From 251f51a6e69bf89584165536b6c0a75f35fe724b Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 7 Sep 2021 11:07:00 +0200
Subject: [PATCH 076/266] Update black GitHub Action

Use official black GitHub Action
---
 .github/workflows/python-lint.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml
index cf41eb67da..b75327e050 100644
--- a/.github/workflows/python-lint.yml
+++ b/.github/workflows/python-lint.yml
@@ -14,7 +14,7 @@ jobs:
       - uses: actions/checkout@v2
 
       - name: Check code lints with Black
-        uses: jpetrucciani/black-check@master
+        uses: psf/black@stable
 
       # If the above check failed, post a comment on the PR explaining the failure
       - name: Post PR comment

From f27d0813ae4ff8108bc35ed97414b49a109271eb Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 8 Sep 2021 15:52:39 +0200
Subject: [PATCH 077/266] Update multiqc link

---
 nf_core/pipeline-template/CITATIONS.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/CITATIONS.md b/nf_core/pipeline-template/CITATIONS.md
index e84b929891..323c681a0a 100644
--- a/nf_core/pipeline-template/CITATIONS.md
+++ b/nf_core/pipeline-template/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

From 8635612b3ee3b1e414414553c4fd72cb048464d3 Mon Sep 17 00:00:00 2001
From: Mahesh Binzer-Panchal <mahesh.binzer-panchal@nbis.se>
Date: Tue, 14 Sep 2021 15:06:48 +0200
Subject: [PATCH 078/266] Update output docs (software_versions.tsv
 ->software_versions.yml)

---
 nf_core/pipeline-template/docs/output.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/docs/output.md b/nf_core/pipeline-template/docs/output.md
index 9646e12290..9fd9e5e127 100644
--- a/nf_core/pipeline-template/docs/output.md
+++ b/nf_core/pipeline-template/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`.
     * Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`.
 
 </details>

From 2af8709fc757c65e2191db0151e48f574d273386 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Thu, 23 Sep 2021 13:17:42 +0200
Subject: [PATCH 079/266] Do not update nf-core modules in pipeline template
 yet

---
 .../modules/nf-core/modules/fastqc/functions.nf |  9 +--------
 .../modules/nf-core/modules/fastqc/main.nf      | 17 +++++------------
 .../modules/nf-core/modules/fastqc/meta.yml     |  2 +-
 .../nf-core/modules/multiqc/functions.nf        |  9 +--------
 .../modules/nf-core/modules/multiqc/main.nf     | 11 ++++-------
 .../modules/nf-core/modules/multiqc/meta.yml    |  2 +-
 6 files changed, 13 insertions(+), 37 deletions(-)

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index 9fbf2c4992..da9da093d3 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -9,13 +9,6 @@ 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
 //
@@ -44,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.equals('versions.yml')) {
+    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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 8173cd8962..39c327b261 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
+include { initOptions; saveFiles; getSoftwareName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -24,31 +24,24 @@ process FASTQC {
     output:
     tuple val(meta), path("*.html"), emit: html
     tuple val(meta), path("*.zip") , emit: zip
-    path  "versions.yml"           , emit: version
+    path  "*.version.txt"          , emit: version
 
     script:
     // 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}"
     if (meta.single_end) {
         """
         [ ! -f  ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz
         fastqc $options.args --threads $task.cpus ${prefix}.fastq.gz
-
-        cat <<-END_VERSIONS > versions.yml
-        ${getProcessName(task.process)}:
-            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
-        END_VERSIONS
+        fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt
         """
     } 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
-
-        cat <<-END_VERSIONS > versions.yml
-        ${getProcessName(task.process)}:
-            fastqc: \$(fastqc --version | sed -e "s/FastQC v//g")
-        END_VERSIONS
+        fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt
         """
     }
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
index 48031356b5..8eb9953dce 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
@@ -43,7 +43,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "versions.yml"
+        pattern: "*.{version.txt}"
 authors:
     - "@drpatelh"
     - "@grst"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index 9fbf2c4992..da9da093d3 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -9,13 +9,6 @@ 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
 //
@@ -44,7 +37,7 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.equals('versions.yml')) {
+    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) {
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index d1e26b6fbe..da78080024 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
+include { initOptions; saveFiles; getSoftwareName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -24,15 +24,12 @@ process MULTIQC {
     path "*multiqc_report.html", emit: report
     path "*_data"              , emit: data
     path "*_plots"             , optional:true, emit: plots
-    path "versions.yml"        , emit: version
+    path "*.version.txt"       , emit: version
 
     script:
+    def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
-
-    cat <<-END_VERSIONS > versions.yml
-    ${getProcessName(task.process)}:
-        multiqc: \$(multiqc --version | sed -e "s/multiqc, version //g")
-    END_VERSIONS
+    multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt
     """
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
index 2d99ec0d12..532a8bb1ef 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
@@ -32,7 +32,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "versions.yml"
+        pattern: "*.{version.txt}"
 authors:
     - "@abhi18av"
     - "@bunop"

From 6c52385764a2c8a736f7199919b5979041fc8530 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Thu, 23 Sep 2021 13:19:57 +0200
Subject: [PATCH 080/266] Update functions.nf in module template and in local
 pipeline modules

---
 nf_core/module-template/modules/functions.nf  | 47 ++++++++++---------
 .../modules/local/functions.nf                | 47 ++++++++++---------
 2 files changed, 50 insertions(+), 44 deletions(-)

diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
index 9fbf2c4992..85628ee0eb 100644
--- a/nf_core/module-template/modules/functions.nf
+++ b/nf_core/module-template/modules/functions.nf
@@ -44,32 +44,35 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.equals('versions.yml')) {
-        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"
     }
 }
diff --git a/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
index 9fbf2c4992..85628ee0eb 100644
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ b/nf_core/pipeline-template/modules/local/functions.nf
@@ -44,32 +44,35 @@ def getPathFromList(path_list) {
 // Function to save/publish module results
 //
 def saveFiles(Map args) {
-    if (!args.filename.equals('versions.yml')) {
-        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"
     }
 }

From da8dadba76bd02343b16d9b89561b92e78a0b9c8 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Thu, 23 Sep 2021 13:31:58 +0200
Subject: [PATCH 081/266] Show linting messages when linting tests failed

---
 tests/modules/lint.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/modules/lint.py b/tests/modules/lint.py
index de29371c58..1cf99c07b9 100644
--- a/tests/modules/lint.py
+++ b/tests/modules/lint.py
@@ -8,7 +8,7 @@ def test_modules_lint_trimgalore(self):
     module_lint.lint(print_results=False, module="trimgalore")
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0
+    assert len(module_lint.failed) == 0, module_lint.failed
 
 
 def test_modules_lint_empty(self):
@@ -19,7 +19,7 @@ def test_modules_lint_empty(self):
     module_lint.lint(print_results=False, all_modules=True)
     assert len(module_lint.passed) == 0
     assert len(module_lint.warned) == 0
-    assert len(module_lint.failed) == 0
+    assert len(module_lint.failed) == 0, module_lint.failed
 
 
 def test_modules_lint_new_modules(self):
@@ -28,4 +28,4 @@ def test_modules_lint_new_modules(self):
     module_lint.lint(print_results=True, all_modules=True)
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0
+    assert len(module_lint.failed) == 0, module_lint.failed

From 9a0dfd9e0e83dd588cbfe6f09b618c96f620b7d3 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Thu, 23 Sep 2021 13:41:06 +0200
Subject: [PATCH 082/266] Print linting error message

---
 tests/modules/lint.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/modules/lint.py b/tests/modules/lint.py
index 1cf99c07b9..8371b92fb7 100644
--- a/tests/modules/lint.py
+++ b/tests/modules/lint.py
@@ -8,7 +8,7 @@ def test_modules_lint_trimgalore(self):
     module_lint.lint(print_results=False, module="trimgalore")
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0, module_lint.failed
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
 
 
 def test_modules_lint_empty(self):
@@ -19,7 +19,7 @@ def test_modules_lint_empty(self):
     module_lint.lint(print_results=False, all_modules=True)
     assert len(module_lint.passed) == 0
     assert len(module_lint.warned) == 0
-    assert len(module_lint.failed) == 0, module_lint.failed
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
 
 
 def test_modules_lint_new_modules(self):
@@ -28,4 +28,4 @@ def test_modules_lint_new_modules(self):
     module_lint.lint(print_results=True, all_modules=True)
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0, module_lint.failed
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"

From 82ce4b4679f026c802cf41f2e78a3e0a87d271c6 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Thu, 23 Sep 2021 15:39:31 +0200
Subject: [PATCH 083/266] Update nf_core/pipeline-template/docs/output.md

Co-authored-by: Harshil Patel <drpatelh@users.noreply.github.com>
---
 nf_core/pipeline-template/docs/output.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/docs/output.md b/nf_core/pipeline-template/docs/output.md
index 9fd9e5e127..4ef9a4ea01 100644
--- a/nf_core/pipeline-template/docs/output.md
+++ b/nf_core/pipeline-template/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.yml`.
+    * 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>

From 13071abfde27313ab24324b6c5a228a311907fac Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Sat, 25 Sep 2021 19:32:49 +0200
Subject: [PATCH 084/266] Update main_nf.py

---
 nf_core/modules/lint/main_nf.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 0393d352eb..cda47fcfbd 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -116,10 +116,16 @@ def main_nf(module_lint_object, module):
 def check_script_section(self, lines):
     """
     Lint the script section
-    Checks whether 'def sotware' and 'def prefix' are defined
+    Checks whether 'def prefix'  is defined and whether getProcessName is used for `versions.yml`.
     """
     script = "".join(lines)
 
+    # check that process name is used for `versions.yml`
+    if re.search("\${\s*getProcessName(\s*task.process\s*)\s*}", script):
+        self.passed.append(("main_nf_version_script", "Process name used for versions.yml", self.main_nf))
+    else:
+        self.failed.append(("main_nf_version_script", "Process name not used for versions.yml", 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):

From 5b5f1b94f2b8ac7983fd7871d7c5a5e8427ed770 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Sat, 25 Sep 2021 19:46:43 +0200
Subject: [PATCH 085/266] update regex

---
 nf_core/modules/lint/main_nf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index cda47fcfbd..4b63295c20 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -121,7 +121,7 @@ def check_script_section(self, lines):
     script = "".join(lines)
 
     # check that process name is used for `versions.yml`
-    if re.search("\${\s*getProcessName(\s*task.process\s*)\s*}", script):
+    if re.search("\$\{\s*getProcessName\s*\(\s*task\.process\s*\)\s*\}", script):
         self.passed.append(("main_nf_version_script", "Process name used for versions.yml", self.main_nf))
     else:
         self.failed.append(("main_nf_version_script", "Process name not used for versions.yml", self.main_nf))

From f0fe09e81009c5197a77d53aa6464601690ff721 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 29 Sep 2021 13:22:02 +0100
Subject: [PATCH 086/266] Fix #1275

---
 nf_core/pipeline-template/modules.json        |  6 +--
 .../nf-core/modules/fastqc/functions.nf       | 54 +++++++++++--------
 .../modules/nf-core/modules/fastqc/main.nf    | 17 ++++--
 .../modules/nf-core/modules/fastqc/meta.yml   |  2 +-
 .../nf-core/modules/multiqc/functions.nf      | 54 +++++++++++--------
 .../modules/nf-core/modules/multiqc/main.nf   | 16 +++---
 .../modules/nf-core/modules/multiqc/meta.yml  |  2 +-
 7 files changed, 91 insertions(+), 60 deletions(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index 3cb20a2e0e..5dd4e438d9 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,11 +4,11 @@
     "repos": {
         "nf-core/modules": {
             "fastqc": {
-                "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d"
+                "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
             },
             "multiqc": {
-                "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d"
+                "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
index da9da093d3..85628ee0eb 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
@@ -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
 //
@@ -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"
     }
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 39c327b261..88bfbf5b02 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -24,24 +24,31 @@ 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: version
 
     script:
     // 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}"
     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
+
+        cat <<-END_VERSIONS > versions.yml
+        ${getProcessName(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
+
+        cat <<-END_VERSIONS > versions.yml
+        ${getProcessName(task.process)}:
+            fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
+        END_VERSIONS
         """
     }
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
index 8eb9953dce..48031356b5 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
@@ -43,7 +43,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "*.{version.txt}"
+        pattern: "versions.yml"
 authors:
     - "@drpatelh"
     - "@grst"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
index da9da093d3..85628ee0eb 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
@@ -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
 //
@@ -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"
     }
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index da78080024..2e7ad932e5 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { initOptions; saveFiles; getSoftwareName } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
 options        = initOptions(params.options)
@@ -10,11 +10,11 @@ process MULTIQC {
         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)
+    conda (params.enable_conda ? 'bioconda::multiqc=1.11' : null)
     if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
-        container "https://depot.galaxyproject.org/singularity/multiqc:1.10.1--py_0"
+        container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0"
     } else {
-        container "quay.io/biocontainers/multiqc:1.10.1--py_0"
+        container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0"
     }
 
     input:
@@ -24,12 +24,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: version
 
     script:
     def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
-    multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt
+
+    cat <<-END_VERSIONS > versions.yml
+    ${getProcessName(task.process)}:
+        multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" )
+    END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
index 532a8bb1ef..2d99ec0d12 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
@@ -32,7 +32,7 @@ output:
     - version:
         type: file
         description: File containing software version
-        pattern: "*.{version.txt}"
+        pattern: "versions.yml"
 authors:
     - "@abhi18av"
     - "@bunop"

From bedd720f3cf25bb4cca428a18078c57da4f4f70a Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 29 Sep 2021 14:28:56 +0100
Subject: [PATCH 087/266] Install custom/dumpsoftwareversions from
 nf-core/modules

---
 nf_core/pipeline-template/conf/base.config    |  3 +
 nf_core/pipeline-template/modules.json        |  3 +
 .../custom/dumpsoftwareversions/functions.nf  | 78 +++++++++++++++++++
 .../custom/dumpsoftwareversions/main.nf}      | 30 ++++---
 .../custom/dumpsoftwareversions/meta.yml      | 33 ++++++++
 .../pipeline-template/workflows/pipeline.nf   | 10 +--
 6 files changed, 140 insertions(+), 17 deletions(-)
 create mode 100644 nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
 rename nf_core/pipeline-template/modules/{local/get_software_versions.nf => nf-core/modules/custom/dumpsoftwareversions/main.nf} (75%)
 create mode 100644 nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml

diff --git a/nf_core/pipeline-template/conf/base.config b/nf_core/pipeline-template/conf/base.config
index 3fb7b48fca..e0557b9e91 100644
--- a/nf_core/pipeline-template/conf/base.config
+++ b/nf_core/pipeline-template/conf/base.config
@@ -54,4 +54,7 @@ process {
         errorStrategy = 'retry'
         maxRetries    = 2
     }
+    withName:CUSTOM_DUMPSOFTWAREVERSIONS {
+        cache = false
+    }
 }
diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index 5dd4e438d9..e02180274b 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -3,6 +3,9 @@
     "homePage": "https://github.com/{{ name }}",
     "repos": {
         "nf-core/modules": {
+            "custom/dumpsoftwareversions": {
+                "git_sha": "b2c2d4deb456d92e21777985bb2eda59002748cc"
+            },
             "fastqc": {
                 "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
             },
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
new file mode 100644
index 0000000000..85628ee0eb
--- /dev/null
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
@@ -0,0 +1,78 @@
+//
+//  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()
+}
+
+//
+// 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
+//
+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) {
+    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"
+            }
+        }
+    } else if (ioptions.publish_files == null) {
+        return "${getPathFromList(path_list)}/$args.filename"
+    }
+}
diff --git a/nf_core/pipeline-template/modules/local/get_software_versions.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
similarity index 75%
rename from nf_core/pipeline-template/modules/local/get_software_versions.nf
rename to nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index 08d58f9c52..79e60cb272 100644
--- a/nf_core/pipeline-template/modules/local/get_software_versions.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -1,35 +1,37 @@
 // Import generic module functions
-include { saveFiles } from './functions'
+include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
 
 params.options = [:]
+options        = initOptions(params.options)
 
-process GET_SOFTWARE_VERSIONS {
+process CUSTOM_DUMPSOFTWAREVERSIONS {
+    label 'process_low'
     publishDir "${params.outdir}",
         mode: params.publish_dir_mode,
         saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) }
 
-    // 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)
+    // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container
+    conda (params.enable_conda ? "bioconda::multiqc=1.11" : null)
     if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
-        container "https://depot.galaxyproject.org/singularity/multiqc:1.10.1--pyhdfd78af_1"
+        container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0"
     } else {
-        container "quay.io/biocontainers/multiqc:1.10.1--pyhdfd78af_1"
+        container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0"
     }
 
-    cache false
-
     input:
     path versions
 
     output:
-    path "software_versions.yml"     , emit: yml
-    path "software_versions_mqc.yml" , emit: mqc_yml
+    path 'software_versions.yml'    , emit: yml
+    path 'software_versions_mqc.yml', emit: mqc_yaml
+    path 'versions.yml'             , emit: versions
 
     script:
     """
     #!/usr/bin/env python
 
     import yaml
+    import platform
     from textwrap import dedent
 
     def _make_versions_html(versions):
@@ -91,5 +93,13 @@ process GET_SOFTWARE_VERSIONS {
         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)
+
+    yaml_version = {}
+    yaml_version["${getProcessName(task.process)}"] = {
+        'python': platform.python_version(),
+        'yaml': yaml.__version__
+    }
+    with open('versions.yml', 'w') as f:
+        yaml.dump(yaml_version, f, default_flow_style=False)
     """
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
new file mode 100644
index 0000000000..1cf616159c
--- /dev/null
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
@@ -0,0 +1,33 @@
+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
+
+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"
+  - version:
+      type: file
+      description: File containing software version
+      pattern: "versions.yml"
+
+authors:
+  - "@drpatelh"
diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 44924b8116..4239f20f50 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -35,11 +35,6 @@ 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 : ['yml':'']] )
-
 //
 // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules
 //
@@ -59,6 +54,7 @@ multiqc_options.args += params.multiqc_title ? Utils.joinModuleArgs(["--title \"
 //
 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 { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main'  addParams( options: [publish_files : ['_versions.yml':'']] )
 
 /*
 ========================================================================================
@@ -88,7 +84,7 @@ workflow {{ short_name|upper }} {
     )
     ch_software_versions = ch_software_versions.mix(FASTQC.out.version.first().ifEmpty(null))
 
-    GET_SOFTWARE_VERSIONS (
+    CUSTOM_DUMPSOFTWAREVERSIONS (
         ch_software_versions.collectFile()
     )
 
@@ -102,7 +98,7 @@ workflow {{ short_name|upper }} {
     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.mqc_yml.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 (

From ebac98ff61e35d84a1752a1104616c55696b1c40 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 29 Sep 2021 16:45:26 +0100
Subject: [PATCH 088/266] Fix tyop in custom/dumpsoftwareversions

---
 nf_core/pipeline-template/modules.json                          | 2 +-
 .../modules/nf-core/modules/custom/dumpsoftwareversions/main.nf | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index e02180274b..9b6539650b 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,7 +4,7 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "b2c2d4deb456d92e21777985bb2eda59002748cc"
+                "git_sha": "5a757b2981b634b94015da5969931b96a9f6b8da"
             },
             "fastqc": {
                 "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index 79e60cb272..94e112f09f 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -23,7 +23,7 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
 
     output:
     path 'software_versions.yml'    , emit: yml
-    path 'software_versions_mqc.yml', emit: mqc_yaml
+    path 'software_versions_mqc.yml', emit: mqc_yml
     path 'versions.yml'             , emit: versions
 
     script:

From cc03754256cfdb482e24829e1b2b8bf6c3645ab2 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 29 Sep 2021 16:51:00 +0100
Subject: [PATCH 089/266] Remove requirement for get_software_versions.py

---
 nf_core/lint/files_exist.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index e1b28bc9a3..41365b1094 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -51,7 +51,6 @@ def files_exist(self):
         lib/NfcoreTemplate.groovy
         lib/Utils.groovy
         lib/WorkflowMain.groovy
-        modules/local/get_software_versions.nf
         nextflow_schema.json
         nextflow.config
         README.md
@@ -133,7 +132,6 @@ def files_exist(self):
         [os.path.join("lib", "NfcoreTemplate.groovy")],
         [os.path.join("lib", "Utils.groovy")],
         [os.path.join("lib", "WorkflowMain.groovy")],
-        [os.path.join("modules", "local", "get_software_versions.nf")],
     ]
 
     files_warn = [

From 5c8d292e23752195c98ec34bfdeb4931baeefc9d Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 29 Sep 2021 16:59:55 +0100
Subject: [PATCH 090/266] Fix module lint test

---
 tests/modules/lint.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/modules/lint.py b/tests/modules/lint.py
index 8371b92fb7..0d25f62880 100644
--- a/tests/modules/lint.py
+++ b/tests/modules/lint.py
@@ -15,6 +15,7 @@ def test_modules_lint_empty(self):
     """Test linting a pipeline with no modules installed"""
     self.mods_remove.remove("fastqc")
     self.mods_remove.remove("multiqc")
+    self.mods_remove.remove("custom/dumpsoftwareversions")
     module_lint = nf_core.modules.ModuleLint(dir=self.pipeline_dir)
     module_lint.lint(print_results=False, all_modules=True)
     assert len(module_lint.passed) == 0

From 2485384624112db989d234b0128c4de97c9f7aa2 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 30 Sep 2021 09:03:19 +0100
Subject: [PATCH 091/266] Dump version for /custom/dumpsoftwareversions module
 itself

---
 nf_core/pipeline-template/modules.json        |  2 +-
 .../custom/dumpsoftwareversions/main.nf       | 21 ++++++++++---------
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index 9b6539650b..c828028317 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,7 +4,7 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "5a757b2981b634b94015da5969931b96a9f6b8da"
+                "git_sha": "22ec5c6007159d441585ef54bfa6272b6f93c78a"
             },
             "fastqc": {
                 "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index 94e112f09f..8424ab07b8 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -72,10 +72,16 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
         html.append("</table>")
         return "\\n".join(html)
 
+    module_versions = {}
+    module_versions["${getProcessName(task.process)}"] = {
+        'python': platform.python_version(),
+        'yaml': yaml.__version__
+    }
+
     with open("$versions") as f:
-        versions = yaml.safe_load(f)
+        workflow_versions = yaml.safe_load(f) | module_versions
 
-    versions["Workflow"] = {
+    workflow_versions["Workflow"] = {
         "Nextflow": "$workflow.nextflow.version",
         "$workflow.manifest.name": "$workflow.manifest.version"
     }
@@ -86,20 +92,15 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
         '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)
+        'data': _make_versions_html(workflow_versions)
     }
 
     with open("software_versions.yml", 'w') as f:
-        yaml.dump(versions, f, default_flow_style=False)
+        yaml.dump(workflow_versions, f, default_flow_style=False)
     with open("software_versions_mqc.yml", 'w') as f:
         yaml.dump(versions_mqc, f, default_flow_style=False)
 
-    yaml_version = {}
-    yaml_version["${getProcessName(task.process)}"] = {
-        'python': platform.python_version(),
-        'yaml': yaml.__version__
-    }
     with open('versions.yml', 'w') as f:
-        yaml.dump(yaml_version, f, default_flow_style=False)
+        yaml.dump(module_versions, f, default_flow_style=False)
     """
 }

From 07d51d9d8c1b7144446ecadd9d327b90e27fd5c7 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 1 Oct 2021 14:24:55 +0100
Subject: [PATCH 092/266] Rename version to versions in meta.yml

---
 nf_core/module-template/modules/meta.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/module-template/modules/meta.yml b/nf_core/module-template/modules/meta.yml
index c42dd613bd..d58df5a371 100644
--- a/nf_core/module-template/modules/meta.yml
+++ b/nf_core/module-template/modules/meta.yml
@@ -37,9 +37,9 @@ output:
         Groovy Map containing sample information
         e.g. [ id:'test', single_end:false ]
   {% endif -%}
-  - version:
+  - versions:
       type: file
-      description: File containing software version
+      description: File containing software versions
       pattern: "versions.yml"
   ## TODO nf-core: Delete / customise this example output
   - bam:

From 2a7da3aa5bb0f03383161c3e8c89bd32f9c6784f Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 1 Oct 2021 14:32:45 +0100
Subject: [PATCH 093/266] Update modules in pipeline template with new versions
 syntax

---
 nf_core/module-template/modules/main.nf               |  6 +++---
 nf_core/pipeline-template/modules.json                |  6 +++---
 .../modules/local/samplesheet_check.nf                |  8 +++++++-
 .../modules/custom/dumpsoftwareversions/main.nf       |  6 +++---
 .../modules/nf-core/modules/fastqc/main.nf            |  8 ++++----
 .../modules/nf-core/modules/multiqc/main.nf           |  5 ++---
 .../subworkflows/local/input_check.nf                 |  3 ++-
 nf_core/pipeline-template/workflows/pipeline.nf       | 11 ++++++-----
 8 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 457f2b39bc..26703aeb57 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -52,11 +52,11 @@ 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 "versions.yml"          , emit: version
+    path "versions.yml"          , emit: versions
 
     script:
     {% if has_meta -%}
-    def prefix   = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
+    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
@@ -80,7 +80,7 @@ process {{ tool_name_underscore|upper }} {
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        samtools: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$//' )
+        ${getSoftwareName(task.process)}: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$//' )
     END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index c828028317..a225b686a8 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,13 +4,13 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "22ec5c6007159d441585ef54bfa6272b6f93c78a"
+                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
             },
             "fastqc": {
-                "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
+                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
             },
             "multiqc": {
-                "git_sha": "ab67a1d41b63bf52fd7c147f7f8f6e8d167590b5"
+                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
             }
         }
     }
diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index 9bada69b98..fbb4fc1101 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -20,12 +20,18 @@ process SAMPLESHEET_CHECK {
     path samplesheet
 
     output:
-    path '*.csv'
+    path '*.csv'       , emit: csv
+    path "versions.yml", emit: versions
 
     script: // This script is bundled with the pipeline, in {{ name }}/bin/
     """
     check_samplesheet.py \\
         $samplesheet \\
         samplesheet.valid.csv
+
+    cat <<-END_VERSIONS > versions.yml
+    ${getProcessName(task.process)}:
+        python: \$(python --version | sed 's/Python //g')
+    END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index 8424ab07b8..cf10a8e072 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -22,9 +22,9 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
     path versions
 
     output:
-    path 'software_versions.yml'    , emit: yml
-    path 'software_versions_mqc.yml', emit: mqc_yml
-    path 'versions.yml'             , emit: versions
+    path "software_versions.yml"    , emit: yml
+    path "software_versions_mqc.yml", emit: mqc_yml
+    path "versions.yml"             , emit: versions
 
     script:
     """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 88bfbf5b02..9f6cfc5538 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -24,11 +24,11 @@ process FASTQC {
     output:
     tuple val(meta), path("*.html"), emit: html
     tuple val(meta), path("*.zip") , emit: zip
-    path  "versions.yml"           , emit: version
+    path  "versions.yml"           , emit: versions
 
     script:
     // Add soft-links to original FastQs for consistent naming in pipeline
-    def prefix   = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
+    def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
     if (meta.single_end) {
         """
         [ ! -f  ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz
@@ -36,7 +36,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
+            ${getSoftwareName(task.process)}: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
     } else {
@@ -47,7 +47,7 @@ process FASTQC {
 
         cat <<-END_VERSIONS > versions.yml
         ${getProcessName(task.process)}:
-            fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
+            ${getSoftwareName(task.process)}: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 2e7ad932e5..0861aa5934 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -24,16 +24,15 @@ process MULTIQC {
     path "*multiqc_report.html", emit: report
     path "*_data"              , emit: data
     path "*_plots"             , optional:true, emit: plots
-    path "versions.yml"        , emit: version
+    path "versions.yml"        , emit: versions
 
     script:
-    def software = getSoftwareName(task.process)
     """
     multiqc -f $options.args .
 
     cat <<-END_VERSIONS > versions.yml
     ${getProcessName(task.process)}:
-        multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" )
+        ${getSoftwareName(task.process)}: \$( multiqc --version | sed -e "s/multiqc, version //g" )
     END_VERSIONS
     """
 }
diff --git a/nf_core/pipeline-template/subworkflows/local/input_check.nf b/nf_core/pipeline-template/subworkflows/local/input_check.nf
index b664bc8caf..6c23c8f16f 100644
--- a/nf_core/pipeline-template/subworkflows/local/input_check.nf
+++ b/nf_core/pipeline-template/subworkflows/local/input_check.nf
@@ -17,7 +17,8 @@ workflow INPUT_CHECK {
         .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/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 4239f20f50..0682ed96cc 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -67,7 +67,7 @@ def multiqc_report = []
 
 workflow {{ short_name|upper }} {
 
-    ch_software_versions = Channel.empty()
+    ch_versions = Channel.empty()
 
     //
     // SUBWORKFLOW: Read in samplesheet, validate and stage input files
@@ -75,6 +75,7 @@ workflow {{ short_name|upper }} {
     INPUT_CHECK (
         ch_input
     )
+    ch_versions = ch_versions.mix(INPUT_CHECK.out.versions)
 
     //
     // MODULE: Run FastQC
@@ -82,10 +83,10 @@ workflow {{ short_name|upper }} {
     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())
 
     CUSTOM_DUMPSOFTWAREVERSIONS (
-        ch_software_versions.collectFile()
+        ch_versions.collectFile()
     )
 
     //
@@ -104,8 +105,8 @@ workflow {{ short_name|upper }} {
     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)
 }
 
 /*

From 575350bb976128104a251c109d44317ede935fb6 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 1 Oct 2021 14:48:42 +0100
Subject: [PATCH 094/266] Fix channel bug

---
 nf_core/pipeline-template/subworkflows/local/input_check.nf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/pipeline-template/subworkflows/local/input_check.nf b/nf_core/pipeline-template/subworkflows/local/input_check.nf
index 6c23c8f16f..c1b071961c 100644
--- a/nf_core/pipeline-template/subworkflows/local/input_check.nf
+++ b/nf_core/pipeline-template/subworkflows/local/input_check.nf
@@ -12,6 +12,7 @@ workflow INPUT_CHECK {
 
     main:
     SAMPLESHEET_CHECK ( samplesheet )
+        .csv
         .splitCsv ( header:true, sep:',' )
         .map { create_fastq_channels(it) }
         .set { reads }

From a56dca36049b0b61c2a6ca5d517561051b4e1193 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Fri, 1 Oct 2021 16:09:47 +0200
Subject: [PATCH 095/266] Include getProcessName

---
 nf_core/pipeline-template/modules/local/samplesheet_check.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index fbb4fc1101..b8354f35e4 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -1,5 +1,5 @@
 // Import generic module functions
-include { saveFiles } from './functions'
+include { saveFiles; getProcessName } from './functions'
 
 params.options = [:]
 

From b402201076a28094b9a70a654897e4b5e842b34d Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 6 Oct 2021 22:51:43 +0200
Subject: [PATCH 096/266] Update template to fix dumpsoftwareversions

---
 nf_core/pipeline-template/modules.json                        | 2 +-
 .../nf-core/modules/custom/dumpsoftwareversions/main.nf       | 2 +-
 .../nf-core/modules/custom/dumpsoftwareversions/meta.yml      | 4 ++--
 nf_core/pipeline-template/workflows/pipeline.nf               | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index a225b686a8..db6aceae73 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,7 +4,7 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
+                "git_sha": "84f2302920078b0cf7716b2a2e5fcc0be5c4531d"
             },
             "fastqc": {
                 "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index cf10a8e072..faf2073f75 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -79,7 +79,7 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
     }
 
     with open("$versions") as f:
-        workflow_versions = yaml.safe_load(f) | module_versions
+        workflow_versions = yaml.load(f, Loader=yaml.BaseLoader) | module_versions
 
     workflow_versions["Workflow"] = {
         "Nextflow": "$workflow.nextflow.version",
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
index 1cf616159c..8d4a6ed42c 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
@@ -24,9 +24,9 @@ output:
       type: file
       description: MultiQC custom content YML file containing software versions
       pattern: "software_versions_mqc.yml"
-  - version:
+  - versions:
       type: file
-      description: File containing software version
+      description: File containing software versions
       pattern: "versions.yml"
 
 authors:
diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 0682ed96cc..68ca75e1de 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -86,7 +86,7 @@ workflow {{ short_name|upper }} {
     ch_versions = ch_versions.mix(FASTQC.out.versions.first())
 
     CUSTOM_DUMPSOFTWAREVERSIONS (
-        ch_versions.collectFile()
+        ch_versions.unique().collectFile(name: 'collated_versions.yml')
     )
 
     //

From 56162a52dad0a73169f706521855caa985b2ce3d Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 6 Oct 2021 22:58:24 +0200
Subject: [PATCH 097/266] Update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc696843c9..c11ad5dab0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Template
 
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
+* Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
 
 ### General
 

From fd6486454160acc8bc27be9626c2146eca613b28 Mon Sep 17 00:00:00 2001
From: "Moritz E. Beber" <midnighter@posteo.net>
Date: Fri, 15 Oct 2021 23:08:54 +0200
Subject: [PATCH 098/266] fix: update build system

---
 MANIFEST.in    | 1 +
 pyproject.toml | 7 +++++++
 setup.py       | 1 -
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index 59d5a1a4e0..1f03217204 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,4 @@
 include LICENSE
 include README.md
 recursive-include nf_core *
+include requirements.txt
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 266acbdcb6..b0055a4e00 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,10 @@
+[build-system]
+build-backend = 'setuptools.build_meta'
+requires = [
+  'setuptools>=40.6.0',
+  'wheel'
+]
+
 [tool.black]
 line-length = 120
 target_version = ['py36','py37','py38']
diff --git a/setup.py b/setup.py
index 513f9d9fec..fc2ee983ec 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,6 @@
     license="MIT",
     entry_points={"console_scripts": ["nf-core=nf_core.__main__:run_nf_core"]},
     install_requires=required,
-    setup_requires=["twine>=1.11.0", "setuptools>=38.6."],
     packages=find_packages(exclude=("docs")),
     include_package_data=True,
     zip_safe=False,

From 3177bd4ce15040f4f415787a5c2e0c1aad667cf0 Mon Sep 17 00:00:00 2001
From: "Moritz E. Beber" <midnighter@posteo.net>
Date: Fri, 15 Oct 2021 23:24:24 +0200
Subject: [PATCH 099/266] chore: explicitly include only template directories

---
 MANIFEST.in | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index 1f03217204..bb3c5d6dac 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,5 @@
 include LICENSE
 include README.md
-recursive-include nf_core *
+graft nf_core/module-template
+graft nf_core/pipeline-template
 include requirements.txt
\ No newline at end of file

From 4e8a065eea4859345fb5d6e531cf4bfd2cf42591 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 22 Oct 2021 15:27:13 +0200
Subject: [PATCH 100/266] authentication using github_api_auto_auth

Passing auth parameters with requests.get
Fix sha query param in get_module_git_log to get log from private branches
---
 nf_core/modules/module_utils.py | 6 +++---
 nf_core/modules/modules_repo.py | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 30d6e4cc08..7c11e0a5d3 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -57,7 +57,7 @@ def get_module_git_log(module_name, modules_repo=None, per_page=30, page_nbr=1,
     if modules_repo is None:
         modules_repo = ModulesRepo()
     api_url = f"https://api.github.com/repos/{modules_repo.name}/commits"
-    api_url += f"?sha{modules_repo.branch}"
+    api_url += f"?sha={modules_repo.branch}"
     if module_name is not None:
         api_url += f"&path=modules/{module_name}"
     api_url += f"&page={page_nbr}"
@@ -259,7 +259,7 @@ def local_module_equal_to_commit(local_files, module_name, modules_repo, commit_
     for i, file in enumerate(files_to_check):
         # Download remote copy and compare
         api_url = f"{module_base_url}/{file}"
-        r = requests.get(url=api_url)
+        r = requests.get(url=api_url, auth=nf_core.utils.github_api_auto_auth())
         if r.status_code != 200:
             log.debug(f"Could not download remote copy of file module {module_name}/{file}")
             log.debug(api_url)
@@ -365,7 +365,7 @@ def verify_pipeline_dir(dir):
         modules_is_software = False
         for repo_name in repo_names:
             api_url = f"https://api.github.com/repos/{repo_name}/contents"
-            response = requests.get(api_url)
+            response = requests.get(api_url, auth=nf_core.utils.github_api_auto_auth())
             if response.status_code == 404:
                 missing_remote.append(repo_name)
                 if repo_name == "nf-core/software":
diff --git a/nf_core/modules/modules_repo.py b/nf_core/modules/modules_repo.py
index d6d2871ecd..9a9c4f032c 100644
--- a/nf_core/modules/modules_repo.py
+++ b/nf_core/modules/modules_repo.py
@@ -39,7 +39,7 @@ def verify_modules_repo(self):
 
         # Check if repository exist
         api_url = f"https://api.github.com/repos/{self.name}/branches"
-        response = requests.get(api_url)
+        response = requests.get(api_url, auth=nf_core.utils.github_api_auto_auth())
         if response.status_code == 200:
             branches = [branch["name"] for branch in response.json()]
             if self.branch not in branches:
@@ -48,7 +48,7 @@ def verify_modules_repo(self):
             raise LookupError(f"Repository '{self.name}' is not available on GitHub")
 
         api_url = f"https://api.github.com/repos/{self.name}/contents?ref={self.branch}"
-        response = requests.get(api_url)
+        response = requests.get(api_url, auth=nf_core.utils.github_api_auto_auth())
         if response.status_code == 200:
             dir_names = [entry["name"] for entry in response.json() if entry["type"] == "dir"]
             if "modules" not in dir_names:

From 22fe40291233c692b68c31f2c7c54ca9eece3758 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 22 Oct 2021 16:20:51 +0200
Subject: [PATCH 101/266] list modules from private repository

passing repo_name information while searching for local modules info
---
 nf_core/modules/list.py         |  3 ++-
 nf_core/modules/module_utils.py | 11 +++++++----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/nf_core/modules/list.py b/nf_core/modules/list.py
index 355b952c4c..743670908d 100644
--- a/nf_core/modules/list.py
+++ b/nf_core/modules/list.py
@@ -103,7 +103,8 @@ def pattern_msg(keywords):
                     if module_entry:
                         version_sha = module_entry["git_sha"]
                         try:
-                            message, date = nf_core.modules.module_utils.get_commit_info(version_sha)
+                            # pass repo_name to get info on modules even outside nf-core/modules
+                            message, date = nf_core.modules.module_utils.get_commit_info(version_sha, repo_name)
                         except LookupError as e:
                             log.warning(e)
                             date = "[red]Not Available"
diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 7c11e0a5d3..3e791b3ecc 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -46,6 +46,7 @@ def get_module_git_log(module_name, modules_repo=None, per_page=30, page_nbr=1,
     update breaking backwards compatibility.
     Args:
         module_name (str): Name of module
+        modules_repo (ModulesRepo): A ModulesRepo object configured for the repository in question
         per_page (int): Number of commits per page returned by API
         page_nbr (int): Page number of the retrieved commits
         since (str): Only show commits later than this timestamp.
@@ -84,19 +85,21 @@ def get_module_git_log(module_name, modules_repo=None, per_page=30, page_nbr=1,
         )
 
 
-def get_commit_info(commit_sha):
+def get_commit_info(commit_sha, repo_name="nf-core/modules"):
     """
     Fetches metadata about the commit (dates, message, etc.)
     Args:
-        module_name (str): Name of module
         commit_sha (str): The SHA of the requested commit
+        repo_name (str): module repos name (def. {0})
     Returns:
         message (str): The commit message for the requested commit
         date (str): The commit date for the requested commit
     Raises:
         LookupError: If the call to the API fails.
-    """
-    api_url = f"https://api.github.com/repos/nf-core/modules/commits/{commit_sha}?stats=false"
+    """.format(
+        repo_name
+    )
+    api_url = f"https://api.github.com/repos/{repo_name}/commits/{commit_sha}?stats=false"
     log.debug(f"Fetching commit metadata for commit at {commit_sha}")
     response = requests.get(api_url, auth=nf_core.utils.github_api_auto_auth())
     if response.status_code == 200:

From 2c8e1993fcbb11ed4d93c3b35a78de93c0293465 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 22 Oct 2021 17:25:59 +0200
Subject: [PATCH 102/266] solve DepreactionWarning in utils

suppress 'DeprecationWarning: invalid escape sequence \.' by using patterns explicitely
---
 nf_core/utils.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/utils.py b/nf_core/utils.py
index de120f10d7..90935e4083 100644
--- a/nf_core/utils.py
+++ b/nf_core/utils.py
@@ -58,14 +58,14 @@ def check_if_outdated(current_version=None, remote_version=None, source_url="htt
     # Set and clean up the current version string
     if current_version == None:
         current_version = nf_core.__version__
-    current_version = re.sub("[^0-9\.]", "", current_version)
+    current_version = re.sub(r"[^0-9\.]", "", current_version)
     # Build the URL to check against
     source_url = os.environ.get("NFCORE_VERSION_URL", source_url)
     source_url = "{}?v={}".format(source_url, current_version)
     # Fetch and clean up the remote version
     if remote_version == None:
         response = requests.get(source_url, timeout=3)
-        remote_version = re.sub("[^0-9\.]", "", response.text)
+        remote_version = re.sub(r"[^0-9\.]", "", response.text)
     # Check if we have an available update
     is_outdated = version.StrictVersion(remote_version) > version.StrictVersion(current_version)
     return (is_outdated, current_version, remote_version)

From b60a7f24ecac9645051d5f84eafbd9497eac652c Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 22 Oct 2021 17:40:03 +0200
Subject: [PATCH 103/266] fix test_wf_use_local_configs after first attempt

tests/test_download.py::DownloadTest::test_wf_use_local_configs was failing forever after first attempt since the first attempt cached a configuration file which is reloaded in all the following cases. Now we disable caching in $HOME/.nextflow/nf-core while during tests
---
 nf_core/download.py    |  2 ++
 nf_core/utils.py       | 11 +++++++++--
 tests/test_download.py |  2 +-
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/nf_core/download.py b/nf_core/download.py
index bb83564953..7f03459cf6 100644
--- a/nf_core/download.py
+++ b/nf_core/download.py
@@ -396,6 +396,7 @@ def wf_use_local_configs(self):
             nfconfig = nfconfig_fh.read()
 
         # Replace the target string
+        log.debug(f"Replacing '{find_str}' with '{repl_str}'")
         nfconfig = nfconfig.replace(find_str, repl_str)
 
         # Append the singularity.cacheDir to the end if we need it
@@ -407,6 +408,7 @@ def wf_use_local_configs(self):
             )
 
         # Write the file out again
+        log.debug(f"Updating '{nfconfig_fn}'")
         with open(nfconfig_fn, "w") as nfconfig_fh:
             nfconfig_fh.write(nfconfig)
 
diff --git a/nf_core/utils.py b/nf_core/utils.py
index 90935e4083..55751ae341 100644
--- a/nf_core/utils.py
+++ b/nf_core/utils.py
@@ -200,17 +200,20 @@ def is_pipeline_directory(wf_path):
             raise UserWarning(f"'{wf_path}' is not a pipeline - '{fn}' is missing")
 
 
-def fetch_wf_config(wf_path):
+def fetch_wf_config(wf_path, cache_config=True):
     """Uses Nextflow to retrieve the the configuration variables
     from a Nextflow workflow.
 
     Args:
         wf_path (str): Nextflow workflow file system path.
+        cache_config (bool): cache configuration or not (def. True)
 
     Returns:
         dict: Workflow configuration settings.
     """
 
+    log.debug(f"Got '{wf_path}' as path")
+
     config = dict()
     cache_fn = None
     cache_basedir = None
@@ -272,7 +275,11 @@ def fetch_wf_config(wf_path):
         log.debug("Could not open {} to look for parameter declarations - {}".format(main_nf, e))
 
     # If we can, save a cached copy
-    if cache_path:
+    # HINT: during testing phase (in test_download, for example) we don't want
+    # to save configuration copy in $HOME, otherwise the tests/test_download.py::DownloadTest::test_wf_use_local_configs
+    # will fail after the first attempt. It's better to not save temporary data
+    # in others folders than tmp when doing tests in general
+    if cache_path and cache_config:
         log.debug("Saving config cache: {}".format(cache_path))
         with open(cache_path, "w") as fh:
             json.dump(config, fh, indent=4)
diff --git a/tests/test_download.py b/tests/test_download.py
index a4ae8e205d..7025cd5ad5 100644
--- a/tests/test_download.py
+++ b/tests/test_download.py
@@ -112,7 +112,7 @@ def test_wf_use_local_configs(self):
 
         # Test the function
         download_obj.wf_use_local_configs()
-        wf_config = nf_core.utils.fetch_wf_config(os.path.join(test_outdir, "workflow"))
+        wf_config = nf_core.utils.fetch_wf_config(os.path.join(test_outdir, "workflow"), cache_config=False)
         assert wf_config["params.custom_config_base"] == f"'{test_outdir}/workflow/../configs/'"
 
     #

From d07653bf88c9ba43615b6157120adbca7fbb9b61 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 22 Oct 2021 18:03:03 +0200
Subject: [PATCH 104/266] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c11ad5dab0..57b8cc2ed6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
 

From 3f2581d81312d6c3e9b2debd460dc9d83687e4c1 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 26 Oct 2021 14:27:58 +0200
Subject: [PATCH 105/266] Fixed bug in `nf-core list` when `NXF_HOME` is set

---
 CHANGELOG.md    | 1 +
 nf_core/list.py | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c11ad5dab0..4ca9808900 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
 * Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
 * Update regex in `readme` test of `nf-core lint` to agree with the pipeline template ([#1260](https://github.com/nf-core/tools/issues/1260))
 * Update 'fix' message in `nf-core lint` to conform to the current command line options. ([#1259](https://github.com/nf-core/tools/issues/1259))
+* Fixed bug in `nf-core list` when `NXF_HOME` is set
 
 ### Modules
 
diff --git a/nf_core/list.py b/nf_core/list.py
index 4d8b31f079..5a94253d00 100644
--- a/nf_core/list.py
+++ b/nf_core/list.py
@@ -334,7 +334,7 @@ def get_local_nf_workflow_details(self):
             if len(os.environ.get("NXF_ASSETS", "")) > 0:
                 nf_wfdir = os.path.join(os.environ.get("NXF_ASSETS"), self.full_name)
             elif len(os.environ.get("NXF_HOME", "")) > 0:
-                nf_wfdir = os.path.join(os.environ.get("NXF_HOME"), "assets")
+                nf_wfdir = os.path.join(os.environ.get("NXF_HOME"), "assets", self.full_name)
             else:
                 nf_wfdir = os.path.join(os.getenv("HOME"), ".nextflow", "assets", self.full_name)
             if os.path.isdir(nf_wfdir):

From d79cdb68af580246511b055e371dccaeeef1b239 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?=
 <mashehu@users.noreply.github.com>
Date: Tue, 26 Oct 2021 16:41:12 +0200
Subject: [PATCH 106/266] change out-dated `/software/` paths to `/modules/

---
 nf_core/module-template/modules/main.nf | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 6e4dcde636..c4db5d6d89 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -2,7 +2,7 @@
 include { initOptions; saveFiles; getSoftwareName } 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
+//               https://github.com/nf-core/modules/tree/master/modules
 //               You can also ask for help via your pull request or on the #modules channel on the nf-core Slack workspace:
 //               https://nf-co.re/join
 
@@ -43,7 +43,7 @@ process {{ tool_name_underscore|upper }} {
     // TODO nf-core: Where applicable all sample-specific information e.g. "id", "single_end", "read_group"
     //               MUST be provided as an input via a Groovy Map called "meta".
     //               This information may not be required in some instances e.g. indexing reference genome files:
-    //               https://github.com/nf-core/modules/blob/master/software/bwa/index/main.nf
+    //               https://github.com/nf-core/modules/blob/master/modules/bwa/index/main.nf
     // TODO nf-core: Where applicable please provide/convert compressed files as input/output
     //               e.g. "*.fastq.gz" and NOT "*.fastq", "*.bam" and NOT "*.sam" etc.
     {{ 'tuple val(meta), path(bam)' if has_meta else 'path bam' }}
@@ -61,7 +61,7 @@ process {{ tool_name_underscore|upper }} {
     {%- 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
+    //               e.g. https://github.com/nf-core/modules/blob/master/modules/homer/annotatepeaks/main.nf
     // 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"

From 0aff2a9f1ce66c6fb8f43bbee25c9311c907af2e Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 2 Nov 2021 16:39:54 +0100
Subject: [PATCH 107/266] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0ff592c45..d7122a3a28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Deal with authentication with private repositories
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.

From bb5f4603e27010f2098179c29b7c17ec725d95f8 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 11:21:16 +0000
Subject: [PATCH 108/266] Remove params.hostnames from pipeline template

---
 .../pipeline-template/lib/NfcoreSchema.groovy |  3 +--
 .../lib/NfcoreTemplate.groovy                 | 25 -------------------
 .../pipeline-template/lib/WorkflowMain.groovy |  3 ---
 nf_core/pipeline-template/nextflow.config     |  1 -
 .../pipeline-template/nextflow_schema.json    |  6 -----
 5 files changed, 1 insertion(+), 37 deletions(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
index 8d6920dd64..dcb39c839e 100755
--- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
@@ -260,13 +260,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
diff --git a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
index 44551e0a35..eb7c554070 100755
--- a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
@@ -18,31 +18,6 @@ class NfcoreTemplate {
         }
     }
 
-    //
-    // Check params.hostnames
-    //
-    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}."
-            }
-        }
-    }
-
     //
     // Construct and send completion email
     //
diff --git a/nf_core/pipeline-template/lib/WorkflowMain.groovy b/nf_core/pipeline-template/lib/WorkflowMain.groovy
index 597129cb50..db05c0f9ad 100755
--- a/nf_core/pipeline-template/lib/WorkflowMain.groovy
+++ b/nf_core/pipeline-template/lib/WorkflowMain.groovy
@@ -69,9 +69,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/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 3f23f45b34..2be5f632ec 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -41,7 +41,6 @@ params {
     // 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
diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json
index aacda0004c..c4b03824a8 100644
--- a/nf_core/pipeline-template/nextflow_schema.json
+++ b/nf_core/pipeline-template/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.",

From 3c7613daff4e807d9b5f6d28eace1dad3d5e3502 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 11:35:53 +0000
Subject: [PATCH 109/266] Add in CI tests for latest edge version

---
 .github/workflows/create-test-wf.yml | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml
index 16c2f1abf9..c2b9914f6e 100644
--- a/.github/workflows/create-test-wf.yml
+++ b/.github/workflows/create-test-wf.yml
@@ -1,12 +1,15 @@
 name: Create a pipeline and test it
 on: [push, pull_request]
 
-# Uncomment if we need an edge release of Nextflow again
-# env: NXF_EDGE: 1
-
 jobs:
   RunTestWorkflow:
     runs-on: ubuntu-latest
+    env:
+      NXF_ANSI_LOG: false
+    strategy:
+      matrix:
+        # Nextflow versions: check pipeline minimum and latest edge version
+        nxf_ver: ["NXF_VER=21.04.0", "NXF_EDGE=1"]
     steps:
       - uses: actions/checkout@v2
         name: Check out source-code repository
@@ -27,6 +30,8 @@ jobs:
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
+          export ${{ matrix.nxf_ver }}
+          nextflow self-update
 
       - name: Run nf-core/tools
         run: |

From c66d15bd6541cf60b955b8b1f2a40bd2ba1371b8 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 11:40:50 +0000
Subject: [PATCH 110/266] Add in CI tests for latest edge version

---
 .github/workflows/create-lint-wf.yml | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/create-lint-wf.yml b/.github/workflows/create-lint-wf.yml
index cdc354a0e6..dfc062eca7 100644
--- a/.github/workflows/create-lint-wf.yml
+++ b/.github/workflows/create-lint-wf.yml
@@ -1,12 +1,15 @@
 name: Create a pipeline and lint it
 on: [push, pull_request]
 
-# Uncomment if we need an edge release of Nextflow again
-# env: NXF_EDGE: 1
-
 jobs:
   MakeTestWorkflow:
     runs-on: ubuntu-latest
+    env:
+      NXF_ANSI_LOG: false
+    strategy:
+      matrix:
+        # Nextflow versions: check pipeline minimum and latest edge version
+        nxf_ver: ["NXF_VER=21.04.0", "NXF_EDGE=1"]
     steps:
       - uses: actions/checkout@v2
         name: Check out source-code repository
@@ -27,6 +30,8 @@ jobs:
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
+          export ${{ matrix.nxf_ver }}
+          nextflow self-update
 
       - name: nf-core create
         run: nf-core --log-file log.txt create -n testpipeline -d "This pipeline is for testing" -a "Testing McTestface"

From 4d018b011411c29e45e6e60aaea5077eae6e714c Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 11:46:04 +0000
Subject: [PATCH 111/266] Update CHANGELOG

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0ff592c45..87c3f19065 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
+* Remove `params.hostnames` from the pipeline template ([#1304](https://github.com/nf-core/tools/issues/1304))
 
 ### General
 
@@ -15,6 +16,7 @@
 * Update regex in `readme` test of `nf-core lint` to agree with the pipeline template ([#1260](https://github.com/nf-core/tools/issues/1260))
 * Update 'fix' message in `nf-core lint` to conform to the current command line options. ([#1259](https://github.com/nf-core/tools/issues/1259))
 * Fixed bug in `nf-core list` when `NXF_HOME` is set
+* Run CI test used to create and lint/run the pipeline template with minimum and latest edge release of NF ([#1304](https://github.com/nf-core/tools/issues/1304))
 
 ### Modules
 

From b15c6c03e3947380a5b65b7808f3dc2bb7d609d0 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 12:50:32 +0000
Subject: [PATCH 112/266] Add a check to see if any custom configuration is
 provided

---
 .../pipeline-template/lib/NfcoreTemplate.groovy    | 14 ++++++++++++++
 nf_core/pipeline-template/lib/WorkflowMain.groovy  |  3 +++
 2 files changed, 17 insertions(+)

diff --git a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
index eb7c554070..46d99097d4 100755
--- a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
@@ -18,6 +18,20 @@ class NfcoreTemplate {
         }
     }
 
+    //
+    //  Warn if a -profile or Nextflow config has not been provided to run the pipeline
+    //
+    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 enviroment but can be acheived by:\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 awsbatch`\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 "
+        }
+    }
+
     //
     // Construct and send completion email
     //
diff --git a/nf_core/pipeline-template/lib/WorkflowMain.groovy b/nf_core/pipeline-template/lib/WorkflowMain.groovy
index db05c0f9ad..3181f592ca 100755
--- a/nf_core/pipeline-template/lib/WorkflowMain.groovy
+++ b/nf_core/pipeline-template/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)

From 89af1d984b3eeefe3d8a985bcfe3abf4ee0d05e8 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 12:57:11 +0000
Subject: [PATCH 113/266] Re-word warning

---
 nf_core/pipeline-template/lib/NfcoreTemplate.groovy | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
index 46d99097d4..6d5a1d1078 100755
--- a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
@@ -24,11 +24,11 @@ class NfcoreTemplate {
     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 enviroment but can be acheived by:\n" +
+                    "This will be dependent on your local compute enviroment but can be acheived 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 awsbatch`\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 "
+                    "Please refer to the quick start section and usage docs for the pipeline.\n "
         }
     }
 

From 49b5642fdabbbceb50d7f47a067a8ca75045448e Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Wed, 3 Nov 2021 14:56:56 +0000
Subject: [PATCH 114/266] Remove remnant of params.hostnames

---
 nf_core/pipeline-template/lib/NfcoreTemplate.groovy | 1 -
 1 file changed, 1 deletion(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
index 6d5a1d1078..06499409c5 100755
--- a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
@@ -157,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}-"
         }
     }

From 74a82945d5928a3d6a97ab7a3cd3e4100a941ce6 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Mon, 8 Nov 2021 12:32:02 +0100
Subject: [PATCH 115/266] Auto-collapse installed modules and subworkflows in
 PRs

See also https://nfcore.slack.com/archives/CE5LG7WMB/p1636276933143100
---
 nf_core/pipeline-template/.gitattributes | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/nf_core/pipeline-template/.gitattributes b/nf_core/pipeline-template/.gitattributes
index 7fe55006f8..050bb12035 100644
--- a/nf_core/pipeline-template/.gitattributes
+++ b/nf_core/pipeline-template/.gitattributes
@@ -1 +1,3 @@
 *.config linguist-language=nextflow
+modules/nf-core/** linguist-generated
+subworkflows/nf-core/** linguist-generated

From 26b12e3addfcf489a17a7556b812517bb2a5cf6d Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Mon, 8 Nov 2021 11:34:36 +0000
Subject: [PATCH 116/266] Update Changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87c3f19065..727077c569 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
 * Remove `params.hostnames` from the pipeline template ([#1304](https://github.com/nf-core/tools/issues/1304))
+* Update `.gitattributes` to mark installed modules and subworkflows as `linguist-generated` ([#1311](https://github.com/nf-core/tools/issues/1311))
 
 ### General
 

From 880345107e6edf72ca11e9052fef24af335a54aa Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 15:49:23 +0100
Subject: [PATCH 117/266] start using context manager for temporary files and
 folders

---
 tests/__init__.py          |  7 ++++
 tests/test_bump_version.py | 16 +++++----
 tests/test_create.py       |  8 +++--
 tests/test_download.py     | 71 +++++++++++++++++---------------------
 tests/utils.py             | 36 +++++++++++++++++++
 5 files changed, 89 insertions(+), 49 deletions(-)
 create mode 100644 tests/__init__.py
 create mode 100644 tests/utils.py

diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000..b3e4769de7
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Nov  9 13:46:10 2021
+
+@author: Paolo Cozzi <paolo.cozzi@ibba.cnr.it>
+"""
diff --git a/tests/test_bump_version.py b/tests/test_bump_version.py
index bd6c5a1a57..13a731eaa6 100644
--- a/tests/test_bump_version.py
+++ b/tests/test_bump_version.py
@@ -2,7 +2,6 @@
 """Some tests covering the bump_version code.
 """
 import os
-import tempfile
 import yaml
 
 import nf_core.bump_version
@@ -10,10 +9,13 @@
 import nf_core.utils
 
 
-def test_bump_pipeline_version(datafiles):
+# pass tmp_path as argument, which is a pytest feature
+# see: https://docs.pytest.org/en/latest/how-to/tmp_path.html#the-tmp-path-fixture
+def test_bump_pipeline_version(datafiles, tmp_path):
     """Test that making a release with the working example files works"""
+
     # Get a workflow and configs
-    test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+    test_pipeline_dir = os.path.join(tmp_path, "nf-core-testpipeline")
     create_obj = nf_core.create.PipelineCreate(
         "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=test_pipeline_dir
     )
@@ -30,10 +32,10 @@ def test_bump_pipeline_version(datafiles):
     assert new_pipeline_obj.nf_config["manifest.version"].strip("'\"") == "1.1"
 
 
-def test_dev_bump_pipeline_version(datafiles):
+def test_dev_bump_pipeline_version(datafiles, tmp_path):
     """Test that making a release works with a dev name and a leading v"""
     # Get a workflow and configs
-    test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+    test_pipeline_dir = os.path.join(tmp_path, "nf-core-testpipeline")
     create_obj = nf_core.create.PipelineCreate(
         "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=test_pipeline_dir
     )
@@ -50,9 +52,9 @@ def test_dev_bump_pipeline_version(datafiles):
     assert new_pipeline_obj.nf_config["manifest.version"].strip("'\"") == "1.2dev"
 
 
-def test_bump_nextflow_version(datafiles):
+def test_bump_nextflow_version(datafiles, tmp_path):
     # Get a workflow and configs
-    test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+    test_pipeline_dir = os.path.join(tmp_path, "nf-core-testpipeline")
     create_obj = nf_core.create.PipelineCreate(
         "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=test_pipeline_dir
     )
diff --git a/tests/test_create.py b/tests/test_create.py
index 5fb1e53a60..cce494b523 100644
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -3,12 +3,14 @@
 """
 import os
 import nf_core.create
-import tempfile
 import unittest
 
+from .utils import with_temporary_folder
+
 
 class NfcoreCreateTest(unittest.TestCase):
-    def setUp(self):
+    @with_temporary_folder
+    def setUp(self, tmp_path):
         self.pipeline_name = "nf-core/test"
         self.pipeline_description = "just for 4w3s0m3 tests"
         self.pipeline_author = "Chuck Norris"
@@ -21,7 +23,7 @@ def setUp(self):
             version=self.pipeline_version,
             no_git=False,
             force=True,
-            outdir=tempfile.mkdtemp(),
+            outdir=tmp_path,
         )
 
     def test_pipeline_creation(self):
diff --git a/tests/test_download.py b/tests/test_download.py
index 7025cd5ad5..f67a0b4f98 100644
--- a/tests/test_download.py
+++ b/tests/test_download.py
@@ -14,6 +14,8 @@
 import tempfile
 import unittest
 
+from .utils import with_temporary_folder, with_temporary_file
+
 
 class DownloadTest(unittest.TestCase):
 
@@ -73,8 +75,8 @@ def test_get_release_hash_non_existent_release(self):
     #
     # Tests for 'download_wf_files'
     #
-    def test_download_wf_files(self):
-        outdir = tempfile.mkdtemp()
+    @with_temporary_folder
+    def test_download_wf_files(self, outdir):
         download_obj = DownloadWorkflow(pipeline="nf-core/methylseq", revision="1.6")
         download_obj.outdir = outdir
         download_obj.wf_sha = "b3e5e3b95aaf01d98391a62a10a3990c0a4de395"
@@ -87,8 +89,8 @@ def test_download_wf_files(self):
     #
     # Tests for 'download_configs'
     #
-    def test_download_configs(self):
-        outdir = tempfile.mkdtemp()
+    @with_temporary_folder
+    def test_download_configs(self, outdir):
         download_obj = DownloadWorkflow(pipeline="nf-core/methylseq", revision="1.6")
         download_obj.outdir = outdir
         download_obj.download_configs()
@@ -97,30 +99,32 @@ def test_download_configs(self):
     #
     # Tests for 'wf_use_local_configs'
     #
-    def test_wf_use_local_configs(self):
+    @with_temporary_folder
+    def test_wf_use_local_configs(self, tmp_path):
         # Get a workflow and configs
-        test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+        test_pipeline_dir = os.path.join(tmp_path, "nf-core-testpipeline")
         create_obj = nf_core.create.PipelineCreate(
             "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=test_pipeline_dir
         )
         create_obj.init_pipeline()
 
-        test_outdir = tempfile.mkdtemp()
-        download_obj = DownloadWorkflow(pipeline="dummy", revision="1.2.0", outdir=test_outdir)
-        shutil.copytree(test_pipeline_dir, os.path.join(test_outdir, "workflow"))
-        download_obj.download_configs()
+        with tempfile.TemporaryDirectory() as test_outdir:
+            download_obj = DownloadWorkflow(pipeline="dummy", revision="1.2.0", outdir=test_outdir)
+            shutil.copytree(test_pipeline_dir, os.path.join(test_outdir, "workflow"))
+            download_obj.download_configs()
 
-        # Test the function
-        download_obj.wf_use_local_configs()
-        wf_config = nf_core.utils.fetch_wf_config(os.path.join(test_outdir, "workflow"), cache_config=False)
-        assert wf_config["params.custom_config_base"] == f"'{test_outdir}/workflow/../configs/'"
+            # Test the function
+            download_obj.wf_use_local_configs()
+            wf_config = nf_core.utils.fetch_wf_config(os.path.join(test_outdir, "workflow"), cache_config=False)
+            assert wf_config["params.custom_config_base"] == f"'{test_outdir}/workflow/../configs/'"
 
     #
     # Tests for 'find_container_images'
     #
+    @with_temporary_folder
     @mock.patch("nf_core.utils.fetch_wf_config")
-    def test_find_container_images(self, mock_fetch_wf_config):
-        download_obj = DownloadWorkflow(pipeline="dummy", outdir=tempfile.mkdtemp())
+    def test_find_container_images(self, tmp_path, mock_fetch_wf_config):
+        download_obj = DownloadWorkflow(pipeline="dummy", outdir=tmp_path)
         mock_fetch_wf_config.return_value = {
             "process.mapping.container": "cutting-edge-container",
             "process.nocontainer": "not-so-cutting-edge",
@@ -132,60 +136,52 @@ def test_find_container_images(self, mock_fetch_wf_config):
     #
     # Tests for 'validate_md5'
     #
-    def test_matching_md5sums(self):
+    @with_temporary_file
+    def test_matching_md5sums(self, tmpfile):
         download_obj = DownloadWorkflow(pipeline="dummy")
         test_hash = hashlib.md5()
         test_hash.update(b"test")
         val_hash = test_hash.hexdigest()
-        tmpfilehandle, tmpfile = tempfile.mkstemp()
 
-        with open(tmpfile[1], "w") as f:
+        with open(tmpfile.name, "w") as f:
             f.write("test")
 
-        download_obj.validate_md5(tmpfile[1], val_hash)
-
-        # Clean up
-        os.remove(tmpfile[1])
+        download_obj.validate_md5(tmpfile.name, val_hash)
 
+    @with_temporary_file
     @pytest.mark.xfail(raises=IOError, strict=True)
-    def test_mismatching_md5sums(self):
+    def test_mismatching_md5sums(self, tmpfile):
         download_obj = DownloadWorkflow(pipeline="dummy")
         test_hash = hashlib.md5()
         test_hash.update(b"other value")
         val_hash = test_hash.hexdigest()
-        tmpfilehandle, tmpfile = tempfile.mkstemp()
 
-        with open(tmpfile, "w") as f:
+        with open(tmpfile.name, "w") as f:
             f.write("test")
 
-        download_obj.validate_md5(tmpfile[1], val_hash)
+        download_obj.validate_md5(tmpfile.name, val_hash)
 
-        # Clean up
-        os.remove(tmpfile)
 
     #
     # Tests for 'singularity_pull_image'
     #
     # If Singularity is not installed, will log an error and exit
     # If Singularity is installed, should raise an OSError due to non-existant image
+    @with_temporary_folder
     @pytest.mark.xfail(raises=OSError)
     @mock.patch("rich.progress.Progress.add_task")
-    def test_singularity_pull_image(self, mock_rich_progress):
-        tmp_dir = tempfile.mkdtemp()
+    def test_singularity_pull_image(self, tmp_dir, mock_rich_progress):
         download_obj = DownloadWorkflow(pipeline="dummy", outdir=tmp_dir)
         download_obj.singularity_pull_image("a-container", tmp_dir, None, mock_rich_progress)
 
-        # Clean up
-        shutil.rmtree(tmp_dir)
 
     #
     # Tests for the main entry method 'download_workflow'
     #
+    @with_temporary_folder
     @mock.patch("nf_core.download.DownloadWorkflow.singularity_pull_image")
     @mock.patch("shutil.which")
-    def test_download_workflow_with_success(self, mock_download_image, mock_singularity_installed):
-
-        tmp_dir = tempfile.mkdtemp()
+    def test_download_workflow_with_success(self, tmp_dir, mock_download_image, mock_singularity_installed):
         os.environ["NXF_SINGULARITY_CACHEDIR"] = "foo"
 
         download_obj = DownloadWorkflow(
@@ -197,6 +193,3 @@ def test_download_workflow_with_success(self, mock_download_image, mock_singular
         )
 
         download_obj.download_workflow()
-
-        # Clean up
-        shutil.rmtree(tmp_dir)
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000000..8acd72ce7b
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Helper functions for tests
+"""
+
+import functools
+import tempfile
+
+
+def with_temporary_folder(func):
+    """
+    Call the decorated funtion under the tempfile.TemporaryDirectory
+    context manager. Pass the temporary directory name to the decorated
+    function
+    """
+
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            return func(*args, tmpdirname, **kwargs)
+
+    return wrapper
+
+
+def with_temporary_file(func):
+    """
+    Call the decorated funtion under the tempfile.NamedTemporaryFile
+    context manager. Pass the opened file handle to the decorated function
+    """
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        with tempfile.NamedTemporaryFile() as tmpfile:
+            return func(*args, tmpfile, **kwargs)
+
+    return wrapper

From cc9845f8f3277c1051dd546808bf5d6f0bf92338 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 17:25:14 +0100
Subject: [PATCH 118/266] refactor test_launch

deal with temporary files in test_launch
---
 tests/test_launch.py | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/tests/test_launch.py b/tests/test_launch.py
index 090414029c..8029213f06 100644
--- a/tests/test_launch.py
+++ b/tests/test_launch.py
@@ -11,6 +11,8 @@
 import tempfile
 import unittest
 
+from .utils import with_temporary_folder, with_temporary_file
+
 
 class TestLaunch(unittest.TestCase):
     """Class for launch tests"""
@@ -20,9 +22,21 @@ def setUp(self):
         # Set up the schema
         root_repo_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
         self.template_dir = os.path.join(root_repo_dir, "nf_core", "pipeline-template")
-        self.nf_params_fn = os.path.join(tempfile.mkdtemp(), "nf-params.json")
+        # cannot use a context manager here, since outside setUp the temporary
+        # file will never exists
+        self.tmp_dir = tempfile.mkdtemp()
+        self.nf_params_fn = os.path.join(self.tmp_dir, "nf-params.json")
         self.launcher = nf_core.launch.Launch(self.template_dir, params_out=self.nf_params_fn)
 
+    def tearDown(self):
+        """Clean up temporary files and folders"""
+
+        if os.path.exists(self.nf_params_fn):
+            os.remove(self.nf_params_fn)
+
+        if os.path.exists(self.tmp_dir):
+            os.rmdir(self.tmp_dir)
+
     @mock.patch.object(nf_core.launch.Launch, "prompt_web_gui", side_effect=[True])
     @mock.patch.object(nf_core.launch.Launch, "launch_web_gui")
     def test_launch_pipeline(self, mock_webbrowser, mock_lauch_web_gui):
@@ -52,9 +66,10 @@ def test_get_pipeline_schema(self):
         self.launcher.get_pipeline_schema()
         assert len(self.launcher.schema_obj.schema["definitions"]["input_output_options"]["properties"]) > 2
 
-    def test_make_pipeline_schema(self):
+    @with_temporary_folder
+    def test_make_pipeline_schema(self, tmp_path):
         """Make a copy of the template workflow, but delete the schema file, then try to load it"""
-        test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "wf")
+        test_pipeline_dir = os.path.join(tmp_path, "wf")
         shutil.copytree(self.template_dir, test_pipeline_dir)
         os.remove(os.path.join(test_pipeline_dir, "nextflow_schema.json"))
         self.launcher = nf_core.launch.Launch(test_pipeline_dir, params_out=self.nf_params_fn)
@@ -74,12 +89,12 @@ def test_get_pipeline_defaults(self):
         assert len(self.launcher.schema_obj.input_params) > 0
         assert self.launcher.schema_obj.input_params["outdir"] == "./results"
 
-    def test_get_pipeline_defaults_input_params(self):
+    @with_temporary_file
+    def test_get_pipeline_defaults_input_params(self, tmp_file):
         """Test fetching default inputs from the pipeline schema with an input params file supplied"""
-        tmp_filehandle, tmp_filename = tempfile.mkstemp()
-        with os.fdopen(tmp_filehandle, "w") as fh:
+        with open(tmp_file.name, "w") as fh:
             json.dump({"outdir": "fubar"}, fh)
-        self.launcher.params_in = tmp_filename
+        self.launcher.params_in = tmp_file.name
         self.launcher.get_pipeline_schema()
         self.launcher.set_schema_inputs()
         assert len(self.launcher.schema_obj.input_params) > 0

From 2d38e0af21537c46b1596944670cdec0499b160d Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 17:26:25 +0100
Subject: [PATCH 119/266] deal with temporary files in test_lint

---
 nf_core/lint/files_unchanged.py |  7 +++++-
 tests/test_lint.py              | 43 +++++++++++++++++++++------------
 2 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/nf_core/lint/files_unchanged.py b/nf_core/lint/files_unchanged.py
index 262e8c4449..afe20eedcb 100644
--- a/nf_core/lint/files_unchanged.py
+++ b/nf_core/lint/files_unchanged.py
@@ -107,7 +107,9 @@ def files_unchanged(self):
     logging.getLogger("nf_core.create").setLevel(logging.ERROR)
 
     # Generate a new pipeline with nf-core create that we can compare to
-    test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-{}".format(short_name))
+    tmp_dir = tempfile.mkdtemp()
+
+    test_pipeline_dir = os.path.join(tmp_dir, "nf-core-{}".format(short_name))
     create_obj = nf_core.create.PipelineCreate(
         self.nf_config["manifest.name"].strip("\"'"),
         self.nf_config["manifest.description"].strip("\"'"),
@@ -192,4 +194,7 @@ def _tf(file_path):
                 except FileNotFoundError:
                     pass
 
+    # cleaning up temporary dir
+    shutil.rmtree(tmp_dir)
+
     return {"passed": passed, "failed": failed, "ignored": ignored, "fixed": fixed, "could_fix": could_fix}
diff --git a/tests/test_lint.py b/tests/test_lint.py
index c6d5dc351d..78c8c26aee 100644
--- a/tests/test_lint.py
+++ b/tests/test_lint.py
@@ -16,6 +16,8 @@
 import nf_core.create
 import nf_core.lint
 
+from .utils import with_temporary_folder
+
 
 class TestLint(unittest.TestCase):
     """Class for lint tests"""
@@ -25,7 +27,9 @@ def setUp(self):
 
         Use nf_core.create() to make a pipeline that we can use for testing
         """
-        self.test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+
+        self.tmp_dir = tempfile.mkdtemp()
+        self.test_pipeline_dir = os.path.join(self.tmp_dir, "nf-core-testpipeline")
         self.create_obj = nf_core.create.PipelineCreate(
             "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=self.test_pipeline_dir
         )
@@ -33,11 +37,17 @@ def setUp(self):
         # Base lint object on this directory
         self.lint_obj = nf_core.lint.PipelineLint(self.test_pipeline_dir)
 
+    def tearDown(self):
+        """Clean up temporary files and folders"""
+
+        if os.path.exists(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
+
     def _make_pipeline_copy(self):
         """Make a copy of the test pipeline that can be edited
 
         Returns: Path to new temp directory with pipeline"""
-        new_pipeline = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+        new_pipeline = os.path.join(self.tmp_dir, "nf-core-testpipeline-copy")
         shutil.copytree(self.test_pipeline_dir, new_pipeline)
         return new_pipeline
 
@@ -72,9 +82,9 @@ def test_load_lint_config_not_found(self):
 
     def test_load_lint_config_ignore_all_tests(self):
         """Try to load a linting config file that ignores all tests"""
+
         # Make a copy of the test pipeline and create a lint object
-        new_pipeline = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
-        shutil.copytree(self.test_pipeline_dir, new_pipeline)
+        new_pipeline = self._make_pipeline_copy()
         lint_obj = nf_core.lint.PipelineLint(new_pipeline)
 
         # Make a config file listing all test names
@@ -93,7 +103,8 @@ def test_load_lint_config_ignore_all_tests(self):
         assert len(lint_obj.failed) == 0
         assert len(lint_obj.ignored) == len(lint_obj.lint_tests)
 
-    def test_json_output(self):
+    @with_temporary_folder
+    def test_json_output(self, tmp_dir):
         """
         Test creation of a JSON file with lint results
 
@@ -122,7 +133,7 @@ def test_json_output(self):
         self.lint_obj.warned.append(("test_three", "This test gave a warning"))
 
         # Make a temp dir for the JSON output
-        json_fn = os.path.join(tempfile.mkdtemp(), "lint_results.json")
+        json_fn = os.path.join(tmp_dir, "lint_results.json")
         self.lint_obj._save_json_results(json_fn)
 
         # Load created JSON file and check its contents
@@ -176,46 +187,46 @@ def test_sphinx_rst_files(self):
     #######################
     # SPECIFIC LINT TESTS #
     #######################
-    from lint.actions_awsfulltest import (
+    from .lint.actions_awsfulltest import (
         test_actions_awsfulltest_warn,
         test_actions_awsfulltest_pass,
         test_actions_awsfulltest_fail,
     )
-    from lint.actions_awstest import test_actions_awstest_pass, test_actions_awstest_fail
-    from lint.files_exist import (
+    from .lint.actions_awstest import test_actions_awstest_pass, test_actions_awstest_fail
+    from .lint.files_exist import (
         test_files_exist_missing_config,
         test_files_exist_missing_main,
         test_files_exist_depreciated_file,
         test_files_exist_pass,
     )
-    from lint.actions_ci import (
+    from .lint.actions_ci import (
         test_actions_ci_pass,
         test_actions_ci_fail_wrong_nf,
         test_actions_ci_fail_wrong_docker_ver,
         test_actions_ci_fail_wrong_trigger,
     )
 
-    from lint.actions_schema_validation import (
+    from .lint.actions_schema_validation import (
         test_actions_schema_validation_missing_jobs,
         test_actions_schema_validation_missing_on,
     )
 
-    from lint.merge_markers import test_merge_markers_found
+    from .lint.merge_markers import test_merge_markers_found
 
-    from lint.nextflow_config import (
+    from .lint.nextflow_config import (
         test_nextflow_config_example_pass,
         test_nextflow_config_bad_name_fail,
         test_nextflow_config_dev_in_release_mode_failed,
     )
 
-    from lint.files_unchanged import (
+    from .lint.files_unchanged import (
         test_files_unchanged_pass,
         test_files_unchanged_fail,
     )
 
-    from lint.version_consistency import test_version_consistency
+    from .lint.version_consistency import test_version_consistency
 
-    from lint.modules_json import test_modules_json_pass
+    from .lint.modules_json import test_modules_json_pass
 
 
 # TODO nf-core: Assess and strip out if no longer required for DSL2

From 74eaab96ef1236b65e979e6b16877741e223bf59 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 17:59:34 +0100
Subject: [PATCH 120/266] solve circular import issue in nf_core.lint

---
 CHANGELOG.md             |  1 +
 nf_core/lint/__init__.py | 10 ++++++----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 727077c569..a3eb4c0fda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Solve circular import when importing `nf_core.modules.lint`
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
diff --git a/nf_core/lint/__init__.py b/nf_core/lint/__init__.py
index b61765b162..4838726928 100644
--- a/nf_core/lint/__init__.py
+++ b/nf_core/lint/__init__.py
@@ -20,8 +20,8 @@
 
 import nf_core.utils
 import nf_core.lint_utils
+import nf_core.modules.lint
 from nf_core.lint_utils import console
-from nf_core.modules.lint import ModuleLint
 
 log = logging.getLogger(__name__)
 
@@ -44,7 +44,9 @@ def run_linting(
 
     # Verify that the requested tests exist
     if key:
-        all_tests = set(PipelineLint._get_all_lint_tests(release_mode)).union(set(ModuleLint._get_all_lint_tests()))
+        all_tests = set(PipelineLint._get_all_lint_tests(release_mode)).union(
+            set(nf_core.modules.lint.ModuleLint._get_all_lint_tests())
+        )
         bad_keys = [k for k in key if k not in all_tests]
         if len(bad_keys) > 0:
             raise AssertionError(
@@ -66,7 +68,7 @@ def run_linting(
     lint_obj._list_files()
 
     # Create the modules lint object
-    module_lint_obj = ModuleLint(pipeline_dir)
+    module_lint_obj = nf_core.modules.lint.ModuleLint(pipeline_dir)
 
     # Verify that the pipeline is correctly configured
     try:
@@ -77,7 +79,7 @@ def run_linting(
     # Run only the tests we want
     if key:
         # Select only the module lint tests
-        module_lint_tests = list(set(key).intersection(set(ModuleLint._get_all_lint_tests())))
+        module_lint_tests = list(set(key).intersection(set(nf_core.modules.lint.ModuleLint._get_all_lint_tests())))
     else:
         # If no key is supplied, run the default modules tests
         module_lint_tests = ("module_changes", "module_version")

From 3556d74d08b1402ebe8a636b5f0b1973e3652dd7 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 19:02:41 +0100
Subject: [PATCH 121/266] deal with temporary files in test_modules

---
 tests/modules/create_test_yml.py | 15 ++++++++-------
 tests/modules/install.py         |  8 +++++---
 tests/test_modules.py            | 30 +++++++++++++++++++-----------
 3 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/tests/modules/create_test_yml.py b/tests/modules/create_test_yml.py
index db286181c5..4e7bf1ab32 100644
--- a/tests/modules/create_test_yml.py
+++ b/tests/modules/create_test_yml.py
@@ -1,13 +1,14 @@
 import os
-import tempfile
 import pytest
 
 import nf_core.modules
 
+from ..utils import with_temporary_folder
 
-def test_modules_custom_yml_dumper(self):
+
+@with_temporary_folder
+def test_modules_custom_yml_dumper(self, out_dir):
     """Try to create a yml file with the custom yml dumper"""
-    out_dir = tempfile.mkdtemp()
     yml_output_path = os.path.join(out_dir, "test.yml")
     meta_builder = nf_core.modules.ModulesTestYmlBuilder("test/tool", False, "./", False, True)
     meta_builder.test_yml_output_path = yml_output_path
@@ -16,9 +17,9 @@ def test_modules_custom_yml_dumper(self):
     assert os.path.isfile(yml_output_path)
 
 
-def test_modules_test_file_dict(self):
+@with_temporary_folder
+def test_modules_test_file_dict(self, test_file_dir):
     """Creat dict of test files and create md5 sums"""
-    test_file_dir = tempfile.mkdtemp()
     meta_builder = nf_core.modules.ModulesTestYmlBuilder("test/tool", False, "./", False, True)
     with open(os.path.join(test_file_dir, "test_file.txt"), "w") as fh:
         fh.write("this line is just for testing")
@@ -27,9 +28,9 @@ def test_modules_test_file_dict(self):
     assert test_files[0]["md5sum"] == "2191e06b28b5ba82378bcc0672d01786"
 
 
-def test_modules_create_test_yml_get_md5(self):
+@with_temporary_folder
+def test_modules_create_test_yml_get_md5(self, test_file_dir):
     """Get md5 sums from a dummy output"""
-    test_file_dir = tempfile.mkdtemp()
     meta_builder = nf_core.modules.ModulesTestYmlBuilder("test/tool", False, "./", False, True)
     with open(os.path.join(test_file_dir, "test_file.txt"), "w") as fh:
         fh.write("this line is just for testing")
diff --git a/tests/modules/install.py b/tests/modules/install.py
index 41307e3666..e4c94f6bdb 100644
--- a/tests/modules/install.py
+++ b/tests/modules/install.py
@@ -1,7 +1,8 @@
-import tempfile
 import pytest
 import os
 
+from ..utils import with_temporary_folder
+
 
 def test_modules_install_nopipeline(self):
     """Test installing a module - no pipeline given"""
@@ -9,9 +10,10 @@ def test_modules_install_nopipeline(self):
     assert self.mods_install.install("foo") is False
 
 
-def test_modules_install_emptypipeline(self):
+@with_temporary_folder
+def test_modules_install_emptypipeline(self, tmpdir):
     """Test installing a module - empty dir given"""
-    self.mods_install.dir = tempfile.mkdtemp()
+    self.mods_install.dir = tmpdir
     with pytest.raises(UserWarning) as excinfo:
         self.mods_install.install("foo")
     assert "Could not find a 'main.nf' or 'nextflow.config' file" in str(excinfo.value)
diff --git a/tests/test_modules.py b/tests/test_modules.py
index 2401dfa763..8b46cfc0fc 100644
--- a/tests/test_modules.py
+++ b/tests/test_modules.py
@@ -10,9 +10,9 @@
 import unittest
 
 
-def create_modules_repo_dummy():
+def create_modules_repo_dummy(tmp_dir):
     """Create a dummy copy of the nf-core/modules repo"""
-    tmp_dir = tempfile.mkdtemp()
+
     root_dir = os.path.join(tmp_dir, "modules")
     os.makedirs(os.path.join(root_dir, "modules"))
     os.makedirs(os.path.join(root_dir, "tests", "modules"))
@@ -31,10 +31,12 @@ class TestModules(unittest.TestCase):
 
     def setUp(self):
         """Create a new PipelineSchema and Launch objects"""
+        self.tmp_dir = tempfile.mkdtemp()
+
         # Set up the schema
         root_repo_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
         self.template_dir = os.path.join(root_repo_dir, "nf_core", "pipeline-template")
-        self.pipeline_dir = os.path.join(tempfile.mkdtemp(), "mypipeline")
+        self.pipeline_dir = os.path.join(self.tmp_dir, "mypipeline")
         shutil.copytree(self.template_dir, self.pipeline_dir)
 
         # Set up install objects
@@ -53,7 +55,13 @@ def setUp(self):
         # self.mods_remove_alt.modules_repo = nf_core.modules.ModulesRepo(repo="ewels/nf-core-modules", branch="master")
 
         # Set up the nf-core/modules repo dummy
-        self.nfcore_modules = create_modules_repo_dummy()
+        self.nfcore_modules = create_modules_repo_dummy(self.tmp_dir)
+
+    def tearDown(self):
+        """Clean up temporary files and folders"""
+
+        if os.path.exists(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
 
     def test_modulesrepo_class(self):
         """Initialise a modules repo object"""
@@ -65,13 +73,13 @@ def test_modulesrepo_class(self):
     # Test of the individual modules commands. #
     ############################################
 
-    from modules.list import (
+    from .modules.list import (
         test_modules_list_remote,
         test_modules_list_pipeline,
         test_modules_install_and_list_pipeline,
     )
 
-    from modules.install import (
+    from .modules.install import (
         test_modules_install_nopipeline,
         test_modules_install_emptypipeline,
         test_modules_install_nomodule,
@@ -79,21 +87,21 @@ def test_modulesrepo_class(self):
         test_modules_install_trimgalore_twice,
     )
 
-    from modules.remove import (
+    from .modules.remove import (
         test_modules_remove_trimgalore,
         test_modules_remove_trimgalore_uninstalled,
     )
 
-    from modules.lint import test_modules_lint_trimgalore, test_modules_lint_empty, test_modules_lint_new_modules
+    from .modules.lint import test_modules_lint_trimgalore, test_modules_lint_empty, test_modules_lint_new_modules
 
-    from modules.create import (
+    from .modules.create import (
         test_modules_create_succeed,
         test_modules_create_fail_exists,
         test_modules_create_nfcore_modules,
         test_modules_create_nfcore_modules_subtool,
     )
 
-    from modules.create_test_yml import (
+    from .modules.create_test_yml import (
         test_modules_custom_yml_dumper,
         test_modules_test_file_dict,
         test_modules_create_test_yml_get_md5,
@@ -101,7 +109,7 @@ def test_modulesrepo_class(self):
         test_modules_create_test_yml_check_inputs,
     )
 
-    from modules.bump_versions import (
+    from .modules.bump_versions import (
         test_modules_bump_versions_single_module,
         test_modules_bump_versions_all_modules,
         test_modules_bump_versions_fail,

From c1a9c3709e6d7e10cdec5465580c1e2e9389f0ca Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 19:25:34 +0100
Subject: [PATCH 122/266] deal with temporary files in schema, sync and utils
 tests

---
 tests/test_schema.py | 36 ++++++++++++++++++++++--------------
 tests/test_sync.py   | 18 ++++++++++++------
 tests/test_utils.py  | 16 ++++++++++++----
 3 files changed, 46 insertions(+), 24 deletions(-)

diff --git a/tests/test_schema.py b/tests/test_schema.py
index 175b23880c..3a060a516c 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -15,6 +15,8 @@
 import unittest
 import yaml
 
+from .utils import with_temporary_file, with_temporary_folder
+
 
 class TestSchema(unittest.TestCase):
     """Class for schema tests"""
@@ -24,11 +26,16 @@ def setUp(self):
         self.schema_obj = nf_core.schema.PipelineSchema()
         self.root_repo_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
         # Copy the template to a temp directory so that we can use that for tests
-        self.template_dir = os.path.join(tempfile.mkdtemp(), "wf")
+        self.tmp_dir = tempfile.mkdtemp()
+        self.template_dir = os.path.join(self.tmp_dir, "wf")
         template_dir = os.path.join(self.root_repo_dir, "nf_core", "pipeline-template")
         shutil.copytree(template_dir, self.template_dir)
         self.template_schema = os.path.join(self.template_dir, "nextflow_schema.json")
 
+    def tearDown(self):
+        if os.path.exists(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
+
     def test_load_lint_schema(self):
         """Check linting with the pipeline template directory"""
         self.schema_obj.get_schema_path(self.template_dir)
@@ -46,13 +53,13 @@ def test_load_lint_schema_notjson(self):
         self.schema_obj.get_schema_path(os.path.join(self.template_dir, "nextflow.config"))
         self.schema_obj.load_lint_schema()
 
+    @with_temporary_file
     @pytest.mark.xfail(raises=AssertionError, strict=True)
-    def test_load_lint_schema_noparams(self):
+    def test_load_lint_schema_noparams(self, tmp_file):
         """
         Check that linting raises properly if a JSON file is given without any params
         """
-        # Make a temporary file to write schema to
-        tmp_file = tempfile.NamedTemporaryFile()
+        # write schema to a temporary file
         with open(tmp_file.name, "w") as fh:
             json.dump({"type": "fubar"}, fh)
         self.schema_obj.get_schema_path(tmp_file.name)
@@ -88,29 +95,29 @@ def test_load_schema(self):
         self.schema_obj.schema_filename = self.template_schema
         self.schema_obj.load_schema()
 
-    def test_save_schema(self):
+    @with_temporary_file
+    def test_save_schema(self, tmp_file):
         """Try to save a schema"""
         # Load the template schema
         self.schema_obj.schema_filename = self.template_schema
         self.schema_obj.load_schema()
 
         # Make a temporary file to write schema to
-        tmp_file = tempfile.NamedTemporaryFile()
         self.schema_obj.schema_filename = tmp_file.name
         self.schema_obj.save_schema()
 
-    def test_load_input_params_json(self):
+    @with_temporary_file
+    def test_load_input_params_json(self, tmp_file):
         """Try to load a JSON file with params for a pipeline run"""
-        # Make a temporary file to write schema to
-        tmp_file = tempfile.NamedTemporaryFile()
+        # write schema to a temporary file
         with open(tmp_file.name, "w") as fh:
             json.dump({"input": "fubar"}, fh)
         self.schema_obj.load_input_params(tmp_file.name)
 
-    def test_load_input_params_yaml(self):
+    @with_temporary_file
+    def test_load_input_params_yaml(self, tmp_file):
         """Try to load a YAML file with params for a pipeline run"""
-        # Make a temporary file to write schema to
-        tmp_file = tempfile.NamedTemporaryFile()
+        # write schema to a temporary file
         with open(tmp_file.name, "w") as fh:
             yaml.dump({"input": "fubar"}, fh)
         self.schema_obj.load_input_params(tmp_file.name)
@@ -293,14 +300,15 @@ def test_build_schema(self):
         """
         param = self.schema_obj.build_schema(self.template_dir, True, False, None)
 
-    def test_build_schema_from_scratch(self):
+    @with_temporary_folder
+    def test_build_schema_from_scratch(self, tmp_dir):
         """
         Build a new schema param from a pipeline with no existing file
         Run code to ensure it doesn't crash. Individual functions tested separately.
 
         Pretty much a copy of test_launch.py test_make_pipeline_schema
         """
-        test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "wf")
+        test_pipeline_dir = os.path.join(tmp_dir, "wf")
         shutil.copytree(self.template_dir, test_pipeline_dir)
         os.remove(os.path.join(test_pipeline_dir, "nextflow_schema.json"))
 
diff --git a/tests/test_sync.py b/tests/test_sync.py
index ce7d07dc7f..727db70104 100644
--- a/tests/test_sync.py
+++ b/tests/test_sync.py
@@ -5,6 +5,7 @@
 import nf_core.create
 import nf_core.sync
 
+import git
 import json
 import mock
 import os
@@ -12,22 +13,27 @@
 import tempfile
 import unittest
 
+from .utils import with_temporary_folder
+
 
 class TestModules(unittest.TestCase):
     """Class for modules tests"""
 
     def setUp(self):
-        self.make_new_pipeline()
-
-    def make_new_pipeline(self):
         """Create a new pipeline to test"""
-        self.pipeline_dir = os.path.join(tempfile.mkdtemp(), "test_pipeline")
+        self.tmp_dir = tempfile.mkdtemp()
+        self.pipeline_dir = os.path.join(self.tmp_dir, "test_pipeline")
         self.create_obj = nf_core.create.PipelineCreate("testing", "test pipeline", "tester", outdir=self.pipeline_dir)
         self.create_obj.init_pipeline()
 
-    def test_inspect_sync_dir_notgit(self):
+    def tearDown(self):
+        if os.path.exists(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
+
+    @with_temporary_folder
+    def test_inspect_sync_dir_notgit(self, tmp_dir):
         """Try syncing an empty directory"""
-        psync = nf_core.sync.PipelineSync(tempfile.mkdtemp())
+        psync = nf_core.sync.PipelineSync(tmp_dir)
         try:
             psync.inspect_sync_dir()
             raise UserWarning("Should have hit an exception")
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 36d533afe4..b62a8c979b 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -12,6 +12,9 @@
 import requests
 import tempfile
 import unittest
+import shutil
+
+from .utils import with_temporary_folder
 
 
 class TestUtils(unittest.TestCase):
@@ -22,7 +25,8 @@ def setUp(self):
 
         Use nf_core.create() to make a pipeline that we can use for testing
         """
-        self.test_pipeline_dir = os.path.join(tempfile.mkdtemp(), "nf-core-testpipeline")
+        self.tmp_dir = tempfile.mkdtemp()
+        self.test_pipeline_dir = os.path.join(self.tmp_dir, "nf-core-testpipeline")
         self.create_obj = nf_core.create.PipelineCreate(
             "testpipeline", "This is a test pipeline", "Test McTestFace", outdir=self.test_pipeline_dir
         )
@@ -30,6 +34,10 @@ def setUp(self):
         # Base Pipeline object on this directory
         self.pipeline_obj = nf_core.utils.Pipeline(self.test_pipeline_dir)
 
+    def tearDown(self):
+        if os.path.exists(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
+
     def test_check_if_outdated_1(self):
         current_version = "1.0"
         remote_version = "2.0"
@@ -89,10 +97,10 @@ def test_list_files_git(self):
         self.pipeline_obj._list_files()
         assert os.path.join(self.test_pipeline_dir, "main.nf") in self.pipeline_obj.files
 
-    def test_list_files_no_git(self):
+    @with_temporary_folder
+    def test_list_files_no_git(self, tmpdir):
         """Test listing pipeline files without `git-ls`"""
-        # Create directory with a test file
-        tmpdir = tempfile.mkdtemp()
+        # Create a test file in a temporary directory
         tmp_fn = os.path.join(tmpdir, "testfile")
         open(tmp_fn, "a").close()
         pipeline_obj = nf_core.utils.Pipeline(tmpdir)

From 4067720c3746148343ba99f869e20f8658a4cec2 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Tue, 9 Nov 2021 19:27:18 +0100
Subject: [PATCH 123/266] update changelog and run black

---
 CHANGELOG.md           | 1 +
 tests/test_download.py | 2 --
 tests/utils.py         | 1 +
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 727077c569..d913559b80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Erase temporary files and folders while performing tests
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
diff --git a/tests/test_download.py b/tests/test_download.py
index f67a0b4f98..2dcc7b8cc5 100644
--- a/tests/test_download.py
+++ b/tests/test_download.py
@@ -161,7 +161,6 @@ def test_mismatching_md5sums(self, tmpfile):
 
         download_obj.validate_md5(tmpfile.name, val_hash)
 
-
     #
     # Tests for 'singularity_pull_image'
     #
@@ -174,7 +173,6 @@ def test_singularity_pull_image(self, tmp_dir, mock_rich_progress):
         download_obj = DownloadWorkflow(pipeline="dummy", outdir=tmp_dir)
         download_obj.singularity_pull_image("a-container", tmp_dir, None, mock_rich_progress)
 
-
     #
     # Tests for the main entry method 'download_workflow'
     #
diff --git a/tests/utils.py b/tests/utils.py
index 8acd72ce7b..1f40525707 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -28,6 +28,7 @@ def with_temporary_file(func):
     Call the decorated funtion under the tempfile.NamedTemporaryFile
     context manager. Pass the opened file handle to the decorated function
     """
+
     @functools.wraps(func)
     def wrapper(*args, **kwargs):
         with tempfile.NamedTemporaryFile() as tmpfile:

From c00f2632966b33692f54e920e46579fbdafd4aab Mon Sep 17 00:00:00 2001
From: Alexander Peltzer <apeltzer@users.noreply.github.com>
Date: Wed, 10 Nov 2021 21:39:48 +0100
Subject: [PATCH 124/266] Add Julia support

Adding in a line to enable support for Julia packages.
---
 nf_core/pipeline-template/nextflow.config | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 3f23f45b34..5d37a05223 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -131,6 +131,7 @@ 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

From f89c7a4e3e73a71bc6f5cd2e6ff26def59218c59 Mon Sep 17 00:00:00 2001
From: Alexander Peltzer <alex.peltzer@gmail.com>
Date: Wed, 10 Nov 2021 21:56:23 +0100
Subject: [PATCH 125/266] Add changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 727077c569..7de988738a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
 * Remove `params.hostnames` from the pipeline template ([#1304](https://github.com/nf-core/tools/issues/1304))
 * Update `.gitattributes` to mark installed modules and subworkflows as `linguist-generated` ([#1311](https://github.com/nf-core/tools/issues/1311))
+* Adding support for [Julia](https://julialang.org) package environments to `nextflow.config`([#1317](https://github.com/nf-core/tools/pull/1317))
 
 ### General
 

From b2f0afc46f7e9ebf8aff418e99f2dd6bfa175b6d Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Tue, 16 Nov 2021 13:54:06 -0700
Subject: [PATCH 126/266] check for nextflow.config

---
 nf_core/modules/create.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index b8fc6d30db..1e0378b20c 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -282,7 +282,7 @@ def get_repo_type(self, directory):
             raise UserWarning(f"Could not find directory: {directory}")
 
         # Determine repository type
-        if os.path.exists(os.path.join(directory, "main.nf")):
+        if os.path.exists(os.path.join(directory, "nextflow.config")):
             return "pipeline"
         elif os.path.exists(os.path.join(directory, "modules")):
             return "modules"

From 37984b601608675426eff6d6f2b778042e8d0ad0 Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Tue, 16 Nov 2021 14:09:06 -0700
Subject: [PATCH 127/266] check if readme is from modules

---
 nf_core/modules/create.py | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index 1e0378b20c..3b2b200abf 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -282,10 +282,12 @@ def get_repo_type(self, directory):
             raise UserWarning(f"Could not find directory: {directory}")
 
         # Determine repository type
-        if os.path.exists(os.path.join(directory, "nextflow.config")):
-            return "pipeline"
-        elif os.path.exists(os.path.join(directory, "modules")):
-            return "modules"
+        if os.path.exists("README.md"):
+            with open("README.md") as fh:
+                if fh.readline().rstrip().startswith("# ![nf-core/modules]"):
+                    return "modules"
+                else:
+                    return "pipeline"
         else:
             raise UserWarning(
                 f"This directory does not look like a clone of nf-core/modules or an nf-core pipeline: '{directory}'"

From 19b9a69e913f95982174edab82abc2a9e97d2cd6 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 16 Nov 2021 23:16:06 +0100
Subject: [PATCH 128/266] Apply suggestions from code review

---
 nf_core/lint/schema_params.py | 2 +-
 nf_core/schema.py             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/lint/schema_params.py b/nf_core/lint/schema_params.py
index 6ec34f6adb..79688491f0 100644
--- a/nf_core/lint/schema_params.py
+++ b/nf_core/lint/schema_params.py
@@ -45,6 +45,6 @@ def schema_params(self):
 
     if len(invalid_config_default_params) > 0:
         for param in invalid_config_default_params:
-            warned.append(f"Default value for param `{param}` invalid or missing in nextflow config")
+            failed.append(f"Default value for param `{param}` invalid or missing in nextflow config")
 
     return {"passed": passed, "warned": warned, "failed": failed}
diff --git a/nf_core/schema.py b/nf_core/schema.py
index a76006f2b4..4e1235e485 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -226,7 +226,7 @@ def validate_default_params(self):
             log.error("[red][✗] Pipeline schema not found")
         except jsonschema.exceptions.ValidationError as e:
             raise AssertionError("Default parameters are invalid: {}".format(e.message))
-        log.info("[green][✓] Default parameters look valid")
+        log.info("[green][✓] Default parameters match schema validation")
 
         # Make sure every default parameter exists in the nextflow.config and is of correct type
         if self.pipeline_params == {}:

From 27b6453cdb71755440474772eb5ab180572b29fa Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Tue, 16 Nov 2021 15:25:20 -0700
Subject: [PATCH 129/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 727077c569..e5561b2117 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 * Fixed typo in `module_utils.py`.
 * Added `--diff` flag to `nf-core modules update` which shows the diff between the installed files and the versions
 * Update `nf-core modules create` help texts which were not changed with the introduction of the `--dir` flag
+* Check if README is from modules repo
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 

From 60384ad9f765b584ab7860cf7e3ce61e924a32cb Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 16 Nov 2021 23:56:56 +0100
Subject: [PATCH 130/266] Improvements to stronger lint checks for param
 defaults.

- Make new code work with `nf-core schema lint` too
- Log type of problem with the default, as it can be several different things
- Remove code that stripped schema param if config was set to false / empty string etc
---
 nf_core/lint/schema_params.py |  4 +--
 nf_core/schema.py             | 53 ++++++++++++++++++++++-------------
 2 files changed, 36 insertions(+), 21 deletions(-)

diff --git a/nf_core/lint/schema_params.py b/nf_core/lint/schema_params.py
index 79688491f0..436e8caf54 100644
--- a/nf_core/lint/schema_params.py
+++ b/nf_core/lint/schema_params.py
@@ -44,7 +44,7 @@ def schema_params(self):
         passed.append("Schema matched params returned from nextflow config")
 
     if len(invalid_config_default_params) > 0:
-        for param in invalid_config_default_params:
-            failed.append(f"Default value for param `{param}` invalid or missing in nextflow config")
+        for param, msg in invalid_config_default_params.items():
+            failed.append(f"Default value for param `{param}` invalid: {msg}")
 
     return {"passed": passed, "warned": warned, "failed": failed}
diff --git a/nf_core/schema.py b/nf_core/schema.py
index 4e1235e485..7128a03bc8 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -38,7 +38,7 @@ def __init__(self):
         self.schema_params = []
         self.input_params = {}
         self.pipeline_params = {}
-        self.invalid_nextflow_config_default_parameters = []
+        self.invalid_nextflow_config_default_parameters = {}
         self.pipeline_manifest = {}
         self.schema_from_scratch = False
         self.no_prompts = False
@@ -83,7 +83,19 @@ def load_lint_schema(self):
             num_params = self.validate_schema()
             self.get_schema_defaults()
             self.validate_default_params()
-            log.info("[green][✓] Pipeline schema looks valid[/] [dim](found {} params)".format(num_params))
+            if len(self.invalid_nextflow_config_default_parameters) > 0:
+                log.info(
+                    "[red][✗] Invalid default parameters found:\n  --{}\n\nNOTE: Use null in config for no default.".format(
+                        "\n  --".join(
+                            [
+                                f"{param}: {msg}"
+                                for param, msg in self.invalid_nextflow_config_default_parameters.items()
+                            ]
+                        )
+                    )
+                )
+            else:
+                log.info("[green][✓] Pipeline schema looks valid[/] [dim](found {} params)".format(num_params))
         except json.decoder.JSONDecodeError as e:
             error_msg = "[bold red]Could not parse schema JSON:[/] {}".format(e)
             log.error(error_msg)
@@ -249,7 +261,7 @@ def validate_default_params(self):
                         param, group_properties[param]["type"], self.pipeline_params[param]
                     )
                 else:
-                    self.invalid_nextflow_config_default_parameters.append(param)
+                    self.invalid_nextflow_config_default_parameters[param] = "Not in pipeline parameters"
 
         # Go over ungrouped params if any exist
         ungrouped_properties = self.schema.get("properties")
@@ -262,7 +274,7 @@ def validate_default_params(self):
                         param, ungrouped_properties[param]["type"], self.pipeline_params[param]
                     )
                 else:
-                    self.invalid_nextflow_config_default_parameters.append(param)
+                    self.invalid_nextflow_config_default_parameters[param] = "Not in pipeline parameters"
 
     def validate_config_default_parameter(self, param, schema_default_type, config_default):
         """
@@ -274,21 +286,29 @@ def validate_config_default_parameter(self, param, schema_default_type, config_d
             return
         # else check for allowed defaults
         if schema_default_type == "string":
-            if config_default in ["false", "true", "''"]:
-                self.invalid_nextflow_config_default_parameters.append(param)
+            if str(config_default) in ["false", "true", "''"]:
+                self.invalid_nextflow_config_default_parameters[
+                    param
+                ] = f"String should not be set to `{config_default}`"
         if schema_default_type == "boolean":
-            if not config_default in ["false", "true"]:
-                self.invalid_nextflow_config_default_parameters.append(param)
+            if not str(config_default) in ["false", "true"]:
+                self.invalid_nextflow_config_default_parameters[
+                    param
+                ] = f"Booleans should only be true or false, not `{config_default}`"
         if schema_default_type == "integer":
             try:
                 int(config_default)
             except ValueError:
-                self.invalid_nextflow_config_default_parameters.append(param)
+                self.invalid_nextflow_config_default_parameters[
+                    param
+                ] = f"Does not look like an integer: `{config_default}`"
         if schema_default_type == "number":
             try:
                 float(config_default)
             except ValueError:
-                self.invalid_nextflow_config_default_parameters.append(param)
+                self.invalid_nextflow_config_default_parameters[
+                    param
+                ] = f"Does not look like a number (float): `{config_default}`"
 
     def validate_schema(self, schema=None):
         """
@@ -577,7 +597,7 @@ def add_schema_found_configs(self):
                 ):
                     if "properties" not in self.schema:
                         self.schema["properties"] = {}
-                    self.schema["properties"][p_key] = self.build_schema_param(p_val)
+                    self.schema["properties"][p_key] = self.load_lint_schema(p_val)
                     log.debug("Adding '{}' to pipeline schema".format(p_key))
                     params_added.append(p_key)
 
@@ -603,18 +623,13 @@ def build_schema_param(self, p_val):
         if p_val == "null":
             p_val = None
 
-        # NB: Only test "True" for booleans, as it is very common to initialise
-        # an empty param as false when really we expect a string at a later date..
-        if p_val == "True":
-            p_val = True
+        # Booleans
+        if p_val == "True" or p_val == "False":
+            p_val = p_val == "True"  # Convert to bool
             p_type = "boolean"
 
         p_schema = {"type": p_type, "default": p_val}
 
-        # Assume that false and empty strings shouldn't be a default
-        if p_val == "false" or p_val == "" or p_val is None:
-            del p_schema["default"]
-
         return p_schema
 
     def launch_web_builder(self):

From 102cc8b498aeb98ce07fa318f61d4e9c6dd70267 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:04:38 +0100
Subject: [PATCH 131/266] Template schema lib - show default if it's not null,
 even if falsey

---
 nf_core/pipeline-template/lib/NfcoreSchema.groovy | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
index 8d6920dd64..a168c3f16a 100755
--- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
@@ -202,7 +202,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

From a1836fe8b596de38907557fb0567002c635e5f10 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:08:26 +0100
Subject: [PATCH 132/266] Revert accidental copy paste replacement.

Thanks pytests\!
---
 nf_core/schema.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/schema.py b/nf_core/schema.py
index 7128a03bc8..4b7c35293b 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -597,7 +597,7 @@ def add_schema_found_configs(self):
                 ):
                     if "properties" not in self.schema:
                         self.schema["properties"] = {}
-                    self.schema["properties"][p_key] = self.load_lint_schema(p_val)
+                    self.schema["properties"][p_key] = self.build_schema_param(p_val)
                     log.debug("Adding '{}' to pipeline schema".format(p_key))
                     params_added.append(p_key)
 

From 738fb12c2c1b7d3e021aafefc2b4c7265754fbfd Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:14:06 +0100
Subject: [PATCH 133/266] Move changelog to correct section

---
 CHANGELOG.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 64eaf064ba..9f6edeb1a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,10 @@
 
 ### General
 
+* Made lint check for parameters defaults stricter [[#992](https://github.com/nf-core/tools/issues/992)]
+    * Defaults must now match the variable type specified in the schema
+    * If you want the parameter to not have a default value, use `null`
+    * Strings set to `false` or an empty string in `nextflow.config` will now fail linting
 * Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
 * Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
 * Update regex in `readme` test of `nf-core lint` to agree with the pipeline template ([#1260](https://github.com/nf-core/tools/issues/1260))
@@ -83,7 +87,6 @@ This marks the first Nextflow DSL2-centric release of `tools` which means that s
 * Regular release sync fix - this time it was to do with JSON serialisation [[#1072](https://github.com/nf-core/tools/pull/1072)]
 * Fixed bug in schema validation that ignores upper/lower-case typos in parameters [[#1087](https://github.com/nf-core/tools/issues/1087)]
 * Bugfix: Download should use path relative to workflow for configs
-* Added lint check for valid default parameters in `nextflow.config` [[#992](https://github.com/nf-core/tools/issues/992)]
 * Remove lint checks for files related to conda and docker as not needed anymore for DSL2
 * Removed `params_used` lint check because of incompatibility with DSL2
 * Added`modules bump-versions` command to `README.md`

From 373fe2904dbdb15d5491016b308259d77fbb656c Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:26:35 +0100
Subject: [PATCH 134/266] Update lint test files_exist for new filenames

---
 nf_core/lint/files_exist.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index 41365b1094..f2195a28ab 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -23,9 +23,9 @@ def files_exist(self):
         .markdownlint.yml
         .github/.dockstore.yml
         .github/CONTRIBUTING.md
-        .github/ISSUE_TEMPLATE/bug_report.md
+        .github/ISSUE_TEMPLATE/bug_report.yml
         .github/ISSUE_TEMPLATE/config.yml
-        .github/ISSUE_TEMPLATE/feature_request.md
+        .github/ISSUE_TEMPLATE/feature_request.yml
         .github/PULL_REQUEST_TEMPLATE.md
         .github/workflows/branch.yml
         .github/workflows/ci.yml
@@ -76,6 +76,7 @@ def files_exist(self):
         bin/markdown_to_html.r
         conf/aws.config
         .github/workflows/push_dockerhub.yml
+        .github/ISSUE_TEMPLATE/bug_report.md
 
     Files that *should not* be present:
 
@@ -107,9 +108,9 @@ def files_exist(self):
         ["README.md"],
         [os.path.join(".github", ".dockstore.yml")],
         [os.path.join(".github", "CONTRIBUTING.md")],
-        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md")],
+        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.yml")],
         [os.path.join(".github", "ISSUE_TEMPLATE", "config.yml")],
-        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md")],
+        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.yml")],
         [os.path.join(".github", "PULL_REQUEST_TEMPLATE.md")],
         [os.path.join(".github", "workflows", "branch.yml")],
         [os.path.join(".github", "workflows", "ci.yml")],
@@ -152,6 +153,8 @@ def files_exist(self):
         os.path.join("bin", "markdown_to_html.r"),
         os.path.join("conf", "aws.config"),
         os.path.join(".github", "workflows", "push_dockerhub.yml"),
+        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md")],
+        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md")],
     ]
     files_warn_ifexists = [".travis.yml"]
 

From 70e0cc7dd1c68328f2a857ab734731d0db7a227d Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:28:41 +0100
Subject: [PATCH 135/266] Fix copy paste error

---
 nf_core/lint/files_exist.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index f2195a28ab..cb7d5f978c 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -153,8 +153,8 @@ def files_exist(self):
         os.path.join("bin", "markdown_to_html.r"),
         os.path.join("conf", "aws.config"),
         os.path.join(".github", "workflows", "push_dockerhub.yml"),
-        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md")],
-        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md")],
+        os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md"),
+        os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md"),
     ]
     files_warn_ifexists = [".travis.yml"]
 

From 690ee2f67991fb4d24520fbc1ca44e98725609a5 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:29:46 +0100
Subject: [PATCH 136/266] Rerun black

---
 tests/test_schema.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_schema.py b/tests/test_schema.py
index 6eb3dbc08e..a15bb07be5 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -127,12 +127,12 @@ def test_validate_params_pass(self):
         self.schema_obj.load_schema()
         self.schema_obj.input_params = {"input": "fubar.csv"}
         assert self.schema_obj.validate_params()
-        
+
     def test_validate_params_pass_ext(self):
         """Try validating an extended set of parameters against a schema"""
         self.schema_obj.schema = {
             "properties": {"foo": {"type": "string"}, "bar": {"type": "string", "default": ""}},
-            "required": ["foo"]
+            "required": ["foo"],
         }
         assert self.schema_obj.validate_params()
 

From 060a38c2e47f263cf388f4bb9dd365c448625743 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:31:08 +0100
Subject: [PATCH 137/266] Update lint test: files_unchanged

---
 nf_core/lint/files_unchanged.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/nf_core/lint/files_unchanged.py b/nf_core/lint/files_unchanged.py
index 262e8c4449..39842ae32c 100644
--- a/nf_core/lint/files_unchanged.py
+++ b/nf_core/lint/files_unchanged.py
@@ -22,9 +22,9 @@ def files_unchanged(self):
         .markdownlint.yml
         .github/.dockstore.yml
         .github/CONTRIBUTING.md
-        .github/ISSUE_TEMPLATE/bug_report.md
+        .github/ISSUE_TEMPLATE/bug_report.yml
         .github/ISSUE_TEMPLATE/config.yml
-        .github/ISSUE_TEMPLATE/feature_request.md
+        .github/ISSUE_TEMPLATE/feature_request.yml
         .github/PULL_REQUEST_TEMPLATE.md
         .github/workflows/branch.yml
         .github/workflows/linting_comment.yml
@@ -81,9 +81,9 @@ def files_unchanged(self):
         ["LICENSE", "LICENSE.md", "LICENCE", "LICENCE.md"],  # NB: British / American spelling
         [os.path.join(".github", ".dockstore.yml")],
         [os.path.join(".github", "CONTRIBUTING.md")],
-        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md")],
+        [os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.yml")],
         [os.path.join(".github", "ISSUE_TEMPLATE", "config.yml")],
-        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md")],
+        [os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.yml")],
         [os.path.join(".github", "PULL_REQUEST_TEMPLATE.md")],
         [os.path.join(".github", "workflows", "branch.yml")],
         [os.path.join(".github", "workflows", "linting_comment.yml")],

From 0774214acf0dfa38c6b93c99eb6d7f8377699300 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:35:48 +0100
Subject: [PATCH 138/266] Remove new test

---
 tests/test_schema.py | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/tests/test_schema.py b/tests/test_schema.py
index a15bb07be5..175b23880c 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -128,14 +128,6 @@ def test_validate_params_pass(self):
         self.schema_obj.input_params = {"input": "fubar.csv"}
         assert self.schema_obj.validate_params()
 
-    def test_validate_params_pass_ext(self):
-        """Try validating an extended set of parameters against a schema"""
-        self.schema_obj.schema = {
-            "properties": {"foo": {"type": "string"}, "bar": {"type": "string", "default": ""}},
-            "required": ["foo"],
-        }
-        assert self.schema_obj.validate_params()
-
     def test_validate_params_fail(self):
         """Check that False is returned if params don't validate against a schema"""
         # Load the template schema

From 012acbd4debdbecfc40683c9ff432ee6320b53c1 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:37:44 +0100
Subject: [PATCH 139/266] Changelog

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 727077c569..a7eb79351c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 * Update `dumpsoftwareversion` module to correctly report versions with trailing zeros.
 * Remove `params.hostnames` from the pipeline template ([#1304](https://github.com/nf-core/tools/issues/1304))
 * Update `.gitattributes` to mark installed modules and subworkflows as `linguist-generated` ([#1311](https://github.com/nf-core/tools/issues/1311))
+* New YAML issue templates for pipeline bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 
 ### General
 
@@ -18,6 +19,7 @@
 * Update 'fix' message in `nf-core lint` to conform to the current command line options. ([#1259](https://github.com/nf-core/tools/issues/1259))
 * Fixed bug in `nf-core list` when `NXF_HOME` is set
 * Run CI test used to create and lint/run the pipeline template with minimum and latest edge release of NF ([#1304](https://github.com/nf-core/tools/issues/1304))
+* New YAML issue templates for tools bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 
 ### Modules
 

From a135b8e93d14b98be09dec1514e6e348a56df49f Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Wed, 17 Nov 2021 00:50:40 +0100
Subject: [PATCH 140/266] Print stdout as well as stderr on nextflow command
 errors.

Update changelog
---
 CHANGELOG.md     | 1 +
 nf_core/utils.py | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7eb79351c..ac8691d05a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 * Fixed bug in `nf-core list` when `NXF_HOME` is set
 * Run CI test used to create and lint/run the pipeline template with minimum and latest edge release of NF ([#1304](https://github.com/nf-core/tools/issues/1304))
 * New YAML issue templates for tools bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
+* Handle synax errors in Nextflow config nicely when running `nf-core schema build` ([#1267](https://github.com/nf-core/tools/pull/1267))
 
 ### Modules
 
diff --git a/nf_core/utils.py b/nf_core/utils.py
index 55751ae341..46320cd092 100644
--- a/nf_core/utils.py
+++ b/nf_core/utils.py
@@ -297,7 +297,7 @@ def nextflow_cmd(cmd):
             raise AssertionError("It looks like Nextflow is not installed. It is required for most nf-core functions.")
     except subprocess.CalledProcessError as e:
         raise AssertionError(
-            f"Command '{cmd}' returned non-zero error code '{e.returncode}':\n[red]> {e.stderr.decode()}"
+            f"Command '{cmd}' returned non-zero error code '{e.returncode}':\n[red]> {e.stderr.decode()}{e.stdout.decode()}"
         )
 
 

From 68e9915b2bd1bfc7de31e4f050c1253e082b6522 Mon Sep 17 00:00:00 2001
From: "Robert A. Petit III" <robbie.petit@gmail.com>
Date: Wed, 17 Nov 2021 09:07:49 -0700
Subject: [PATCH 141/266] fix get_repo_type

---
 nf_core/modules/module_utils.py | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 30d6e4cc08..6ef675bf95 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -344,10 +344,12 @@ def get_repo_type(dir):
         raise LookupError("Could not find directory: {}".format(dir))
 
     # Determine repository type
-    if os.path.exists(os.path.join(dir, "main.nf")):
-        return "pipeline"
-    elif os.path.exists(os.path.join(dir, "modules")):
-        return "modules"
+    if os.path.exists("README.md"):
+        with open("README.md") as fh:
+            if fh.readline().rstrip().startswith("# ![nf-core/modules]"):
+                return "modules"
+            else:
+                return "pipeline"
     else:
         raise LookupError("Could not determine repository type of '{}'".format(dir))
 

From ad5f91f8d5b585b8edeb1a8ddc2c7f75b1055457 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 18 Nov 2021 15:38:19 +0100
Subject: [PATCH 142/266] pipeline readme - docs tweak for config profile
 quickstart

Trying to make the quickstart section easier to read, as we keep having people running the pipelines with just `-profile test`.

* Avoid using `<docker>` etc as that is less clear that it's required
* Avoid very long string of different container technologies
* Minor tweaks of text
---
 nf_core/pipeline-template/README.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index c502f2ea50..dfb8baeb37 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -40,11 +40,14 @@ On release, automated continuous integration tests run the pipeline on a full-si
 3. Download the pipeline and test it on a minimal dataset with a single command:
 
     ```console
-    nextflow run {{ name }} -profile test,<docker/singularity/podman/shifter/charliecloud/conda/institute>
+    nextflow run {{ name }} -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!

From bf46dc55d5016b7c53d0a970490ae1c253853bcf Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 18 Nov 2021 23:38:06 +0100
Subject: [PATCH 143/266] Update AWS test for v2 syntax of the tower-action

---
 nf_core/pipeline-template/.github/workflows/awstest.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/awstest.yml b/nf_core/pipeline-template/.github/workflows/awstest.yml
index fff75db64c..3ef7d2b93d 100644
--- a/nf_core/pipeline-template/.github/workflows/awstest.yml
+++ b/nf_core/pipeline-template/.github/workflows/awstest.yml
@@ -11,11 +11,11 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Launch workflow via tower
-        uses: nf-core/tower-action@master
+        uses: nf-core/tower-action@v2
         {% raw %}
         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 }}
@@ -24,5 +24,5 @@ jobs:
             {
               "outdir": "s3://{% raw %}${{ secrets.AWS_S3_BUCKET }}{% endraw %}/{{ short_name }}/{% raw %}results-${{ github.sha }}{% endraw %}"
             }
-          profiles: '[ "test", "aws_tower" ]'
+          profiles: test,aws_tower
 

From ce9c73c01fb2e26d6307dfcda1b03062c86f0616 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 18 Nov 2021 23:39:41 +0100
Subject: [PATCH 144/266] Update AWS full test to use v2 tower action syntax

---
 nf_core/pipeline-template/.github/workflows/awsfulltest.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/awsfulltest.yml b/nf_core/pipeline-template/.github/workflows/awsfulltest.yml
index 9ec98b29f9..2b83987597 100644
--- a/nf_core/pipeline-template/.github/workflows/awsfulltest.yml
+++ b/nf_core/pipeline-template/.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
         {% raw %}
         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://{% raw %}${{ secrets.AWS_S3_BUCKET }}{% endraw %}/{{ short_name }}/{% raw %}results-${{ github.sha }}{% endraw %}"
             }
-          profiles: '[ "test_full", "aws_tower" ]'
+          profiles: test_full,aws_tower
 

From 698cb1fa99ba351acbbb932b751a492e2e7dc272 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 18 Nov 2021 23:40:34 +0100
Subject: [PATCH 145/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddd680bd76..fcd034f39c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 * Remove `params.hostnames` from the pipeline template ([#1304](https://github.com/nf-core/tools/issues/1304))
 * Update `.gitattributes` to mark installed modules and subworkflows as `linguist-generated` ([#1311](https://github.com/nf-core/tools/issues/1311))
 * New YAML issue templates for pipeline bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
+* Update AWS test GitHub Actions to use v2 of [nf-core/tower-action](https://github.com/nf-core/tower-action)
 
 ### General
 

From cdda843c902f494263545fd54ca4a92919afc43a Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 19 Nov 2021 20:07:31 +0100
Subject: [PATCH 146/266] Update nf_core/pipeline-template/README.md

---
 nf_core/pipeline-template/README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index dfb8baeb37..3a837f431a 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -42,7 +42,6 @@ On release, automated continuous integration tests run the pipeline on a full-si
     ```console
     nextflow run {{ name }} -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`.

From d928c0a745fe18896a6c60df4654fbd2dd2a4af0 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 19 Nov 2021 20:08:27 +0100
Subject: [PATCH 147/266] Apply suggestions from code review

---
 nf_core/pipeline-template/README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index 3a837f431a..6003672ebc 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -42,6 +42,7 @@ On release, automated continuous integration tests run the pipeline on a full-si
     ```console
     nextflow run {{ name }} -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`.

From d08a2afdcbaee9f4654fe6c5a1ac9a434011dbb0 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Mon, 22 Nov 2021 16:16:50 +0100
Subject: [PATCH 148/266] Update modules template to DSL2 v2.0 syntax

---
 CHANGELOG.md                                 |  1 +
 nf_core/module-template/modules/functions.nf | 78 --------------------
 nf_core/module-template/modules/main.nf      | 24 ++----
 nf_core/modules/create.py                    |  2 -
 4 files changed, 9 insertions(+), 96 deletions(-)
 delete mode 100644 nf_core/module-template/modules/functions.nf

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fcd034f39c..06410ba0f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 * Added `--diff` flag to `nf-core modules update` which shows the diff between the installed files and the versions
 * Update `nf-core modules create` help texts which were not changed with the introduction of the `--dir` flag
 * Check if README is from modules repo
+* Update module template to DSL2 v2.0 (remove `functions.nf` from modules template and updating `main.nf` ([#1289](https://github.com/nf-core/tools/pull/))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
diff --git a/nf_core/module-template/modules/functions.nf b/nf_core/module-template/modules/functions.nf
deleted file mode 100644
index 85628ee0eb..0000000000
--- a/nf_core/module-template/modules/functions.nf
+++ /dev/null
@@ -1,78 +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()
-}
-
-//
-// 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
-//
-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) {
-    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"
-            }
-        }
-    } else if (ioptions.publish_files == null) {
-        return "${getPathFromList(path_list)}/$args.filename"
-    }
-}
diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 8b4360e3de..ee28a4a117 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -1,14 +1,12 @@
-// Import generic module 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/modules
 //               You can also ask for help via your pull request or on the #modules channel on the nf-core Slack workspace:
 //               https://nf-co.re/join
 
 // TODO nf-core: A module file SHOULD only define input and output files as command-line parameters.
-//               All other parameters MUST be provided as a string i.e. "options.args"
-//               where "params.options" is a Groovy Map that MUST be provided via the addParams section of the including workflow.
+//               All other parameters MUST be provided using "process.ext" directive, see here:
+//               https://www.nextflow.io/docs/latest/process.html#ext
+//               where "process.ext" is a Groovy Map that MUST be provided via the modules.config file.
 //               Any parameters that need to be evaluated in the context of a particular sample
 //               e.g. single-end/paired-end data MUST also be defined and evaluated appropriately.
 // TODO nf-core: Software that can be piped together SHOULD be added to separate module files
@@ -17,17 +15,10 @@ include { initOptions; saveFiles; getSoftwareName; getProcessName } from './func
 //                 bwa mem | samtools view -B -T ref.fasta
 // TODO nf-core: Optional inputs are not currently supported by Nextflow. However, using an empty
 //               list (`[]`) instead of a file can be used to work around this issue.
-
-params.options = [:]
-options        = initOptions(params.options)
-
 process {{ tool_name_underscore|upper }} {
     tag {{ '"$meta.id"' if has_meta else "'$bam'" }}
     label '{{ process_label }}'
-    publishDir "${params.outdir}",
-        mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:{{ 'meta' if has_meta else "[:]" }}, publish_by_meta:{{ "['id']" if has_meta else "[]" }}) }
-
+    
     // TODO nf-core: List required Conda package(s).
     //               Software MUST be pinned to channel (i.e. "bioconda"), version (i.e. "1.10").
     //               For Conda, the build (i.e. "h9402c20_2") must be EXCLUDED to support installation on different operating systems.
@@ -56,13 +47,14 @@ process {{ tool_name_underscore|upper }} {
 
     script:
     {% if has_meta -%}
-    def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
+    def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
     {%- endif %}
+    def args   = task.ext.args ?: ''
     // 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/modules/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: It MUST be possible to pass additional parameters to the tool as a command-line string via the "process.ext.args" directive
     // 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"
     // TODO nf-core: Please replace the example samtools command below with your module's command
@@ -70,7 +62,7 @@ process {{ tool_name_underscore|upper }} {
     """
     samtools \\
         sort \\
-        $options.args \\
+        $args \\
         -@ $task.cpus \\
         {%- if has_meta %}
         -o ${prefix}.bam \\
diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index 3b2b200abf..9c0a0bf7ba 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -63,7 +63,6 @@ def create(self):
         modules/modules/tool/subtool/
             * main.nf
             * meta.yml
-            * functions.nf
         modules/tests/modules/tool/subtool/
             * main.nf
             * test.yml
@@ -355,7 +354,6 @@ def get_module_dirs(self):
                 )
 
             # Set file paths - can be tool/ or tool/subtool/ so can't do in template directory structure
-            file_paths[os.path.join("modules", "functions.nf")] = os.path.join(software_dir, "functions.nf")
             file_paths[os.path.join("modules", "main.nf")] = os.path.join(software_dir, "main.nf")
             file_paths[os.path.join("modules", "meta.yml")] = os.path.join(software_dir, "meta.yml")
             file_paths[os.path.join("tests", "main.nf")] = os.path.join(test_dir, "main.nf")

From 78b0d8a4fa35a12ac6f0de6b35b1b783941ad4e2 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Mon, 22 Nov 2021 17:20:08 +0100
Subject: [PATCH 149/266] Update lint for modules for DSL2 v2.0 (some code just
 commented, WIP)

---
 nf_core/modules/lint/__init__.py     |  6 +-
 nf_core/modules/lint/functions_nf.py | 98 ++++++++++++++--------------
 nf_core/modules/lint/main_nf.py      | 56 ++++++++--------
 nf_core/modules/module_utils.py      |  6 +-
 nf_core/modules/nfcore_module.py     |  1 -
 5 files changed, 82 insertions(+), 85 deletions(-)

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index f9de48a304..50a6a2a573 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -61,7 +61,6 @@ class ModuleLint(ModuleCommand):
 
     # Import lint functions
     from .main_nf import main_nf
-    from .functions_nf import functions_nf
     from .meta_yml import meta_yml
     from .module_changes import module_changes
     from .module_tests import module_tests
@@ -96,7 +95,7 @@ def __init__(self, dir):
 
     @staticmethod
     def _get_all_lint_tests():
-        return ["main_nf", "functions_nf", "meta_yml", "module_changes", "module_todos"]
+        return ["main_nf", "meta_yml", "module_changes", "module_todos"]
 
     def lint(self, module=None, key=(), all_modules=False, print_results=True, show_passed=False, local=False):
         """
@@ -231,7 +230,7 @@ def get_installed_modules(self):
             # Filter local modules
             if os.path.exists(local_modules_dir):
                 local_modules = os.listdir(local_modules_dir)
-                local_modules = sorted([x for x in local_modules if (x.endswith(".nf") and not x == "functions.nf")])
+                local_modules = sorted([x for x in local_modules if x.endswith(".nf")])
 
         # nf-core/modules
         if self.repo_type == "modules":
@@ -309,7 +308,6 @@ def lint_module(self, mod, local=False):
         If the module is a nf-core module we check for existence of the files
         - main.nf
         - meta.yml
-        - functions.nf
         And verify that their content conform to the nf-core standards.
 
         If the linting is run for modules in the central nf-core/modules repo
diff --git a/nf_core/modules/lint/functions_nf.py b/nf_core/modules/lint/functions_nf.py
index aef0d115ea..648b1c3173 100644
--- a/nf_core/modules/lint/functions_nf.py
+++ b/nf_core/modules/lint/functions_nf.py
@@ -1,56 +1,56 @@
-#!/usr/bin/env python
-import logging
-import os
-import nf_core
+# #!/usr/bin/env python
+# import logging
+# import os
+# import nf_core
 
-log = logging.getLogger(__name__)
+# log = logging.getLogger(__name__)
 
 
-def functions_nf(module_lint_object, module):
-    """
-    Lint a functions.nf file
-    Verifies that the file exists and contains all necessary functions
-    """
-    local_copy = None
-    template_copy = None
-    try:
-        with open(module.function_nf, "r") as fh:
-            lines = fh.readlines()
-        module.passed.append(("functions_nf_exists", "'functions.nf' exists", module.function_nf))
-    except FileNotFoundError as e:
-        module.failed.append(("functions_nf_exists", "'functions.nf' does not exist", module.function_nf))
-        return
+# def functions_nf(module_lint_object, module):
+#     """
+#     Lint a functions.nf file
+#     Verifies that the file exists and contains all necessary functions
+#     """
+#     local_copy = None
+#     template_copy = None
+#     try:
+#         with open(module.function_nf, "r") as fh:
+#             lines = fh.readlines()
+#         module.passed.append(("functions_nf_exists", "'functions.nf' exists", module.function_nf))
+#     except FileNotFoundError as e:
+#         module.failed.append(("functions_nf_exists", "'functions.nf' does not exist", module.function_nf))
+#         return
 
-    # Test whether all required functions are present
-    required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
-    lines = "\n".join(lines)
-    contains_all_functions = True
-    for f in required_functions:
-        if not "def " + f in lines:
-            module.failed.append(("functions_nf_func_exist", "Function is missing: `{f}`", module.function_nf))
-            contains_all_functions = False
-    if contains_all_functions:
-        module.passed.append(("functions_nf_func_exist", "All functions present", module.function_nf))
+#     # Test whether all required functions are present
+#     required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
+#     lines = "\n".join(lines)
+#     contains_all_functions = True
+#     for f in required_functions:
+#         if not "def " + f in lines:
+#             module.failed.append(("functions_nf_func_exist", "Function is missing: `{f}`", module.function_nf))
+#             contains_all_functions = False
+#     if contains_all_functions:
+#         module.passed.append(("functions_nf_func_exist", "All functions present", module.function_nf))
 
-    # Compare functions.nf file to the most recent template
-    # Get file content of the module functions.nf
-    try:
-        local_copy = open(module.function_nf, "r").read()
-    except FileNotFoundError as e:
-        log.error(f"Could not open {module.function_nf}")
+#     # Compare functions.nf file to the most recent template
+#     # Get file content of the module functions.nf
+#     try:
+#         local_copy = open(module.function_nf, "r").read()
+#     except FileNotFoundError as e:
+#         log.error(f"Could not open {module.function_nf}")
 
-    # Get the template file
-    template_copy_path = os.path.join(os.path.dirname(nf_core.__file__), "module-template/modules/functions.nf")
-    try:
-        template_copy = open(template_copy_path, "r").read()
-    except FileNotFoundError as e:
-        log.error(f"Could not open {template_copy_path}")
+#     # Get the template file
+#     template_copy_path = os.path.join(os.path.dirname(nf_core.__file__), "module-template/modules/functions.nf")
+#     try:
+#         template_copy = open(template_copy_path, "r").read()
+#     except FileNotFoundError as e:
+#         log.error(f"Could not open {template_copy_path}")
 
-    # Compare the files
-    if local_copy and template_copy:
-        if local_copy != template_copy:
-            module.failed.append(
-                ("function_nf_comparison", "New version of functions.nf available", module.function_nf)
-            )
-        else:
-            module.passed.append(("function_nf_comparison", "functions.nf is up to date", module.function_nf))
+#     # Compare the files
+#     if local_copy and template_copy:
+#         if local_copy != template_copy:
+#             module.failed.append(
+#                 ("function_nf_comparison", "New version of functions.nf available", module.function_nf)
+#             )
+#         else:
+#             module.passed.append(("function_nf_comparison", "functions.nf is up to date", module.function_nf))
diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 4b63295c20..8101472dfb 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -27,12 +27,12 @@ def main_nf(module_lint_object, module):
         return
 
     # Check that options are defined
-    initoptions_re = re.compile(r"\s*options\s*=\s*initOptions\s*\(\s*params\.options\s*\)\s*")
-    paramsoptions_re = re.compile(r"\s*params\.options\s*=\s*\[:\]\s*")
-    if any(initoptions_re.match(l) for l in lines) and any(paramsoptions_re.match(l) for l in lines):
-        module.passed.append(("main_nf_options", "'options' variable specified", module.main_nf))
-    else:
-        module.warned.append(("main_nf_options", "'options' variable not specified", module.main_nf))
+    # initoptions_re = re.compile(r"\s*options\s*=\s*initOptions\s*\(\s*params\.options\s*\)\s*")
+    # paramsoptions_re = re.compile(r"\s*params\.options\s*=\s*\[:\]\s*")
+    # if any(initoptions_re.match(l) for l in lines) and any(paramsoptions_re.match(l) for l in lines):
+    #     module.passed.append(("main_nf_options", "'options' variable specified", module.main_nf))
+    # else:
+    #     module.warned.append(("main_nf_options", "'options' variable not specified", module.main_nf))
 
     # Go through module main.nf file and switch state according to current section
     # Perform section-specific linting
@@ -81,28 +81,28 @@ def main_nf(module_lint_object, module):
             module.failed.append(("main_nf_meta_output", "'meta' map not emitted in output channel(s)", module.main_nf))
 
         # if meta is specified, it should also be used as "saveAs ... meta:meta, publish_by_meta:['id']"
-        save_as = [pl for pl in process_lines if "saveAs" in pl]
-        if len(save_as) > 0 and re.search("\s*meta\s*:\s*meta", save_as[0]):
-            module.passed.append(("main_nf_meta_saveas", "'meta:meta' specified in saveAs function", module.main_nf))
-        else:
-            module.failed.append(("main_nf_meta_saveas", "'meta:meta' unspecified in saveAs function", module.main_nf))
-
-        if len(save_as) > 0 and re.search("\s*publish_by_meta\s*:\s*\['id'\]", save_as[0]):
-            module.passed.append(
-                (
-                    "main_nf_publish_meta_saveas",
-                    "'publish_by_meta:['id']' specified in saveAs function",
-                    module.main_nf,
-                )
-            )
-        else:
-            module.failed.append(
-                (
-                    "main_nf_publish_meta_saveas",
-                    "'publish_by_meta:['id']' unspecified in saveAs function",
-                    module.main_nf,
-                )
-            )
+        # save_as = [pl for pl in process_lines if "saveAs" in pl]
+        # if len(save_as) > 0 and re.search("\s*meta\s*:\s*meta", save_as[0]):
+        #     module.passed.append(("main_nf_meta_saveas", "'meta:meta' specified in saveAs function", module.main_nf))
+        # else:
+        #     module.failed.append(("main_nf_meta_saveas", "'meta:meta' unspecified in saveAs function", module.main_nf))
+
+        # if len(save_as) > 0 and re.search("\s*publish_by_meta\s*:\s*\['id'\]", save_as[0]):
+        #     module.passed.append(
+        #         (
+        #             "main_nf_publish_meta_saveas",
+        #             "'publish_by_meta:['id']' specified in saveAs function",
+        #             module.main_nf,
+        #         )
+        #     )
+        # else:
+        #     module.failed.append(
+        #         (
+        #             "main_nf_publish_meta_saveas",
+        #             "'publish_by_meta:['id']' unspecified in saveAs function",
+        #             module.main_nf,
+        #         )
+        #     )
 
     # Check that a software version is emitted
     if "version" in outputs:
diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 6ef675bf95..e14afd795b 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -225,7 +225,7 @@ def iterate_commit_log_page(module_name, module_path, modules_repo, commit_shas)
         are identical to remote files
     """
 
-    files_to_check = ["main.nf", "functions.nf", "meta.yml"]
+    files_to_check = ["main.nf", "meta.yml"]
     local_file_contents = [None, None, None]
     for i, file in enumerate(files_to_check):
         try:
@@ -251,7 +251,7 @@ def local_module_equal_to_commit(local_files, module_name, modules_repo, commit_
         bool: Whether all local files are identical to remote version
     """
 
-    files_to_check = ["main.nf", "functions.nf", "meta.yml"]
+    files_to_check = ["main.nf", "meta.yml"]
     files_are_equal = [False, False, False]
     remote_copies = [None, None, None]
 
@@ -304,7 +304,7 @@ def get_installed_modules(dir, repo_type="modules"):
         # Filter local modules
         if os.path.exists(local_modules_dir):
             local_modules = os.listdir(local_modules_dir)
-            local_modules = sorted([x for x in local_modules if (x.endswith(".nf") and not x == "functions.nf")])
+            local_modules = sorted([x for x in local_modules if x.endswith(".nf")])
 
     # nf-core/modules
     if repo_type == "modules":
diff --git a/nf_core/modules/nfcore_module.py b/nf_core/modules/nfcore_module.py
index e6e490febd..f828142fda 100644
--- a/nf_core/modules/nfcore_module.py
+++ b/nf_core/modules/nfcore_module.py
@@ -26,7 +26,6 @@ def __init__(self, module_dir, repo_type, base_dir, nf_core_module=True):
             # Initialize the important files
             self.main_nf = os.path.join(self.module_dir, "main.nf")
             self.meta_yml = os.path.join(self.module_dir, "meta.yml")
-            self.function_nf = os.path.join(self.module_dir, "functions.nf")
             if self.repo_type == "pipeline":
                 self.module_name = module_dir.split("nf-core/modules" + os.sep)[1]
             else:

From dc361b09cb380fdc2e42dee5827507c580bec9e3 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Mon, 22 Nov 2021 17:30:25 +0100
Subject: [PATCH 150/266] Update "nf-core modules create" prompt

---
 README.md | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index c874796049..e440979bde 100644
--- a/README.md
+++ b/README.md
@@ -1103,7 +1103,7 @@ $ nf-core modules create
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     Press enter to use default values (shown in brackets) or type your own responses. ctrl+click underlined text to open links.
@@ -1118,12 +1118,11 @@ INFO     Where applicable all sample-specific information e.g. 'id', 'single_end
          Groovy Map called 'meta'. This information may not be required in some instances, for example indexing reference genome files.
 Will the module require a meta map of sample information? (yes/no) [y/n] (y): y
 INFO     Created / edited following files:
-           ./software/star/align/functions.nf
            ./software/star/align/main.nf
            ./software/star/align/meta.yml
            ./tests/software/star/align/main.nf
            ./tests/software/star/align/test.yml
-           ./tests/config/pytest_software.yml
+           ./tests/config/pytest_modules.yml
 ```
 
 ### Create a module test config file

From 500e7c4df13d9a9c67a1ec548bbf05bf4f2630d3 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Mon, 22 Nov 2021 22:18:36 +0100
Subject: [PATCH 151/266] Remove functions.nf from pipeline template local
 module (fix lint)

---
 .../modules/local/functions.nf                | 78 -------------------
 1 file changed, 78 deletions(-)
 delete mode 100644 nf_core/pipeline-template/modules/local/functions.nf

diff --git a/nf_core/pipeline-template/modules/local/functions.nf b/nf_core/pipeline-template/modules/local/functions.nf
deleted file mode 100644
index 85628ee0eb..0000000000
--- a/nf_core/pipeline-template/modules/local/functions.nf
+++ /dev/null
@@ -1,78 +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()
-}
-
-//
-// 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
-//
-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) {
-    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"
-            }
-        }
-    } else if (ioptions.publish_files == null) {
-        return "${getPathFromList(path_list)}/$args.filename"
-    }
-}

From f5cf6b71d291439917e93f1b74d05879922e2ff2 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Mon, 22 Nov 2021 22:40:34 +0100
Subject: [PATCH 152/266] Join dir to readme to determine repository type

---
 nf_core/modules/module_utils.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index 6ef675bf95..ce8a802746 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -344,8 +344,8 @@ def get_repo_type(dir):
         raise LookupError("Could not find directory: {}".format(dir))
 
     # Determine repository type
-    if os.path.exists("README.md"):
-        with open("README.md") as fh:
+    if os.path.exists(os.path.join(dir, "README.md")):
+        with open(os.path.join(dir, "README.md")) as fh:
             if fh.readline().rstrip().startswith("# ![nf-core/modules]"):
                 return "modules"
             else:

From ba6c35685128f0c08040ddc47ba1ef048a59cf7d Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 10:23:53 +0100
Subject: [PATCH 153/266] Remove functions_nf script

---
 nf_core/modules/lint/functions_nf.py | 56 ----------------------------
 1 file changed, 56 deletions(-)
 delete mode 100644 nf_core/modules/lint/functions_nf.py

diff --git a/nf_core/modules/lint/functions_nf.py b/nf_core/modules/lint/functions_nf.py
deleted file mode 100644
index 648b1c3173..0000000000
--- a/nf_core/modules/lint/functions_nf.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# #!/usr/bin/env python
-# import logging
-# import os
-# import nf_core
-
-# log = logging.getLogger(__name__)
-
-
-# def functions_nf(module_lint_object, module):
-#     """
-#     Lint a functions.nf file
-#     Verifies that the file exists and contains all necessary functions
-#     """
-#     local_copy = None
-#     template_copy = None
-#     try:
-#         with open(module.function_nf, "r") as fh:
-#             lines = fh.readlines()
-#         module.passed.append(("functions_nf_exists", "'functions.nf' exists", module.function_nf))
-#     except FileNotFoundError as e:
-#         module.failed.append(("functions_nf_exists", "'functions.nf' does not exist", module.function_nf))
-#         return
-
-#     # Test whether all required functions are present
-#     required_functions = ["getSoftwareName", "getProcessName", "initOptions", "getPathFromList", "saveFiles"]
-#     lines = "\n".join(lines)
-#     contains_all_functions = True
-#     for f in required_functions:
-#         if not "def " + f in lines:
-#             module.failed.append(("functions_nf_func_exist", "Function is missing: `{f}`", module.function_nf))
-#             contains_all_functions = False
-#     if contains_all_functions:
-#         module.passed.append(("functions_nf_func_exist", "All functions present", module.function_nf))
-
-#     # Compare functions.nf file to the most recent template
-#     # Get file content of the module functions.nf
-#     try:
-#         local_copy = open(module.function_nf, "r").read()
-#     except FileNotFoundError as e:
-#         log.error(f"Could not open {module.function_nf}")
-
-#     # Get the template file
-#     template_copy_path = os.path.join(os.path.dirname(nf_core.__file__), "module-template/modules/functions.nf")
-#     try:
-#         template_copy = open(template_copy_path, "r").read()
-#     except FileNotFoundError as e:
-#         log.error(f"Could not open {template_copy_path}")
-
-#     # Compare the files
-#     if local_copy and template_copy:
-#         if local_copy != template_copy:
-#             module.failed.append(
-#                 ("function_nf_comparison", "New version of functions.nf available", module.function_nf)
-#             )
-#         else:
-#             module.passed.append(("function_nf_comparison", "functions.nf is up to date", module.function_nf))

From 0e7915b7ab036c6e0984a850fa0ce59e6e627fe7 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 10:24:44 +0100
Subject: [PATCH 154/266] Remove checks for options defined in module main

---
 nf_core/modules/lint/main_nf.py | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 8101472dfb..227b080dba 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -26,14 +26,6 @@ def main_nf(module_lint_object, module):
         module.failed.append(("main_nf_exists", "Module file does not exist", module.main_nf))
         return
 
-    # Check that options are defined
-    # initoptions_re = re.compile(r"\s*options\s*=\s*initOptions\s*\(\s*params\.options\s*\)\s*")
-    # paramsoptions_re = re.compile(r"\s*params\.options\s*=\s*\[:\]\s*")
-    # if any(initoptions_re.match(l) for l in lines) and any(paramsoptions_re.match(l) for l in lines):
-    #     module.passed.append(("main_nf_options", "'options' variable specified", module.main_nf))
-    # else:
-    #     module.warned.append(("main_nf_options", "'options' variable not specified", module.main_nf))
-
     # Go through module main.nf file and switch state according to current section
     # Perform section-specific linting
     state = "module"

From 80ae5f04296bc3b0b753778f9412f2928a14c381 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 10:25:18 +0100
Subject: [PATCH 155/266] Remove functions.nf from module_changes checks

---
 nf_core/modules/lint/module_changes.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/lint/module_changes.py b/nf_core/modules/lint/module_changes.py
index 57f3b9a03f..773e5db408 100644
--- a/nf_core/modules/lint/module_changes.py
+++ b/nf_core/modules/lint/module_changes.py
@@ -11,12 +11,12 @@ def module_changes(module_lint_object, module):
     """
     Checks whether installed nf-core modules have changed compared to the
     original repository
-    Downloads the 'main.nf', 'functions.nf' and 'meta.yml' files for every module
+    Downloads the 'main.nf' and 'meta.yml' files for every module
     and compares them to the local copies
 
     If the module has a 'git_sha', the file content is checked against this sha
     """
-    files_to_check = ["main.nf", "functions.nf", "meta.yml"]
+    files_to_check = ["main.nf", "meta.yml"]
 
     # Loop over nf-core modules
     module_base_url = f"https://raw.githubusercontent.com/{module_lint_object.modules_repo.name}/{module_lint_object.modules_repo.branch}/modules/{module.module_name}/"

From b50670443a85e9bfa702d8eaead71a353726f0a4 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 11:19:47 +0100
Subject: [PATCH 156/266] Update pipeline template to DSL2 v2.0

---
 nf_core/pipeline-template/conf/modules.config | 58 +++++++++++--------
 .../modules/local/samplesheet_check.nf        | 10 +---
 .../pipeline-template/workflows/pipeline.nf   | 12 ++--
 3 files changed, 42 insertions(+), 38 deletions(-)

diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 0b1bfdec20..207c641461 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -1,32 +1,44 @@
 /*
 ========================================================================================
-    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.suffix          = File name suffix for output files.
 ----------------------------------------------------------------------------------------
 */
 
-params {
-    modules {
-        'fastqc' {
-            args = "--quiet"
-        }
-        'multiqc' {
-            args = ""
-        }
+process {
+
+    publishDir = [
+        path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" },
+        mode: params.publish_dir_mode,
+        saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
+    ]
+
+    withName: FASTQC {
+        ext.args   = '--quiet'
+    }
+    withName: CUSTOM_DUMPSOFTWAREVERSIONS {
+        publishDir = [
+            path: { "${params.outdir}/pipeline_info" },
+            mode: params.publish_dir_mode,
+            pattern: '*_versions.yml'
+        ]
+    }
+    withName: MULTIQC {
+        ext.args   = ''
+    }
+
+    // Local Subworkflows
+    // INPUT_CHECK
+    withName: '.*:INPUT_CHECK:SAMPLESHEET_CHECK' {
+        publishDir = [
+            path: { "${params.outdir}/pipeline_info" },
+            mode: params.publish_dir_mode,
+            saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
+        ]
     }
 }
diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index b8354f35e4..a8b5fb13c0 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -1,13 +1,5 @@
-// Import generic module functions
-include { saveFiles; getProcessName } 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) {
@@ -30,7 +22,7 @@ process SAMPLESHEET_CHECK {
         samplesheet.valid.csv
 
     cat <<-END_VERSIONS > versions.yml
-    ${getProcessName(task.process)}:
+    SAMPLESHEET_CHECK:
         python: \$(python --version | sed 's/Python //g')
     END_VERSIONS
     """
diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 68ca75e1de..bd7dacd2b5 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -33,7 +33,7 @@ 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()
+// def modules = params.modules.clone()
 
 //
 // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules
@@ -46,15 +46,15 @@ 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\""]) : ''
+// 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 { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main'  addParams( options: [publish_files : ['_versions.yml':'']] )
+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 { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main'  // addParams( options: [publish_files : ['_versions.yml':'']] )
 
 /*
 ========================================================================================

From 8bf5af620bbbf7d9b01f1b1fd63c0f9bb08df770 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 13:15:23 +0100
Subject: [PATCH 157/266] Manually update modules in pipeline template

---
 .../custom/dumpsoftwareversions/functions.nf  | 78 -------------------
 .../custom/dumpsoftwareversions/main.nf       | 25 +++---
 .../nf-core/modules/fastqc/functions.nf       | 78 -------------------
 .../modules/nf-core/modules/fastqc/main.nf    | 26 +++----
 .../nf-core/modules/multiqc/functions.nf      | 78 -------------------
 .../modules/nf-core/modules/multiqc/main.nf   | 17 ++--
 6 files changed, 29 insertions(+), 273 deletions(-)
 delete mode 100644 nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
 delete mode 100644 nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
 delete mode 100644 nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
deleted file mode 100644
index 85628ee0eb..0000000000
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf
+++ /dev/null
@@ -1,78 +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()
-}
-
-//
-// 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
-//
-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) {
-    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"
-            }
-        }
-    } else if (ioptions.publish_files == null) {
-        return "${getPathFromList(path_list)}/$args.filename"
-    }
-}
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index faf2073f75..3b7f7d8647 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -1,15 +1,6 @@
-// Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions'
-
-params.options = [:]
-options        = initOptions(params.options)
-
 process CUSTOM_DUMPSOFTWAREVERSIONS {
     label 'process_low'
-    publishDir "${params.outdir}",
-        mode: params.publish_dir_mode,
-        saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) }
-
+    
     // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container
     conda (params.enable_conda ? "bioconda::multiqc=1.11" : null)
     if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
@@ -104,3 +95,17 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
         yaml.dump(module_versions, f, default_flow_style=False)
     """
 }
+
+//
+// Extract name of software tool from process name using $task.process
+//
+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]
+}
\ No newline at end of file
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
deleted file mode 100644
index 85628ee0eb..0000000000
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/functions.nf
+++ /dev/null
@@ -1,78 +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()
-}
-
-//
-// 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
-//
-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) {
-    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"
-            }
-        }
-    } else if (ioptions.publish_files == null) {
-        return "${getPathFromList(path_list)}/$args.filename"
-    }
-}
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 9f6cfc5538..ae44581bf4 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,16 +1,7 @@
-// Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } 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"
@@ -28,26 +19,27 @@ process FASTQC {
 
     script:
     // Add soft-links to original FastQs for consistent naming in pipeline
-    def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}"
+    def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
+    def args   = task.ext.args ?: ''
     if (meta.single_end) {
         """
         [ ! -f  ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz
-        fastqc $options.args --threads $task.cpus ${prefix}.fastq.gz
+        fastqc $args --threads $task.cpus ${prefix}.fastq.gz
 
         cat <<-END_VERSIONS > versions.yml
-        ${getProcessName(task.process)}:
-            ${getSoftwareName(task.process)}: \$( fastqc --version | sed -e "s/FastQC v//g" )
+        FASTQC:
+            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 $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz
 
         cat <<-END_VERSIONS > versions.yml
-        ${getProcessName(task.process)}:
-            ${getSoftwareName(task.process)}: \$( fastqc --version | sed -e "s/FastQC v//g" )
+        FASTQC:
+            fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
deleted file mode 100644
index 85628ee0eb..0000000000
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/functions.nf
+++ /dev/null
@@ -1,78 +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()
-}
-
-//
-// 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
-//
-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) {
-    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"
-            }
-        }
-    } else if (ioptions.publish_files == null) {
-        return "${getPathFromList(path_list)}/$args.filename"
-    }
-}
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 0861aa5934..5033d0ca12 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,15 +1,7 @@
-// Import generic module functions
-include { initOptions; saveFiles; getSoftwareName; getProcessName } 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.11' : null)
     if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
         container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0"
@@ -27,12 +19,13 @@ process MULTIQC {
     path "versions.yml"        , emit: versions
 
     script:
+    def args   = task.ext.args ?: ''
     """
-    multiqc -f $options.args .
+    multiqc -f $args .
 
     cat <<-END_VERSIONS > versions.yml
-    ${getProcessName(task.process)}:
-        ${getSoftwareName(task.process)}: \$( multiqc --version | sed -e "s/multiqc, version //g" )
+    MULTIQC:
+        multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" )
     END_VERSIONS
     """
 }

From 3846ebe8b4090d5c7580360103220e96d14ff250 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 15:08:58 +0100
Subject: [PATCH 158/266] Use task.process within versions.yml creation

---
 nf_core/pipeline-template/modules/local/samplesheet_check.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 .../pipeline-template/modules/nf-core/modules/multiqc/main.nf   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index a8b5fb13c0..21dd208ade 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -22,7 +22,7 @@ process SAMPLESHEET_CHECK {
         samplesheet.valid.csv
 
     cat <<-END_VERSIONS > versions.yml
-    SAMPLESHEET_CHECK:
+    ${task.process}:
         python: \$(python --version | sed 's/Python //g')
     END_VERSIONS
     """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index ae44581bf4..ac96a8c2e6 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -38,7 +38,7 @@ process FASTQC {
         fastqc $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz
 
         cat <<-END_VERSIONS > versions.yml
-        FASTQC:
+        ${task.process}:
             fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 5033d0ca12..6f22aa0920 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -24,7 +24,7 @@ process MULTIQC {
     multiqc -f $args .
 
     cat <<-END_VERSIONS > versions.yml
-    MULTIQC:
+    ${task.process}:
         multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" )
     END_VERSIONS
     """

From 126f3da6a20995b13f76778070b13c4407b5bbf5 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 23:06:16 +0100
Subject: [PATCH 159/266] Update how process and software name are retrieve for
 versions.yml

---
 CHANGELOG.md                            | 1 +
 nf_core/module-template/modules/main.nf | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06410ba0f8..88cb2574ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@
 * Update `nf-core modules create` help texts which were not changed with the introduction of the `--dir` flag
 * Check if README is from modules repo
 * Update module template to DSL2 v2.0 (remove `functions.nf` from modules template and updating `main.nf` ([#1289](https://github.com/nf-core/tools/pull/))
+* Substitute get process/module name custom functions in module `main.nf` using template replacement ([#1284](https://github.com/nf-core/tools/issues/1284))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index ee28a4a117..530a2c6e90 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -71,8 +71,8 @@ process {{ tool_name_underscore|upper }} {
         $bam
 
     cat <<-END_VERSIONS > versions.yml
-    ${getProcessName(task.process)}:
-        ${getSoftwareName(task.process)}: \$( samtools --version 2>&1 | sed 's/^.*samtools //; s/Using.*\$//' )
+    ${task.process}:
+        {{ tool }}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' ))
     END_VERSIONS
     """
 }

From d2004637051a8378a6aad2da4cb6d5752d6aa59c Mon Sep 17 00:00:00 2001
From: Jose Espinosa-Carrasco <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 23:12:18 +0100
Subject: [PATCH 160/266] Update nf_core/module-template/modules/main.nf

Co-authored-by: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index ee28a4a117..c68d8c5273 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -6,7 +6,7 @@
 // TODO nf-core: A module file SHOULD only define input and output files as command-line parameters.
 //               All other parameters MUST be provided using "process.ext" directive, see here:
 //               https://www.nextflow.io/docs/latest/process.html#ext
-//               where "process.ext" is a Groovy Map that MUST be provided via the modules.config file.
+//               where "process.ext" is a Groovy map. 
 //               Any parameters that need to be evaluated in the context of a particular sample
 //               e.g. single-end/paired-end data MUST also be defined and evaluated appropriately.
 // TODO nf-core: Software that can be piped together SHOULD be added to separate module files

From 84d1a31bbbaf815a43ea11b17466978d8dbc6215 Mon Sep 17 00:00:00 2001
From: Jose Espinosa-Carrasco <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 23:12:58 +0100
Subject: [PATCH 161/266] Update nf_core/modules/create.py

Co-authored-by: Gregor Sturm <mail@gregor-sturm.de>
---
 nf_core/modules/create.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index 9c0a0bf7ba..c31c040c80 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -66,6 +66,7 @@ def create(self):
         modules/tests/modules/tool/subtool/
             * main.nf
             * test.yml
+            * nextflow.config
         tests/config/pytest_modules.yml
 
         The function will attempt to automatically find a Bioconda package called <tool>

From 070bbd79b3390ce21b8054cfe93355fc8412fc4e Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 23:16:32 +0100
Subject: [PATCH 162/266] Error in functions.nf found in local or nf-core
 modules

---
 nf_core/modules/lint/__init__.py | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index 50a6a2a573..a3ef862980 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -230,7 +230,14 @@ def get_installed_modules(self):
             # Filter local modules
             if os.path.exists(local_modules_dir):
                 local_modules = os.listdir(local_modules_dir)
-                local_modules = sorted([x for x in local_modules if x.endswith(".nf")])
+                for m in sorted([m for m in local_modules if m.endswith(".nf")]):
+                    # Deprecation error if functions.nf is found
+                    if m == "functions.nf":
+                        raise ModuleLintException(
+                            f"File '{m}' found in '{local_modules_dir}'has been deprecated since DSL2 v2.0!"
+                    )
+                else:
+                    local_modules.append(m)
 
         # nf-core/modules
         if self.repo_type == "modules":
@@ -248,6 +255,11 @@ def get_installed_modules(self):
                 if not "main.nf" in m_content:
                     for tool in m_content:
                         nfcore_modules.append(os.path.join(m, tool))
+                # Deprecation error if functions.nf is found
+                elif "functions.nf" in m_content:
+                    raise ModuleLintException(
+                        f"File 'functions.nf' found in '{m}' has been deprecated since DSL2 v2.0!"
+                    )
                 else:
                     nfcore_modules.append(m)
 

From de79a0e2f036360f3e0a3158d0eb6315f3e0dda0 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Tue, 23 Nov 2021 23:21:21 +0100
Subject: [PATCH 163/266] Add errors if any of the deprecated functions after
 DSL2 v2.0 syntax adopted is found

---
 nf_core/modules/lint/main_nf.py | 36 +++++++++++----------------------
 1 file changed, 12 insertions(+), 24 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 227b080dba..d05b7c2be8 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -26,6 +26,18 @@ def main_nf(module_lint_object, module):
         module.failed.append(("main_nf_exists", "Module file does not exist", module.main_nf))
         return
 
+    deprecated_f = ["initOptions", "saveFiles", "getSoftwareName", "getProcessName"] 
+    lines_j = "\n".join(lines)
+    for f in deprecated_f:
+        if f in lines_j:
+            module.failed.append(
+                (
+                    "deprecated_dsl2",
+                    f"Function `{f}` has been deprecated since DSL2 v2.0",
+                    module.main_nf,
+                )
+            )
+
     # Go through module main.nf file and switch state according to current section
     # Perform section-specific linting
     state = "module"
@@ -72,30 +84,6 @@ def main_nf(module_lint_object, module):
         else:
             module.failed.append(("main_nf_meta_output", "'meta' map not emitted in output channel(s)", module.main_nf))
 
-        # if meta is specified, it should also be used as "saveAs ... meta:meta, publish_by_meta:['id']"
-        # save_as = [pl for pl in process_lines if "saveAs" in pl]
-        # if len(save_as) > 0 and re.search("\s*meta\s*:\s*meta", save_as[0]):
-        #     module.passed.append(("main_nf_meta_saveas", "'meta:meta' specified in saveAs function", module.main_nf))
-        # else:
-        #     module.failed.append(("main_nf_meta_saveas", "'meta:meta' unspecified in saveAs function", module.main_nf))
-
-        # if len(save_as) > 0 and re.search("\s*publish_by_meta\s*:\s*\['id'\]", save_as[0]):
-        #     module.passed.append(
-        #         (
-        #             "main_nf_publish_meta_saveas",
-        #             "'publish_by_meta:['id']' specified in saveAs function",
-        #             module.main_nf,
-        #         )
-        #     )
-        # else:
-        #     module.failed.append(
-        #         (
-        #             "main_nf_publish_meta_saveas",
-        #             "'publish_by_meta:['id']' unspecified in saveAs function",
-        #             module.main_nf,
-        #         )
-        #     )
-
     # Check that a software version is emitted
     if "version" in outputs:
         module.passed.append(("main_nf_version_emitted", "Module emits software version", module.main_nf))

From 027a53496b4e39b8122bd6604e9c283a34990645 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 09:54:52 +0100
Subject: [PATCH 164/266] Add nextflow.config when a module is created

---
 nf_core/module-template/tests/nextflow.config | 7 +++++++
 nf_core/module-template/tests/test.yml        | 2 +-
 nf_core/modules/create.py                     | 1 +
 3 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 nf_core/module-template/tests/nextflow.config

diff --git a/nf_core/module-template/tests/nextflow.config b/nf_core/module-template/tests/nextflow.config
new file mode 100644
index 0000000000..495b860cc0
--- /dev/null
+++ b/nf_core/module-template/tests/nextflow.config
@@ -0,0 +1,7 @@
+process {
+    publishDir = [
+        path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" },
+        mode: params.publish_dir_mode,
+        saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
+    ]
+}
\ No newline at end of file
diff --git a/nf_core/module-template/tests/test.yml b/nf_core/module-template/tests/test.yml
index 337444cb7b..7363cd231e 100644
--- a/nf_core/module-template/tests/test.yml
+++ b/nf_core/module-template/tests/test.yml
@@ -1,7 +1,7 @@
 ## TODO nf-core: Please run the following command to build this file:
 #                nf-core modules create-test-yml {{ tool }}{%- if subtool %}/{{ subtool }}{%- endif %}
 - name: {{ tool }}{{ ' '+subtool if subtool else '' }}
-  command: nextflow run ./tests/modules/{{ tool_dir }} -entry test_{{ tool_name_underscore }} -c tests/config/nextflow.config
+  command: nextflow run ./tests/modules/{{ tool_dir }} -entry test_{{ tool_name_underscore }} -c tests/config/nextflow.config -c tests/modules/{{ tool_dir }}/nextflow.config
   tags:
     - {{ tool }}
     {%- if subtool %}
diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index c31c040c80..819f00eee9 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -359,5 +359,6 @@ def get_module_dirs(self):
             file_paths[os.path.join("modules", "meta.yml")] = os.path.join(software_dir, "meta.yml")
             file_paths[os.path.join("tests", "main.nf")] = os.path.join(test_dir, "main.nf")
             file_paths[os.path.join("tests", "test.yml")] = os.path.join(test_dir, "test.yml")
+            file_paths[os.path.join("tests", "nextflow.config")] = os.path.join(test_dir, "nextflow.config")
 
         return file_paths

From 00371d1d2580ace3132df596bce066243efb4324 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 09:55:42 +0100
Subject: [PATCH 165/266] Remove module parameters from pipeline template

---
 nf_core/pipeline-template/workflows/pipeline.nf | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index bd7dacd2b5..9649e27ec8 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -32,9 +32,6 @@ 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()
-
 //
 // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules
 //
@@ -46,15 +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 { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main'  // addParams( options: [publish_files : ['_versions.yml':'']] )
+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'
 
 /*
 ========================================================================================

From 5bb4e83a1f08f0071c0a3780d51320ecee7e6129 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 10:46:05 +0100
Subject: [PATCH 166/266] Update container directive with new syntax

---
 nf_core/module-template/modules/main.nf | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 6434ab43ac..f25fd62c5d 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -24,11 +24,10 @@ process {{ tool_name_underscore|upper }} {
     //               For Conda, the build (i.e. "h9402c20_2") must be EXCLUDED to support installation on different operating systems.
     // TODO nf-core: See section in main README for further information regarding finding and adding container addresses to the section below.
     conda (params.enable_conda ? "{{ bioconda if bioconda else 'YOUR-TOOL-HERE' }}" : null)
-    if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
-        container "{{ singularity_container if singularity_container else 'https://depot.galaxyproject.org/singularity/YOUR-TOOL-HERE' }}"
-    } else {
-        container "{{ docker_container if docker_container else 'quay.io/biocontainers/YOUR-TOOL-HERE' }}"
-    }
+    container workflow.containerEngine == 'singularity' &&
+        !task.ext.singularity_pull_docker_container ?
+        '{{ singularity_container if singularity_container else 'https://depot.galaxyproject.org/singularity/YOUR-TOOL-HERE' }}':
+        '{{ docker_container if docker_container else 'quay.io/biocontainers/YOUR-TOOL-HERE' }}'
 
     input:
     // TODO nf-core: Where applicable all sample-specific information e.g. "id", "single_end", "read_group"

From 4c874ebf72300747a80bb7103069042b3a5dc944 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 10:55:49 +0100
Subject: [PATCH 167/266] Adding publishDir to deprecated items in modules lint

---
 nf_core/modules/lint/main_nf.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index d05b7c2be8..204ab78518 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -26,14 +26,14 @@ def main_nf(module_lint_object, module):
         module.failed.append(("main_nf_exists", "Module file does not exist", module.main_nf))
         return
 
-    deprecated_f = ["initOptions", "saveFiles", "getSoftwareName", "getProcessName"] 
+    deprecated_i = ["initOptions", "saveFiles", "getSoftwareName", "getProcessName", "publishDir"] 
     lines_j = "\n".join(lines)
-    for f in deprecated_f:
-        if f in lines_j:
+    for i in deprecated_i:
+        if i in lines_j:
             module.failed.append(
                 (
                     "deprecated_dsl2",
-                    f"Function `{f}` has been deprecated since DSL2 v2.0",
+                    f"`{i}` has been deprecated since DSL2 v2.0",
                     module.main_nf,
                 )
             )

From dbeee2b39b91cc1a1d4fd5d32c6131638ec02997 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 17:03:15 +0100
Subject: [PATCH 168/266] Update nextflow.config in module test template

---
 nf_core/module-template/tests/nextflow.config | 8 +++-----
 nf_core/modules/lint/__init__.py              | 2 +-
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/nf_core/module-template/tests/nextflow.config b/nf_core/module-template/tests/nextflow.config
index 495b860cc0..50f50a7a35 100644
--- a/nf_core/module-template/tests/nextflow.config
+++ b/nf_core/module-template/tests/nextflow.config
@@ -1,7 +1,5 @@
 process {
-    publishDir = [
-        path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" },
-        mode: params.publish_dir_mode,
-        saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
-    ]
+
+    publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }
+    
 }
\ No newline at end of file
diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index a3ef862980..69e093614b 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -234,7 +234,7 @@ def get_installed_modules(self):
                     # Deprecation error if functions.nf is found
                     if m == "functions.nf":
                         raise ModuleLintException(
-                            f"File '{m}' found in '{local_modules_dir}'has been deprecated since DSL2 v2.0!"
+                            f"File '{m}' found in '{local_modules_dir}' has been deprecated since DSL2 v2.0!"
                     )
                 else:
                     local_modules.append(m)

From 83dc6882f981f9188b63680aab4263884b7262ac Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 17:38:00 +0100
Subject: [PATCH 169/266] Update test.yml for consistency

---
 nf_core/module-template/tests/test.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/tests/test.yml b/nf_core/module-template/tests/test.yml
index 7363cd231e..8679a44943 100644
--- a/nf_core/module-template/tests/test.yml
+++ b/nf_core/module-template/tests/test.yml
@@ -1,7 +1,7 @@
 ## TODO nf-core: Please run the following command to build this file:
 #                nf-core modules create-test-yml {{ tool }}{%- if subtool %}/{{ subtool }}{%- endif %}
 - name: {{ tool }}{{ ' '+subtool if subtool else '' }}
-  command: nextflow run ./tests/modules/{{ tool_dir }} -entry test_{{ tool_name_underscore }} -c tests/config/nextflow.config -c tests/modules/{{ tool_dir }}/nextflow.config
+  command: nextflow run ./tests/modules/{{ tool_dir }} -entry test_{{ tool_name_underscore }} -c ./tests/config/nextflow.config -c ./tests/modules/{{ tool_dir }}/nextflow.config
   tags:
     - {{ tool }}
     {%- if subtool %}

From 3360f38098e7bcd6bc1c18b33a3804e25ba443a2 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 21:46:29 +0100
Subject: [PATCH 170/266] Quote the ${task.process} in versions.yml modules
 template

---
 nf_core/module-template/modules/main.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index f25fd62c5d..26a969cc1e 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -70,7 +70,7 @@ process {{ tool_name_underscore|upper }} {
         $bam
 
     cat <<-END_VERSIONS > versions.yml
-    ${task.process}:
+    "${task.process}":
         {{ tool }}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' ))
     END_VERSIONS
     """

From 17e78a0d0d87c555c0df2b4887cc1c526113b564 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 24 Nov 2021 22:33:45 +0100
Subject: [PATCH 171/266] Update container directive in main.nf module template

---
 nf_core/module-template/modules/main.nf | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 26a969cc1e..8f24774d8a 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -24,10 +24,10 @@ process {{ tool_name_underscore|upper }} {
     //               For Conda, the build (i.e. "h9402c20_2") must be EXCLUDED to support installation on different operating systems.
     // TODO nf-core: See section in main README for further information regarding finding and adding container addresses to the section below.
     conda (params.enable_conda ? "{{ bioconda if bioconda else 'YOUR-TOOL-HERE' }}" : null)
-    container workflow.containerEngine == 'singularity' &&
+    container "${ workflow.containerEngine == 'singularity' &&
         !task.ext.singularity_pull_docker_container ?
         '{{ singularity_container if singularity_container else 'https://depot.galaxyproject.org/singularity/YOUR-TOOL-HERE' }}':
-        '{{ docker_container if docker_container else 'quay.io/biocontainers/YOUR-TOOL-HERE' }}'
+        '{{ docker_container if docker_container else 'quay.io/biocontainers/YOUR-TOOL-HERE' }}' }"
 
     input:
     // TODO nf-core: Where applicable all sample-specific information e.g. "id", "single_end", "read_group"

From bee3c37b3107b1eefc43c346686c41b981de6e2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Thu, 25 Nov 2021 11:39:24 +0100
Subject: [PATCH 172/266] add missing space before `--fix`, update readme

---
 README.md             | 46 +++++++++++++++++++++----------------------
 nf_core/lint_utils.py |  2 +-
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/README.md b/README.md
index c874796049..616f806423 100644
--- a/README.md
+++ b/README.md
@@ -194,7 +194,7 @@ $ nf-core list
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
 ┃ Pipeline Name     ┃ Stars ┃ Latest Release ┃      Released ┃  Last Pulled ┃ Have latest release?  ┃
@@ -219,7 +219,7 @@ $ nf-core list rna rna-seq
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ┏━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓
 ┃ Pipeline Name ┃ Stars ┃ Latest Release ┃     Released ┃ Last Pulled ┃ Have latest release? ┃
@@ -248,7 +248,7 @@ $ nf-core list -s stars
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
 ┃ Pipeline Name     ┃ Stars ┃ Latest Release ┃      Released ┃  Last Pulled ┃ Have latest release?  ┃
@@ -292,7 +292,7 @@ $ nf-core launch rnaseq
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     This tool ignores any pipeline parameter defaults overwritten by Nextflow config files or profiles
@@ -372,7 +372,7 @@ $ nf-core download
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 Specify the name of a nf-core pipeline or a GitHub repository name (user/repo).
@@ -504,7 +504,7 @@ $ nf-core licences rnaseq
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 1.10
+    nf-core/tools version 2.2
 
   INFO     Fetching licence information for 25 tools
   INFO     Warning: This tool only prints licence information for the software tools packaged using conda.
@@ -558,7 +558,7 @@ $ nf-core create
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 Workflow Name: nextbigthing
 Description: This pipeline analyses data from the next big 'omics technique
@@ -603,7 +603,7 @@ $ nf-core lint
     |\ | |__  __ /  ` /  \ |__) |__         }  {
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 INFO     Testing pipeline: .
 ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
@@ -713,7 +713,7 @@ $ nf-core schema validate rnaseq nf-params.json
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 
@@ -745,7 +745,7 @@ $ nf-core schema build nf-core-testpipeline
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
   INFO     [✓] Default parameters look valid
   INFO     [✓] Pipeline schema looks valid (found 25 params)
@@ -784,7 +784,7 @@ $ nf-core schema lint nextflow_schema.json
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
   ERROR    [✗] Pipeline schema does not follow nf-core specs:
             Definition subschema 'input_output_options' not included in schema 'allOf'
@@ -800,14 +800,14 @@ Usage is `nf-core bump-version <new_version>`, eg:
 
 ```console
 $ cd path/to/my_pipeline
-$ nf-core bump-version . 1.7
+$ nf-core bump-version 1.7
                                           ,--./,-.
           ___     __   __   __   ___     /,-._.--~\
     |\ | |__  __ /  ` /  \ |__) |__         }  {
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 
@@ -862,7 +862,7 @@ $ nf-core sync my_pipeline/
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 
@@ -926,7 +926,7 @@ $ nf-core modules list remote
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     Modules available from nf-core/modules (master)
@@ -957,7 +957,7 @@ $ nf-core modules list local
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     Modules installed in '.':
@@ -983,7 +983,7 @@ $ nf-core modules install
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ? Tool name: cat/fastq
 INFO     Installing cat/fastq
@@ -1010,7 +1010,7 @@ $ nf-core modules update
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ? Tool name: fastqc
 INFO     Updating 'nf-core/modules/fastqc'
@@ -1072,7 +1072,7 @@ $ nf-core modules remove
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ? Tool name: star/align
 INFO     Removing star/align
@@ -1103,7 +1103,7 @@ $ nf-core modules create
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     Press enter to use default values (shown in brackets) or type your own responses. ctrl+click underlined text to open links.
@@ -1141,7 +1141,7 @@ $ nf-core modules create-test-yml
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 INFO     Press enter to use default values (shown in brackets) or type your own responses
@@ -1187,7 +1187,7 @@ $ nf-core modules lint
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 ? Lint all modules or a single named module? Named module
 ? Tool name: star/align
@@ -1226,7 +1226,7 @@ $ nf-core modules bump-versions -d modules
     | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                           `._,._,'
 
-    nf-core/tools version 2.0
+    nf-core/tools version 2.2
 
 
 
diff --git a/nf_core/lint_utils.py b/nf_core/lint_utils.py
index ea62dd2b00..757a244ed9 100644
--- a/nf_core/lint_utils.py
+++ b/nf_core/lint_utils.py
@@ -38,7 +38,7 @@ def print_fixes(lint_obj, module_lint_obj):
     """Prints available and applied fixes"""
 
     if len(lint_obj.could_fix):
-        fix_cmd = "nf-core lint {}--fix {}".format(
+        fix_cmd = "nf-core lint {} --fix {}".format(
             "" if lint_obj.wf_path == "." else f"--dir {lint_obj.wf_path}", " --fix ".join(lint_obj.could_fix)
         )
         console.print(

From 8f4350afecb9a2ebe3008558df21922e101a64eb Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelh@users.noreply.github.com>
Date: Thu, 25 Nov 2021 14:57:09 +0000
Subject: [PATCH 173/266] Apply suggestions from code review

---
 nf_core/modules/lint/__init__.py                             | 4 ++--
 nf_core/pipeline-template/conf/modules.config                | 3 +++
 nf_core/pipeline-template/modules/local/samplesheet_check.nf | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index 69e093614b..cef7669f0f 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -234,7 +234,7 @@ def get_installed_modules(self):
                     # Deprecation error if functions.nf is found
                     if m == "functions.nf":
                         raise ModuleLintException(
-                            f"File '{m}' found in '{local_modules_dir}' has been deprecated since DSL2 v2.0!"
+                            f"Deprecated file '{m}' found in '{local_modules_dir}' please delete it and update to latest syntax!"
                     )
                 else:
                     local_modules.append(m)
@@ -258,7 +258,7 @@ def get_installed_modules(self):
                 # Deprecation error if functions.nf is found
                 elif "functions.nf" in m_content:
                     raise ModuleLintException(
-                        f"File 'functions.nf' found in '{m}' has been deprecated since DSL2 v2.0!"
+                        f"Deprecated file '{m}' found in '{local_modules_dir}' please delete it and update to latest syntax!"
                     )
                 else:
                     nfcore_modules.append(m)
diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 207c641461..309c2abf58 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -21,6 +21,7 @@ process {
     withName: FASTQC {
         ext.args   = '--quiet'
     }
+
     withName: CUSTOM_DUMPSOFTWAREVERSIONS {
         publishDir = [
             path: { "${params.outdir}/pipeline_info" },
@@ -28,6 +29,7 @@ process {
             pattern: '*_versions.yml'
         ]
     }
+
     withName: MULTIQC {
         ext.args   = ''
     }
@@ -41,4 +43,5 @@ process {
             saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
         ]
     }
+
 }
diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index 21dd208ade..504bf4bd9d 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -22,7 +22,7 @@ process SAMPLESHEET_CHECK {
         samplesheet.valid.csv
 
     cat <<-END_VERSIONS > versions.yml
-    ${task.process}:
+    "${task.process}":
         python: \$(python --version | sed 's/Python //g')
     END_VERSIONS
     """

From 9e884741ac53d4d4a620880640ed9b7206a7c039 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 15:25:24 +0000
Subject: [PATCH 174/266] Update module template

---
 nf_core/module-template/modules/main.nf | 10 +++++-----
 nf_core/module-template/tests/main.nf   |  8 +++++---
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 8f24774d8a..506a840134 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -2,11 +2,10 @@
 //               https://github.com/nf-core/modules/tree/master/modules
 //               You can also ask for help via your pull request or on the #modules channel on the nf-core Slack workspace:
 //               https://nf-co.re/join
-
 // TODO nf-core: A module file SHOULD only define input and output files as command-line parameters.
-//               All other parameters MUST be provided using "process.ext" directive, see here:
+//               All other parameters MUST be provided using the "task.ext" directive, see here:
 //               https://www.nextflow.io/docs/latest/process.html#ext
-//               where "process.ext" is a Groovy map. 
+//               where "task.ext" is a string. 
 //               Any parameters that need to be evaluated in the context of a particular sample
 //               e.g. single-end/paired-end data MUST also be defined and evaluated appropriately.
 // TODO nf-core: Software that can be piped together SHOULD be added to separate module files
@@ -15,6 +14,7 @@
 //                 bwa mem | samtools view -B -T ref.fasta
 // TODO nf-core: Optional inputs are not currently supported by Nextflow. However, using an empty
 //               list (`[]`) instead of a file can be used to work around this issue.
+
 process {{ tool_name_underscore|upper }} {
     tag {{ '"$meta.id"' if has_meta else "'$bam'" }}
     label '{{ process_label }}'
@@ -48,12 +48,12 @@ process {{ tool_name_underscore|upper }} {
     {% if has_meta -%}
     def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
     {%- endif %}
-    def args   = task.ext.args ?: ''
+    def args = task.ext.args ?: ''
     // 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/modules/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 "process.ext.args" directive
+    // TODO nf-core: It MUST be possible to pass additional parameters to the tool as a command-line string via the "task.ext.args" directive
     // 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"
     // TODO nf-core: Please replace the example samtools command below with your module's command
diff --git a/nf_core/module-template/tests/main.nf b/nf_core/module-template/tests/main.nf
index 63bb1f7795..b172736c3a 100644
--- a/nf_core/module-template/tests/main.nf
+++ b/nf_core/module-template/tests/main.nf
@@ -2,12 +2,14 @@
 
 nextflow.enable.dsl = 2
 
-include { {{ tool_name_underscore|upper }} } from '../../../{{ "../" if subtool else "" }}modules/{{ tool_dir }}/main.nf' addParams( options: [:] )
+include { {{ tool_name_underscore|upper }} } from '../../../{{ "../" if subtool else "" }}modules/{{ tool_dir }}/main.nf'
 
 workflow test_{{ tool_name_underscore }} {
     {% if has_meta %}
-    input = [ [ id:'test', single_end:false ], // meta map
-              file(params.test_data['sarscov2']['illumina']['test_paired_end_bam'], checkIfExists: true) ]
+    input = [ 
+        [ id:'test', single_end:false ], // meta map
+        file(params.test_data['sarscov2']['illumina']['test_paired_end_bam'], checkIfExists: true) 
+    ]
     {%- else %}
     input = file(params.test_data['sarscov2']['illumina']['test_single_end_bam'], checkIfExists: true)
     {%- endif %}

From e7b21eae0333a696d5493371a992ae1ff2fa37a9 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 15:34:50 +0000
Subject: [PATCH 175/266] Update module main.nf

---
 nf_core/module-template/modules/main.nf | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 506a840134..0e4b549b25 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -24,8 +24,7 @@ process {{ tool_name_underscore|upper }} {
     //               For Conda, the build (i.e. "h9402c20_2") must be EXCLUDED to support installation on different operating systems.
     // TODO nf-core: See section in main README for further information regarding finding and adding container addresses to the section below.
     conda (params.enable_conda ? "{{ bioconda if bioconda else 'YOUR-TOOL-HERE' }}" : null)
-    container "${ workflow.containerEngine == 'singularity' &&
-        !task.ext.singularity_pull_docker_container ?
+    container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
         '{{ singularity_container if singularity_container else 'https://depot.galaxyproject.org/singularity/YOUR-TOOL-HERE' }}':
         '{{ docker_container if docker_container else 'quay.io/biocontainers/YOUR-TOOL-HERE' }}' }"
 
@@ -42,13 +41,13 @@ 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 "versions.yml"          , emit: versions
+    path "versions.yml"           , emit: versions
 
     script:
+    def args = task.ext.args ?: ''
     {% if has_meta -%}
     def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
     {%- endif %}
-    def args = task.ext.args ?: ''
     // 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/modules/homer/annotatepeaks/main.nf

From f3c6041d8f2aff38e1389e163b4746106c8ec074 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 15:56:51 +0000
Subject: [PATCH 176/266] Bump NF version from 21.04.0 to 21.10.3

---
 .github/ISSUE_TEMPLATE/bug_report.yml                     | 2 +-
 .github/workflows/create-lint-wf.yml                      | 2 +-
 .github/workflows/create-test-wf.yml                      | 2 +-
 CHANGELOG.md                                              | 2 ++
 nf_core/lint/readme.py                                    | 4 ++--
 .../.github/ISSUE_TEMPLATE/bug_report.yml                 | 2 +-
 nf_core/pipeline-template/.github/workflows/ci.yml        | 2 +-
 nf_core/pipeline-template/README.md                       | 4 ++--
 nf_core/pipeline-template/nextflow.config                 | 2 +-
 tests/test_bump_version.py                                | 8 ++++----
 10 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 1046a3fa20..431cce198a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -34,7 +34,7 @@ body:
     attributes:
       label: System information
       description: |
-        * Nextflow version _(eg. 21.04.01)_
+        * Nextflow version _(eg. 21.10.3)_
         * Hardware _(eg. HPC, Desktop, Cloud)_
         * Executor _(eg. slurm, local, awsbatch)_
         * OS _(eg. CentOS Linux, macOS, Linux Mint)_
diff --git a/.github/workflows/create-lint-wf.yml b/.github/workflows/create-lint-wf.yml
index dfc062eca7..d7cff18e29 100644
--- a/.github/workflows/create-lint-wf.yml
+++ b/.github/workflows/create-lint-wf.yml
@@ -9,7 +9,7 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ["NXF_VER=21.04.0", "NXF_EDGE=1"]
+        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
     steps:
       - uses: actions/checkout@v2
         name: Check out source-code repository
diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml
index c2b9914f6e..a5e72f1ded 100644
--- a/.github/workflows/create-test-wf.yml
+++ b/.github/workflows/create-test-wf.yml
@@ -9,7 +9,7 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ["NXF_VER=21.04.0", "NXF_EDGE=1"]
+        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
     steps:
       - uses: actions/checkout@v2
         name: Check out source-code repository
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88cb2574ce..c0b41da2dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Bump minimun Nextflow version to 21.10.3
 * Solve circular import when importing `nf_core.modules.lint`
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.
@@ -15,6 +16,7 @@
 
 ### General
 
+* Bump minimun Nextflow version to 21.10.3
 * Changed `questionary` `ask()` to `unsafe_ask()` to not catch `KeyboardInterupts` ([#1237](https://github.com/nf-core/tools/issues/1237))
 * Fixed bug in `nf-core launch` due to revisions specified with `-r` not being added to nextflow command. ([#1246](https://github.com/nf-core/tools/issues/1246))
 * Update regex in `readme` test of `nf-core lint` to agree with the pipeline template ([#1260](https://github.com/nf-core/tools/issues/1260))
diff --git a/nf_core/lint/readme.py b/nf_core/lint/readme.py
index ab240fc267..8df6155c5c 100644
--- a/nf_core/lint/readme.py
+++ b/nf_core/lint/readme.py
@@ -38,7 +38,7 @@ def readme(self):
         content = fh.read()
 
     # Check that there is a readme badge showing the minimum required version of Nextflow
-    # [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.04.0-23aa62.svg?labelColor=000000)](https://www.nextflow.io/)
+    # [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.10.3-23aa62.svg?labelColor=000000)](https://www.nextflow.io/)
     # and that it has the correct version
     nf_badge_re = r"\[!\[Nextflow\]\(https://img\.shields\.io/badge/nextflow%20DSL2-%E2%89%A5([\d\.]+)-23aa62\.svg\?labelColor=000000\)\]\(https://www\.nextflow\.io/\)"
     match = re.search(nf_badge_re, content)
@@ -62,7 +62,7 @@ def readme(self):
         warned.append("README did not have a Nextflow minimum version badge.")
 
     # Check that the minimum version mentioned in the quick start section is consistent
-    # Looking for: "1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.04.0`)"
+    # Looking for: "1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.10.3`)"
     nf_version_re = r"1\.\s*Install\s*\[`Nextflow`\]\(https://www.nextflow.io/docs/latest/getstarted.html#installation\)\s*\(`>=(\d*\.\d*\.\d*)`\)"
     match = re.search(nf_version_re, content)
     if match:
diff --git a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
index 9148922314..246362bf55 100644
--- a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -44,7 +44,7 @@ body:
     attributes:
       label: System information
       description: |
-        * Nextflow version _(eg. 21.04.01)_
+        * 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)_
diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 458e98d758..22d6cf21d2 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -23,7 +23,7 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and current latest
-        nxf_ver: ['21.04.0', '']
+        nxf_ver: ['21.10.3', '']
     steps:
       - name: Check out pipeline code
         uses: actions/checkout@v2
diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index 6003672ebc..e63c28f29b 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -5,7 +5,7 @@
 [![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/{{ short_name }}/results)
 [![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)
 
-[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.04.0-23aa62.svg?labelColor=000000)](https://www.nextflow.io/)
+[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.10.3-23aa62.svg?labelColor=000000)](https://www.nextflow.io/)
 [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)
 [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)
 [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)
@@ -33,7 +33,7 @@ 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))_
 
diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index b6d7337714..51665264f8 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -159,7 +159,7 @@ manifest {
     homePage        = 'https://github.com/{{ name }}'
     description     = '{{ description }}'
     mainScript      = 'main.nf'
-    nextflowVersion = '!>=21.04.0'
+    nextflowVersion = '!>=21.10.3'
     version         = '{{ version }}'
 }
 
diff --git a/tests/test_bump_version.py b/tests/test_bump_version.py
index bd6c5a1a57..94d91e5bc2 100644
--- a/tests/test_bump_version.py
+++ b/tests/test_bump_version.py
@@ -61,24 +61,24 @@ def test_bump_nextflow_version(datafiles):
     pipeline_obj._load()
 
     # Bump the version number
-    nf_core.bump_version.bump_nextflow_version(pipeline_obj, "21.04.0")
+    nf_core.bump_version.bump_nextflow_version(pipeline_obj, "21.10.3")
     new_pipeline_obj = nf_core.utils.Pipeline(test_pipeline_dir)
 
     # Check nextflow.config
     new_pipeline_obj._load_pipeline_config()
-    assert new_pipeline_obj.nf_config["manifest.nextflowVersion"].strip("'\"") == "!>=21.04.0"
+    assert new_pipeline_obj.nf_config["manifest.nextflowVersion"].strip("'\"") == "!>=21.10.3"
 
     # Check .github/workflows/ci.yml
     with open(new_pipeline_obj._fp(".github/workflows/ci.yml")) as fh:
         ci_yaml = yaml.safe_load(fh)
-    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"][0] == "21.04.0"
+    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"][0] == "21.10.3"
 
     # Check README.md
     with open(new_pipeline_obj._fp("README.md")) as fh:
         readme = fh.read().splitlines()
     assert (
         "[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A5{}-23aa62.svg?labelColor=000000)](https://www.nextflow.io/)".format(
-            "21.04.0"
+            "21.10.3"
         )
         in readme
     )

From f98c17b2c8fac960045825944dc27b0b9b481328 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Thu, 25 Nov 2021 17:02:24 +0100
Subject: [PATCH 177/266] Create README.md when a dummy module is created

---
 tests/test_modules.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/test_modules.py b/tests/test_modules.py
index 2401dfa763..ef799e157b 100644
--- a/tests/test_modules.py
+++ b/tests/test_modules.py
@@ -19,6 +19,8 @@ def create_modules_repo_dummy():
     os.makedirs(os.path.join(root_dir, "tests", "config"))
     with open(os.path.join(root_dir, "tests", "config", "pytest_modules.yml"), "w") as fh:
         fh.writelines(["test:", "\n  - modules/test/**", "\n  - tests/modules/test/**"])
+    with open(os.path.join(root_dir, "README.md"), "w") as fh:
+        fh.writelines(["# ![nf-core/modules](docs/images/nfcore-modules_logo.png)", "\n"])
 
     module_create = nf_core.modules.ModuleCreate(root_dir, "star/align", "@author", "process_medium", False, False)
     module_create.create()

From 6708ec537da2b8134975d87912493334dcad53e4 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:04:48 +0000
Subject: [PATCH 178/266] Update pipeline template CI to use specified version
 and latest edge release

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 22d6cf21d2..7595614d10 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -8,9 +8,6 @@ on:
   release:
     types: [published]
 
-# Uncomment if we need an edge release of Nextflow again
-# env: NXF_EDGE: 1
-
 jobs:
   test:
     name: Run workflow tests
@@ -18,12 +15,11 @@ jobs:
     if: {% raw %}${{{% endraw %} github.event_name != 'push' || (github.event_name == 'push' && github.repository == '{{ name }}') {% raw %}}}{% endraw %}
     runs-on: ubuntu-latest
     env:
-      NXF_VER: {% raw %}${{ matrix.nxf_ver }}{% endraw %}
       NXF_ANSI_LOG: false
     strategy:
       matrix:
-        # Nextflow versions: check pipeline minimum and current latest
-        nxf_ver: ['21.10.3', '']
+        # Nextflow versions: check pipeline minimum and latest edge version
+        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
     steps:
       - name: Check out pipeline code
         uses: actions/checkout@v2
@@ -34,6 +30,8 @@ jobs:
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
+          export ${{ matrix.nxf_ver }}
+          nextflow self-update
 
       - name: Run pipeline with test data
         # TODO nf-core: You can customise CI pipeline run tests as required

From 552f2bad8da5229059558bf47f6ab184bc8230e1 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:44:33 +0000
Subject: [PATCH 179/266] Update CHANGELOG

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0b41da2dd..efa8c1441d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Template
 
 * Bump minimun Nextflow version to 21.10.3
+* Convert pipeline template to updated Nextflow DSL2 syntax
 * Solve circular import when importing `nf_core.modules.lint`
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.

From 26362a801bc3fee8eb7d4e99f7c792e40ad9341f Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:44:46 +0000
Subject: [PATCH 180/266] Change syntax in modules.config

---
 nf_core/pipeline-template/conf/modules.config | 24 +++++++------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 309c2abf58..07c9720631 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -14,33 +14,27 @@ process {
 
     publishDir = [
         path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" },
-        mode: params.publish_dir_mode,
+        mode: 'copy',
         saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
     ]
 
-    withName: FASTQC {
-        ext.args   = '--quiet'
-    }
-
-    withName: CUSTOM_DUMPSOFTWAREVERSIONS {
+    withName: SAMPLESHEET_CHECK' {
         publishDir = [
             path: { "${params.outdir}/pipeline_info" },
-            mode: params.publish_dir_mode,
-            pattern: '*_versions.yml'
+            mode: 'copy',
+            saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
         ]
     }
 
-    withName: MULTIQC {
-        ext.args   = ''
+    withName: FASTQC {
+        ext.args = '--quiet'
     }
 
-    // Local Subworkflows
-    // INPUT_CHECK
-    withName: '.*:INPUT_CHECK:SAMPLESHEET_CHECK' {
+    withName: CUSTOM_DUMPSOFTWAREVERSIONS {
         publishDir = [
             path: { "${params.outdir}/pipeline_info" },
-            mode: params.publish_dir_mode,
-            saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
+            mode: 'copy',
+            pattern: '*_versions.yml'
         ]
     }
 

From aa3dee85d6166594db8cfffcf1ea2131dbfab379 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:45:03 +0000
Subject: [PATCH 181/266] Remove docs relative to old DSL2 syntax

---
 nf_core/pipeline-template/docs/usage.md | 36 -------------------------
 1 file changed, 36 deletions(-)

diff --git a/nf_core/pipeline-template/docs/usage.md b/nf_core/pipeline-template/docs/usage.md
index b4a708d8ec..485af3af44 100644
--- a/nf_core/pipeline-template/docs/usage.md
+++ b/nf_core/pipeline-template/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`.

From 6b473289daa2dd2c1a496f538707f71cfbd292f7 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:46:13 +0000
Subject: [PATCH 182/266] Update nextflow.config for new syntax

---
 nf_core/pipeline-template/nextflow.config | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 51665264f8..912d90bb4c 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -34,9 +34,8 @@ 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'
@@ -57,9 +56,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"
@@ -163,6 +159,9 @@ manifest {
     version         = '{{ version }}'
 }
 
+// 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) {

From 1490771a6120ef93fa6d94dd62400dcb187bbf05 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:46:31 +0000
Subject: [PATCH 183/266] Remve --singularity_pull_docker_container

---
 nf_core/pipeline-template/nextflow_schema.json | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json
index c4b03824a8..fd95d67238 100644
--- a/nf_core/pipeline-template/nextflow_schema.json
+++ b/nf_core/pipeline-template/nextflow_schema.json
@@ -254,13 +254,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."
                 }
             }
         }

From 175ab50a60e43edf8c21e53c1efabc092c052c15 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:46:47 +0000
Subject: [PATCH 184/266] Remove addParams call!

---
 nf_core/pipeline-template/workflows/pipeline.nf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 9649e27ec8..7ac029844f 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -35,7 +35,7 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi
 //
 // 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'
 
 /*
 ========================================================================================

From 19f49539ccb34bf42f3e330169442dc527d9afa7 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:46:54 +0000
Subject: [PATCH 185/266] Remove addParams call!

---
 nf_core/pipeline-template/subworkflows/local/input_check.nf | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/subworkflows/local/input_check.nf b/nf_core/pipeline-template/subworkflows/local/input_check.nf
index c1b071961c..cddcbb3ce0 100644
--- a/nf_core/pipeline-template/subworkflows/local/input_check.nf
+++ b/nf_core/pipeline-template/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:

From e4874b55c2ce3acbd326f99059d12b95aad822ef Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:47:13 +0000
Subject: [PATCH 186/266] Update container syntax

---
 .../pipeline-template/modules/local/samplesheet_check.nf  | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/modules/local/samplesheet_check.nf b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
index 504bf4bd9d..ccfdfecc2c 100644
--- a/nf_core/pipeline-template/modules/local/samplesheet_check.nf
+++ b/nf_core/pipeline-template/modules/local/samplesheet_check.nf
@@ -2,11 +2,9 @@ process SAMPLESHEET_CHECK {
     tag "$samplesheet"
 
     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

From dffa555f160109bcf4374606859bb177f6d67ee1 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 16:50:20 +0000
Subject: [PATCH 187/266] Remove --publish_dir_mode from pipeline template

---
 nf_core/pipeline-template/nextflow.config      |  1 -
 nf_core/pipeline-template/nextflow_schema.json | 16 ----------------
 2 files changed, 17 deletions(-)

diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 912d90bb4c..dd8cb66102 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/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
diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json
index fd95d67238..9ccc78f362 100644
--- a/nf_core/pipeline-template/nextflow_schema.json
+++ b/nf_core/pipeline-template/nextflow_schema.json
@@ -178,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.",

From 1c7727cc39095238a47b2c85fece8fd9ac22ecb3 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 17:06:29 +0000
Subject: [PATCH 188/266] Artificially update nf-core/modules to fix linting
 everywhere else

---
 .../custom/dumpsoftwareversions/main.nf       | 102 ++----------------
 .../custom/dumpsoftwareversions/meta.yml      |   3 +-
 .../templates/dumpsoftwareversions.py         |  89 +++++++++++++++
 .../modules/nf-core/modules/fastqc/main.nf    |  16 ++-
 .../modules/nf-core/modules/fastqc/meta.yml   |   5 +-
 .../modules/nf-core/modules/multiqc/main.nf   |  15 ++-
 .../modules/nf-core/modules/multiqc/meta.yml  |   5 +-
 7 files changed, 116 insertions(+), 119 deletions(-)
 create mode 100755 nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py

diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
index 3b7f7d8647..934bb46723 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf
@@ -1,13 +1,11 @@
 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)
-    if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
-        container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0"
-    } else {
-        container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0"
-    }
+    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
@@ -18,94 +16,6 @@ process CUSTOM_DUMPSOFTWAREVERSIONS {
     path "versions.yml"             , emit: versions
 
     script:
-    """
-    #!/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)
-
-    module_versions = {}
-    module_versions["${getProcessName(task.process)}"] = {
-        'python': platform.python_version(),
-        'yaml': yaml.__version__
-    }
-
-    with open("$versions") as f:
-        workflow_versions = yaml.load(f, Loader=yaml.BaseLoader) | module_versions
-
-    workflow_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(workflow_versions)
-    }
-
-    with open("software_versions.yml", 'w') as f:
-        yaml.dump(workflow_versions, 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(module_versions, f, default_flow_style=False)
-    """
+    def args = task.ext.args ?: ''
+    template 'dumpsoftwareversions.py'
 }
-
-//
-// Extract name of software tool from process name using $task.process
-//
-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]
-}
\ No newline at end of file
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
index 8d4a6ed42c..5b5b8a6026 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
+++ b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml
@@ -8,7 +8,7 @@ tools:
       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
@@ -31,3 +31,4 @@ output:
 
 authors:
   - "@drpatelh"
+  - "@grst"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py
new file mode 100755
index 0000000000..d139039254
--- /dev/null
+++ b/nf_core/pipeline-template/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/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index ac96a8c2e6..673a00b841 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -1,13 +1,11 @@
 process FASTQC {
     tag "$meta.id"
     label 'process_medium'
-    
+
     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)
@@ -18,16 +16,16 @@ process FASTQC {
     path  "versions.yml"           , emit: versions
 
     script:
+    def args = task.ext.args ?: ''
     // Add soft-links to original FastQs for consistent naming in pipeline
     def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
-    def args   = task.ext.args ?: ''
     if (meta.single_end) {
         """
         [ ! -f  ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz
         fastqc $args --threads $task.cpus ${prefix}.fastq.gz
 
         cat <<-END_VERSIONS > versions.yml
-        FASTQC:
+        "${task.process}":
             fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
@@ -38,7 +36,7 @@ process FASTQC {
         fastqc $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz
 
         cat <<-END_VERSIONS > versions.yml
-        ${task.process}:
+        "${task.process}":
             fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" )
         END_VERSIONS
         """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
index 48031356b5..b09553a3c3 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/meta.yml
+++ b/nf_core/pipeline-template/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,9 +41,9 @@ output:
         type: file
         description: FastQC report archive
         pattern: "*_{fastqc.zip}"
-    - version:
+    - versions:
         type: file
-        description: File containing software version
+        description: File containing software versions
         pattern: "versions.yml"
 authors:
     - "@drpatelh"
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
index 6f22aa0920..3dceb162a3 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/main.nf
@@ -1,13 +1,10 @@
-
 process MULTIQC {
     label 'process_medium'
-    
+
     conda (params.enable_conda ? 'bioconda::multiqc=1.11' : null)
-    if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) {
-        container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0"
-    } else {
-        container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0"
-    }
+    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
@@ -19,12 +16,12 @@ process MULTIQC {
     path "versions.yml"        , emit: versions
 
     script:
-    def args   = task.ext.args ?: ''
+    def args = task.ext.args ?: ''
     """
     multiqc -f $args .
 
     cat <<-END_VERSIONS > versions.yml
-    ${task.process}:
+    "${task.process}":
         multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" )
     END_VERSIONS
     """
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml b/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
index 2d99ec0d12..63c75a450a 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/multiqc/meta.yml
+++ b/nf_core/pipeline-template/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,9 +30,9 @@ output:
         type: file
         description: Plots created by MultiQC
         pattern: "*_data"
-    - version:
+    - versions:
         type: file
-        description: File containing software version
+        description: File containing software versions
         pattern: "versions.yml"
 authors:
     - "@abhi18av"

From bc1e63edf37615dbf3d6dd95d611e6d867d2ab11 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 25 Nov 2021 17:06:42 +0000
Subject: [PATCH 189/266] Update modules.json

---
 nf_core/pipeline-template/modules.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index db6aceae73..2cb00e761a 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,13 +4,13 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "84f2302920078b0cf7716b2a2e5fcc0be5c4531d"
+                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
             },
             "fastqc": {
-                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
+                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
             },
             "multiqc": {
-                "git_sha": "7b3315591a149609e27914965f858c9a7e071564"
+                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
             }
         }
     }

From 66fc9e733523def307828e386f5f98ebdf76ee01 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Fri, 26 Nov 2021 10:29:57 +0100
Subject: [PATCH 190/266] Fix modules.config

---
 nf_core/pipeline-template/conf/modules.config | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 07c9720631..478869953b 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -18,7 +18,7 @@ process {
         saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
     ]
 
-    withName: SAMPLESHEET_CHECK' {
+    withName: SAMPLESHEET_CHECK {
         publishDir = [
             path: { "${params.outdir}/pipeline_info" },
             mode: 'copy',

From ae1f2a672bc1c2c6b7e4edc44eb58aaa522684ee Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Fri, 26 Nov 2021 11:08:57 +0100
Subject: [PATCH 191/266] Fix ci.yml template

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 7595614d10..6a2646972b 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -30,7 +30,7 @@ jobs:
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
-          export ${{ matrix.nxf_ver }}
+          export ${% raw %}{{ matrix.nxf_ver }}{% endraw %}
           nextflow self-update
 
       - name: Run pipeline with test data

From 8c030f6256947294f03d0512e44caa7bb6d155ca Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelh@users.noreply.github.com>
Date: Fri, 26 Nov 2021 10:35:34 +0000
Subject: [PATCH 192/266] Update modules.config

---
 nf_core/pipeline-template/conf/modules.config | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 07c9720631..478869953b 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -18,7 +18,7 @@ process {
         saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
     ]
 
-    withName: SAMPLESHEET_CHECK' {
+    withName: SAMPLESHEET_CHECK {
         publishDir = [
             path: { "${params.outdir}/pipeline_info" },
             mode: 'copy',

From 156c86c1a0296ee61d2f0c4208b4a9841557e553 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 11:37:42 +0100
Subject: [PATCH 193/266] Update pytest options to only look search tests
 directory

This makes pytest work in vscode even if you setup tests
using the root directory of the repo.
---
 pyproject.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pyproject.toml b/pyproject.toml
index 266acbdcb6..223c17afb7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,3 +7,4 @@ markers = [
     "datafiles: load datafiles"
 ]
 testpaths = ["tests"]
+norecursedirs = [ '.*', 'build', 'dist', '*.egg', 'data', '__pycache__', '.github', 'nf_core', 'docs']
\ No newline at end of file

From 2aca38b93cf314ca555ce8053f28eedf7883d9db Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Fri, 26 Nov 2021 11:47:44 +0100
Subject: [PATCH 194/266] Fix nextflow version in ci.yml template

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 6a2646972b..2b38ccf951 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
+        nxf_ver: ['21.10.3', 'NXF_EDGE=1']
     steps:
       - name: Check out pipeline code
         uses: actions/checkout@v2

From 24cfa0f56bf3e7f8b42ad5b5d7804a241a52a1f9 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 26 Nov 2021 12:04:56 +0100
Subject: [PATCH 195/266] Fix the nasty nasty bug

---
 nf_core/modules/module_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/module_utils.py b/nf_core/modules/module_utils.py
index b5969c29e0..b3e47b812c 100644
--- a/nf_core/modules/module_utils.py
+++ b/nf_core/modules/module_utils.py
@@ -57,7 +57,7 @@ def get_module_git_log(module_name, modules_repo=None, per_page=30, page_nbr=1,
     if modules_repo is None:
         modules_repo = ModulesRepo()
     api_url = f"https://api.github.com/repos/{modules_repo.name}/commits"
-    api_url += f"?sha{modules_repo.branch}"
+    api_url += f"?sha={modules_repo.branch}"
     if module_name is not None:
         api_url += f"&path=modules/{module_name}"
     api_url += f"&page={page_nbr}"

From 286abaf8247573d42ae8d9846106694f4fc059d4 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 12:09:55 +0100
Subject: [PATCH 196/266] Fix modules test not discovering right repo type

---
 nf_core/modules/create.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/create.py b/nf_core/modules/create.py
index 819f00eee9..a9c071c4ce 100644
--- a/nf_core/modules/create.py
+++ b/nf_core/modules/create.py
@@ -281,9 +281,10 @@ def get_repo_type(self, directory):
         if dir is None or not os.path.exists(directory):
             raise UserWarning(f"Could not find directory: {directory}")
 
+        readme = os.path.join(directory, "README.md")
         # Determine repository type
-        if os.path.exists("README.md"):
-            with open("README.md") as fh:
+        if os.path.exists(readme):
+            with open(readme) as fh:
                 if fh.readline().rstrip().startswith("# ![nf-core/modules]"):
                     return "modules"
                 else:

From a7be87dea17b2b7eccae1099d88e7c08b627cb43 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 12:12:26 +0100
Subject: [PATCH 197/266] Fix process name not used by new modules

---
 nf_core/modules/lint/main_nf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 204ab78518..5676430c39 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -26,7 +26,7 @@ def main_nf(module_lint_object, module):
         module.failed.append(("main_nf_exists", "Module file does not exist", module.main_nf))
         return
 
-    deprecated_i = ["initOptions", "saveFiles", "getSoftwareName", "getProcessName", "publishDir"] 
+    deprecated_i = ["initOptions", "saveFiles", "getSoftwareName", "getProcessName", "publishDir"]
     lines_j = "\n".join(lines)
     for i in deprecated_i:
         if i in lines_j:
@@ -101,7 +101,7 @@ def check_script_section(self, lines):
     script = "".join(lines)
 
     # check that process name is used for `versions.yml`
-    if re.search("\$\{\s*getProcessName\s*\(\s*task\.process\s*\)\s*\}", script):
+    if re.search("\$\{\s*task\.process\s*\}", script):
         self.passed.append(("main_nf_version_script", "Process name used for versions.yml", self.main_nf))
     else:
         self.failed.append(("main_nf_version_script", "Process name not used for versions.yml", self.main_nf))

From a8842fb46a497d4aea5cfb1f770d02c861de4db9 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 12:14:02 +0100
Subject: [PATCH 198/266] Fix black

---
 nf_core/modules/lint/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index cef7669f0f..fdef39252c 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -235,7 +235,7 @@ def get_installed_modules(self):
                     if m == "functions.nf":
                         raise ModuleLintException(
                             f"Deprecated file '{m}' found in '{local_modules_dir}' please delete it and update to latest syntax!"
-                    )
+                        )
                 else:
                     local_modules.append(m)
 

From 1d60935ba4156b7652422d906badfd08fc20c2ba Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 12:22:06 +0100
Subject: [PATCH 199/266] Temporarily disable NXF_EDGE tests for
 CreateAndTestPipeline

---
 .github/workflows/create-test-wf.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml
index a5e72f1ded..4aa24e0894 100644
--- a/.github/workflows/create-test-wf.yml
+++ b/.github/workflows/create-test-wf.yml
@@ -9,7 +9,9 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
+        nxf_ver: 
+          - "NXF_VER=21.10.3"
+          # - "NXF_EDGE=1"
     steps:
       - uses: actions/checkout@v2
         name: Check out source-code repository

From c8ee21a43558ff32096799923fef152e534a073e Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 13:05:03 +0100
Subject: [PATCH 200/266] Temporarily enable NXF_EDGE also in the pipeline
 template

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 2b38ccf951..30ffef79ab 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -19,7 +19,9 @@ jobs:
     strategy:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ['21.10.3', 'NXF_EDGE=1']
+        nxf_ver:
+         - 'NXF_EDGE=21.10.3'
+        #  - 'NXF_EDGE=1'
     steps:
       - name: Check out pipeline code
         uses: actions/checkout@v2

From 9f11c855528dfba774d9d168140bde041c5168d4 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 14:23:00 +0100
Subject: [PATCH 201/266] Check for functions.nf

---
 nf_core/modules/lint/__init__.py              | 33 ++++++++-----------
 nf_core/modules/lint/module_deprecations.py   | 20 +++++++++++
 .../.github/workflows/ci.yml                  |  2 +-
 tests/modules/lint.py                         |  6 ++--
 tests/test_bump_version.py                    |  2 +-
 5 files changed, 38 insertions(+), 25 deletions(-)
 create mode 100644 nf_core/modules/lint/module_deprecations.py

diff --git a/nf_core/modules/lint/__init__.py b/nf_core/modules/lint/__init__.py
index fdef39252c..aae62e86a2 100644
--- a/nf_core/modules/lint/__init__.py
+++ b/nf_core/modules/lint/__init__.py
@@ -65,6 +65,7 @@ class ModuleLint(ModuleCommand):
     from .module_changes import module_changes
     from .module_tests import module_tests
     from .module_todos import module_todos
+    from .module_deprecations import module_deprecations
     from .module_version import module_version
 
     def __init__(self, dir):
@@ -95,7 +96,7 @@ def __init__(self, dir):
 
     @staticmethod
     def _get_all_lint_tests():
-        return ["main_nf", "meta_yml", "module_changes", "module_todos"]
+        return ["main_nf", "meta_yml", "module_changes", "module_todos", "module_deprecations"]
 
     def lint(self, module=None, key=(), all_modules=False, print_results=True, show_passed=False, local=False):
         """
@@ -229,15 +230,7 @@ def get_installed_modules(self):
 
             # Filter local modules
             if os.path.exists(local_modules_dir):
-                local_modules = os.listdir(local_modules_dir)
-                for m in sorted([m for m in local_modules if m.endswith(".nf")]):
-                    # Deprecation error if functions.nf is found
-                    if m == "functions.nf":
-                        raise ModuleLintException(
-                            f"Deprecated file '{m}' found in '{local_modules_dir}' please delete it and update to latest syntax!"
-                        )
-                else:
-                    local_modules.append(m)
+                local_modules = sorted([x for x in local_modules if x.endswith(".nf")])
 
         # nf-core/modules
         if self.repo_type == "modules":
@@ -245,21 +238,21 @@ def get_installed_modules(self):
 
         # Get nf-core modules
         if os.path.exists(nfcore_modules_dir):
-            for m in sorted([m for m in os.listdir(nfcore_modules_dir) if not m == "lib"]):
+            for m in sorted(os.listdir(nfcore_modules_dir)):
                 if not os.path.isdir(os.path.join(nfcore_modules_dir, m)):
                     raise ModuleLintException(
                         f"File found in '{nfcore_modules_dir}': '{m}'! This directory should only contain module directories."
                     )
-                m_content = os.listdir(os.path.join(nfcore_modules_dir, m))
+
+                module_dir = os.path.join(nfcore_modules_dir, m)
+                module_subdir = os.listdir(module_dir)
                 # Not a module, but contains sub-modules
-                if not "main.nf" in m_content:
-                    for tool in m_content:
-                        nfcore_modules.append(os.path.join(m, tool))
-                # Deprecation error if functions.nf is found
-                elif "functions.nf" in m_content:
-                    raise ModuleLintException(
-                        f"Deprecated file '{m}' found in '{local_modules_dir}' please delete it and update to latest syntax!"
-                    )
+                if "main.nf" not in module_subdir:
+                    for path in module_subdir:
+                        module_subdir_path = os.path.join(nfcore_modules_dir, m, path)
+                        if os.path.isdir(module_subdir_path):
+                            if os.path.exists(os.path.join(module_subdir_path, "main.nf")):
+                                nfcore_modules.append(os.path.join(m, path))
                 else:
                     nfcore_modules.append(m)
 
diff --git a/nf_core/modules/lint/module_deprecations.py b/nf_core/modules/lint/module_deprecations.py
new file mode 100644
index 0000000000..4c7904985b
--- /dev/null
+++ b/nf_core/modules/lint/module_deprecations.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+import logging
+import os
+
+log = logging.getLogger(__name__)
+
+
+def module_deprecations(module_lint_object, module):
+    """
+    Check that the modules are up to the latest nf-core standard
+    """
+    module.wf_path = module.module_dir
+    if "functions.nf" in os.listdir(module.module_dir):
+        module.failed.append(
+            (
+                "module_deprecations",
+                f"Deprecated file 'functions.nf' found please delete it and update to latest syntax!",
+                module.module_dir,
+            )
+        )
diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 30ffef79ab..0d477a075b 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
       matrix:
         # Nextflow versions: check pipeline minimum and latest edge version
         nxf_ver:
-         - 'NXF_EDGE=21.10.3'
+         - 'NXF_VER=21.10.3'
         #  - 'NXF_EDGE=1'
     steps:
       - name: Check out pipeline code
diff --git a/tests/modules/lint.py b/tests/modules/lint.py
index 0d25f62880..0f60377d5e 100644
--- a/tests/modules/lint.py
+++ b/tests/modules/lint.py
@@ -6,9 +6,9 @@ def test_modules_lint_trimgalore(self):
     self.mods_install.install("trimgalore")
     module_lint = nf_core.modules.ModuleLint(dir=self.pipeline_dir)
     module_lint.lint(print_results=False, module="trimgalore")
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
 
 
 def test_modules_lint_empty(self):
@@ -18,15 +18,15 @@ def test_modules_lint_empty(self):
     self.mods_remove.remove("custom/dumpsoftwareversions")
     module_lint = nf_core.modules.ModuleLint(dir=self.pipeline_dir)
     module_lint.lint(print_results=False, all_modules=True)
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
     assert len(module_lint.passed) == 0
     assert len(module_lint.warned) == 0
-    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
 
 
 def test_modules_lint_new_modules(self):
     """lint all modules in nf-core/modules repo clone"""
     module_lint = nf_core.modules.ModuleLint(dir=self.nfcore_modules)
     module_lint.lint(print_results=True, all_modules=True)
+    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
     assert len(module_lint.passed) > 0
     assert len(module_lint.warned) >= 0
-    assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
diff --git a/tests/test_bump_version.py b/tests/test_bump_version.py
index 94d91e5bc2..39840fe6d1 100644
--- a/tests/test_bump_version.py
+++ b/tests/test_bump_version.py
@@ -71,7 +71,7 @@ def test_bump_nextflow_version(datafiles):
     # Check .github/workflows/ci.yml
     with open(new_pipeline_obj._fp(".github/workflows/ci.yml")) as fh:
         ci_yaml = yaml.safe_load(fh)
-    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"][0] == "21.10.3"
+    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"][0] == "NXF_VER=21.10.3"
 
     # Check README.md
     with open(new_pipeline_obj._fp("README.md")) as fh:

From c3c8f8711de699e16c603619cc22e0cda760b9a1 Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 14:31:43 +0100
Subject: [PATCH 202/266] Make missing task.process in script section a warning

It was a failure before, but there are some legitimate cases to not have
it, e.g. when using the `template` feature.
---
 nf_core/modules/lint/main_nf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 5676430c39..dcee7d380b 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -104,7 +104,7 @@ def check_script_section(self, lines):
     if re.search("\$\{\s*task\.process\s*\}", script):
         self.passed.append(("main_nf_version_script", "Process name used for versions.yml", self.main_nf))
     else:
-        self.failed.append(("main_nf_version_script", "Process name not used for versions.yml", self.main_nf))
+        self.warned.append(("main_nf_version_script", "Process name not used for versions.yml", self.main_nf))
 
     # check for prefix (only if module has a meta map as input)
     if self.has_meta:

From c83156f5a6cb8b82f0bbe12501612ec097b5b9ac Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 14:36:33 +0100
Subject: [PATCH 203/266] Fix lint for nextflow version in CI script

---
 nf_core/lint/actions_ci.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/lint/actions_ci.py b/nf_core/lint/actions_ci.py
index fd138ed0ab..48056a9422 100644
--- a/nf_core/lint/actions_ci.py
+++ b/nf_core/lint/actions_ci.py
@@ -131,7 +131,7 @@ def actions_ci(self):
     # Check that we are testing the minimum nextflow version
     try:
         matrix = ciwf["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"]
-        assert any([self.minNextflowVersion in matrix])
+        assert any([f"NXF_VER={self.minNextflowVersion}" in matrix])
     except (KeyError, TypeError):
         failed.append("'.github/workflows/ci.yml' does not check minimum NF version")
     except AssertionError:

From b882bee215c3b53734e368e857da180a0d70018e Mon Sep 17 00:00:00 2001
From: Gregor Sturm <mail@gregor-sturm.de>
Date: Fri, 26 Nov 2021 14:40:07 +0100
Subject: [PATCH 204/266] against dev branch


From 1bab2c40e7abfb8cc67e8122545606b72922f7d4 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 26 Nov 2021 14:31:33 +0000
Subject: [PATCH 205/266] Re-install nf-core/modules in pipeline template

---
 nf_core/pipeline-template/modules.json                      | 6 +++---
 .../dumpsoftwareversions/templates/dumpsoftwareversions.py  | 0
 2 files changed, 3 insertions(+), 3 deletions(-)
 mode change 100755 => 100644 nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index 2cb00e761a..e02a95aba8 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -4,13 +4,13 @@
     "repos": {
         "nf-core/modules": {
             "custom/dumpsoftwareversions": {
-                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
+                "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"
             },
             "fastqc": {
-                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
+                "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"
             },
             "multiqc": {
-                "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae"
+                "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"
             }
         }
     }
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/nf_core/pipeline-template/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py
old mode 100755
new mode 100644

From 2d33ffbf85296dc724985025d7d82eae75a2af54 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 26 Nov 2021 15:00:09 +0000
Subject: [PATCH 206/266] Amend error messages for old DSL2 syntax

---
 nf_core/modules/lint/main_nf.py             | 2 +-
 nf_core/modules/lint/module_deprecations.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index c772e37102..d8d7516c9a 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -33,7 +33,7 @@ def main_nf(module_lint_object, module):
             module.failed.append(
                 (
                     "deprecated_dsl2",
-                    f"`{i}` has been deprecated since DSL2 v2.0",
+                    f"`{i}` specified. No longer required for the latest nf-core/modules syntax!",
                     module.main_nf,
                 )
             )
diff --git a/nf_core/modules/lint/module_deprecations.py b/nf_core/modules/lint/module_deprecations.py
index 4c7904985b..e697342950 100644
--- a/nf_core/modules/lint/module_deprecations.py
+++ b/nf_core/modules/lint/module_deprecations.py
@@ -14,7 +14,7 @@ def module_deprecations(module_lint_object, module):
         module.failed.append(
             (
                 "module_deprecations",
-                f"Deprecated file 'functions.nf' found please delete it and update to latest syntax!",
+                f"Deprecated file 'functions.nf' found. No longer required for the latest nf-core/modules syntax!",
                 module.module_dir,
             )
         )

From d52a0aca051f01f28a9f24db17da26a31808b28b Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 26 Nov 2021 15:08:55 +0000
Subject: [PATCH 207/266] Highlight functions.nf

---
 nf_core/modules/lint/module_deprecations.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/module_deprecations.py b/nf_core/modules/lint/module_deprecations.py
index e697342950..0a2990d9d0 100644
--- a/nf_core/modules/lint/module_deprecations.py
+++ b/nf_core/modules/lint/module_deprecations.py
@@ -14,7 +14,7 @@ def module_deprecations(module_lint_object, module):
         module.failed.append(
             (
                 "module_deprecations",
-                f"Deprecated file 'functions.nf' found. No longer required for the latest nf-core/modules syntax!",
+                f"Deprecated file `functions.nf` found. No longer required for the latest nf-core/modules syntax!",
                 module.module_dir,
             )
         )

From 0dc877603f40d874d96da61c3e04f4e7d0a7e52b Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 26 Nov 2021 17:29:24 +0100
Subject: [PATCH 208/266] describe how to access private modules repos

---
 README.md | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/README.md b/README.md
index e440979bde..9c26b483ab 100644
--- a/README.md
+++ b/README.md
@@ -902,6 +902,8 @@ This allows multiple pipelines to use the same code for share tools and gives a
 
 The nf-core DSL2 modules repository is at <https://github.com/nf-core/modules>
 
+### Custom remote modules
+
 The modules supercommand comes with two flags for specifying a custom remote:
 
 * `--github-repository <github repo>`: Specify the repository from which the modules should be fetched. Defaults to `nf-core/modules`.
@@ -909,6 +911,34 @@ The modules supercommand comes with two flags for specifying a custom remote:
 
 Note that a custom remote must follow a similar directory structure to that of `nf-core/moduleś` for the `nf-core modules` commands to work properly.
 
+### Private remote modules
+
+In order to get access to your private modules repo, you need to create
+the `~/.config/gh/hosts.yml` file, which is the same file required by
+[GitHub CLI](https://cli.github.com/) to deal with private repositories.
+Such file is structured as follow:
+
+```conf
+github.com:
+    oauth_token: <your github access token>
+    user: <your github user>
+    git_protocol: <ssh or https are valid choices>
+```
+
+The easiest way to create this configuration file is through *GitHub CLI*: follow
+its [installation instructions](https://cli.github.com/manual/installation)
+and then call:
+
+```bash
+gh auth login
+```
+
+After that, you will be able to list and install your private modules without 
+providing your github credentials through command line, by using `--github-repository`
+and `--branch` options properly. 
+See the documentation on [gh auth login](https://cli.github.com/manual/gh_auth_login>)
+to get more information.
+
 ### List modules
 
 The `nf-core modules list` command provides the subcommands `remote` and `local` for listing modules installed in a remote repository and in the local pipeline respectively. Both subcommands come with the `--key <keywords>` option for filtering the modules by keywords.

From f5b41c28395beca1a0ce550c47f4df48d78c2e65 Mon Sep 17 00:00:00 2001
From: Paolo Cozzi <bunop@libero.it>
Date: Fri, 26 Nov 2021 17:36:14 +0100
Subject: [PATCH 209/266] solve issue with markdownlint

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 9c26b483ab..385e8f9820 100644
--- a/README.md
+++ b/README.md
@@ -933,9 +933,9 @@ and then call:
 gh auth login
 ```
 
-After that, you will be able to list and install your private modules without 
+After that, you will be able to list and install your private modules without
 providing your github credentials through command line, by using `--github-repository`
-and `--branch` options properly. 
+and `--branch` options properly.
 See the documentation on [gh auth login](https://cli.github.com/manual/gh_auth_login>)
 to get more information.
 

From 7baee696d103ba5c1249856ebea9a32c3f82381e Mon Sep 17 00:00:00 2001
From: ggabernet <gisela.gabernet@qbic.uni-tuebingen.de>
Date: Fri, 26 Nov 2021 20:03:14 +0100
Subject: [PATCH 210/266] fix linting comment

---
 CHANGELOG.md                                                    | 1 +
 nf_core/pipeline-template/.github/workflows/linting_comment.yml | 1 +
 2 files changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe02faadcf..89372f0495 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
 * Update `.gitattributes` to mark installed modules and subworkflows as `linguist-generated` ([#1311](https://github.com/nf-core/tools/issues/1311))
 * New YAML issue templates for pipeline bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 * Update AWS test GitHub Actions to use v2 of [nf-core/tower-action](https://github.com/nf-core/tower-action)
+* Post linting comment even when `linting.yml` fails
 
 ### General
 
diff --git a/nf_core/pipeline-template/.github/workflows/linting_comment.yml b/nf_core/pipeline-template/.github/workflows/linting_comment.yml
index 0c718c0d9b..68a0feee18 100644
--- a/nf_core/pipeline-template/.github/workflows/linting_comment.yml
+++ b/nf_core/pipeline-template/.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

From deb5bb20218f584f237ae0413ea807e338bf0dac Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 26 Nov 2021 20:03:36 +0100
Subject: [PATCH 211/266] Nicer syntax for ci.yml matrix env variables

Trying to make it a bit easier to read / understand
---
 .../pipeline-template/.github/workflows/ci.yml  | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 0d477a075b..b9e59ec692 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -18,10 +18,14 @@ jobs:
       NXF_ANSI_LOG: false
     strategy:
       matrix:
-        # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver:
-         - 'NXF_VER=21.10.3'
-        #  - 'NXF_EDGE=1'
+        # 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
@@ -29,10 +33,13 @@ jobs:
       - name: Install Nextflow
         env:
           CAPSULE_LOG: none
+          {% raw %-}
+          NXF_VER: ${{ matrix.NXF_VER }}
+          NXF_EDGE: ${{ matrix.NXF_EDGE }}
+          {-% endraw %}
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
-          export ${% raw %}{{ matrix.nxf_ver }}{% endraw %}
           nextflow self-update
 
       - name: Run pipeline with test data

From a75ca80ad60f2b69416c21ffccd4ccde104aad55 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 26 Nov 2021 20:08:13 +0100
Subject: [PATCH 212/266] Hyphens in the wrong place

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index b9e59ec692..a7189aac69 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -33,10 +33,10 @@ jobs:
       - name: Install Nextflow
         env:
           CAPSULE_LOG: none
-          {% raw %-}
+          {% raw -%}
           NXF_VER: ${{ matrix.NXF_VER }}
           NXF_EDGE: ${{ matrix.NXF_EDGE }}
-          {-% endraw %}
+          {%- endraw %}
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/

From 179ed262e255a2c9a7229262a3acecf9bf9ded4c Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 26 Nov 2021 20:20:52 +0100
Subject: [PATCH 213/266] Comment out NXF_EDGE

---
 nf_core/pipeline-template/.github/workflows/ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index a7189aac69..0e8ec45ae2 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -35,7 +35,9 @@ jobs:
           CAPSULE_LOG: none
           {% raw -%}
           NXF_VER: ${{ matrix.NXF_VER }}
-          NXF_EDGE: ${{ matrix.NXF_EDGE }}
+          # 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 }}
           {%- endraw %}
         run: |
           wget -qO- get.nextflow.io | bash

From 2d9a75c3e39f976f34622e9496c2bc6b6cf23455 Mon Sep 17 00:00:00 2001
From: Alexander Peltzer <apeltzer@users.noreply.github.com>
Date: Mon, 29 Nov 2021 09:36:10 +0100
Subject: [PATCH 214/266] Update nextflow.config

Update with comment
---
 nf_core/pipeline-template/nextflow.config | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config
index 19e023192d..cb29bfa2de 100644
--- a/nf_core/pipeline-template/nextflow.config
+++ b/nf_core/pipeline-template/nextflow.config
@@ -126,6 +126,9 @@ profiles {
 }
 
 // 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"

From 551a5f09e1857d9fe913e0b98dc2743378e5a9f9 Mon Sep 17 00:00:00 2001
From: James Fellows Yates <jfy133@gmail.com>
Date: Tue, 30 Nov 2021 10:46:01 +0100
Subject: [PATCH 215/266] Replaces suffix with completely customisable prefix
 variable

---
 nf_core/module-template/modules/main.nf                         | 2 +-
 nf_core/modules/lint/main_nf.py                                 | 2 +-
 nf_core/pipeline-template/conf/modules.config                   | 2 +-
 .../pipeline-template/modules/nf-core/modules/fastqc/main.nf    | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/nf_core/module-template/modules/main.nf b/nf_core/module-template/modules/main.nf
index 0e4b549b25..d152e970b3 100644
--- a/nf_core/module-template/modules/main.nf
+++ b/nf_core/module-template/modules/main.nf
@@ -46,7 +46,7 @@ process {{ tool_name_underscore|upper }} {
     script:
     def args = task.ext.args ?: ''
     {% if has_meta -%}
-    def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
+    def prefix = task.ext.prefix ?: "${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
diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index d8d7516c9a..032463f6bc 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -108,7 +108,7 @@ def check_script_section(self, lines):
 
     # 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):
+        if re.search("\s*prefix\s*=\s*task.args.prefix", script):
             self.passed.append(("main_nf_meta_prefix", "'prefix' specified in script section", self.main_nf))
         else:
             self.failed.append(("main_nf_meta_prefix", "'prefix' unspecified in script section", self.main_nf))
diff --git a/nf_core/pipeline-template/conf/modules.config b/nf_core/pipeline-template/conf/modules.config
index 478869953b..a0506a4db4 100644
--- a/nf_core/pipeline-template/conf/modules.config
+++ b/nf_core/pipeline-template/conf/modules.config
@@ -6,7 +6,7 @@
         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.suffix          = File name suffix for output files.
+        ext.prefix          = File name prefix for output files.
 ----------------------------------------------------------------------------------------
 */
 
diff --git a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
index 673a00b841..d250eca075 100644
--- a/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
+++ b/nf_core/pipeline-template/modules/nf-core/modules/fastqc/main.nf
@@ -18,7 +18,7 @@ process FASTQC {
     script:
     def args = task.ext.args ?: ''
     // Add soft-links to original FastQs for consistent naming in pipeline
-    def prefix = task.ext.suffix ? "${meta.id}${task.ext.suffix}" : "${meta.id}"
+    def prefix = task.ext.prefix ?: "${meta.id}"
     if (meta.single_end) {
         """
         [ ! -f  ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz

From 26cb85773081df2722126ae08fab2569cae6dc80 Mon Sep 17 00:00:00 2001
From: "James A. Fellows Yates" <jfy133@gmail.com>
Date: Tue, 30 Nov 2021 11:24:04 +0100
Subject: [PATCH 216/266] Update nf_core/modules/lint/main_nf.py

---
 nf_core/modules/lint/main_nf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/main_nf.py b/nf_core/modules/lint/main_nf.py
index 032463f6bc..065910d3b8 100644
--- a/nf_core/modules/lint/main_nf.py
+++ b/nf_core/modules/lint/main_nf.py
@@ -108,7 +108,7 @@ def check_script_section(self, lines):
 
     # check for prefix (only if module has a meta map as input)
     if self.has_meta:
-        if re.search("\s*prefix\s*=\s*task.args.prefix", script):
+        if re.search("\s*prefix\s*=\s*task.ext.prefix", script):
             self.passed.append(("main_nf_meta_prefix", "'prefix' specified in script section", self.main_nf))
         else:
             self.failed.append(("main_nf_meta_prefix", "'prefix' unspecified in script section", self.main_nf))

From 5cd3b385acc757d56ccd11b7793388c8eb5c3479 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Wed, 1 Dec 2021 15:46:37 +0100
Subject: [PATCH 217/266] Update contributing in pipeline template

---
 .../pipeline-template/.github/CONTRIBUTING.md | 38 ++++---------------
 1 file changed, 7 insertions(+), 31 deletions(-)

diff --git a/nf_core/pipeline-template/.github/CONTRIBUTING.md b/nf_core/pipeline-template/.github/CONTRIBUTING.md
index bf43ef3fd4..edc89666ab 100644
--- a/nf_core/pipeline-template/.github/CONTRIBUTING.md
+++ b/nf_core/pipeline-template/.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).

From 73052b8e0a79ae3a3261d6f1a49c545263d73548 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Thu, 2 Dec 2021 11:48:21 +0100
Subject: [PATCH 218/266] Add NXF_VER to aws test yml files

---
 nf_core/pipeline-template/.github/workflows/awsfulltest.yml | 2 +-
 nf_core/pipeline-template/.github/workflows/awstest.yml     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/awsfulltest.yml b/nf_core/pipeline-template/.github/workflows/awsfulltest.yml
index 2b83987597..8e0ab65b23 100644
--- a/nf_core/pipeline-template/.github/workflows/awsfulltest.yml
+++ b/nf_core/pipeline-template/.github/workflows/awsfulltest.yml
@@ -31,4 +31,4 @@ jobs:
               "outdir": "s3://{% raw %}${{ secrets.AWS_S3_BUCKET }}{% endraw %}/{{ short_name }}/{% raw %}results-${{ github.sha }}{% endraw %}"
             }
           profiles: test_full,aws_tower
-
+          pre_run_script: 'export NXF_VER=21.10.3'
diff --git a/nf_core/pipeline-template/.github/workflows/awstest.yml b/nf_core/pipeline-template/.github/workflows/awstest.yml
index 3ef7d2b93d..ffa04f14d4 100644
--- a/nf_core/pipeline-template/.github/workflows/awstest.yml
+++ b/nf_core/pipeline-template/.github/workflows/awstest.yml
@@ -25,4 +25,4 @@ jobs:
               "outdir": "s3://{% raw %}${{ secrets.AWS_S3_BUCKET }}{% endraw %}/{{ short_name }}/{% raw %}results-${{ github.sha }}{% endraw %}"
             }
           profiles: test,aws_tower
-
+          pre_run_script: 'export NXF_VER=21.10.3'

From 78174c01b1c44a1bc8f2df9b01b7ad0fc4e74790 Mon Sep 17 00:00:00 2001
From: JoseEspinosa <kadomu@gmail.com>
Date: Thu, 2 Dec 2021 11:59:21 +0100
Subject: [PATCH 219/266] Update changelog

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 202e87d7cb..ae5038965c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@
 * New YAML issue templates for pipeline bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 * Update AWS test GitHub Actions to use v2 of [nf-core/tower-action](https://github.com/nf-core/tower-action)
 * Post linting comment even when `linting.yml` fails
+* Update `CONTRIBUTION.md` bullets to remove points related to `scrape_software_versions.py`
+* Update AWS test to set Nextflow version to 21.10.3
 
 ### General
 

From a505a2b536b124987221abf64b577e1c9f25910f Mon Sep 17 00:00:00 2001
From: James Fellows Yates <jfy133@gmail.com>
Date: Thu, 2 Dec 2021 13:49:10 +0100
Subject: [PATCH 220/266] update modules in pipeline template to new prefix
 syntax

---
 nf_core/pipeline-template/modules.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/modules.json b/nf_core/pipeline-template/modules.json
index e02a95aba8..853ad6a1e3 100644
--- a/nf_core/pipeline-template/modules.json
+++ b/nf_core/pipeline-template/modules.json
@@ -7,7 +7,7 @@
                 "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"
             },
             "fastqc": {
-                "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"
+                "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961"
             },
             "multiqc": {
                 "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41"

From d53f8e092e3c4fa8ad6e3ed8ff1d12f6f60e8073 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Thu, 2 Dec 2021 17:08:54 +0100
Subject: [PATCH 221/266] update repo logos for light and dark theme

---
 README.md                                     |   3 ++-
 docs/images/nfcore-tools_logo.png             | Bin 31762 -> 0 bytes
 docs/images/nfcore-tools_logo_dark.png        | Bin 0 -> 64937 bytes
 docs/images/nfcore-tools_logo_light.png       | Bin 0 -> 64856 bytes
 nf_core/create.py                             |  19 +++++++++---------
 nf_core/lint/files_exist.py                   |  12 +++++++----
 nf_core/lint/files_unchanged.py               |  12 +++++++----
 nf_core/pipeline-template/README.md           |   3 ++-
 .../assets/sendmail_template.txt              |   4 ++--
 9 files changed, 31 insertions(+), 22 deletions(-)
 delete mode 100644 docs/images/nfcore-tools_logo.png
 create mode 100644 docs/images/nfcore-tools_logo_dark.png
 create mode 100644 docs/images/nfcore-tools_logo_light.png

diff --git a/README.md b/README.md
index 616f806423..9981755fc5 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# ![nf-core/tools](docs/images/nfcore-tools_logo.png) <!-- omit in toc -->
+# ![nf-core/tools](docs/images/nfcore-tools_logo_light.png#gh-light-mode-only) <!-- omit in toc -->
+# ![nf-core/tools](docs/images/nfcore-tools_logo_dark.png#gh-dark-mode-only) <!-- omit in toc -->
 
 [![Python tests](https://github.com/nf-core/tools/workflows/Python%20tests/badge.svg?branch=master&event=push)](https://github.com/nf-core/tools/actions?query=workflow%3A%22Python+tests%22+branch%3Amaster)
 [![codecov](https://codecov.io/gh/nf-core/tools/branch/master/graph/badge.svg)](https://codecov.io/gh/nf-core/tools)
diff --git a/docs/images/nfcore-tools_logo.png b/docs/images/nfcore-tools_logo.png
deleted file mode 100644
index e45813591e90a97ac9db506cfc2d854814e3726c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 31762
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
MUQMo0=CR-Z0jKc0jsO4v

diff --git a/docs/images/nfcore-tools_logo_dark.png b/docs/images/nfcore-tools_logo_dark.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b9cc02b17522232a22912d2e069f87db0ed823e
GIT binary patch
literal 64937
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%0D6Aha9ZZvy3Owj0R@{kHIA1_W(q
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%0AMbV!W1C`kJJUi;hzNtQgf8qKi8~
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

literal 0
HcmV?d00001

diff --git a/docs/images/nfcore-tools_logo_light.png b/docs/images/nfcore-tools_logo_light.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc4ccea1cb12e60311b513bc7de0c339ebd591cb
GIT binary patch
literal 64856
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

literal 0
HcmV?d00001

diff --git a/nf_core/create.py b/nf_core/create.py
index de30f8d3dd..763673a312 100644
--- a/nf_core/create.py
+++ b/nf_core/create.py
@@ -145,23 +145,22 @@ def render_template(self):
     def make_pipeline_logo(self):
         """Fetch a logo for the new pipeline from the nf-core website"""
 
-        logo_url = f"https://nf-co.re/logo/{self.short_name}"
+        logo_url = f"https://nf-co.re/logo/{self.short_name}?theme=light"
         log.debug(f"Fetching logo from {logo_url}")
 
-        email_logo_path = f"{self.outdir}/assets/{self.name_noslash}_logo.png"
+        email_logo_path = f"{self.outdir}/assets/{self.name_noslash}_logo_light.png"
         os.makedirs(os.path.dirname(email_logo_path), exist_ok=True)
         log.debug(f"Writing logo to '{email_logo_path}'")
         r = requests.get(f"{logo_url}?w=400")
         with open(email_logo_path, "wb") as fh:
             fh.write(r.content)
-
-        readme_logo_path = f"{self.outdir}/docs/images/{self.name_noslash}_logo.png"
-
-        log.debug(f"Writing logo to '{readme_logo_path}'")
-        os.makedirs(os.path.dirname(readme_logo_path), exist_ok=True)
-        r = requests.get(f"{logo_url}?w=600")
-        with open(readme_logo_path, "wb") as fh:
-            fh.write(r.content)
+        for theme in ["dark", "light"]:
+            readme_logo_path = f"{self.outdir}/docs/images/{self.name_noslash}_logo_{theme}.png"
+            log.debug(f"Writing logo to '{readme_logo_path}'")
+            os.makedirs(os.path.dirname(readme_logo_path), exist_ok=True)
+            r = requests.get(f"{logo_url}?w=600&theme={theme}")
+            with open(readme_logo_path, "wb") as fh:
+                fh.write(r.content)
 
     def git_init_pipeline(self):
         """Initialises the new pipeline as a Git repository and submits first commit."""
diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index cb7d5f978c..8800157a7f 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -34,7 +34,8 @@ def files_exist(self):
         [LICENSE, LICENSE.md, LICENCE, LICENCE.md]  # NB: British / American spelling
         assets/email_template.html
         assets/email_template.txt
-        assets/nf-core-PIPELINE_logo.png
+        assets/nf-core-PIPELINE_logo_light.png
+        assets/nf-core-PIPELINE_logo_dark.png
         assets/sendmail_template.txt
         conf/modules.config
         conf/test.config
@@ -42,7 +43,8 @@ def files_exist(self):
         CHANGELOG.md
         CITATIONS.md
         CODE_OF_CONDUCT.md
-        docs/images/nf-core-PIPELINE_logo.png
+        docs/images/nf-core-PIPELINE_logo_light.png
+        docs/images/nf-core-PIPELINE_logo_dark.png
         docs/output.md
         docs/README.md
         docs/usage.md
@@ -119,11 +121,13 @@ def files_exist(self):
         [os.path.join("assets", "email_template.html")],
         [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("assets", f"nf-core-{short_name}_logo_light.png")],
+        [os.path.join("assets", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("conf", "modules.config")],
         [os.path.join("conf", "test.config")],
         [os.path.join("conf", "test_full.config")],
-        [os.path.join("docs", "images", f"nf-core-{short_name}_logo.png")],
+        [os.path.join("docs", "images", f"nf-core-{short_name}_logo_light.png")],
+        [os.path.join("docs", "images", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("docs", "output.md")],
         [os.path.join("docs", "README.md")],
         [os.path.join("docs", "README.md")],
diff --git a/nf_core/lint/files_unchanged.py b/nf_core/lint/files_unchanged.py
index 39842ae32c..806e201f46 100644
--- a/nf_core/lint/files_unchanged.py
+++ b/nf_core/lint/files_unchanged.py
@@ -31,10 +31,12 @@ def files_unchanged(self):
         .github/workflows/linting.yml
         assets/email_template.html
         assets/email_template.txt
-        assets/nf-core-PIPELINE_logo.png
+        assets/nf-core-PIPELINE_logo_light.png
+        assets/nf-core-PIPELINE_logo_dark.png
         assets/sendmail_template.txt
         CODE_OF_CONDUCT.md
-        docs/images/nf-core-PIPELINE_logo.png
+        docs/images/nf-core-PIPELINE_logo_light.png
+        docs/images/nf-core-PIPELINE_logo_dark.png
         docs/README.md'
         lib/nfcore_external_java_deps.jar
         lib/NfcoreSchema.groovy
@@ -91,8 +93,10 @@ def files_unchanged(self):
         [os.path.join("assets", "email_template.html")],
         [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("docs", "images", f"nf-core-{short_name}_logo.png")],
+        [os.path.join("assets", f"nf-core-{short_name}_logo_light.png")],
+        [os.path.join("assets", f"nf-core-{short_name}_logo_dark.png")],
+        [os.path.join("docs", "images", f"nf-core-{short_name}_logo_light.png")],
+        [os.path.join("docs", "images", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("docs", "README.md")],
         [os.path.join("lib", "nfcore_external_java_deps.jar")],
         [os.path.join("lib", "NfcoreSchema.groovy")],
diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index 6003672ebc..193a994bbd 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -1,4 +1,5 @@
-# ![{{ name }}](docs/images/{{ name_noslash }}_logo.png)
+# ![{{ name }}](docs/images/{{ name_noslash }}_logo_light.png#gh-light-mode-only)
+# ![{{ name }}](docs/images/{{ name_noslash }}_logo_dark.png#gh-dark-mode-only)
 
 [![GitHub Actions CI Status](https://github.com/{{ name }}/workflows/nf-core%20CI/badge.svg)](https://github.com/{{ name }}/actions?query=workflow%3A%22nf-core+CI%22)
 [![GitHub Actions Linting Status](https://github.com/{{ name }}/workflows/nf-core%20linting/badge.svg)](https://github.com/{{ name }}/actions?query=workflow%3A%22nf-core+linting%22)
diff --git a/nf_core/pipeline-template/assets/sendmail_template.txt b/nf_core/pipeline-template/assets/sendmail_template.txt
index 1abf5b0ab5..3e59cd2d6d 100644
--- a/nf_core/pipeline-template/assets/sendmail_template.txt
+++ b/nf_core/pipeline-template/assets/sendmail_template.txt
@@ -12,9 +12,9 @@ $email_html
 Content-Type: image/png;name="{{ name_noslash }}_logo.png"
 Content-Transfer-Encoding: base64
 Content-ID: <nfcorepipelinelogo>
-Content-Disposition: inline; filename="{{ name_noslash }}_logo.png"
+Content-Disposition: inline; filename="{{ name_noslash }}_logo_light.png"
 
-<% out << new File("$projectDir/assets/{{ name_noslash }}_logo.png").
+<% out << new File("$projectDir/assets/{{ name_noslash }}_logo_light.png").
     bytes.
     encodeBase64().
     toString().

From 6709dc9ce008336aebbcbbb3967b933e5982c0eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Thu, 2 Dec 2021 17:17:50 +0100
Subject: [PATCH 222/266] fix linting errors

---
 README.md                           | 3 +--
 nf_core/pipeline-template/README.md | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 9981755fc5..fc02bce284 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-# ![nf-core/tools](docs/images/nfcore-tools_logo_light.png#gh-light-mode-only) <!-- omit in toc -->
-# ![nf-core/tools](docs/images/nfcore-tools_logo_dark.png#gh-dark-mode-only) <!-- omit in toc -->
+# ![nf-core/tools](docs/images/nfcore-tools_logo_light.png#gh-light-mode-only) ![nf-core/tools](docs/images/nfcore-tools_logo_dark.png#gh-dark-mode-only) <!-- omit in toc -->
 
 [![Python tests](https://github.com/nf-core/tools/workflows/Python%20tests/badge.svg?branch=master&event=push)](https://github.com/nf-core/tools/actions?query=workflow%3A%22Python+tests%22+branch%3Amaster)
 [![codecov](https://codecov.io/gh/nf-core/tools/branch/master/graph/badge.svg)](https://codecov.io/gh/nf-core/tools)
diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md
index 193a994bbd..e7e539d990 100644
--- a/nf_core/pipeline-template/README.md
+++ b/nf_core/pipeline-template/README.md
@@ -1,5 +1,4 @@
-# ![{{ name }}](docs/images/{{ name_noslash }}_logo_light.png#gh-light-mode-only)
-# ![{{ name }}](docs/images/{{ name_noslash }}_logo_dark.png#gh-dark-mode-only)
+# ![{{ name }}](docs/images/{{ name_noslash }}_logo_light.png#gh-light-mode-only) ![{{ name }}](docs/images/{{ name_noslash }}_logo_dark.png#gh-dark-mode-only)
 
 [![GitHub Actions CI Status](https://github.com/{{ name }}/workflows/nf-core%20CI/badge.svg)](https://github.com/{{ name }}/actions?query=workflow%3A%22nf-core+CI%22)
 [![GitHub Actions Linting Status](https://github.com/{{ name }}/workflows/nf-core%20linting/badge.svg)](https://github.com/{{ name }}/actions?query=workflow%3A%22nf-core+linting%22)

From 3e44f0718e86dd2c252c565f660b783ffcc3f87e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Thu, 2 Dec 2021 17:33:02 +0100
Subject: [PATCH 223/266] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fcd034f39c..6958e7bc13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Template
 
+* Update repo logos to utilize [GitHub's `#gh-light/dark-mode-only`](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to), to switch between logos optimized for light or dark themes.
 * Solve circular import when importing `nf_core.modules.lint`
 * Disable cache in `nf_core.utils.fetch_wf_config` while performing `test_wf_use_local_configs`.
 * Modify software version channel handling to support multiple software version emissions (e.g. from mulled containers), and multiple software versions.

From 31df888f428492e5524e622aa95e61a7d4921d61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Fri, 3 Dec 2021 09:50:17 +0100
Subject: [PATCH 224/266] add lint warning to remove old style repo logo

---
 nf_core/lint/files_exist.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index 8800157a7f..2687f35563 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -79,6 +79,7 @@ def files_exist(self):
         conf/aws.config
         .github/workflows/push_dockerhub.yml
         .github/ISSUE_TEMPLATE/bug_report.md
+        docs/images/nf-core-PIPELINE_logo.png
 
     Files that *should not* be present:
 
@@ -159,6 +160,7 @@ def files_exist(self):
         os.path.join(".github", "workflows", "push_dockerhub.yml"),
         os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md"),
         os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md"),
+        os.path.join("docs", "images", "nf-core-PIPELINE_logo.png"),
     ]
     files_warn_ifexists = [".travis.yml"]
 

From e1a8e3167a45d1f7e3feac34469ea9b67bd5ecd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Fri, 3 Dec 2021 11:21:59 +0100
Subject: [PATCH 225/266] make linting pass (only check for light logo in email
 template)

---
 nf_core/create.py               | 2 +-
 nf_core/lint/files_exist.py     | 4 +---
 nf_core/lint/files_unchanged.py | 2 --
 3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/nf_core/create.py b/nf_core/create.py
index 763673a312..02dd50a1bc 100644
--- a/nf_core/create.py
+++ b/nf_core/create.py
@@ -151,7 +151,7 @@ def make_pipeline_logo(self):
         email_logo_path = f"{self.outdir}/assets/{self.name_noslash}_logo_light.png"
         os.makedirs(os.path.dirname(email_logo_path), exist_ok=True)
         log.debug(f"Writing logo to '{email_logo_path}'")
-        r = requests.get(f"{logo_url}?w=400")
+        r = requests.get(f"{logo_url}&w=400")
         with open(email_logo_path, "wb") as fh:
             fh.write(r.content)
         for theme in ["dark", "light"]:
diff --git a/nf_core/lint/files_exist.py b/nf_core/lint/files_exist.py
index 2687f35563..23759b8989 100644
--- a/nf_core/lint/files_exist.py
+++ b/nf_core/lint/files_exist.py
@@ -35,7 +35,6 @@ def files_exist(self):
         assets/email_template.html
         assets/email_template.txt
         assets/nf-core-PIPELINE_logo_light.png
-        assets/nf-core-PIPELINE_logo_dark.png
         assets/sendmail_template.txt
         conf/modules.config
         conf/test.config
@@ -123,7 +122,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_light.png")],
-        [os.path.join("assets", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("conf", "modules.config")],
         [os.path.join("conf", "test.config")],
         [os.path.join("conf", "test_full.config")],
@@ -160,7 +158,7 @@ def files_exist(self):
         os.path.join(".github", "workflows", "push_dockerhub.yml"),
         os.path.join(".github", "ISSUE_TEMPLATE", "bug_report.md"),
         os.path.join(".github", "ISSUE_TEMPLATE", "feature_request.md"),
-        os.path.join("docs", "images", "nf-core-PIPELINE_logo.png"),
+        os.path.join("docs", "images", f"nf-core-{short_name}_logo.png"),
     ]
     files_warn_ifexists = [".travis.yml"]
 
diff --git a/nf_core/lint/files_unchanged.py b/nf_core/lint/files_unchanged.py
index 806e201f46..6795e62930 100644
--- a/nf_core/lint/files_unchanged.py
+++ b/nf_core/lint/files_unchanged.py
@@ -32,7 +32,6 @@ def files_unchanged(self):
         assets/email_template.html
         assets/email_template.txt
         assets/nf-core-PIPELINE_logo_light.png
-        assets/nf-core-PIPELINE_logo_dark.png
         assets/sendmail_template.txt
         CODE_OF_CONDUCT.md
         docs/images/nf-core-PIPELINE_logo_light.png
@@ -94,7 +93,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_light.png")],
-        [os.path.join("assets", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("docs", "images", f"nf-core-{short_name}_logo_light.png")],
         [os.path.join("docs", "images", f"nf-core-{short_name}_logo_dark.png")],
         [os.path.join("docs", "README.md")],

From 932b97107e7675e70c4c6e8f81a7b62eeb894a48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6rtenhuber?= <mashehu3@gmail.com>
Date: Fri, 3 Dec 2021 11:30:33 +0100
Subject: [PATCH 226/266] mention removal of old logos in changelog

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48d4a88837..4a6f7926e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
 
 ### Template
 
-* Update repo logos to utilize [GitHub's `#gh-light/dark-mode-only`](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to), to switch between logos optimized for light or dark themes.
+* Update repo logos to utilize [GitHub's `#gh-light/dark-mode-only`](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to), to switch between logos optimized for light or dark themes. The old repo logos have to be removed (in `docs/images` and `assets/`).
 * Deal with authentication with private repositories
 * Bump minimun Nextflow version to 21.10.3
 * Convert pipeline template to updated Nextflow DSL2 syntax

From 0c8d1d7a5d2b73c26c6442297bb6a57fce969816 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 12:46:54 +0100
Subject: [PATCH 227/266] Don't assume that the module repo branch is master if
 not set

---
 nf_core/modules/modules_command.py |  8 ++++++--
 nf_core/modules/modules_repo.py    | 19 +++++++++++++++++--
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/nf_core/modules/modules_command.py b/nf_core/modules/modules_command.py
index ba8bba9ef1..866bffabb9 100644
--- a/nf_core/modules/modules_command.py
+++ b/nf_core/modules/modules_command.py
@@ -161,6 +161,7 @@ def modules_json_up_to_date(self):
                     modules_repo.get_modules_file_tree()
                     install_folder = [modules_repo.owner, modules_repo.repo]
                 except LookupError as e:
+                    log.warn(f"Could not get module's file tree for '{repo}': {e}")
                     remove_from_mod_json[repo] = list(modules.keys())
                     continue
 
@@ -169,6 +170,9 @@ def modules_json_up_to_date(self):
                     if sha is None:
                         if repo not in remove_from_mod_json:
                             remove_from_mod_json[repo] = []
+                        log.warn(
+                            f"Could not find git SHA for module '{module}' in '{repo}' - removing from modules.json"
+                        )
                         remove_from_mod_json[repo].append(module)
                         continue
                     module_dir = os.path.join(self.dir, "modules", *install_folder, module)
@@ -228,8 +232,8 @@ def _s(some_list):
                     return "" if len(some_list) == 1 else "s"
 
                 log.info(
-                    f"Could not determine 'git_sha' for module{_s(failed_to_find_commit_sha)}: '{', '.join(failed_to_find_commit_sha)}'."
-                    f"\nPlease try to install a newer version of {'this' if len(failed_to_find_commit_sha) == 1 else 'these'}  module{_s(failed_to_find_commit_sha)}."
+                    f"Could not determine 'git_sha' for module{_s(failed_to_find_commit_sha)}: {', '.join(failed_to_find_commit_sha)}."
+                    f"\nPlease try to install a newer version of {'this' if len(failed_to_find_commit_sha) == 1 else 'these'} module{_s(failed_to_find_commit_sha)}."
                 )
 
         self.dump_modules_json(fresh_mod_json)
diff --git a/nf_core/modules/modules_repo.py b/nf_core/modules/modules_repo.py
index 9a9c4f032c..5f2f567d9c 100644
--- a/nf_core/modules/modules_repo.py
+++ b/nf_core/modules/modules_repo.py
@@ -16,12 +16,17 @@ class ModulesRepo(object):
     so that this can be used in the same way by all sub-commands.
     """
 
-    def __init__(self, repo="nf-core/modules", branch="master"):
+    def __init__(self, repo="nf-core/modules", branch=None):
         self.name = repo
         self.branch = branch
 
         # Verify that the repo seems to be correctly configured
-        if self.name != "nf-core/modules" or self.branch != "master":
+        if self.name != "nf-core/modules" or self.branch:
+
+            # Get the default branch if not set
+            if not self.branch:
+                self.get_default_branch()
+
             try:
                 self.verify_modules_repo()
             except LookupError:
@@ -31,6 +36,16 @@ def __init__(self, repo="nf-core/modules", branch="master"):
         self.modules_file_tree = {}
         self.modules_avail_module_names = []
 
+    def get_default_branch(self):
+        """Get the default branch for a GitHub repo"""
+        api_url = f"https://api.github.com/repos/{self.name}"
+        response = requests.get(api_url, auth=nf_core.utils.github_api_auto_auth())
+        if response.status_code == 200:
+            self.branch = response.json()["default_branch"]
+            log.debug(f"Found default branch to be '{self.branch}'")
+        else:
+            raise LookupError(f"Could not find repository '{self.name}' on GitHub")
+
     def verify_modules_repo(self):
 
         # Check if name seems to be well formed

From 82b08198a4b7585569525b02ad0c9e3c62c4ba31 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 12:48:20 +0100
Subject: [PATCH 228/266] Set to master without checking if using
 nf-core/modules

---
 nf_core/modules/modules_repo.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/nf_core/modules/modules_repo.py b/nf_core/modules/modules_repo.py
index 5f2f567d9c..a59e3262aa 100644
--- a/nf_core/modules/modules_repo.py
+++ b/nf_core/modules/modules_repo.py
@@ -20,6 +20,10 @@ def __init__(self, repo="nf-core/modules", branch=None):
         self.name = repo
         self.branch = branch
 
+        # Don't bother fetching default branch if we're using nf-core
+        if not self.branch and self.name == "nf-core/modules":
+            self.branch = "master"
+
         # Verify that the repo seems to be correctly configured
         if self.name != "nf-core/modules" or self.branch:
 

From aff69ca0687d869d1f38fab437229ffa164236f7 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 13:09:19 +0100
Subject: [PATCH 229/266] Fix linting for new syntax

---
 nf_core/lint/actions_ci.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/lint/actions_ci.py b/nf_core/lint/actions_ci.py
index 48056a9422..c87931b77a 100644
--- a/nf_core/lint/actions_ci.py
+++ b/nf_core/lint/actions_ci.py
@@ -130,12 +130,12 @@ def actions_ci(self):
 
     # Check that we are testing the minimum nextflow version
     try:
-        matrix = ciwf["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"]
-        assert any([f"NXF_VER={self.minNextflowVersion}" in matrix])
+        matrix = ciwf["jobs"]["test"]["strategy"]["matrix"]["include"]
+        assert any([i["NXF_VER"] == self.minNextflowVersion for i in matrix])
     except (KeyError, TypeError):
         failed.append("'.github/workflows/ci.yml' does not check minimum NF version")
     except AssertionError:
-        failed.append("Minimum NF version in '.github/workflows/ci.yml' different to pipeline's manifest")
+        failed.append("Minimum NF version in '.github/workflows/ci.yml' is not tested")
     else:
         passed.append("'.github/workflows/ci.yml' checks minimum NF version")
 

From 518a52706bce44cc23caafec37856342a5e5da71 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 13:11:28 +0100
Subject: [PATCH 230/266] Linting: better error message

---
 nf_core/lint/actions_ci.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/nf_core/lint/actions_ci.py b/nf_core/lint/actions_ci.py
index c87931b77a..e9adf706b7 100644
--- a/nf_core/lint/actions_ci.py
+++ b/nf_core/lint/actions_ci.py
@@ -135,7 +135,9 @@ def actions_ci(self):
     except (KeyError, TypeError):
         failed.append("'.github/workflows/ci.yml' does not check minimum NF version")
     except AssertionError:
-        failed.append("Minimum NF version in '.github/workflows/ci.yml' is not tested")
+        failed.append(
+            f"Minimum pipeline NF version '{self.minNextflowVersion}' is not tested in '.github/workflows/ci.yml'"
+        )
     else:
         passed.append("'.github/workflows/ci.yml' checks minimum NF version")
 

From a42e530f0b30050226a0d9e6c48655aa7a8ed312 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 13:20:19 +0100
Subject: [PATCH 231/266] Update Nextflow bump-version

---
 nf_core/bump_version.py | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/nf_core/bump_version.py b/nf_core/bump_version.py
index 29f3a39f90..94bc17b66c 100644
--- a/nf_core/bump_version.py
+++ b/nf_core/bump_version.py
@@ -81,9 +81,9 @@ def bump_nextflow_version(pipeline_obj, new_version):
         pipeline_obj,
         [
             (
-                # example: nxf_ver: ['20.04.0', '']
-                r"nxf_ver: \[[\'\"]{}[\'\"], [\'\"][\'\"]\]".format(current_version.replace(".", r"\.")),
-                "nxf_ver: ['{}', '']".format(new_version),
+                # example: - NXF_VER: '20.04.0'
+                r"- NXF_VER: [\'\"]{}[\'\"]".format(current_version.replace(".", r"\.")),
+                "- NXF_VER: '{}'".format(new_version),
             )
         ],
     )
@@ -97,14 +97,11 @@ def bump_nextflow_version(pipeline_obj, new_version):
                 r"nextflow%20DSL2-%E2%89%A5{}-23aa62.svg".format(current_version.replace(".", r"\.")),
                 "nextflow%20DSL2-%E2%89%A5{}-23aa62.svg".format(new_version),
             ),
-            (
-                # Replace links to 'nf-co.re' installation page with links to Nextflow installation page
-                r"https://nf-co.re/usage/installation",
-                "https://www.nextflow.io/docs/latest/getstarted.html#installation",
-            ),
             (
                 # example: 1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=20.04.0`)
-                r"1\.\s*Install\s*\[`Nextflow`\]\(y\)\s*\(`>={}`\)".format(current_version.replace(".", r"\.")),
+                r"1\.\s*Install\s*\[`Nextflow`\]\(https:\/\/www\.nextflow\.io\/docs\/latest\/getstarted\.html#installation\)\s*\(`>={}`\)".format(
+                    current_version.replace(".", r"\.")
+                ),
                 "1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>={}`)".format(
                     new_version
                 ),

From d7f186283db098d9ec5eb5c7a8cdff24b5408812 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Mon, 6 Dec 2021 13:23:41 +0100
Subject: [PATCH 232/266] Update pytests

---
 tests/lint/actions_ci.py   | 2 +-
 tests/test_bump_version.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/lint/actions_ci.py b/tests/lint/actions_ci.py
index 13cdfbbc00..3329089975 100644
--- a/tests/lint/actions_ci.py
+++ b/tests/lint/actions_ci.py
@@ -23,7 +23,7 @@ def test_actions_ci_fail_wrong_nf(self):
     self.lint_obj._load()
     self.lint_obj.minNextflowVersion = "1.2.3"
     results = self.lint_obj.actions_ci()
-    assert results["failed"] == ["Minimum NF version in '.github/workflows/ci.yml' different to pipeline's manifest"]
+    assert results["failed"] == ["Minimum pipeline NF version '1.2.3' is not tested in '.github/workflows/ci.yml'"]
 
 
 def test_actions_ci_fail_wrong_docker_ver(self):
diff --git a/tests/test_bump_version.py b/tests/test_bump_version.py
index 39840fe6d1..b9fc27a153 100644
--- a/tests/test_bump_version.py
+++ b/tests/test_bump_version.py
@@ -71,7 +71,7 @@ def test_bump_nextflow_version(datafiles):
     # Check .github/workflows/ci.yml
     with open(new_pipeline_obj._fp(".github/workflows/ci.yml")) as fh:
         ci_yaml = yaml.safe_load(fh)
-    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["nxf_ver"][0] == "NXF_VER=21.10.3"
+    assert ci_yaml["jobs"]["test"]["strategy"]["matrix"]["include"][0]["NXF_VER"] == "21.10.3"
 
     # Check README.md
     with open(new_pipeline_obj._fp("README.md")) as fh:

From 58c37cff9151a201eb79b6128b172dc9600c18b3 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 9 Dec 2021 13:14:01 +0100
Subject: [PATCH 233/266] Schema linting - check that schema default matches
 nextflow.config value

---
 nf_core/schema.py | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/nf_core/schema.py b/nf_core/schema.py
index de84f80002..c3825b4ba6 100644
--- a/nf_core/schema.py
+++ b/nf_core/schema.py
@@ -258,9 +258,7 @@ def validate_default_params(self):
                 if param in params_ignore:
                     continue
                 if param in self.pipeline_params:
-                    self.validate_config_default_parameter(
-                        param, group_properties[param]["type"], self.pipeline_params[param]
-                    )
+                    self.validate_config_default_parameter(param, group_properties[param], self.pipeline_params[param])
                 else:
                     self.invalid_nextflow_config_default_parameters[param] = "Not in pipeline parameters"
 
@@ -272,38 +270,52 @@ def validate_default_params(self):
                     continue
                 if param in self.pipeline_params:
                     self.validate_config_default_parameter(
-                        param, ungrouped_properties[param]["type"], self.pipeline_params[param]
+                        param, ungrouped_properties[param], self.pipeline_params[param]
                     )
                 else:
                     self.invalid_nextflow_config_default_parameters[param] = "Not in pipeline parameters"
 
-    def validate_config_default_parameter(self, param, schema_default_type, config_default):
+    def validate_config_default_parameter(self, param, schema_param, config_default):
         """
         Assure that default parameters in the nextflow.config are correctly set
         by comparing them to their type in the schema
         """
+
+        # If we have a default in the schema, check it matches the config
+        if "default" in schema_param and (
+            (schema_param["type"] == "boolean" and str(config_default).lower() != str(schema_param["default"]).lower())
+            and (str(schema_param["default"]) != str(config_default).strip('"').strip("'"))
+        ):
+            # Check that we are not deferring the execution of this parameter in the schema default with squiggly brakcets
+            if schema_param["type"] != "string" or "{" not in schema_param["default"]:
+                self.invalid_nextflow_config_default_parameters[
+                    param
+                ] = f"Schema default (`{schema_param['default']}`) does not match the config default (`{config_default}`)"
+                return
+
         # if default is null, we're good
         if config_default == "null":
             return
-        # else check for allowed defaults
-        if schema_default_type == "string":
+
+        # Check variable types in nextflow.config
+        if schema_param["type"] == "string":
             if str(config_default) in ["false", "true", "''"]:
                 self.invalid_nextflow_config_default_parameters[
                     param
                 ] = f"String should not be set to `{config_default}`"
-        if schema_default_type == "boolean":
+        if schema_param["type"] == "boolean":
             if not str(config_default) in ["false", "true"]:
                 self.invalid_nextflow_config_default_parameters[
                     param
                 ] = f"Booleans should only be true or false, not `{config_default}`"
-        if schema_default_type == "integer":
+        if schema_param["type"] == "integer":
             try:
                 int(config_default)
             except ValueError:
                 self.invalid_nextflow_config_default_parameters[
                     param
                 ] = f"Does not look like an integer: `{config_default}`"
-        if schema_default_type == "number":
+        if schema_param["type"] == "number":
             try:
                 float(config_default)
             except ValueError:

From df49066894d7ee0057ecd52c3c23a7997c654045 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 9 Dec 2021 13:17:19 +0100
Subject: [PATCH 234/266] Changelog update

---
 CHANGELOG.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f3cb5e1f6..0b99988184 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,7 +18,8 @@
 ### General
 
 * Made lint check for parameters defaults stricter [[#992](https://github.com/nf-core/tools/issues/992)]
-    * Defaults must now match the variable type specified in the schema
+    * Default values in `nextflow.config` must match the defaults given in the schema (anything with `{` in, or in `main.nf` is ignored)
+    * Defaults in `nextflow.config` must now match the variable _type_ specified in the schema
     * If you want the parameter to not have a default value, use `null`
     * Strings set to `false` or an empty string in `nextflow.config` will now fail linting
 * Bump minimun Nextflow version to 21.10.3

From 5130cc4dba8612d99f778a752dbec33b3c856334 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Thu, 9 Dec 2021 13:15:07 +0000
Subject: [PATCH 235/266] Bump version to 2.2

---
 CHANGELOG.md | 2 +-
 setup.py     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41b57e7e2c..0614fa089f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # nf-core/tools: Changelog
 
-## v2.2dev
+## [v2.2 - Lead Liger](https://github.com/nf-core/tools/releases/tag/2.2) - [2021-12-09]
 
 ### Template
 
diff --git a/setup.py b/setup.py
index 513f9d9fec..86252375b1 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 from setuptools import setup, find_packages
 
-version = "2.2dev"
+version = "2.2"
 
 with open("README.md") as f:
     readme = f.read()

From 64285182f6aba48a1548e288abbea5e8a4af739d Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 9 Dec 2021 23:35:58 +0100
Subject: [PATCH 236/266] Update miniconda base in base.Dockerfile

---
 base.Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/base.Dockerfile b/base.Dockerfile
index 8d3fe9941e..fee0797a1c 100644
--- a/base.Dockerfile
+++ b/base.Dockerfile
@@ -1,4 +1,4 @@
-FROM continuumio/miniconda3:4.9.2
+FROM continuumio/miniconda3:4.10.3
 LABEL authors="phil.ewels@scilifelab.se,alexander.peltzer@boehringer-ingelheim.com" \
       description="Docker image containing base requirements for the nfcore pipelines"
 

From 14ed38233c3588b0262fe5dc036655b57d38aebf Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 9 Dec 2021 23:43:15 +0100
Subject: [PATCH 237/266] Strip DSL1 base-image Dockerfile and build

---
 .github/workflows/push_dockerhub_dev.yml     | 13 +++++--------
 .github/workflows/push_dockerhub_release.yml | 10 ++++------
 CHANGELOG.md                                 |  1 +
 tools.Dockerfile => Dockerfile               |  0
 base.Dockerfile                              | 13 -------------
 5 files changed, 10 insertions(+), 27 deletions(-)
 rename tools.Dockerfile => Dockerfile (100%)
 delete mode 100644 base.Dockerfile

diff --git a/.github/workflows/push_dockerhub_dev.yml b/.github/workflows/push_dockerhub_dev.yml
index 6d8b2457b7..e0c26f2f4e 100644
--- a/.github/workflows/push_dockerhub_dev.yml
+++ b/.github/workflows/push_dockerhub_dev.yml
@@ -3,31 +3,28 @@ name: nf-core Docker push (dev)
 # Runs on nf-core repo releases and push event to 'dev' branch (PR merges)
 on:
   push:
-    branches:
-      - dev
+    branches: [dev]
 
 jobs:
   push_dockerhub:
     name: Push new Docker image to Docker Hub (dev)
     runs-on: ubuntu-latest
     # Only run for the nf-core repo, for releases and merged PRs
-    if: ${{ github.repository == 'nf-core/tools'}}
+    if: ${{ github.repository == 'nf-core/tools' }}
     env:
       DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
       DOCKERHUB_PASS: ${{ secrets.DOCKERHUB_PASS }}
     strategy:
-      matrix:
-        image: [base, tools]
       fail-fast: false
     steps:
-      - name: Check out tools code
+      - name: Check out code
         uses: actions/checkout@v2
 
       - name: Build new docker image
-        run: docker build --no-cache . -t nfcore/${{ matrix.image }}:dev -f ${{ matrix.image }}.Dockerfile
+        run: docker build --no-cache . -t nfcore/tools:dev
 
       - name: Push Docker image to DockerHub (dev)
         run: |
           echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
-          docker push nfcore/${{ matrix.image }}:dev
+          docker push nfcore/tools:dev
 
diff --git a/.github/workflows/push_dockerhub_release.yml b/.github/workflows/push_dockerhub_release.yml
index 7a58d02938..2aebb34400 100644
--- a/.github/workflows/push_dockerhub_release.yml
+++ b/.github/workflows/push_dockerhub_release.yml
@@ -15,19 +15,17 @@ jobs:
       DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
       DOCKERHUB_PASS: ${{ secrets.DOCKERHUB_PASS }}
     strategy:
-      matrix:
-        image: [base, tools]
       fail-fast: false
     steps:
       - name: Check out code
         uses: actions/checkout@v2
 
       - name: Build new docker image
-        run: docker build --no-cache . -t nfcore/${{ matrix.image }}:latest -f ${{ matrix.image }}.Dockerfile
+        run: docker build --no-cache . -t nfcore/tools:latest
 
       - name: Push Docker image to DockerHub (release)
         run: |
           echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
-          docker push nfcore/${{ matrix.image }}:latest
-          docker tag nfcore/${{ matrix.image }}:latest nfcore/${{ matrix.image }}:${{ github.event.release.tag_name }}
-          docker push nfcore/${{ matrix.image }}:${{ github.event.release.tag_name }}
+          docker push nfcore/tools:latest
+          docker tag nfcore/tools:latest nfcore/tools:${{ github.event.release.tag_name }}
+          docker push nfcore/tools:${{ github.event.release.tag_name }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41b57e7e2c..00d089b305 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@
 * Run CI test used to create and lint/run the pipeline template with minimum and latest edge release of NF ([#1304](https://github.com/nf-core/tools/issues/1304))
 * New YAML issue templates for tools bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 * Handle synax errors in Nextflow config nicely when running `nf-core schema build` ([#1267](https://github.com/nf-core/tools/pull/1267))
+* Remove base `Dockerfile` used for DSL1 pipeline container builds
 
 ### Modules
 
diff --git a/tools.Dockerfile b/Dockerfile
similarity index 100%
rename from tools.Dockerfile
rename to Dockerfile
diff --git a/base.Dockerfile b/base.Dockerfile
deleted file mode 100644
index fee0797a1c..0000000000
--- a/base.Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM continuumio/miniconda3:4.10.3
-LABEL authors="phil.ewels@scilifelab.se,alexander.peltzer@boehringer-ingelheim.com" \
-      description="Docker image containing base requirements for the nfcore pipelines"
-
-# Install procps so that Nextflow can poll CPU usage and
-# deep clean the apt cache to reduce image/layer size
-RUN apt-get update \
-      && apt-get install -y procps \
-      && apt-get clean -y && rm -rf /var/lib/apt/lists/*
-
-# Instruct R processes to use these empty files instead of clashing with a local version
-RUN touch .Rprofile
-RUN touch .Renviron

From 6524a3e2a87f0585231fe7d315909fc629d18a74 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Thu, 9 Dec 2021 23:57:31 +0100
Subject: [PATCH 238/266] Remove contents of tests/__init__.py

---
 tests/__init__.py | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/tests/__init__.py b/tests/__init__.py
index b3e4769de7..e69de29bb2 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,7 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Tue Nov  9 13:46:10 2021
-
-@author: Paolo Cozzi <paolo.cozzi@ibba.cnr.it>
-"""

From d4d0439b94e2f0f70778cea2240ec9c3b021f092 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 10 Dec 2021 00:15:19 +0100
Subject: [PATCH 239/266] Module lint: Fail instead of warn if local copy
 edited

---
 CHANGELOG.md                           | 1 +
 nf_core/modules/lint/module_changes.py | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a172ca097c..2b03ffaef1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@
 * Check if README is from modules repo
 * Update module template to DSL2 v2.0 (remove `functions.nf` from modules template and updating `main.nf` ([#1289](https://github.com/nf-core/tools/pull/))
 * Substitute get process/module name custom functions in module `main.nf` using template replacement ([#1284](https://github.com/nf-core/tools/issues/1284))
+* Linting now fails instead of warning if a local copy of a module does not match the remote ([#1313](https://github.com/nf-core/tools/issues/1313))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
diff --git a/nf_core/modules/lint/module_changes.py b/nf_core/modules/lint/module_changes.py
index 773e5db408..f36035e2cd 100644
--- a/nf_core/modules/lint/module_changes.py
+++ b/nf_core/modules/lint/module_changes.py
@@ -49,10 +49,10 @@ def module_changes(module_lint_object, module):
                 remote_copy = r.content.decode("utf-8")
 
                 if local_copy != remote_copy:
-                    module.warned.append(
+                    module.failed.append(
                         (
                             "check_local_copy",
-                            "Local copy of module outdated",
+                            "Local copy of module does not match remote",
                             f"{os.path.join(module.module_dir, f)}",
                         )
                     )

From ea9f8d2a59acdb925b8e651120256a42f2b993cf Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 10 Dec 2021 00:27:57 +0100
Subject: [PATCH 240/266] Modules lint: Check for md5sums of empty files

---
 CHANGELOG.md                         |  1 +
 nf_core/modules/lint/module_tests.py | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a172ca097c..a69e4c4159 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@
 * Check if README is from modules repo
 * Update module template to DSL2 v2.0 (remove `functions.nf` from modules template and updating `main.nf` ([#1289](https://github.com/nf-core/tools/pull/))
 * Substitute get process/module name custom functions in module `main.nf` using template replacement ([#1284](https://github.com/nf-core/tools/issues/1284))
+* Check test YML file for md5sums corresponding to empty files ([#1302](https://github.com/nf-core/tools/issues/1302))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 
diff --git a/nf_core/modules/lint/module_tests.py b/nf_core/modules/lint/module_tests.py
index fad25d20ac..7bef0112d1 100644
--- a/nf_core/modules/lint/module_tests.py
+++ b/nf_core/modules/lint/module_tests.py
@@ -52,11 +52,31 @@ def module_tests(module_lint_object, module):
                     if not tag in [module.module_name, module.module_name.split("/")[0]]:
                         all_tags_correct = False
 
+                # Look for md5sums of empty files
+                for tfile in test.get("files", []):
+                    if tfile.get("md5sum") == "d41d8cd98f00b204e9800998ecf8427e":
+                        module.warned.append(
+                            (
+                                "test_yml_md5sum",
+                                "md5sum for empty file found: d41d8cd98f00b204e9800998ecf8427e",
+                                module.test_yml,
+                            )
+                        )
+                    if tfile.get("md5sum") == "7029066c27ac6f5ef18d660d5741979a":
+                        module.warned.append(
+                            (
+                                "test_yml_md5sum",
+                                "md5sum for compressed empty file found: 7029066c27ac6f5ef18d660d5741979a",
+                                module.test_yml,
+                            )
+                        )
+
             if all_tags_correct:
                 module.passed.append(("test_yml_tags", "tags adhere to guidelines", module.test_yml))
             else:
                 module.failed.append(("test_yml_tags", "tags do not adhere to guidelines", module.test_yml))
 
+        # Test that the file exists
         module.passed.append(("test_yml_exists", "Test `test.yml` exists", module.test_yml))
     except FileNotFoundError:
         module.failed.append(("test_yml_exists", "Test `test.yml` does not exist", module.test_yml))

From a501ba2c14529a7c478c3415ff526865a50d4256 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Fri, 10 Dec 2021 00:32:01 +0100
Subject: [PATCH 241/266] Modules test builder: Don't add md5s for empty files

---
 nf_core/modules/test_yml_builder.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index 9b05a97070..c305e6ffc6 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -205,7 +205,11 @@ def create_test_file_dict(self, results_dir):
                 elem_md5 = self._md5(elem)
                 # Switch out the results directory path with the expected 'output' directory
                 elem = elem.replace(results_dir, "output")
-                test_files.append({"path": elem, "md5sum": elem_md5})
+                test_file = {"path": elem}
+                # Only add the md5 if it's not for an empty file / compressed empty file
+                if elem_md5 != "d41d8cd98f00b204e9800998ecf8427e" and elem_md5 != "7029066c27ac6f5ef18d660d5741979a":
+                    test_file["md5sum"] = elem_md5
+                test_files.append(test_file)
 
         test_files = sorted(test_files, key=operator.itemgetter("path"))
 

From c62517e1a3c1240e8bbbd7a6106bdcb910ce2b87 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 10 Dec 2021 01:14:36 +0000
Subject: [PATCH 242/266] Fix bug when printing help for enums

---
 nf_core/pipeline-template/lib/NfcoreSchema.groovy | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
index 3b02c0a217..40ab65f205 100755
--- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreSchema.groovy
@@ -362,7 +362,7 @@ class NfcoreSchema {
             }
         }
         for (ex in causingExceptions) {
-            printExceptions(ex, params_json, log)
+            printExceptions(ex, params_json, log, enums)
         }
     }
 

From d60fc5370302dcda1f52e7c7644b7fb296e4c8cf Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 10 Dec 2021 12:19:17 +0000
Subject: [PATCH 243/266] Remove joinModuleArgs function from template

---
 nf_core/pipeline-template/lib/Utils.groovy | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/nf_core/pipeline-template/lib/Utils.groovy b/nf_core/pipeline-template/lib/Utils.groovy
index 18173e9850..1b88aec0ea 100755
--- a/nf_core/pipeline-template/lib/Utils.groovy
+++ b/nf_core/pipeline-template/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(' ')
-    }
 }

From 06e0403840557737e5318459af6efe0768676853 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Fri, 10 Dec 2021 14:16:47 +0000
Subject: [PATCH 244/266] Fix wording in bug_report.yml

---
 nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
index 246362bf55..e904de573f 100644
--- a/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/nf_core/pipeline-template/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -36,7 +36,7 @@ body:
     attributes:
       label: Relevant files
       description: |
-        Please upload (drag and drop) and relevant files. Make into a `.zip` file if the extension is not allowed.
+        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

From 9ae5ddcb78b8cc1c8231d005782293b1e2ce7861 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelh@users.noreply.github.com>
Date: Tue, 14 Dec 2021 07:45:38 +0000
Subject: [PATCH 245/266] Update test.config

---
 nf_core/pipeline-template/conf/test.config | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/conf/test.config b/nf_core/pipeline-template/conf/test.config
index e4419fd0d1..eb2a725c87 100644
--- a/nf_core/pipeline-template/conf/test.config
+++ b/nf_core/pipeline-template/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

From f61613d60f849d3c632dcc64bee075f15a5f1591 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 10:34:06 +0100
Subject: [PATCH 246/266] test-yaml-generate - don't check md5s, check for
 empty files and raise UserWarning

---
 nf_core/modules/test_yml_builder.py | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index c305e6ffc6..aa66efd9c5 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -8,8 +8,10 @@
 from rich.syntax import Syntax
 
 import errno
+import gzip
 import hashlib
 import logging
+import operator
 import os
 import questionary
 import re
@@ -18,7 +20,6 @@
 import subprocess
 import tempfile
 import yaml
-import operator
 
 import nf_core.utils
 
@@ -187,6 +188,15 @@ def build_single_test(self, entry_point):
 
         return ep_test
 
+    def check_if_empty_file(self, fname):
+        """Check if the file is empty, or compressed empty"""
+        if os.path.getsize(fname) == 0:
+            return True
+        with gzip.open(fname, "rb") as fh:
+            if fh.read() == b"":
+                return True
+        return False
+
     def _md5(self, fname):
         """Generate md5 sum for file"""
         hash_md5 = hashlib.md5()
@@ -202,14 +212,13 @@ def create_test_file_dict(self, results_dir):
         for root, dir, file in os.walk(results_dir):
             for elem in file:
                 elem = os.path.join(root, elem)
+                # Check that this isn't an empty file
+                if self.check_if_empty_file(elem):
+                    raise UserWarning(f"Empty file found: '{elem}'")
                 elem_md5 = self._md5(elem)
                 # Switch out the results directory path with the expected 'output' directory
                 elem = elem.replace(results_dir, "output")
-                test_file = {"path": elem}
-                # Only add the md5 if it's not for an empty file / compressed empty file
-                if elem_md5 != "d41d8cd98f00b204e9800998ecf8427e" and elem_md5 != "7029066c27ac6f5ef18d660d5741979a":
-                    test_file["md5sum"] = elem_md5
-                test_files.append(test_file)
+                test_files.append({"path": elem, "md5sum": elem_md5})
 
         test_files = sorted(test_files, key=operator.itemgetter("path"))
 

From abe5c7ec19a07faa847c16494562ae2046285c2f Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 10:42:09 +0100
Subject: [PATCH 247/266] Fix gzip empty file check

---
 nf_core/modules/test_yml_builder.py | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index aa66efd9c5..eb48b24bd8 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -192,9 +192,12 @@ def check_if_empty_file(self, fname):
         """Check if the file is empty, or compressed empty"""
         if os.path.getsize(fname) == 0:
             return True
-        with gzip.open(fname, "rb") as fh:
-            if fh.read() == b"":
-                return True
+        try:
+            with gzip.open(fname, "rb") as fh:
+                if fh.read() == b"":
+                    return True
+        except gzip.BadGzipFile:
+            pass
         return False
 
     def _md5(self, fname):

From 055d2d06f684e93683b780d2cbfec2c3d220b045 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 10:43:00 +0100
Subject: [PATCH 248/266] Changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a69e4c4159..4ddab6c4e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
 * Update module template to DSL2 v2.0 (remove `functions.nf` from modules template and updating `main.nf` ([#1289](https://github.com/nf-core/tools/pull/))
 * Substitute get process/module name custom functions in module `main.nf` using template replacement ([#1284](https://github.com/nf-core/tools/issues/1284))
 * Check test YML file for md5sums corresponding to empty files ([#1302](https://github.com/nf-core/tools/issues/1302))
+* Exit with an error if empty files are found when generating the test YAML file ([#1302](https://github.com/nf-core/tools/issues/1302))
 
 ## [v2.1 - Zinc Zebra](https://github.com/nf-core/tools/releases/tag/2.1) - [2021-07-27]
 

From 4efad123f689ccd56b8458b2ada6d9f1d9b7548b Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 11:11:03 +0100
Subject: [PATCH 249/266] Don't exit with an error immediately, finish run then
 raise the UserWarning

---
 nf_core/modules/test_yml_builder.py | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index eb48b24bd8..04378a8714 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -47,6 +47,7 @@ def __init__(
         self.module_test_main = None
         self.entry_points = []
         self.tests = []
+        self.errors = []
 
     def run(self):
         """Run build steps"""
@@ -58,6 +59,9 @@ def run(self):
         self.scrape_workflow_entry_points()
         self.build_all_tests()
         self.print_test_yml()
+        if len(self.errors) > 0:
+            errors = "\n - ".join(self.errors)
+            raise UserWarning(f"Ran, but found errors:\n - {errors}")
 
     def check_inputs(self):
         """Do more complex checks about supplied flags."""
@@ -209,19 +213,23 @@ def _md5(self, fname):
         md5sum = hash_md5.hexdigest()
         return md5sum
 
-    def create_test_file_dict(self, results_dir):
+    def create_test_file_dict(self, results_dir, is_repeat=False):
         """Walk through directory and collect md5 sums"""
         test_files = []
         for root, dir, file in os.walk(results_dir):
             for elem in file:
                 elem = os.path.join(root, elem)
+                test_file = {"path": elem}
                 # Check that this isn't an empty file
                 if self.check_if_empty_file(elem):
-                    raise UserWarning(f"Empty file found: '{elem}'")
-                elem_md5 = self._md5(elem)
+                    if not is_repeat:
+                        self.errors.append(f"Empty file, skipping md5sum: '{os.path.basename(elem)}'")
+                else:
+                    elem_md5 = self._md5(elem)
+                    test_file["md5sum"] = elem_md5
                 # Switch out the results directory path with the expected 'output' directory
                 elem = elem.replace(results_dir, "output")
-                test_files.append({"path": elem, "md5sum": elem_md5})
+                test_files.append(test_file)
 
         test_files = sorted(test_files, key=operator.itemgetter("path"))
 
@@ -253,11 +261,11 @@ def get_md5_sums(self, entry_point, command, results_dir=None, results_dir_repea
 
         # If test was repeated, compare the md5 sums
         if results_dir_repeat:
-            test_files_repeat = self.create_test_file_dict(results_dir=results_dir_repeat)
+            test_files_repeat = self.create_test_file_dict(results_dir=results_dir_repeat, is_repeat=True)
 
             # Compare both test.yml files
             for i in range(len(test_files)):
-                if not test_files[i]["md5sum"] == test_files_repeat[i]["md5sum"]:
+                if test_files[i].get("md5sum") and not test_files[i].get("md5sum") == test_files_repeat[i]["md5sum"]:
                     test_files[i].pop("md5sum")
                     test_files[i][
                         "contains"

From 1eb80188ea1ab3d206b7e8eae7b7891ae454f806 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 11:20:57 +0100
Subject: [PATCH 250/266] Try alternative gzip method to make pytest work on
 GitHub Actions

---
 nf_core/modules/test_yml_builder.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index 04378a8714..a95fcf74b7 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -197,8 +197,9 @@ def check_if_empty_file(self, fname):
         if os.path.getsize(fname) == 0:
             return True
         try:
-            with gzip.open(fname, "rb") as fh:
-                if fh.read() == b"":
+            with open(fname, "rb") as fh:
+                g_f = gzip.GzipFile(fileobj=fh, mode="rb")
+                if g_f.read() == b"":
                     return True
         except gzip.BadGzipFile:
             pass

From 0f8059e5526330f23b4e1ecae73ed84a9c6910ae Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 11:24:10 +0100
Subject: [PATCH 251/266] Also run CI tests with Python 3.10

---
 .github/workflows/pytest.yml | 2 +-
 CHANGELOG.md                 | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index 263a4f5659..cb8b8805c1 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -17,7 +17,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
+        python-version: [3.6, 3.7, 3.8, 3.9, 3.10]
 
     steps:
       - uses: actions/checkout@v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ddab6c4e0..23e6127341 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@
 * New YAML issue templates for tools bug reports and feature requests, with a much richer interface ([#1165](https://github.com/nf-core/tools/pull/1165))
 * Handle synax errors in Nextflow config nicely when running `nf-core schema build` ([#1267](https://github.com/nf-core/tools/pull/1267))
 * Remove base `Dockerfile` used for DSL1 pipeline container builds
+* Run tests with Python 3.10
 
 ### Modules
 

From a20e95341b86918b9a4d115518ffdc7f8c63f733 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 11:27:29 +0100
Subject: [PATCH 252/266] Use quotes for Python versions. Don't want to test
 3.1

---
 .github/workflows/pytest.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index cb8b8805c1..39aa3acae8 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -17,7 +17,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, 3.10]
+        python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
 
     steps:
       - uses: actions/checkout@v2

From 7698bad3a8592bf7cb287fdbd312dff8343fc718 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 11:30:55 +0100
Subject: [PATCH 253/266] Support python 3.7

---
 nf_core/modules/test_yml_builder.py | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index a95fcf74b7..db0b51f6e5 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -201,7 +201,15 @@ def check_if_empty_file(self, fname):
                 g_f = gzip.GzipFile(fileobj=fh, mode="rb")
                 if g_f.read() == b"":
                     return True
-        except gzip.BadGzipFile:
+        except Exception as e:
+            # Python 3.8+
+            if hasattr(gzip, "BadGzipFile"):
+                if isinstance(e, gzip.BadGzipFile):
+                    pass
+            # Python 3.7
+            else:
+                if isinstance(e, OSError):
+                    pass
             pass
         return False
 

From 94e694a4da9531047f72ac3a7f5080132c29a4dd Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 13:37:35 +0100
Subject: [PATCH 254/266] Fix exception logic

---
 nf_core/modules/test_yml_builder.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py
index db0b51f6e5..d917a81053 100644
--- a/nf_core/modules/test_yml_builder.py
+++ b/nf_core/modules/test_yml_builder.py
@@ -207,10 +207,10 @@ def check_if_empty_file(self, fname):
                 if isinstance(e, gzip.BadGzipFile):
                     pass
             # Python 3.7
+            elif isinstance(e, OSError):
+                pass
             else:
-                if isinstance(e, OSError):
-                    pass
-            pass
+                raise e
         return False
 
     def _md5(self, fname):

From 6e0f809d547afd037b9bbd699c59875bd3d82a51 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 13:47:29 +0100
Subject: [PATCH 255/266] Module pytest - use a bioconda package that we don't
 have in modules

---
 tests/test_modules.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/test_modules.py b/tests/test_modules.py
index ef799e157b..b24c883485 100644
--- a/tests/test_modules.py
+++ b/tests/test_modules.py
@@ -22,7 +22,8 @@ def create_modules_repo_dummy():
     with open(os.path.join(root_dir, "README.md"), "w") as fh:
         fh.writelines(["# ![nf-core/modules](docs/images/nfcore-modules_logo.png)", "\n"])
 
-    module_create = nf_core.modules.ModuleCreate(root_dir, "star/align", "@author", "process_medium", False, False)
+    # bpipe is a valid package on bioconda that is very unlikely to ever be added to nf-core/modules
+    module_create = nf_core.modules.ModuleCreate(root_dir, "bpipe/test", "@author", "process_medium", False, False)
     module_create.create()
 
     return root_dir

From a67bbafc4ab27a9d26429a5d532726d83597918c Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 13:53:06 +0100
Subject: [PATCH 256/266] Update other tests execting star/align

---
 tests/modules/bump_versions.py   | 12 ++++++------
 tests/modules/create_test_yml.py | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/tests/modules/bump_versions.py b/tests/modules/bump_versions.py
index 5defa7d740..86b99e7c73 100644
--- a/tests/modules/bump_versions.py
+++ b/tests/modules/bump_versions.py
@@ -8,15 +8,15 @@
 
 def test_modules_bump_versions_single_module(self):
     """Test updating a single module"""
-    # Change the star/align version to an older version
-    main_nf_path = os.path.join(self.nfcore_modules, "modules", "star", "align", "main.nf")
+    # Change the bpipe/test version to an older version
+    main_nf_path = os.path.join(self.nfcore_modules, "modules", "bpipe", "test", "main.nf")
     with open(main_nf_path, "r") as fh:
         content = fh.read()
     new_content = re.sub(r"bioconda::star=\d.\d.\d\D?", r"bioconda::star=2.6.1d", content)
     with open(main_nf_path, "w") as fh:
         fh.write(new_content)
     version_bumper = nf_core.modules.ModuleVersionBumper(pipeline_dir=self.nfcore_modules)
-    version_bumper.bump_versions(module="star/align")
+    version_bumper.bump_versions(module="bpipe/test")
     assert len(version_bumper.failed) == 0
 
 
@@ -37,13 +37,13 @@ def test_modules_bump_versions_fail(self):
 
 def test_modules_bump_versions_fail_unknown_version(self):
     """Fail because of an unknown version"""
-    # Change the star/align version to an older version
-    main_nf_path = os.path.join(self.nfcore_modules, "modules", "star", "align", "main.nf")
+    # Change the bpipe/test version to an older version
+    main_nf_path = os.path.join(self.nfcore_modules, "modules", "bpipe", "test", "main.nf")
     with open(main_nf_path, "r") as fh:
         content = fh.read()
     new_content = re.sub(r"bioconda::star=\d.\d.\d\D?", r"bioconda::star=xxx", content)
     with open(main_nf_path, "w") as fh:
         fh.write(new_content)
     version_bumper = nf_core.modules.ModuleVersionBumper(pipeline_dir=self.nfcore_modules)
-    version_bumper.bump_versions(module="star/align")
+    version_bumper.bump_versions(module="bpipe/test")
     assert "Conda package had unknown version" in version_bumper.failed[0][0]
diff --git a/tests/modules/create_test_yml.py b/tests/modules/create_test_yml.py
index db286181c5..3ea802b27c 100644
--- a/tests/modules/create_test_yml.py
+++ b/tests/modules/create_test_yml.py
@@ -41,18 +41,18 @@ def test_modules_create_test_yml_get_md5(self):
 
 def test_modules_create_test_yml_entry_points(self):
     """Test extracting test entry points from a main.nf file"""
-    meta_builder = nf_core.modules.ModulesTestYmlBuilder("star/align", False, "./", False, True)
-    meta_builder.module_test_main = os.path.join(self.nfcore_modules, "tests", "modules", "star", "align", "main.nf")
+    meta_builder = nf_core.modules.ModulesTestYmlBuilder("bpipe/test", False, "./", False, True)
+    meta_builder.module_test_main = os.path.join(self.nfcore_modules, "tests", "modules", "bpipe", "test", "main.nf")
     meta_builder.scrape_workflow_entry_points()
-    assert meta_builder.entry_points[0] == "test_star_align"
+    assert meta_builder.entry_points[0] == "test_bpipe_test"
 
 
 def test_modules_create_test_yml_check_inputs(self):
     """Test the check_inputs() function - raise UserWarning because test.yml exists"""
     cwd = os.getcwd()
     os.chdir(self.nfcore_modules)
-    meta_builder = nf_core.modules.ModulesTestYmlBuilder("star/align", False, "./", False, True)
-    meta_builder.module_test_main = os.path.join(self.nfcore_modules, "tests", "modules", "star", "align", "main.nf")
+    meta_builder = nf_core.modules.ModulesTestYmlBuilder("bpipe/test", False, "./", False, True)
+    meta_builder.module_test_main = os.path.join(self.nfcore_modules, "tests", "modules", "bpipe", "test", "main.nf")
     with pytest.raises(UserWarning) as excinfo:
         meta_builder.check_inputs()
     os.chdir(cwd)

From 5f9774210d195fb2c759a4bdc54bcdcb9ae80531 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 13:58:37 +0100
Subject: [PATCH 257/266] Caught one more occurence of star in tests

---
 tests/modules/bump_versions.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/modules/bump_versions.py b/tests/modules/bump_versions.py
index 86b99e7c73..df891cd4c7 100644
--- a/tests/modules/bump_versions.py
+++ b/tests/modules/bump_versions.py
@@ -41,7 +41,7 @@ def test_modules_bump_versions_fail_unknown_version(self):
     main_nf_path = os.path.join(self.nfcore_modules, "modules", "bpipe", "test", "main.nf")
     with open(main_nf_path, "r") as fh:
         content = fh.read()
-    new_content = re.sub(r"bioconda::star=\d.\d.\d\D?", r"bioconda::star=xxx", content)
+    new_content = re.sub(r"bioconda::bpipe=\d.\d.\d\D?", r"bioconda::bpipe=xxx", content)
     with open(main_nf_path, "w") as fh:
         fh.write(new_content)
     version_bumper = nf_core.modules.ModuleVersionBumper(pipeline_dir=self.nfcore_modules)

From 303fb6e636b578ce83741d028d9f70e556871c21 Mon Sep 17 00:00:00 2001
From: Phil Ewels <phil.ewels@scilifelab.se>
Date: Tue, 14 Dec 2021 14:27:22 +0100
Subject: [PATCH 258/266] Don't run `nextflow -self-update` in CI tests

Also, move static env vars to top level of the workflow.
---
 nf_core/pipeline-template/.github/workflows/ci.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml
index 0e8ec45ae2..d8a7240f73 100644
--- a/nf_core/pipeline-template/.github/workflows/ci.yml
+++ b/nf_core/pipeline-template/.github/workflows/ci.yml
@@ -8,14 +8,16 @@ on:
   release:
     types: [published]
 
+env:
+  NXF_ANSI_LOG: false
+  CAPSULE_LOG: none
+
 jobs:
   test:
     name: Run workflow tests
     # Only run on push if this is the nf-core dev branch (merged PRs)
     if: {% raw %}${{{% endraw %} github.event_name != 'push' || (github.event_name == 'push' && github.repository == '{{ name }}') {% raw %}}}{% endraw %}
     runs-on: ubuntu-latest
-    env:
-      NXF_ANSI_LOG: false
     strategy:
       matrix:
         # Nextflow versions
@@ -32,7 +34,6 @@ jobs:
 
       - name: Install Nextflow
         env:
-          CAPSULE_LOG: none
           {% raw -%}
           NXF_VER: ${{ matrix.NXF_VER }}
           # Uncomment only if the edge release is more recent than the latest stable release
@@ -42,7 +43,6 @@ jobs:
         run: |
           wget -qO- get.nextflow.io | bash
           sudo mv nextflow /usr/local/bin/
-          nextflow self-update
 
       - name: Run pipeline with test data
         # TODO nf-core: You can customise CI pipeline run tests as required

From da4150a87ea00ad2521294856b2ac8c101e8c6be Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Tue, 14 Dec 2021 14:27:15 +0000
Subject: [PATCH 259/266] Change date in CHANGELOG

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a64c7fc101..f9520973f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # nf-core/tools: Changelog
 
-## [v2.2 - Lead Liger](https://github.com/nf-core/tools/releases/tag/2.2) - [2021-12-09]
+## [v2.2 - Lead Liger](https://github.com/nf-core/tools/releases/tag/2.2) - [2021-12-15]
 
 ### Template
 

From 8585b248150d3e0ecd31cbcf40b8698e509bed34 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Tue, 14 Dec 2021 14:29:03 +0000
Subject: [PATCH 260/266] Change date in CHANGELOG again

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9520973f9..a4dde35113 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # nf-core/tools: Changelog
 
-## [v2.2 - Lead Liger](https://github.com/nf-core/tools/releases/tag/2.2) - [2021-12-15]
+## [v2.2 - Lead Liger](https://github.com/nf-core/tools/releases/tag/2.2) - [2021-12-14]
 
 ### Template
 

From fed2e9b56b40c2fabaf0f166a3d05948c85209c3 Mon Sep 17 00:00:00 2001
From: ggabernet <gisela.gabernet@qbic.uni-tuebingen.de>
Date: Tue, 14 Dec 2021 15:58:59 +0100
Subject: [PATCH 261/266] fix gh wf workflow

---
 .github/workflows/create-lint-wf.yml | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/create-lint-wf.yml b/.github/workflows/create-lint-wf.yml
index d7cff18e29..d714477651 100644
--- a/.github/workflows/create-lint-wf.yml
+++ b/.github/workflows/create-lint-wf.yml
@@ -1,6 +1,10 @@
 name: Create a pipeline and lint it
 on: [push, pull_request]
 
+env:
+  NXF_ANSI_LOG: false
+  CAPSULE_LOG: none
+
 jobs:
   MakeTestWorkflow:
     runs-on: ubuntu-latest
@@ -8,8 +12,14 @@ jobs:
       NXF_ANSI_LOG: false
     strategy:
       matrix:
-        # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: ["NXF_VER=21.10.3", "NXF_EDGE=1"]
+        # 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:
       - uses: actions/checkout@v2
         name: Check out source-code repository
@@ -26,12 +36,13 @@ jobs:
 
       - 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/
-          export ${{ matrix.nxf_ver }}
-          nextflow self-update
 
       - name: nf-core create
         run: nf-core --log-file log.txt create -n testpipeline -d "This pipeline is for testing" -a "Testing McTestface"

From f51c46299e912ed17d6937c75d349374485508ed Mon Sep 17 00:00:00 2001
From: ggabernet <gisela.gabernet@qbic.uni-tuebingen.de>
Date: Tue, 14 Dec 2021 16:00:30 +0100
Subject: [PATCH 262/266] fix test wf

---
 .github/workflows/create-test-wf.yml | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml
index 4aa24e0894..9b13cadd39 100644
--- a/.github/workflows/create-test-wf.yml
+++ b/.github/workflows/create-test-wf.yml
@@ -1,6 +1,10 @@
 name: Create a pipeline and test it
 on: [push, pull_request]
 
+env:
+  NXF_ANSI_LOG: false
+  CAPSULE_LOG: none
+
 jobs:
   RunTestWorkflow:
     runs-on: ubuntu-latest
@@ -8,10 +12,14 @@ jobs:
       NXF_ANSI_LOG: false
     strategy:
       matrix:
-        # Nextflow versions: check pipeline minimum and latest edge version
-        nxf_ver: 
-          - "NXF_VER=21.10.3"
-          # - "NXF_EDGE=1"
+        # 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:
       - uses: actions/checkout@v2
         name: Check out source-code repository
@@ -28,12 +36,13 @@ jobs:
 
       - 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/
-          export ${{ matrix.nxf_ver }}
-          nextflow self-update
 
       - name: Run nf-core/tools
         run: |

From 7719f3c614c305e54ede36eab357f8af39776fcf Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelh@users.noreply.github.com>
Date: Tue, 14 Dec 2021 15:01:12 +0000
Subject: [PATCH 263/266] Update
 nf_core/pipeline-template/workflows/pipeline.nf

Co-authored-by: Maxime U. Garcia <max.u.garcia@gmail.com>
---
 nf_core/pipeline-template/workflows/pipeline.nf | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/nf_core/pipeline-template/workflows/pipeline.nf b/nf_core/pipeline-template/workflows/pipeline.nf
index 7ac029844f..460a3cf20a 100644
--- a/nf_core/pipeline-template/workflows/pipeline.nf
+++ b/nf_core/pipeline-template/workflows/pipeline.nf
@@ -46,8 +46,8 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check'
 //
 // MODULE: Installed directly from nf-core/modules
 //
-include { FASTQC  } from '../modules/nf-core/modules/fastqc/main'
-include { MULTIQC } from '../modules/nf-core/modules/multiqc/main'
+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'
 
 /*

From cc54b9e7cccaec61acb99f35bf97e1722cc00f83 Mon Sep 17 00:00:00 2001
From: ggabernet <gisela.gabernet@qbic.uni-tuebingen.de>
Date: Tue, 14 Dec 2021 16:03:25 +0100
Subject: [PATCH 264/266] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4dde35113..93ec4fa4a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,7 @@
 * Erase temporary files and folders while performing Python tests (pytest)
 * Remove base `Dockerfile` used for DSL1 pipeline container builds
 * Run tests with Python 3.10
+* [#1363](https://github.com/nf-core/tools/pull/1363) Fix tools CI workflow nextflow versions.
 
 ### Modules
 

From 5f8fad6789826869466ab3827cd8339915ead94f Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelh@users.noreply.github.com>
Date: Tue, 14 Dec 2021 15:34:59 +0000
Subject: [PATCH 265/266] Update
 nf_core/pipeline-template/lib/NfcoreTemplate.groovy

Co-authored-by: Jose Espinosa-Carrasco <kadomu@gmail.com>
---
 nf_core/pipeline-template/lib/NfcoreTemplate.groovy | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
index 06499409c5..2fc0a9b9b6 100755
--- a/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
+++ b/nf_core/pipeline-template/lib/NfcoreTemplate.groovy
@@ -24,7 +24,7 @@ class NfcoreTemplate {
     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 enviroment but can be acheived via one or more of the following:\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" +

From a5dc3c85c5fe6a9f21792396d0f6142dcc2a4a52 Mon Sep 17 00:00:00 2001
From: Harshil Patel <drpatelhh@gmail.com>
Date: Tue, 14 Dec 2021 15:57:34 +0000
Subject: [PATCH 266/266] Warn instead of fail if module is different from
 remote

---
 nf_core/modules/lint/module_changes.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nf_core/modules/lint/module_changes.py b/nf_core/modules/lint/module_changes.py
index f36035e2cd..44c2501424 100644
--- a/nf_core/modules/lint/module_changes.py
+++ b/nf_core/modules/lint/module_changes.py
@@ -49,7 +49,7 @@ def module_changes(module_lint_object, module):
                 remote_copy = r.content.decode("utf-8")
 
                 if local_copy != remote_copy:
-                    module.failed.append(
+                    module.warned.append(
                         (
                             "check_local_copy",
                             "Local copy of module does not match remote",