From ee2241a4e763239c3d45f343f7d5e930c8f1561f Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 25 Jun 2024 14:52:57 +0200 Subject: [PATCH 01/18] Replace check_max with resourceLimits --- CHANGELOG.md | 1 + nf_core/pipeline-template/conf/base.config | 40 +++++++++++++--------- nf_core/pipeline-template/nextflow.config | 33 ------------------ 3 files changed, 24 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 008c755848..c65b999a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ - Don't cache pip in `linting.yml` ([#2961](https://github.com/nf-core/tools/pull/2961)) - Lint pipelines with the nf-core template version and post comment if it is outdated ([#2978](https://github.com/nf-core/tools/pull/2978)) +- Replaces the old custom `check_max()` function with the Nextflow native `resourceLimits` directive ([]()) ### General diff --git a/nf_core/pipeline-template/conf/base.config b/nf_core/pipeline-template/conf/base.config index 9c62bf0634..72a912fe47 100644 --- a/nf_core/pipeline-template/conf/base.config +++ b/nf_core/pipeline-template/conf/base.config @@ -11,9 +11,15 @@ process { // TODO nf-core: Check the defaults for all processes - cpus = { check_max( 1 * task.attempt, 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 1 * task.attempt } + memory = { 6.GB * task.attempt } + time = { 4.h * task.attempt } + + resourceLimits = [ + cpus: params.max_cpus, + memory: params.max_memory, + time: params.max_time + ] errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } maxRetries = 1 @@ -27,30 +33,30 @@ process { // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_single { - cpus = { check_max( 1 , 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 1 } + memory = { 6.GB * task.attempt } + time = { 4.h * task.attempt } } withLabel:process_low { - cpus = { check_max( 2 * task.attempt, 'cpus' ) } - memory = { check_max( 12.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 2 * task.attempt } + memory = { 12.GB * task.attempt } + time = { 4.h * task.attempt } } withLabel:process_medium { - cpus = { check_max( 6 * task.attempt, 'cpus' ) } - memory = { check_max( 36.GB * task.attempt, 'memory' ) } - time = { check_max( 8.h * task.attempt, 'time' ) } + cpus = { 6 * task.attempt } + memory = { 36.GB * task.attempt } + time = { 8.h * task.attempt } } withLabel:process_high { - cpus = { check_max( 12 * task.attempt, 'cpus' ) } - memory = { check_max( 72.GB * task.attempt, 'memory' ) } - time = { check_max( 16.h * task.attempt, 'time' ) } + cpus = { 12 * task.attempt } + memory = { 72.GB * task.attempt } + time = { 16.h * task.attempt } } withLabel:process_long { - time = { check_max( 20.h * task.attempt, 'time' ) } + time = { 20.h * task.attempt } } withLabel:process_high_memory { - memory = { check_max( 200.GB * task.attempt, 'memory' ) } + memory = { 200.GB * task.attempt } } withLabel:error_ignore { errorStrategy = 'ignore' diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config index 2e6a56b001..0316c2e0c5 100644 --- a/nf_core/pipeline-template/nextflow.config +++ b/nf_core/pipeline-template/nextflow.config @@ -265,36 +265,3 @@ manifest { // 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) { - if (type == 'memory') { - try { - if (obj.compareTo(params.max_memory as nextflow.util.MemoryUnit) == 1) - return params.max_memory as nextflow.util.MemoryUnit - else - return obj - } catch (all) { - println " ### ERROR ### Max memory '${params.max_memory}' is not valid! Using default value: $obj" - return obj - } - } else if (type == 'time') { - try { - if (obj.compareTo(params.max_time as nextflow.util.Duration) == 1) - return params.max_time as nextflow.util.Duration - else - return obj - } catch (all) { - println " ### ERROR ### Max time '${params.max_time}' is not valid! Using default value: $obj" - return obj - } - } else if (type == 'cpus') { - try { - return Math.min( obj, params.max_cpus as int ) - } catch (all) { - println " ### ERROR ### Max cpus '${params.max_cpus}' is not valid! Using default value: $obj" - return obj - } - } -} From ce49baddf0a39478d1d5483d5cfeac56c32e1e77 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 25 Jun 2024 14:55:43 +0200 Subject: [PATCH 02/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c65b999a5a..60808610d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ - Don't cache pip in `linting.yml` ([#2961](https://github.com/nf-core/tools/pull/2961)) - Lint pipelines with the nf-core template version and post comment if it is outdated ([#2978](https://github.com/nf-core/tools/pull/2978)) -- Replaces the old custom `check_max()` function with the Nextflow native `resourceLimits` directive ([]()) +- Replaces the old custom `check_max()` function with the Nextflow native `resourceLimits` directive ([#3037](https://github.com/nf-core/tools/pull/3037)) ### General From c8abbef61080458001fe8eb590a26a05c627cea1 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 25 Jun 2024 15:13:28 +0200 Subject: [PATCH 03/18] Code alignment, and try to get test to run on GHA Actions runner --- .github/workflows/create-test-wf.yml | 2 +- nf_core/pipeline-template/conf/base.config | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml index a95a477459..cb2aad2675 100644 --- a/.github/workflows/create-test-wf.yml +++ b/.github/workflows/create-test-wf.yml @@ -75,7 +75,7 @@ jobs: pwd # echo content of current directory ls -la - nextflow run nf-core-testpipeline -profile test,self_hosted_runner --outdir ./results + nextflow run nf-core-testpipeline -profile test,self_hosted_runner --outdir ./results --max_cpus 4 - name: Upload log file artifact if: ${{ always() }} diff --git a/nf_core/pipeline-template/conf/base.config b/nf_core/pipeline-template/conf/base.config index 72a912fe47..fb3a8456bd 100644 --- a/nf_core/pipeline-template/conf/base.config +++ b/nf_core/pipeline-template/conf/base.config @@ -16,9 +16,9 @@ process { time = { 4.h * task.attempt } resourceLimits = [ - cpus: params.max_cpus, + cpus: params.max_cpus, memory: params.max_memory, - time: params.max_time + time: params.max_time ] errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } From 044fc1d6750f4cf9d3ccf87bcb6ed9f3e61760b8 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 1 Jul 2024 14:30:55 +0200 Subject: [PATCH 04/18] Remove default basic resource limits in template (should be set by users) --- nf_core/pipeline-template/conf/base.config | 6 ------ 1 file changed, 6 deletions(-) diff --git a/nf_core/pipeline-template/conf/base.config b/nf_core/pipeline-template/conf/base.config index fb3a8456bd..fa292339e3 100644 --- a/nf_core/pipeline-template/conf/base.config +++ b/nf_core/pipeline-template/conf/base.config @@ -15,12 +15,6 @@ process { memory = { 6.GB * task.attempt } time = { 4.h * task.attempt } - resourceLimits = [ - cpus: params.max_cpus, - memory: params.max_memory, - time: params.max_time - ] - errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } maxRetries = 1 maxErrors = '-1' From 528a207ca8e2e5169140876b5d7cdbf5c5845d5f Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 1 Jul 2024 14:31:31 +0200 Subject: [PATCH 05/18] Add a basic set of resource limits in test configs, matching GithubActions runner limits --- nf_core/pipeline-template/conf/test.config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nf_core/pipeline-template/conf/test.config b/nf_core/pipeline-template/conf/test.config index 827e21b7b7..2b9715efb6 100644 --- a/nf_core/pipeline-template/conf/test.config +++ b/nf_core/pipeline-template/conf/test.config @@ -10,6 +10,14 @@ ---------------------------------------------------------------------------------------- */ +process { + resourceLimits = [ + cpus: 4, + memory: '16.GB', + time: '1.h' + ] +} + params { config_profile_name = 'Test profile' config_profile_description = 'Minimal test dataset to check pipeline function' From 936802fbfc553dde07a2acb9df134c995f5ab2d3 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 1 Jul 2024 14:37:13 +0200 Subject: [PATCH 06/18] Bump minimum NXF version to allow resourceLimits --- nf_core/pipeline-template/nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config index 0316c2e0c5..d575c6186f 100644 --- a/nf_core/pipeline-template/nextflow.config +++ b/nf_core/pipeline-template/nextflow.config @@ -258,7 +258,7 @@ manifest { homePage = 'https://github.com/{{ name }}' description = """{{ description }}""" mainScript = 'main.nf' - nextflowVersion = '!>=23.04.0' + nextflowVersion = '!>=24.04.2' version = '{{ version }}' doi = '' } From 06a1dbe41203c284b92d34912d14f12d77aebb72 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 1 Jul 2024 14:43:23 +0200 Subject: [PATCH 07/18] Add linting check for now deprecated params --- nf_core/pipelines/lint/nextflow_config.py | 89 ++++++++++++++++++----- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/nf_core/pipelines/lint/nextflow_config.py b/nf_core/pipelines/lint/nextflow_config.py index f62100a70a..f1c4c536c5 100644 --- a/nf_core/pipelines/lint/nextflow_config.py +++ b/nf_core/pipelines/lint/nextflow_config.py @@ -87,6 +87,9 @@ def nextflow_config(self): * ``params.nf_required_version``: The old method for specifying the minimum Nextflow version. Replaced by ``manifest.nextflowVersion`` * ``params.container``: The old method for specifying the dockerhub container address. Replaced by ``process.container`` * ``igenomesIgnore``: Changed to ``igenomes_ignore`` + * ``params.max_cpus``: Old method of specifying the maximum number of CPUs a process can request. Replaced by native Nextflow `resourceLimits`directive. + * ``params.max_memory``: Old method of specifying the maximum number of memory can request. Replaced by native Nextflow `resourceLimits`directive. + * ``params.max_time``: Old method of specifying the maximum number of CPUs can request. Replaced by native Nextflow `resourceLimits`directive. .. tip:: The ``snake_case`` convention should now be used when defining pipeline parameters @@ -170,6 +173,9 @@ def nextflow_config(self): "params.igenomesIgnore", "params.name", "params.enable_conda", + "params.max_cpus", + "params.max_memory", + "params.max_time", ] # Remove field that should be ignored according to the linting config @@ -200,9 +206,13 @@ def nextflow_config(self): ignored.append(f"Config variable ignored: {self._wrap_quotes(cf)}") break if cf not in self.nf_config.keys(): - passed.append(f"Config variable (correctly) not found: {self._wrap_quotes(cf)}") + passed.append( + f"Config variable (correctly) not found: {self._wrap_quotes(cf)}" + ) else: - failed.append(f"Config variable (incorrectly) found: {self._wrap_quotes(cf)}") + failed.append( + f"Config variable (incorrectly) found: {self._wrap_quotes(cf)}" + ) # Check and warn if the process configuration is done with deprecated syntax process_with_deprecated_syntax = list( @@ -222,9 +232,13 @@ def nextflow_config(self): if k in ignore_configs: continue if self.nf_config.get(k) == "true": - passed.append(f"Config ``{k}`` had correct value: ``{self.nf_config.get(k)}``") + passed.append( + f"Config ``{k}`` had correct value: ``{self.nf_config.get(k)}``" + ) else: - failed.append(f"Config ``{k}`` did not have correct value: ``{self.nf_config.get(k)}``") + failed.append( + f"Config ``{k}`` did not have correct value: ``{self.nf_config.get(k)}``" + ) if "manifest.name" not in ignore_configs: # Check that the pipeline name starts with nf-core @@ -233,7 +247,9 @@ def nextflow_config(self): if not manifest_name.startswith("nf-core/"): raise AssertionError() except (AssertionError, IndexError): - failed.append(f"Config ``manifest.name`` did not begin with ``nf-core/``:\n {manifest_name}") + failed.append( + f"Config ``manifest.name`` did not begin with ``nf-core/``:\n {manifest_name}" + ) else: passed.append("Config ``manifest.name`` began with ``nf-core/``") @@ -249,7 +265,9 @@ def nextflow_config(self): ) else: - passed.append("Config variable ``manifest.homePage`` began with https://github.com/nf-core/") + passed.append( + "Config variable ``manifest.homePage`` began with https://github.com/nf-core/" + ) # Check that the DAG filename ends in ``.svg`` if "dag.file" in self.nf_config: @@ -257,12 +275,21 @@ def nextflow_config(self): if self.nf_config["dag.file"].strip("'\"").endswith(default_dag_format): passed.append(f"Config ``dag.file`` ended with ``{default_dag_format}``") else: - failed.append(f"Config ``dag.file`` did not end with ``{default_dag_format}``") + failed.append( + f"Config ``dag.file`` did not end with ``{default_dag_format}``" + ) # Check that the minimum nextflowVersion is set properly if "manifest.nextflowVersion" in self.nf_config: - if self.nf_config.get("manifest.nextflowVersion", "").strip("\"'").lstrip("!").startswith(">="): - passed.append("Config variable ``manifest.nextflowVersion`` started with >= or !>=") + if ( + self.nf_config.get("manifest.nextflowVersion", "") + .strip("\"'") + .lstrip("!") + .startswith(">=") + ): + passed.append( + "Config variable ``manifest.nextflowVersion`` started with >= or !>=" + ) else: failed.append( "Config ``manifest.nextflowVersion`` did not start with ``>=`` or ``!>=`` : " @@ -272,7 +299,9 @@ def nextflow_config(self): # Check that the pipeline version contains ``dev`` if not self.release_mode and "manifest.version" in self.nf_config: if self.nf_config["manifest.version"].strip(" '\"").endswith("dev"): - passed.append(f"Config ``manifest.version`` ends in ``dev``: ``{self.nf_config['manifest.version']}``") + passed.append( + f"Config ``manifest.version`` ends in ``dev``: ``{self.nf_config['manifest.version']}``" + ) else: warned.append( f"Config ``manifest.version`` should end in ``dev``: ``{self.nf_config['manifest.version']}``" @@ -291,18 +320,32 @@ def nextflow_config(self): if "custom_config" not in ignore_configs: # Check if custom profile params are set correctly - if self.nf_config.get("params.custom_config_version", "").strip("'") == "master": + if ( + self.nf_config.get("params.custom_config_version", "").strip("'") + == "master" + ): passed.append("Config `params.custom_config_version` is set to `master`") else: - failed.append("Config `params.custom_config_version` is not set to `master`") + failed.append( + "Config `params.custom_config_version` is not set to `master`" + ) - custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/{}".format( - self.nf_config.get("params.custom_config_version", "").strip("'") + custom_config_base = ( + "https://raw.githubusercontent.com/nf-core/configs/{}".format( + self.nf_config.get("params.custom_config_version", "").strip("'") + ) ) - if self.nf_config.get("params.custom_config_base", "").strip("'") == custom_config_base: - passed.append(f"Config `params.custom_config_base` is set to `{custom_config_base}`") + if ( + self.nf_config.get("params.custom_config_base", "").strip("'") + == custom_config_base + ): + passed.append( + f"Config `params.custom_config_base` is set to `{custom_config_base}`" + ) else: - failed.append(f"Config `params.custom_config_base` is not set to `{custom_config_base}`") + failed.append( + f"Config `params.custom_config_base` is not set to `{custom_config_base}`" + ) # Check that lines for loading custom profiles exist lines = [ @@ -344,7 +387,9 @@ def nextflow_config(self): match = re.search(r"\bprofiles\s*{", cleaned_content) if not match: - failed.append("nextflow.config does not contain `profiles` scope, but `test` profile is required") + failed.append( + "nextflow.config does not contain `profiles` scope, but `test` profile is required" + ) else: # Extract profiles scope content and check for test profile start = match.end() @@ -360,7 +405,9 @@ def nextflow_config(self): if re.search(r"\btest\s*{", profiles_content): passed.append("nextflow.config contains configuration profile `test`") else: - failed.append("nextflow.config does not contain configuration profile `test`") + failed.append( + "nextflow.config does not contain configuration profile `test`" + ) # Check that the default values in nextflow.config match the default values defined in the nextflow_schema.json ignore_defaults = [] @@ -404,7 +451,9 @@ def nextflow_config(self): schema_default = str(schema.schema_defaults[param_name]) config_default = str(self.nf_config[param]) if config_default is not None and config_default == schema_default: - passed.append(f"Config default value correct: {param}= {schema_default}") + passed.append( + f"Config default value correct: {param}= {schema_default}" + ) else: failed.append( f"Config default value incorrect: `{param}` is set as {self._wrap_quotes(schema_default)} in `nextflow_schema.json` but is {self._wrap_quotes(self.nf_config[param])} in `nextflow.config`." From 47abfc2a2dbf9f8ae42a70bc5b833e1836fdb121 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 1 Jul 2024 14:44:55 +0200 Subject: [PATCH 08/18] Add linting check for check_ params and continue removing from everywhere (not finished) --- nf_core/pipeline-template/conf/test.config | 5 --- nf_core/pipeline-template/nextflow.config | 6 ---- .../pipeline-template/nextflow_schema.json | 35 ------------------- 3 files changed, 46 deletions(-) diff --git a/nf_core/pipeline-template/conf/test.config b/nf_core/pipeline-template/conf/test.config index 2b9715efb6..0f1d974101 100644 --- a/nf_core/pipeline-template/conf/test.config +++ b/nf_core/pipeline-template/conf/test.config @@ -22,11 +22,6 @@ params { config_profile_name = 'Test profile' config_profile_description = 'Minimal test dataset to check pipeline function' - // Limit resources so that this can run on GitHub Actions - max_cpus = 2 - 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 // TODO nf-core: Give any required params for the test so that command line flags are not needed diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config index d575c6186f..7d9040104c 100644 --- a/nf_core/pipeline-template/nextflow.config +++ b/nf_core/pipeline-template/nextflow.config @@ -50,12 +50,6 @@ params { config_profile_url = null {%- endif %} - // Max resource options - // Defaults only, expecting to be overwritten - max_memory = '128.GB' - max_cpus = 16 - max_time = '240.h' - // Schema validation default options validationFailUnrecognisedParams = false validationLenientMode = false diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json index 18bad71b76..07a90f6259 100644 --- a/nf_core/pipeline-template/nextflow_schema.json +++ b/nf_core/pipeline-template/nextflow_schema.json @@ -124,41 +124,6 @@ } } }, - "max_job_request_options": { - "title": "Max job request options", - "type": "object", - "fa_icon": "fab fa-acquisitions-incorporated", - "description": "Set the top limit for requested resources for any single job.", - "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", - "properties": { - "max_cpus": { - "type": "integer", - "description": "Maximum number of CPUs that can be requested for any single job.", - "default": 16, - "fa_icon": "fas fa-microchip", - "hidden": true, - "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" - }, - "max_memory": { - "type": "string", - "description": "Maximum amount of memory that can be requested for any single job.", - "default": "128.GB", - "fa_icon": "fas fa-memory", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "hidden": true, - "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" - }, - "max_time": { - "type": "string", - "description": "Maximum amount of time that can be requested for any single job.", - "default": "240.h", - "fa_icon": "far fa-clock", - "pattern": "^(\\d+\\.?\\s*(s|m|h|d|day)\\s*)+$", - "hidden": true, - "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" - } - } - }, "generic_options": { "title": "Generic options", "type": "object", From 736d5e8ce5764555020074cb03d11267fe4eef54 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 4 Sep 2024 11:51:11 +0200 Subject: [PATCH 09/18] Removes all remaining references to max_* params --- .github/workflows/create-test-wf.yml | 2 +- CHANGELOG.md | 2 +- nf_core/pipelines/lint/nextflow_config.py | 16 ++- tests/pipelines/lint/test_nextflow_config.py | 87 ++------------ tests/pipelines/test_launch.py | 118 +++++++++++++++---- 5 files changed, 116 insertions(+), 109 deletions(-) diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml index 7d69cb7b7b..56c6c822a9 100644 --- a/.github/workflows/create-test-wf.yml +++ b/.github/workflows/create-test-wf.yml @@ -75,7 +75,7 @@ jobs: pwd # echo content of current directory ls -la - nextflow run nf-core-testpipeline -profile test,self_hosted_runner --outdir ./results --max_cpus 4 + nextflow run nf-core-testpipeline -profile test,self_hosted_runner --outdir ./results - name: Upload log file artifact if: ${{ always() }} diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3e558153..c5e22af180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - add option to exclude test configs from pipeline template ([#3133](https://github.com/nf-core/tools/pull/3133)) - add option to exclude tower.yml from pipeline template ([#3134](https://github.com/nf-core/tools/pull/3134)) - Add tests to ensure all files are part of a template customisation group and all groups are tested ([#3099](https://github.com/nf-core/tools/pull/3099)) +- Replaces the old custom `check_max()` function with the Nextflow native `resourceLimits` directive ([#3037](https://github.com/nf-core/tools/pull/3037)) ### Linting @@ -98,7 +99,6 @@ - Don't cache pip in `linting.yml` ([#2961](https://github.com/nf-core/tools/pull/2961)) - Lint pipelines with the nf-core template version and post comment if it is outdated ([#2978](https://github.com/nf-core/tools/pull/2978)) -- Replaces the old custom `check_max()` function with the Nextflow native `resourceLimits` directive ([#3037](https://github.com/nf-core/tools/pull/3037)) ### General diff --git a/nf_core/pipelines/lint/nextflow_config.py b/nf_core/pipelines/lint/nextflow_config.py index f694a1ab2c..4179517880 100644 --- a/nf_core/pipelines/lint/nextflow_config.py +++ b/nf_core/pipelines/lint/nextflow_config.py @@ -80,7 +80,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: * ``params.nf_required_version``: The old method for specifying the minimum Nextflow version. Replaced by ``manifest.nextflowVersion`` * ``params.container``: The old method for specifying the dockerhub container address. Replaced by ``process.container`` * ``igenomesIgnore``: Changed to ``igenomes_ignore`` - * ``params.max_cpus``: Old method of specifying the maximum number of CPUs a process can request. Replaced by native Nextflow `resourceLimits`directive. + * ``params.max_cpus``: Old method of specifying the maximum number of CPUs a process can request. Replaced by native Nextflow `resourceLimits`directive in config files. * ``params.max_memory``: Old method of specifying the maximum number of memory can request. Replaced by native Nextflow `resourceLimits`directive. * ``params.max_time``: Old method of specifying the maximum number of CPUs can request. Replaced by native Nextflow `resourceLimits`directive. @@ -149,7 +149,13 @@ def nextflow_config(self) -> Dict[str, List[str]]: ["params.input"], ] # Throw a warning if these are missing - config_warn = [["manifest.mainScript"], ["timeline.file"], ["trace.file"], ["report.file"], ["dag.file"]] + config_warn = [ + ["manifest.mainScript"], + ["timeline.file"], + ["trace.file"], + ["report.file"], + ["dag.file"], + ] # Old depreciated vars - fail if present config_fail_ifdefined = [ "params.nf_required_version", @@ -207,7 +213,11 @@ def nextflow_config(self) -> Dict[str, List[str]]: ) # Remove field that should be ignored according to the linting config - ignore_configs = self.lint_config.get("nextflow_config", []) if self.lint_config is not None else [] + ignore_configs = ( + self.lint_config.get("nextflow_config", []) + if self.lint_config is not None + else [] + ) for cfs in config_fail: for cf in cfs: diff --git a/tests/pipelines/lint/test_nextflow_config.py b/tests/pipelines/lint/test_nextflow_config.py index 3cc9355452..88b5e4503e 100644 --- a/tests/pipelines/lint/test_nextflow_config.py +++ b/tests/pipelines/lint/test_nextflow_config.py @@ -30,8 +30,9 @@ def test_default_values_match(self): result = lint_obj.nextflow_config() assert len(result["failed"]) == 0 assert len(result["warned"]) == 0 - assert "Config default value correct: params.max_cpus" in str(result["passed"]) - assert "Config default value correct: params.validate_params" in str(result["passed"]) + assert "Config default value correct: params.validate_params" in str( + result["passed"] + ) def test_nextflow_config_bad_name_fail(self): """Tests that config variable existence test fails with bad pipeline name""" @@ -69,80 +70,6 @@ def test_nextflow_config_missing_test_profile_failed(self): assert len(result["failed"]) > 0 assert len(result["warned"]) == 0 - def test_default_values_fail(self): - """Test linting fails if the default values in nextflow.config do not match the ones defined in the nextflow_schema.json.""" - # Change the default value of max_cpus in nextflow.config - nf_conf_file = Path(self.new_pipeline) / "nextflow.config" - with open(nf_conf_file) as f: - content = f.read() - fail_content = re.sub(r"\bmax_cpus\s*=\s*16\b", "max_cpus = 0", content) - with open(nf_conf_file, "w") as f: - f.write(fail_content) - # Change the default value of max_memory in nextflow_schema.json - nf_schema_file = Path(self.new_pipeline) / "nextflow_schema.json" - with open(nf_schema_file) as f: - content = f.read() - fail_content = re.sub(r'"default": "128.GB"', '"default": "18.GB"', content) - with open(nf_schema_file, "w") as f: - f.write(fail_content) - lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) - lint_obj.load_pipeline_config() - result = lint_obj.nextflow_config() - assert len(result["failed"]) == 2 - assert ( - "Config default value incorrect: `params.max_cpus` is set as `16` in `nextflow_schema.json` but is `0` in `nextflow.config`." - in result["failed"] - ) - assert ( - "Config default value incorrect: `params.max_memory` is set as `18.GB` in `nextflow_schema.json` but is `128.GB` in `nextflow.config`." - in result["failed"] - ) - - def test_catch_params_assignment_in_main_nf(self): - """Test linting fails if main.nf contains an assignment to a parameter from nextflow_schema.json.""" - # Add parameter assignment in main.nf - main_nf_file = Path(self.new_pipeline) / "main.nf" - with open(main_nf_file, "a") as f: - f.write("params.max_time = 42") - lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) - lint_obj.load_pipeline_config() - result = lint_obj.nextflow_config() - assert len(result["failed"]) == 1 - assert ( - result["failed"][0] - == "Config default value incorrect: `params.max_time` is set as `240.h` in `nextflow_schema.json` but is `null` in `nextflow.config`." - ) - - def test_allow_params_reference_in_main_nf(self): - """Test linting allows for references like `params.aligner == 'bwa'` in main.nf. The test will detect if the bug mentioned in GitHub-issue #2833 reemerges.""" - # Add parameter reference in main.nf - main_nf_file = Path(self.new_pipeline) / "main.nf" - with open(main_nf_file, "a") as f: - f.write("params.max_time == 42") - lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) - lint_obj.load_pipeline_config() - result = lint_obj.nextflow_config() - assert len(result["failed"]) == 0 - - def test_default_values_ignored(self): - """Test ignoring linting of default values.""" - # Add max_cpus to the ignore list - nf_core_yml_path = Path(self.new_pipeline) / ".nf-core.yml" - nf_core_yml = NFCoreYamlConfig( - repository_type="pipeline", lint={"nextflow_config": [{"config_defaults": ["params.max_cpus"]}]} - ) - with open(nf_core_yml_path, "w") as f: - yaml.dump(nf_core_yml.model_dump(), f) - - lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) - lint_obj.load_pipeline_config() - lint_obj._load_lint_config() - result = lint_obj.nextflow_config() - assert len(result["failed"]) == 0 - assert len(result["ignored"]) == 1 - assert "Config default value correct: params.max_cpu" not in str(result["passed"]) - assert "Config default ignored: params.max_cpus" in str(result["ignored"]) - def test_default_values_float(self): """Test comparing two float values.""" # Add a float value `dummy=0.0001` to the nextflow.config below `validate_params` @@ -150,7 +77,9 @@ def test_default_values_float(self): with open(nf_conf_file) as f: content = f.read() fail_content = re.sub( - r"validate_params\s*=\s*true", "params.validate_params = true\ndummy = 0.000000001", content + r"validate_params\s*=\s*true", + "params.validate_params = true\ndummy = 0.000000001", + content, ) with open(nf_conf_file, "w") as f: f.write(fail_content) @@ -180,7 +109,9 @@ def test_default_values_float_fail(self): with open(nf_conf_file) as f: content = f.read() fail_content = re.sub( - r"validate_params\s*=\s*true", "params.validate_params = true\ndummy = 0.000000001", content + r"validate_params\s*=\s*true", + "params.validate_params = true\ndummy = 0.000000001", + content, ) with open(nf_conf_file, "w") as f: f.write(fail_content) diff --git a/tests/pipelines/test_launch.py b/tests/pipelines/test_launch.py index d63e7b6dc5..5598fc695b 100644 --- a/tests/pipelines/test_launch.py +++ b/tests/pipelines/test_launch.py @@ -17,9 +17,13 @@ class TestLaunch(TestPipelines): def setUp(self) -> None: super().setUp() self.nf_params_fn = Path(self.pipeline_dir, "nf-params.json") - self.launcher = nf_core.pipelines.launch.Launch(self.pipeline_dir, params_out=self.nf_params_fn) + self.launcher = nf_core.pipelines.launch.Launch( + self.pipeline_dir, params_out=self.nf_params_fn + ) - @mock.patch.object(nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True]) + @mock.patch.object( + nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True] + ) @mock.patch.object(nf_core.pipelines.launch.Launch, "launch_web_gui") def test_launch_pipeline(self, mock_webbrowser, mock_lauch_web_gui): """Test the main launch function""" @@ -34,10 +38,14 @@ def test_launch_file_exists(self, mock_confirm): # Try and to launch, return with error assert self.launcher.launch_pipeline() is False - @mock.patch.object(nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True]) + @mock.patch.object( + nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True] + ) @mock.patch.object(nf_core.pipelines.launch.Launch, "launch_web_gui") @mock.patch.object(nf_core.pipelines.launch.Confirm, "ask", side_effect=[False]) - def test_launch_file_exists_overwrite(self, mock_webbrowser, mock_lauch_web_gui, mock_confirm): + def test_launch_file_exists_overwrite( + self, mock_webbrowser, mock_lauch_web_gui, mock_confirm + ): """Test that we detect an existing params file and we overwrite it""" # Make an empty params file to be overwritten open(self.nf_params_fn, "a").close() @@ -47,7 +55,14 @@ def test_launch_file_exists_overwrite(self, mock_webbrowser, mock_lauch_web_gui, def test_get_pipeline_schema(self): """Test loading the params schema from a pipeline""" self.launcher.get_pipeline_schema() - assert len(self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]) > 2 + assert ( + len( + self.launcher.schema_obj.schema["$defs"]["input_output_options"][ + "properties" + ] + ) + > 2 + ) @with_temporary_folder def test_make_pipeline_schema(self, tmp_path): @@ -58,10 +73,21 @@ def test_make_pipeline_schema(self, tmp_path): ) create_obj.init_pipeline() Path(test_pipeline_dir, "nextflow_schema.json").unlink() - self.launcher = nf_core.pipelines.launch.Launch(test_pipeline_dir, params_out=self.nf_params_fn) + self.launcher = nf_core.pipelines.launch.Launch( + test_pipeline_dir, params_out=self.nf_params_fn + ) self.launcher.get_pipeline_schema() - assert len(self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]) >= 2 - assert self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]["outdir"] == { + assert ( + len( + self.launcher.schema_obj.schema["$defs"]["input_output_options"][ + "properties" + ] + ) + >= 2 + ) + assert self.launcher.schema_obj.schema["$defs"]["input_output_options"][ + "properties" + ]["outdir"] == { "type": "string", "format": "directory-path", "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", @@ -91,8 +117,13 @@ def test_nf_merge_schema(self): self.launcher.get_pipeline_schema() self.launcher.set_schema_inputs() self.launcher.merge_nxf_flag_schema() - assert self.launcher.schema_obj.schema["allOf"][0] == {"$ref": "#/$defs/coreNextflow"} - assert "-resume" in self.launcher.schema_obj.schema["$defs"]["coreNextflow"]["properties"] + assert self.launcher.schema_obj.schema["allOf"][0] == { + "$ref": "#/$defs/coreNextflow" + } + assert ( + "-resume" + in self.launcher.schema_obj.schema["$defs"]["coreNextflow"]["properties"] + ) def test_ob_to_questionary_string(self): """Check converting a python dict to a pyenquirer format - simple strings""" @@ -101,14 +132,21 @@ def test_ob_to_questionary_string(self): "default": "data/*{1,2}.fastq.gz", } result = self.launcher.single_param_to_questionary("input", sc_obj) - assert result == {"type": "input", "name": "input", "message": "", "default": "data/*{1,2}.fastq.gz"} + assert result == { + "type": "input", + "name": "input", + "message": "", + "default": "data/*{1,2}.fastq.gz", + } @mock.patch("questionary.unsafe_prompt", side_effect=[{"use_web_gui": "Web based"}]) def test_prompt_web_gui_true(self, mock_prompt): """Check the prompt to launch the web schema or use the cli""" assert self.launcher.prompt_web_gui() is True - @mock.patch("questionary.unsafe_prompt", side_effect=[{"use_web_gui": "Command line"}]) + @mock.patch( + "questionary.unsafe_prompt", side_effect=[{"use_web_gui": "Command line"}] + ) def test_prompt_web_gui_false(self, mock_prompt): """Check the prompt to launch the web schema or use the cli""" assert self.launcher.prompt_web_gui() is False @@ -123,17 +161,23 @@ def test_launch_web_gui_missing_keys(self, mock_poll_nfcore_web_api): assert exc_info.value.args[0].startswith("Web launch response not recognised:") @mock.patch( - "nf_core.utils.poll_nfcore_web_api", side_effect=[{"api_url": "foo", "web_url": "bar", "status": "recieved"}] + "nf_core.utils.poll_nfcore_web_api", + side_effect=[{"api_url": "foo", "web_url": "bar", "status": "recieved"}], ) @mock.patch("webbrowser.open") @mock.patch("nf_core.utils.wait_cli_function") - def test_launch_web_gui(self, mock_poll_nfcore_web_api, mock_webbrowser, mock_wait_cli_function): + def test_launch_web_gui( + self, mock_poll_nfcore_web_api, mock_webbrowser, mock_wait_cli_function + ): """Check the code that opens the web browser""" self.launcher.get_pipeline_schema() self.launcher.merge_nxf_flag_schema() assert self.launcher.launch_web_gui() is None - @mock.patch("nf_core.utils.poll_nfcore_web_api", side_effect=[{"status": "error", "message": "foo"}]) + @mock.patch( + "nf_core.utils.poll_nfcore_web_api", + side_effect=[{"status": "error", "message": "foo"}], + ) def test_get_web_launch_response_error(self, mock_poll_nfcore_web_api): """Test polling the website for a launch response - status error""" with pytest.raises(AssertionError) as exc_info: @@ -145,14 +189,22 @@ def test_get_web_launch_response_unexpected(self, mock_poll_nfcore_web_api): """Test polling the website for a launch response - status error""" with pytest.raises(AssertionError) as exc_info: self.launcher.get_web_launch_response() - assert exc_info.value.args[0].startswith("Web launch GUI returned unexpected status (foo): ") + assert exc_info.value.args[0].startswith( + "Web launch GUI returned unexpected status (foo): " + ) - @mock.patch("nf_core.utils.poll_nfcore_web_api", side_effect=[{"status": "waiting_for_user"}]) + @mock.patch( + "nf_core.utils.poll_nfcore_web_api", + side_effect=[{"status": "waiting_for_user"}], + ) def test_get_web_launch_response_waiting(self, mock_poll_nfcore_web_api): """Test polling the website for a launch response - status waiting_for_user""" assert self.launcher.get_web_launch_response() is False - @mock.patch("nf_core.utils.poll_nfcore_web_api", side_effect=[{"status": "launch_params_complete"}]) + @mock.patch( + "nf_core.utils.poll_nfcore_web_api", + side_effect=[{"status": "launch_params_complete"}], + ) def test_get_web_launch_response_missing_keys(self, mock_poll_nfcore_web_api): """Test polling the website for a launch response - complete, but missing keys""" with pytest.raises(AssertionError) as exc_info: @@ -175,7 +227,9 @@ def test_get_web_launch_response_missing_keys(self, mock_poll_nfcore_web_api): ], ) @mock.patch.object(nf_core.pipelines.launch.Launch, "sanitise_web_response") - def test_get_web_launch_response_valid(self, mock_poll_nfcore_web_api, mock_sanitise): + def test_get_web_launch_response_valid( + self, mock_poll_nfcore_web_api, mock_sanitise + ): """Test polling the website for a launch response - complete, valid response""" self.launcher.get_pipeline_schema() assert self.launcher.get_web_launch_response() is True @@ -185,11 +239,9 @@ def test_sanitise_web_response(self): self.launcher.get_pipeline_schema() self.launcher.nxf_flags["-name"] = "" self.launcher.schema_obj.input_params["igenomes_ignore"] = "true" - self.launcher.schema_obj.input_params["max_cpus"] = "12" self.launcher.sanitise_web_response() assert "-name" not in self.launcher.nxf_flags assert self.launcher.schema_obj.input_params["igenomes_ignore"] is True - assert self.launcher.schema_obj.input_params["max_cpus"] == 12 def test_ob_to_questionary_bool(self): """Check converting a python dict to a pyenquirer format - booleans""" @@ -262,7 +314,10 @@ def test_ob_to_questionary_enum(self): def test_ob_to_questionary_pattern(self): """Check converting a python dict to a questionary format - with pattern""" - sc_obj = {"type": "string", "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$"} + sc_obj = { + "type": "string", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", + } result = self.launcher.single_param_to_questionary("email", sc_obj) assert result["type"] == "input" assert result["validate"]("test@email.com") is True @@ -295,7 +350,10 @@ def test_build_command_nf(self): self.launcher.nxf_flags["-name"] = "Test_Workflow" self.launcher.nxf_flags["-resume"] = True self.launcher.build_command() - assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} -name "Test_Workflow" -resume' + assert ( + self.launcher.nextflow_cmd + == f'nextflow run {self.pipeline_dir} -name "Test_Workflow" -resume' + ) def test_build_command_params(self): """Test the functionality to build a nextflow command - params supplied""" @@ -303,13 +361,18 @@ def test_build_command_params(self): self.launcher.schema_obj.input_params.update({"input": "custom_input"}) self.launcher.build_command() # Check command - assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} -params-file "{self.nf_params_fn}"' + assert ( + self.launcher.nextflow_cmd + == f'nextflow run {self.pipeline_dir} -params-file "{self.nf_params_fn}"' + ) # Check saved parameters file with open(self.nf_params_fn) as fh: try: saved_json = json.load(fh) except json.JSONDecodeError as e: - raise UserWarning(f"Unable to load JSON file '{self.nf_params_fn}' due to error {e}") + raise UserWarning( + f"Unable to load JSON file '{self.nf_params_fn}' due to error {e}" + ) assert saved_json == {"input": "custom_input"} @@ -319,4 +382,7 @@ def test_build_command_params_cl(self): self.launcher.get_pipeline_schema() self.launcher.schema_obj.input_params.update({"input": "custom_input"}) self.launcher.build_command() - assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} --input "custom_input"' + assert ( + self.launcher.nextflow_cmd + == f'nextflow run {self.pipeline_dir} --input "custom_input"' + ) From 556336034ffc1107a2c212fc2cd326a57953e150 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Wed, 4 Sep 2024 09:57:17 +0000 Subject: [PATCH 10/18] [automated] Fix code linting --- nf_core/pipelines/lint/nextflow_config.py | 89 +++++--------------- tests/pipelines/lint/test_nextflow_config.py | 7 +- tests/pipelines/test_launch.py | 86 ++++--------------- 3 files changed, 40 insertions(+), 142 deletions(-) diff --git a/nf_core/pipelines/lint/nextflow_config.py b/nf_core/pipelines/lint/nextflow_config.py index 4179517880..14e91cc609 100644 --- a/nf_core/pipelines/lint/nextflow_config.py +++ b/nf_core/pipelines/lint/nextflow_config.py @@ -213,11 +213,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: ) # Remove field that should be ignored according to the linting config - ignore_configs = ( - self.lint_config.get("nextflow_config", []) - if self.lint_config is not None - else [] - ) + ignore_configs = self.lint_config.get("nextflow_config", []) if self.lint_config is not None else [] for cfs in config_fail: for cf in cfs: @@ -244,13 +240,9 @@ def nextflow_config(self) -> Dict[str, List[str]]: ignored.append(f"Config variable ignored: {self._wrap_quotes(cf)}") break if cf not in self.nf_config.keys(): - passed.append( - f"Config variable (correctly) not found: {self._wrap_quotes(cf)}" - ) + passed.append(f"Config variable (correctly) not found: {self._wrap_quotes(cf)}") else: - failed.append( - f"Config variable (incorrectly) found: {self._wrap_quotes(cf)}" - ) + failed.append(f"Config variable (incorrectly) found: {self._wrap_quotes(cf)}") # Check and warn if the process configuration is done with deprecated syntax @@ -271,13 +263,9 @@ def nextflow_config(self) -> Dict[str, List[str]]: if k in ignore_configs: continue if self.nf_config.get(k) == "true": - passed.append( - f"Config ``{k}`` had correct value: ``{self.nf_config.get(k)}``" - ) + passed.append(f"Config ``{k}`` had correct value: ``{self.nf_config.get(k)}``") else: - failed.append( - f"Config ``{k}`` did not have correct value: ``{self.nf_config.get(k)}``" - ) + failed.append(f"Config ``{k}`` did not have correct value: ``{self.nf_config.get(k)}``") if "manifest.name" not in ignore_configs: # Check that the pipeline name starts with nf-core @@ -286,9 +274,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: if not manifest_name.startswith("nf-core/"): raise AssertionError() except (AssertionError, IndexError): - failed.append( - f"Config ``manifest.name`` did not begin with ``nf-core/``:\n {manifest_name}" - ) + failed.append(f"Config ``manifest.name`` did not begin with ``nf-core/``:\n {manifest_name}") else: passed.append("Config ``manifest.name`` began with ``nf-core/``") @@ -304,9 +290,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: ) else: - passed.append( - "Config variable ``manifest.homePage`` began with https://github.com/nf-core/" - ) + passed.append("Config variable ``manifest.homePage`` began with https://github.com/nf-core/") # Check that the DAG filename ends in ``.svg`` if "dag.file" in self.nf_config: @@ -314,21 +298,12 @@ def nextflow_config(self) -> Dict[str, List[str]]: if self.nf_config["dag.file"].strip("'\"").endswith(default_dag_format): passed.append(f"Config ``dag.file`` ended with ``{default_dag_format}``") else: - failed.append( - f"Config ``dag.file`` did not end with ``{default_dag_format}``" - ) + failed.append(f"Config ``dag.file`` did not end with ``{default_dag_format}``") # Check that the minimum nextflowVersion is set properly if "manifest.nextflowVersion" in self.nf_config: - if ( - self.nf_config.get("manifest.nextflowVersion", "") - .strip("\"'") - .lstrip("!") - .startswith(">=") - ): - passed.append( - "Config variable ``manifest.nextflowVersion`` started with >= or !>=" - ) + if self.nf_config.get("manifest.nextflowVersion", "").strip("\"'").lstrip("!").startswith(">="): + passed.append("Config variable ``manifest.nextflowVersion`` started with >= or !>=") else: failed.append( "Config ``manifest.nextflowVersion`` did not start with ``>=`` or ``!>=`` : " @@ -338,9 +313,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: # Check that the pipeline version contains ``dev`` if not self.release_mode and "manifest.version" in self.nf_config: if self.nf_config["manifest.version"].strip(" '\"").endswith("dev"): - passed.append( - f"Config ``manifest.version`` ends in ``dev``: ``{self.nf_config['manifest.version']}``" - ) + passed.append(f"Config ``manifest.version`` ends in ``dev``: ``{self.nf_config['manifest.version']}``") else: warned.append( f"Config ``manifest.version`` should end in ``dev``: ``{self.nf_config['manifest.version']}``" @@ -359,32 +332,18 @@ def nextflow_config(self) -> Dict[str, List[str]]: if "custom_config" not in ignore_configs: # Check if custom profile params are set correctly - if ( - self.nf_config.get("params.custom_config_version", "").strip("'") - == "master" - ): + if self.nf_config.get("params.custom_config_version", "").strip("'") == "master": passed.append("Config `params.custom_config_version` is set to `master`") else: - failed.append( - "Config `params.custom_config_version` is not set to `master`" - ) + failed.append("Config `params.custom_config_version` is not set to `master`") - custom_config_base = ( - "https://raw.githubusercontent.com/nf-core/configs/{}".format( - self.nf_config.get("params.custom_config_version", "").strip("'") - ) + custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/{}".format( + self.nf_config.get("params.custom_config_version", "").strip("'") ) - if ( - self.nf_config.get("params.custom_config_base", "").strip("'") - == custom_config_base - ): - passed.append( - f"Config `params.custom_config_base` is set to `{custom_config_base}`" - ) + if self.nf_config.get("params.custom_config_base", "").strip("'") == custom_config_base: + passed.append(f"Config `params.custom_config_base` is set to `{custom_config_base}`") else: - failed.append( - f"Config `params.custom_config_base` is not set to `{custom_config_base}`" - ) + failed.append(f"Config `params.custom_config_base` is not set to `{custom_config_base}`") # Check that lines for loading custom profiles exist lines = [ @@ -426,9 +385,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: match = re.search(r"\bprofiles\s*{", cleaned_content) if not match: - failed.append( - "nextflow.config does not contain `profiles` scope, but `test` profile is required" - ) + failed.append("nextflow.config does not contain `profiles` scope, but `test` profile is required") else: # Extract profiles scope content and check for test profile start = match.end() @@ -444,9 +401,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: if re.search(r"\btest\s*{", profiles_content): passed.append("nextflow.config contains configuration profile `test`") else: - failed.append( - "nextflow.config does not contain configuration profile `test`" - ) + failed.append("nextflow.config does not contain configuration profile `test`") # Check that the default values in nextflow.config match the default values defined in the nextflow_schema.json ignore_defaults = [] @@ -490,9 +445,7 @@ def nextflow_config(self) -> Dict[str, List[str]]: schema_default = str(schema.schema_defaults[param_name]) config_default = str(self.nf_config[param]) if config_default is not None and config_default == schema_default: - passed.append( - f"Config default value correct: {param}= {schema_default}" - ) + passed.append(f"Config default value correct: {param}= {schema_default}") else: failed.append( f"Config default value incorrect: `{param}` is set as {self._wrap_quotes(schema_default)} in `nextflow_schema.json` but is {self._wrap_quotes(self.nf_config[param])} in `nextflow.config`." diff --git a/tests/pipelines/lint/test_nextflow_config.py b/tests/pipelines/lint/test_nextflow_config.py index 88b5e4503e..4c3893f4c4 100644 --- a/tests/pipelines/lint/test_nextflow_config.py +++ b/tests/pipelines/lint/test_nextflow_config.py @@ -2,11 +2,8 @@ import re from pathlib import Path -import yaml - import nf_core.pipelines.create.create import nf_core.pipelines.lint -from nf_core.utils import NFCoreYamlConfig from ..test_lint import TestLint @@ -30,9 +27,7 @@ def test_default_values_match(self): result = lint_obj.nextflow_config() assert len(result["failed"]) == 0 assert len(result["warned"]) == 0 - assert "Config default value correct: params.validate_params" in str( - result["passed"] - ) + assert "Config default value correct: params.validate_params" in str(result["passed"]) def test_nextflow_config_bad_name_fail(self): """Tests that config variable existence test fails with bad pipeline name""" diff --git a/tests/pipelines/test_launch.py b/tests/pipelines/test_launch.py index 5598fc695b..5e230528a7 100644 --- a/tests/pipelines/test_launch.py +++ b/tests/pipelines/test_launch.py @@ -17,13 +17,9 @@ class TestLaunch(TestPipelines): def setUp(self) -> None: super().setUp() self.nf_params_fn = Path(self.pipeline_dir, "nf-params.json") - self.launcher = nf_core.pipelines.launch.Launch( - self.pipeline_dir, params_out=self.nf_params_fn - ) + self.launcher = nf_core.pipelines.launch.Launch(self.pipeline_dir, params_out=self.nf_params_fn) - @mock.patch.object( - nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True] - ) + @mock.patch.object(nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True]) @mock.patch.object(nf_core.pipelines.launch.Launch, "launch_web_gui") def test_launch_pipeline(self, mock_webbrowser, mock_lauch_web_gui): """Test the main launch function""" @@ -38,14 +34,10 @@ def test_launch_file_exists(self, mock_confirm): # Try and to launch, return with error assert self.launcher.launch_pipeline() is False - @mock.patch.object( - nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True] - ) + @mock.patch.object(nf_core.pipelines.launch.Launch, "prompt_web_gui", side_effect=[True]) @mock.patch.object(nf_core.pipelines.launch.Launch, "launch_web_gui") @mock.patch.object(nf_core.pipelines.launch.Confirm, "ask", side_effect=[False]) - def test_launch_file_exists_overwrite( - self, mock_webbrowser, mock_lauch_web_gui, mock_confirm - ): + def test_launch_file_exists_overwrite(self, mock_webbrowser, mock_lauch_web_gui, mock_confirm): """Test that we detect an existing params file and we overwrite it""" # Make an empty params file to be overwritten open(self.nf_params_fn, "a").close() @@ -55,14 +47,7 @@ def test_launch_file_exists_overwrite( def test_get_pipeline_schema(self): """Test loading the params schema from a pipeline""" self.launcher.get_pipeline_schema() - assert ( - len( - self.launcher.schema_obj.schema["$defs"]["input_output_options"][ - "properties" - ] - ) - > 2 - ) + assert len(self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]) > 2 @with_temporary_folder def test_make_pipeline_schema(self, tmp_path): @@ -73,21 +58,10 @@ def test_make_pipeline_schema(self, tmp_path): ) create_obj.init_pipeline() Path(test_pipeline_dir, "nextflow_schema.json").unlink() - self.launcher = nf_core.pipelines.launch.Launch( - test_pipeline_dir, params_out=self.nf_params_fn - ) + self.launcher = nf_core.pipelines.launch.Launch(test_pipeline_dir, params_out=self.nf_params_fn) self.launcher.get_pipeline_schema() - assert ( - len( - self.launcher.schema_obj.schema["$defs"]["input_output_options"][ - "properties" - ] - ) - >= 2 - ) - assert self.launcher.schema_obj.schema["$defs"]["input_output_options"][ - "properties" - ]["outdir"] == { + assert len(self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]) >= 2 + assert self.launcher.schema_obj.schema["$defs"]["input_output_options"]["properties"]["outdir"] == { "type": "string", "format": "directory-path", "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", @@ -117,13 +91,8 @@ def test_nf_merge_schema(self): self.launcher.get_pipeline_schema() self.launcher.set_schema_inputs() self.launcher.merge_nxf_flag_schema() - assert self.launcher.schema_obj.schema["allOf"][0] == { - "$ref": "#/$defs/coreNextflow" - } - assert ( - "-resume" - in self.launcher.schema_obj.schema["$defs"]["coreNextflow"]["properties"] - ) + assert self.launcher.schema_obj.schema["allOf"][0] == {"$ref": "#/$defs/coreNextflow"} + assert "-resume" in self.launcher.schema_obj.schema["$defs"]["coreNextflow"]["properties"] def test_ob_to_questionary_string(self): """Check converting a python dict to a pyenquirer format - simple strings""" @@ -144,9 +113,7 @@ def test_prompt_web_gui_true(self, mock_prompt): """Check the prompt to launch the web schema or use the cli""" assert self.launcher.prompt_web_gui() is True - @mock.patch( - "questionary.unsafe_prompt", side_effect=[{"use_web_gui": "Command line"}] - ) + @mock.patch("questionary.unsafe_prompt", side_effect=[{"use_web_gui": "Command line"}]) def test_prompt_web_gui_false(self, mock_prompt): """Check the prompt to launch the web schema or use the cli""" assert self.launcher.prompt_web_gui() is False @@ -166,9 +133,7 @@ def test_launch_web_gui_missing_keys(self, mock_poll_nfcore_web_api): ) @mock.patch("webbrowser.open") @mock.patch("nf_core.utils.wait_cli_function") - def test_launch_web_gui( - self, mock_poll_nfcore_web_api, mock_webbrowser, mock_wait_cli_function - ): + def test_launch_web_gui(self, mock_poll_nfcore_web_api, mock_webbrowser, mock_wait_cli_function): """Check the code that opens the web browser""" self.launcher.get_pipeline_schema() self.launcher.merge_nxf_flag_schema() @@ -189,9 +154,7 @@ def test_get_web_launch_response_unexpected(self, mock_poll_nfcore_web_api): """Test polling the website for a launch response - status error""" with pytest.raises(AssertionError) as exc_info: self.launcher.get_web_launch_response() - assert exc_info.value.args[0].startswith( - "Web launch GUI returned unexpected status (foo): " - ) + assert exc_info.value.args[0].startswith("Web launch GUI returned unexpected status (foo): ") @mock.patch( "nf_core.utils.poll_nfcore_web_api", @@ -227,9 +190,7 @@ def test_get_web_launch_response_missing_keys(self, mock_poll_nfcore_web_api): ], ) @mock.patch.object(nf_core.pipelines.launch.Launch, "sanitise_web_response") - def test_get_web_launch_response_valid( - self, mock_poll_nfcore_web_api, mock_sanitise - ): + def test_get_web_launch_response_valid(self, mock_poll_nfcore_web_api, mock_sanitise): """Test polling the website for a launch response - complete, valid response""" self.launcher.get_pipeline_schema() assert self.launcher.get_web_launch_response() is True @@ -350,10 +311,7 @@ def test_build_command_nf(self): self.launcher.nxf_flags["-name"] = "Test_Workflow" self.launcher.nxf_flags["-resume"] = True self.launcher.build_command() - assert ( - self.launcher.nextflow_cmd - == f'nextflow run {self.pipeline_dir} -name "Test_Workflow" -resume' - ) + assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} -name "Test_Workflow" -resume' def test_build_command_params(self): """Test the functionality to build a nextflow command - params supplied""" @@ -361,18 +319,13 @@ def test_build_command_params(self): self.launcher.schema_obj.input_params.update({"input": "custom_input"}) self.launcher.build_command() # Check command - assert ( - self.launcher.nextflow_cmd - == f'nextflow run {self.pipeline_dir} -params-file "{self.nf_params_fn}"' - ) + assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} -params-file "{self.nf_params_fn}"' # Check saved parameters file with open(self.nf_params_fn) as fh: try: saved_json = json.load(fh) except json.JSONDecodeError as e: - raise UserWarning( - f"Unable to load JSON file '{self.nf_params_fn}' due to error {e}" - ) + raise UserWarning(f"Unable to load JSON file '{self.nf_params_fn}' due to error {e}") assert saved_json == {"input": "custom_input"} @@ -382,7 +335,4 @@ def test_build_command_params_cl(self): self.launcher.get_pipeline_schema() self.launcher.schema_obj.input_params.update({"input": "custom_input"}) self.launcher.build_command() - assert ( - self.launcher.nextflow_cmd - == f'nextflow run {self.pipeline_dir} --input "custom_input"' - ) + assert self.launcher.nextflow_cmd == f'nextflow run {self.pipeline_dir} --input "custom_input"' From 6633769fa4cf5c6f9a84745a9424fb07443b5a90 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 4 Sep 2024 12:06:55 +0200 Subject: [PATCH 11/18] Fix linting fail --- nf_core/pipeline-template/nextflow_schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json index f75354d4e1..818adf9619 100644 --- a/nf_core/pipeline-template/nextflow_schema.json +++ b/nf_core/pipeline-template/nextflow_schema.json @@ -135,6 +135,7 @@ } } }, + {%- endif %} "generic_options": { "title": "Generic options", "type": "object", From d819232314df5ef888ea84ed366253f0f3ddf3ae Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 4 Sep 2024 12:10:36 +0200 Subject: [PATCH 12/18] Fix schema error --- nf_core/pipeline-template/nextflow_schema.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/nf_core/pipeline-template/nextflow_schema.json b/nf_core/pipeline-template/nextflow_schema.json index 818adf9619..4136a0b490 100644 --- a/nf_core/pipeline-template/nextflow_schema.json +++ b/nf_core/pipeline-template/nextflow_schema.json @@ -243,9 +243,6 @@ {% if nf_core_configs %}{ "$ref": "#/$defs/institutional_config_options" },{% endif %} - { - "$ref": "#/$defs/max_job_request_options" - }, { "$ref": "#/$defs/generic_options" } From 559903976a9dd5d81ed58df81f13bd8523de3618 Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Mon, 16 Sep 2024 16:49:39 +0200 Subject: [PATCH 13/18] add tests back --- tests/pipelines/lint/test_nextflow_config.py | 78 ++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tests/pipelines/lint/test_nextflow_config.py b/tests/pipelines/lint/test_nextflow_config.py index 4c3893f4c4..d2296618b0 100644 --- a/tests/pipelines/lint/test_nextflow_config.py +++ b/tests/pipelines/lint/test_nextflow_config.py @@ -2,8 +2,11 @@ import re from pathlib import Path +import yaml + import nf_core.pipelines.create.create import nf_core.pipelines.lint +from nf_core.utils import NFCoreYamlConfig from ..test_lint import TestLint @@ -65,6 +68,81 @@ def test_nextflow_config_missing_test_profile_failed(self): assert len(result["failed"]) > 0 assert len(result["warned"]) == 0 + def test_default_values_fail(self): + """Test linting fails if the default values in nextflow.config do not match the ones defined in the nextflow_schema.json.""" + # Change the default value of max_multiqc_email_size in nextflow.config + nf_conf_file = Path(self.new_pipeline) / "nextflow.config" + with open(nf_conf_file) as f: + content = f.read() + fail_content = re.sub(r"\bmax_multiqc_email_size\s*=\s*'25.MB'", "max_multiqc_email_size = '0'", content) + with open(nf_conf_file, "w") as f: + f.write(fail_content) + # Change the default value of custom_config_version in nextflow_schema.json + nf_schema_file = Path(self.new_pipeline) / "nextflow_schema.json" + with open(nf_schema_file) as f: + content = f.read() + fail_content = re.sub(r'"default": "master"', '"default": "main"', content) + with open(nf_schema_file, "w") as f: + f.write(fail_content) + lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) + lint_obj.load_pipeline_config() + result = lint_obj.nextflow_config() + assert len(result["failed"]) == 2 + assert ( + "Config default value incorrect: `params.max_multiqc_email_size` is set as `25.MB` in `nextflow_schema.json` but is `0` in `nextflow.config`." + in result["failed"] + ) + assert ( + "Config default value incorrect: `params.custom_config_version` is set as `main` in `nextflow_schema.json` but is `master` in `nextflow.config`." + in result["failed"] + ) + + def test_catch_params_assignment_in_main_nf(self): + """Test linting fails if main.nf contains an assignment to a parameter from nextflow_schema.json.""" + # Add parameter assignment in main.nf + main_nf_file = Path(self.new_pipeline) / "main.nf" + with open(main_nf_file, "a") as f: + f.write("params.custom_config_base = 'test'") + lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) + lint_obj.load_pipeline_config() + result = lint_obj.nextflow_config() + assert len(result["failed"]) == 1 + assert ( + result["failed"][0] + == "Config default value incorrect: `params.custom_config_base` is set as `https://raw.githubusercontent.com/nf-core/configs/master` in `nextflow_schema.json` but is `null` in `nextflow.config`." + ) + + def test_allow_params_reference_in_main_nf(self): + """Test linting allows for references like `params.aligner == 'bwa'` in main.nf. The test will detect if the bug mentioned in GitHub-issue #2833 reemerges.""" + # Add parameter reference in main.nf + main_nf_file = Path(self.new_pipeline) / "main.nf" + with open(main_nf_file, "a") as f: + f.write("params.custom_config_version == 'main'") + lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) + lint_obj.load_pipeline_config() + result = lint_obj.nextflow_config() + assert len(result["failed"]) == 0 + + def test_default_values_ignored(self): + """Test ignoring linting of default values.""" + # Add custom_config_version to the ignore list + nf_core_yml_path = Path(self.new_pipeline) / ".nf-core.yml" + nf_core_yml = NFCoreYamlConfig( + repository_type="pipeline", + lint={"nextflow_config": [{"config_defaults": ["params.custom_config_version"]}]}, + ) + with open(nf_core_yml_path, "w") as f: + yaml.dump(nf_core_yml.model_dump(), f) + + lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) + lint_obj.load_pipeline_config() + lint_obj._load_lint_config() + result = lint_obj.nextflow_config() + assert len(result["failed"]) == 0 + assert len(result["ignored"]) == 1 + assert "Config default value correct: params.custom_config_version" not in str(result["passed"]) + assert "Config default ignored: params.custom_config_version" in str(result["ignored"]) + def test_default_values_float(self): """Test comparing two float values.""" # Add a float value `dummy=0.0001` to the nextflow.config below `validate_params` From c568ce9e207d6c2bd9b43954912754661004147f Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Mon, 16 Sep 2024 17:06:04 +0200 Subject: [PATCH 14/18] more pytest fixing --- tests/pipelines/lint/test_nextflow_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pipelines/lint/test_nextflow_config.py b/tests/pipelines/lint/test_nextflow_config.py index d2296618b0..a655fb8ace 100644 --- a/tests/pipelines/lint/test_nextflow_config.py +++ b/tests/pipelines/lint/test_nextflow_config.py @@ -106,9 +106,9 @@ def test_catch_params_assignment_in_main_nf(self): lint_obj = nf_core.pipelines.lint.PipelineLint(self.new_pipeline) lint_obj.load_pipeline_config() result = lint_obj.nextflow_config() - assert len(result["failed"]) == 1 + assert len(result["failed"]) == 2 assert ( - result["failed"][0] + result["failed"][1] == "Config default value incorrect: `params.custom_config_base` is set as `https://raw.githubusercontent.com/nf-core/configs/master` in `nextflow_schema.json` but is `null` in `nextflow.config`." ) From 17dc0bc34cfd22782221532508a883d65a2c6590 Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Thu, 19 Sep 2024 12:39:02 +0200 Subject: [PATCH 15/18] change order of profiles used for CI --- .github/workflows/create-test-wf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml index 56c6c822a9..e3918bffdf 100644 --- a/.github/workflows/create-test-wf.yml +++ b/.github/workflows/create-test-wf.yml @@ -75,7 +75,7 @@ jobs: pwd # echo content of current directory ls -la - nextflow run nf-core-testpipeline -profile test,self_hosted_runner --outdir ./results + nextflow run nf-core-testpipeline -profile self_hosted_runner,test --outdir ./results - name: Upload log file artifact if: ${{ always() }} From c494c268e0873f82e340a47aabcf24e36d59a29a Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Thu, 19 Sep 2024 12:55:36 +0200 Subject: [PATCH 16/18] update nextflow version on CI --- .github/workflows/create-lint-wf.yml | 2 +- .github/workflows/create-test-wf.yml | 2 +- nf_core/pipeline-template/.github/workflows/ci.yml | 2 +- nf_core/pipeline-template/README.md | 2 +- nf_core/pipelines/lint/readme.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/create-lint-wf.yml b/.github/workflows/create-lint-wf.yml index f07b31e9de..1a3e283d00 100644 --- a/.github/workflows/create-lint-wf.yml +++ b/.github/workflows/create-lint-wf.yml @@ -38,7 +38,7 @@ jobs: strategy: matrix: NXF_VER: - - "23.10.0" + - "24.04.2" - "latest-everything" steps: - name: go to subdirectory and change nextflow workdir diff --git a/.github/workflows/create-test-wf.yml b/.github/workflows/create-test-wf.yml index e3918bffdf..09c5b01c37 100644 --- a/.github/workflows/create-test-wf.yml +++ b/.github/workflows/create-test-wf.yml @@ -39,7 +39,7 @@ jobs: strategy: matrix: NXF_VER: - - "23.10.0" + - "24.04.2" - "latest-everything" steps: - name: go to working directory diff --git a/nf_core/pipeline-template/.github/workflows/ci.yml b/nf_core/pipeline-template/.github/workflows/ci.yml index 63fa99cec8..06371cc974 100644 --- a/nf_core/pipeline-template/.github/workflows/ci.yml +++ b/nf_core/pipeline-template/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: NXF_VER: - - "23.10.0" + - "24.04.2" - "latest-everything" steps: - name: Check out pipeline code diff --git a/nf_core/pipeline-template/README.md b/nf_core/pipeline-template/README.md index beb45ed511..4331454288 100644 --- a/nf_core/pipeline-template/README.md +++ b/nf_core/pipeline-template/README.md @@ -20,7 +20,7 @@ [![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) [![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com) -[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.10.0-23aa62.svg)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A524.04.2-23aa62.svg)](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/) diff --git a/nf_core/pipelines/lint/readme.py b/nf_core/pipelines/lint/readme.py index 1c09104258..bdfad5200f 100644 --- a/nf_core/pipelines/lint/readme.py +++ b/nf_core/pipelines/lint/readme.py @@ -36,7 +36,7 @@ def readme(self): if "nextflow_badge" not in ignore_configs: # Check that there is a readme badge showing the minimum required version of Nextflow - # [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.10.0-23aa62.svg)](https://www.nextflow.io/) + # [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A524.04.2-23aa62.svg)](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|%3E%3D)([\d\.]+)-23aa62\.svg\)\]\(https://www\.nextflow\.io/\)" match = re.search(nf_badge_re, content) From 2c0d2f6a325892a407500df8022c534167b4a7e0 Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Thu, 19 Sep 2024 13:31:14 +0200 Subject: [PATCH 17/18] configs are included after profiles --- nf_core/pipeline-template/nextflow.config | 31 ++++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/nf_core/pipeline-template/nextflow.config b/nf_core/pipeline-template/nextflow.config index 8c1d33244f..d129d1a492 100644 --- a/nf_core/pipeline-template/nextflow.config +++ b/nf_core/pipeline-template/nextflow.config @@ -76,21 +76,6 @@ process { maxErrors = '-1' } {% endif %} -{% if nf_core_configs -%} -// Load nf-core custom profiles from different Institutions -try { - includeConfig "${params.custom_config_base}/nfcore_custom.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") -} - -// Load {{ name }} custom profiles from different institutions. -try { - includeConfig "${params.custom_config_base}/pipeline/{{ short_name }}.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config/{{ short_name }} profiles: ${params.custom_config_base}/pipeline/{{ short_name }}.config") -} -{% endif -%} profiles { debug { @@ -199,6 +184,22 @@ profiles { {%- endif %} } +{% if nf_core_configs -%} +// Load nf-core custom profiles from different Institutions +try { + includeConfig "${params.custom_config_base}/nfcore_custom.config" +} catch (Exception e) { + System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") +} + +// Load {{ name }} custom profiles from different institutions. +try { + includeConfig "${params.custom_config_base}/pipeline/{{ short_name }}.config" +} catch (Exception e) { + System.err.println("WARNING: Could not load nf-core/config/{{ short_name }} profiles: ${params.custom_config_base}/pipeline/{{ short_name }}.config") +} +{% endif -%} + // Set default registry for Apptainer, Docker, Podman, Charliecloud and Singularity independent of -profile // Will not be used unless Apptainer / Docker / Podman / Charliecloud / Singularity are enabled // Set to your registry if you have a mirror of containers From d2959edebb53efb1479785d3258d2577546cf67d Mon Sep 17 00:00:00 2001 From: mirpedrol Date: Thu, 19 Sep 2024 13:44:58 +0200 Subject: [PATCH 18/18] reduce memory in test profile --- nf_core/pipeline-template/conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nf_core/pipeline-template/conf/test.config b/nf_core/pipeline-template/conf/test.config index 0f1d974101..bea6f670d0 100644 --- a/nf_core/pipeline-template/conf/test.config +++ b/nf_core/pipeline-template/conf/test.config @@ -13,7 +13,7 @@ process { resourceLimits = [ cpus: 4, - memory: '16.GB', + memory: '15.GB', time: '1.h' ] }