diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd31237..d5cbaed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore +- Clear all `nextflow lint` warnings (use `channel` factory API, explicit closure parameters, unused-parameter prefixes). ([#542](https://github.com/nf-core/scrnaseq/pull/542)) - Migrate local subworkflows to directory-based layout with `main.nf`, matching the new nf-core standard structure for modules and subworkflows ([#553](https://github.com/nf-core/scrnaseq/pull/553)) - Template update for nf-core/tools v3.5.1 ([#509](https://github.com/nf-core/scrnaseq/pull/509)) - Template update for nf-core/tools v4.0.2 ([#541](https://github.com/nf-core/scrnaseq/pull/541)) diff --git a/conf/modules.config b/conf/modules.config index 1b54441b..635a64aa 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -159,7 +159,7 @@ process { publishDir = [ path: { "${params.outdir}/${params.aligner}/${meta.id}" }, mode: params.publish_dir_mode, - saveAs: { (!it.endsWith('.bam') || params.save_align_intermeds) ? it : null } + saveAs: { filename -> (!filename.endsWith('.bam') || params.save_align_intermeds) ? filename : null } ] } diff --git a/subworkflows/local/align_cellranger/main.nf b/subworkflows/local/align_cellranger/main.nf index 0d525e83..c91c5af6 100644 --- a/subworkflows/local/align_cellranger/main.nf +++ b/subworkflows/local/align_cellranger/main.nf @@ -41,8 +41,8 @@ workflow CELLRANGER_ALIGN { ch_matrices_raw = CELLRANGER_COUNT.out.outs.map { meta, mtx_files -> def desired_files = [] - mtx_files.each{ - if ( it.toString().contains("raw_feature_bc_matrix") ) { desired_files.add( it ) } + mtx_files.each{ path -> + if ( path.toString().contains("raw_feature_bc_matrix") ) { desired_files.add( path ) } } [ meta + [input_type: 'raw'], desired_files ] } @@ -50,8 +50,8 @@ workflow CELLRANGER_ALIGN { ch_matrices_filtered = CELLRANGER_COUNT.out.outs.map { meta, mtx_files -> def desired_files = [] - mtx_files.each{ - if ( it.toString().contains("filtered_feature_bc_matrix") ) { desired_files.add( it ) } + mtx_files.each{ path -> + if ( path.toString().contains("filtered_feature_bc_matrix") ) { desired_files.add( path ) } } [ meta + [input_type: 'filtered'], desired_files ] } diff --git a/subworkflows/local/align_cellrangerarc/main.nf b/subworkflows/local/align_cellrangerarc/main.nf index 3230efd4..d49bd9d3 100644 --- a/subworkflows/local/align_cellrangerarc/main.nf +++ b/subworkflows/local/align_cellrangerarc/main.nf @@ -67,7 +67,7 @@ def parse_demultiplexed_output_channels(in_ch, pattern) { meta_clone.input_type = pattern.contains('raw_') ? 'raw' : 'filtered' // Iterate over the matrix files and add the ones matching the pattern to the desired files list def desired_files = [] - mtx_files.each{ if ( it.toString().contains("${pattern}") ) { desired_files.add( it ) } } + mtx_files.each{ path -> if ( path.toString().contains("${pattern}") ) { desired_files.add( path ) } } [ meta_clone, desired_files ] } diff --git a/subworkflows/local/align_cellrangermulti/main.nf b/subworkflows/local/align_cellrangermulti/main.nf index ddea5375..ef48552f 100644 --- a/subworkflows/local/align_cellrangermulti/main.nf +++ b/subworkflows/local/align_cellrangermulti/main.nf @@ -29,7 +29,7 @@ workflow CELLRANGER_MULTI_ALIGN { .flatten() .map{ meta -> def meta_clone = meta.clone() - def data_dict = meta_clone.find{ it.key == "${meta_clone.feature_type}" } + def data_dict = meta_clone.find{ entry -> entry.key == "${meta_clone.feature_type}" } def fastqs = data_dict?.value meta_clone.remove( data_dict?.key ) [ meta_clone, fastqs ] @@ -90,26 +90,26 @@ workflow CELLRANGER_MULTI_ALIGN { // CMO ch_grouped_fastq.gex - .map{ [it[0].id] } - .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.cmo.flatten().map { [ "${it.baseName}" - "_cmo", it ] } ) + .map{ pair -> [pair[0].id] } + .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.cmo.flatten().map { csv -> [ "${csv.baseName}" - "_cmo", csv ] } ) .groupTuple() - .map { if ( it.size() == 2 ) { it[1] } else { [] } } // a correct tuple from snippet will have: [ sample, cmo.csv ] + .map { grp -> if ( grp.size() == 2 ) { grp[1] } else { [] } } // a correct tuple from snippet will have: [ sample, cmo.csv ] .set { ch_cmo_barcode_csv } // OCM ch_grouped_fastq.gex - .map{ [it[0].id] } - .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.ocm.flatten().map { [ "${it.baseName}" - "_ocm", it ] } ) + .map{ pair -> [pair[0].id] } + .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.ocm.flatten().map { csv -> [ "${csv.baseName}" - "_ocm", csv ] } ) .groupTuple() - .map { if ( it.size() == 2 ) { it[1] } else { [] } } // a correct tuple from snippet will have: [ sample, ocm.csv ] + .map { grp -> if ( grp.size() == 2 ) { grp[1] } else { [] } } // a correct tuple from snippet will have: [ sample, ocm.csv ] .set { ch_ocm_barcode_csv } // FRNA ch_grouped_fastq.gex - .map{ [it[0].id] } - .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.frna.flatten().map { [ "${it.baseName}" - "_frna", it ] } ) + .map{ pair -> [pair[0].id] } + .concat( PARSE_CELLRANGERMULTI_SAMPLESHEET.out.frna.flatten().map { csv -> [ "${csv.baseName}" - "_frna", csv ] } ) .groupTuple() - .map { if ( it.size() == 2 ) { it[1] } else { [] } } // a correct tuple from snippet will have: [ sample, frna.csv ] + .map { grp -> if ( grp.size() == 2 ) { grp[1] } else { [] } } // a correct tuple from snippet will have: [ sample, frna.csv ] .set { ch_frna_sample_csv } } else { @@ -197,7 +197,7 @@ workflow CELLRANGER_MULTI_ALIGN { // MODULE: cellranger multi // CELLRANGER_MULTI( - ch_grouped_fastq.gex.map{ it[0] }, + ch_grouped_fastq.gex.map{ pair -> pair[0] }, ch_grouped_fastq.gex, ch_grouped_fastq.vdj, ch_grouped_fastq.ab, @@ -244,7 +244,7 @@ def parse_demultiplexed_output_channels(in_ch, pattern) { def out_ch = in_ch .map { meta, mtx_files -> def desired_files = [] - mtx_files.each{ if ( it.toString().contains("${pattern}") ) { desired_files.add( it ) } } + mtx_files.each{ path -> if ( path.toString().contains("${pattern}") ) { desired_files.add( path ) } } [ meta, desired_files ] } // separate only desired files .transpose() // transpose for handling one meta/file pair at a time @@ -260,7 +260,7 @@ def parse_demultiplexed_output_channels(in_ch, pattern) { } [ meta_clone, mtx_files ] } // check if output is from demultiplexed sample, if yes, correct meta.id for proper conversion naming - .filter{ it != null } // remove nulls from previous step + .filter{ item -> item != null } // remove nulls from previous step .groupTuple( by: 0 ) // group it back as one file collection per sample return out_ch diff --git a/subworkflows/local/fastqc/main.nf b/subworkflows/local/fastqc/main.nf index 6174719a..43b7354b 100644 --- a/subworkflows/local/fastqc/main.nf +++ b/subworkflows/local/fastqc/main.nf @@ -26,7 +26,7 @@ workflow FASTQC_CHECK { .map { it -> [ it[1] ] } .set { fastqc_html_only } - fastqc_multiqc = Channel.empty() + fastqc_multiqc = channel.empty() fastqc_multiqc = fastqc_multiqc.mix( fastqc_zip_only, fastqc_html_only ) emit: diff --git a/subworkflows/local/kallisto_bustools/main.nf b/subworkflows/local/kallisto_bustools/main.nf index 4003b7bc..8aaf99bd 100644 --- a/subworkflows/local/kallisto_bustools/main.nf +++ b/subworkflows/local/kallisto_bustools/main.nf @@ -17,7 +17,7 @@ workflow KALLISTO_BUSTOOLS { ch_fastq main: - ch_versions = Channel.empty() + ch_versions = channel.empty() assert (txp2gene && kallisto_index) || (genome_fasta && gtf): "Must provide a genome fasta file ('--fasta') and a gtf file ('--gtf') if no index is given!" diff --git a/subworkflows/local/simpleaf/main.nf b/subworkflows/local/simpleaf/main.nf index 26c10049..ea1a5fb2 100644 --- a/subworkflows/local/simpleaf/main.nf +++ b/subworkflows/local/simpleaf/main.nf @@ -20,7 +20,7 @@ workflow SIMPLEAF { map_dir main: - ch_versions = Channel.empty() + ch_versions = channel.empty() /* * Build simpleaf index if needed @@ -60,20 +60,20 @@ workflow SIMPLEAF { if (!txp2gene) { txp2gene = SIMPLEAF_INDEX.out.t2g.collect().map { _meta, it -> it } } else { - txp2gene = Channel.of( txp2gene ) + txp2gene = channel.of( txp2gene ) } } else { // we have a map dir, so we do not need to build the index - simpleaf_index = Channel.of( [ [:], [] ] ) + simpleaf_index = channel.of( [ [:], [] ] ) } } else { // we have a simpleaf index, we use it directly // ensure simpleaf index and txp2gene are Channels - simpleaf_index = Channel.of( [ [ id: simpleaf_index.getName() ], simpleaf_index ] ) + simpleaf_index = channel.of( [ [ id: simpleaf_index.getName() ], simpleaf_index ] ) // channel or null if (txp2gene) { - txp2gene = Channel.of( txp2gene ) + txp2gene = channel.of( txp2gene ) } } @@ -119,7 +119,7 @@ workflow SIMPLEAF { /* * Run qcatch QC (optional) */ - ch_qcatch_report = Channel.empty() + ch_qcatch_report = channel.empty() if ( !skip_qcatch ) { // Map quant channel to include chemistry for qcatch: tuple(meta, chemistry, quant_dir) ch_qcatch_input = ch_af_quant.map { meta, quant_dir -> [meta, qcatch_chemistry, quant_dir] } diff --git a/subworkflows/local/starsolo/main.nf b/subworkflows/local/starsolo/main.nf index 5d66d48d..f4961180 100644 --- a/subworkflows/local/starsolo/main.nf +++ b/subworkflows/local/starsolo/main.nf @@ -69,5 +69,5 @@ workflow STARSOLO { star_counts = STAR_ALIGN.out.counts raw_counts = raw_counts filtered_counts = filtered_counts - for_multiqc = STAR_ALIGN.out.log_final.map{ meta, it -> it } + for_multiqc = STAR_ALIGN.out.log_final.map{ _meta, logFinal -> logFinal } } diff --git a/subworkflows/local/utils_nfcore_scrnaseq_pipeline/main.nf b/subworkflows/local/utils_nfcore_scrnaseq_pipeline/main.nf index 8a535d5a..e3b34fb3 100644 --- a/subworkflows/local/utils_nfcore_scrnaseq_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_scrnaseq_pipeline/main.nf @@ -31,7 +31,7 @@ workflow PIPELINE_INITIALISATION { monochrome_logs // boolean: Do not use coloured log outputs nextflow_cli_args // array: List of positional nextflow CLI args outdir // string: The output directory where the results will be saved - input // string: Path to input samplesheet + _input // string: Path to input samplesheet help // boolean: Display help message and exit help_full // boolean: Show the full help message show_hidden // boolean: Show hidden parameters in the help message @@ -107,7 +107,7 @@ workflow PIPELINE_INITIALISATION { // Create channel from input file provided through params.input // if (params.aligner == 'cellrangermulti') { // the cellrangermulti sub-workflow logic needs that channels have reads separated by feature_type. Cannot merge all. - Channel + channel .fromList(samplesheetToList(params.input, "${projectDir}/assets/schema_input.json")) .map { meta, fastq_1, fastq_2 -> @@ -118,9 +118,9 @@ workflow PIPELINE_INITIALISATION { } } .groupTuple( by: [0,1] ) - .map{ id, type, meta, reads -> [ id, meta, reads ] } - .map { - validateInputSamplesheet(it) + .map{ id, _type, meta, reads -> [ id, meta, reads ] } + .map { sheet_row -> + validateInputSamplesheet(sheet_row) } .map { meta, fastqs -> @@ -128,7 +128,7 @@ workflow PIPELINE_INITIALISATION { } .set { ch_samplesheet } } else if (params.aligner == 'cellrangerarc') { // the cellrangerarc sub-workflow logic needs that channels have a meta, type, subsample, fastqs structure. - Channel + channel .fromList(samplesheetToList(params.input, "${projectDir}/assets/schema_input.json")) .map { meta, fastq_1, fastq_2 -> if (!fastq_2 || (meta.sample_type == "atac" && !meta.fastq_barcode)) { @@ -141,12 +141,12 @@ workflow PIPELINE_INITIALISATION { } } .groupTuple() - .map { - cellrangerarcStructure(it) + .map { structure_input -> + cellrangerarcStructure(structure_input) } .set { ch_samplesheet } } else { - Channel + channel .fromList(samplesheetToList(params.input, "${projectDir}/assets/schema_input.json")) .map { meta, fastq_1, fastq_2 -> @@ -157,8 +157,8 @@ workflow PIPELINE_INITIALISATION { } } .groupTuple() - .map { - validateInputSamplesheet(it) + .map { sheet_row -> + validateInputSamplesheet(sheet_row) } .map { meta, fastqs -> @@ -241,7 +241,7 @@ def validateCellrangerMultiBarcodes() { def cellranger_multi_barcodes = file(params.cellranger_multi_barcodes).splitCsv(header: true) // Get unique samples from input samplesheet for cross-validation - def inputSamples = file(params.input).splitCsv(header: true).collect { it.sample }.toSet() + def inputSamples = file(params.input).splitCsv(header: true).collect { row -> row.sample }.toSet() // Check that at least one barcode column is provided for each row // and that each sample uses only one type of barcode @@ -268,14 +268,14 @@ def validateCellrangerMultiBarcodes() { // Validate that at least one barcode identifier is populated in each row if (rowsWithoutBarcodes) { - def errorDetails = rowsWithoutBarcodes.collect { "row ${it.row} (${it.multiplexed_sample_id})" }.join(', ') + def errorDetails = rowsWithoutBarcodes.collect { missing -> "row ${missing.row} (${missing.multiplexed_sample_id})" }.join(', ') error("Please check cellranger_multi_barcodes samplesheet -> " + "The following rows have no barcode identifiers: ${errorDetails}. " + "Each row must have exactly one of: 'probe_barcode_ids', 'cmo_ids', or 'ocm_ids'.") } // Validate that no more than one barcode identifier is populated in each row - def samplesWithMixedBarcodes = sampleBarcodeTypes.findAll { multiplexed_sample_id, info -> info.types.size() > 1 } + def samplesWithMixedBarcodes = sampleBarcodeTypes.findAll { _multiplexed_sample_id, info -> info.types.size() > 1 } if (samplesWithMixedBarcodes) { def errorMsg = samplesWithMixedBarcodes.collect { multiplexed_sample_id, info -> "'${multiplexed_sample_id}' (row ${info.row}) uses multiple barcode types: ${info.types.join(', ')}" @@ -321,7 +321,7 @@ def cellrangerarcStructure(input) { // Validate that the property "sample_type" is present and has valid values def valid_sample_types = ["gex", "atac"] - def sample_type_ok = metas.collect { meta -> meta.sample_type }.unique().every { it in valid_sample_types } + def sample_type_ok = metas.collect { meta -> meta.sample_type }.unique().every { st -> st in valid_sample_types } if (!sample_type_ok) { error("Please check input samplesheet -> The property 'sample_type' is required and can only be 'gex' or 'atac'.") } diff --git a/workflows/scrnaseq.nf b/workflows/scrnaseq.nf index e36ded39..95df6a96 100644 --- a/workflows/scrnaseq.nf +++ b/workflows/scrnaseq.nf @@ -109,7 +109,7 @@ workflow SCRNASEQ { // if (fasta) { if (fasta.endsWith('.gz')) { - ch_genome_fasta = GUNZIP_FASTA ( [ [:], ch_genome_fasta ] ).gunzip.map { it[1] } + ch_genome_fasta = GUNZIP_FASTA ( [ [:], ch_genome_fasta ] ).gunzip.map { tuple -> tuple[1] } } else { ch_genome_fasta = channel.value( ch_genome_fasta ) } @@ -120,7 +120,7 @@ workflow SCRNASEQ { // if (gtf) { if (gtf.endsWith('.gz')) { - ch_gtf = GUNZIP_GTF ( [ [:], ch_gtf ] ).gunzip.map { it[1] } + ch_gtf = GUNZIP_GTF ( [ [:], ch_gtf ] ).gunzip.map { tuple -> tuple[1] } } else { ch_gtf = channel.value( ch_gtf ) } @@ -205,7 +205,7 @@ workflow SCRNASEQ { ) ch_mtx_matrices = ch_mtx_matrices.mix( CELLRANGER_ALIGN.out.cellranger_matrices_raw, CELLRANGER_ALIGN.out.cellranger_matrices_filtered ) ch_multiqc_files = ch_multiqc_files.mix(CELLRANGER_ALIGN.out.cellranger_out.map { - meta, outs -> outs.findAll{ it -> it.name == "web_summary.html"} + _meta, outs -> outs.findAll{ summary -> summary.name == "web_summary.html"} }) } @@ -263,12 +263,12 @@ workflow SCRNASEQ { // needs to have a collected map like that, so every sample from the samplesheet is analysed one at a time, // allowing to have multiple samples in the sheet, having all the data-type tuples initialized, // either empty or populated. It will be branched inside the subworkflow. - if (!map_collection_clone.any{ it.feature_type == 'gex' }) { map_collection_clone.add( [id: sample_id, feature_type: 'gex' , gex: empty_file, options:[:] ] ) } - if (!map_collection_clone.any{ it.feature_type == 'vdj' }) { map_collection_clone.add( [id: sample_id, feature_type: 'vdj' , vdj: empty_file, options:[:] ] ) } - if (!map_collection_clone.any{ it.feature_type == 'ab' }) { map_collection_clone.add( [id: sample_id, feature_type: 'ab' , ab: empty_file, options:[:] ] ) } - if (!map_collection_clone.any{ it.feature_type == 'beam' }) { map_collection_clone.add( [id: sample_id, feature_type: 'beam' , beam: empty_file, options:[:] ] ) } // currently not implemented, the input samplesheet checking will not allow it. - if (!map_collection_clone.any{ it.feature_type == 'crispr' }) { map_collection_clone.add( [id: sample_id, feature_type: 'crispr', crispr: empty_file, options:[:] ] ) } - if (!map_collection_clone.any{ it.feature_type == 'cmo' }) { map_collection_clone.add( [id: sample_id, feature_type: 'cmo' , cmo: empty_file, options:[:] ] ) } + if (!map_collection_clone.any{ m -> m.feature_type == 'gex' }) { map_collection_clone.add( [id: sample_id, feature_type: 'gex' , gex: empty_file, options:[:] ] ) } + if (!map_collection_clone.any{ m -> m.feature_type == 'vdj' }) { map_collection_clone.add( [id: sample_id, feature_type: 'vdj' , vdj: empty_file, options:[:] ] ) } + if (!map_collection_clone.any{ m -> m.feature_type == 'ab' }) { map_collection_clone.add( [id: sample_id, feature_type: 'ab' , ab: empty_file, options:[:] ] ) } + if (!map_collection_clone.any{ m -> m.feature_type == 'beam' }) { map_collection_clone.add( [id: sample_id, feature_type: 'beam' , beam: empty_file, options:[:] ] ) } // currently not implemented, the input samplesheet checking will not allow it. + if (!map_collection_clone.any{ m -> m.feature_type == 'crispr' }) { map_collection_clone.add( [id: sample_id, feature_type: 'crispr', crispr: empty_file, options:[:] ] ) } + if (!map_collection_clone.any{ m -> m.feature_type == 'cmo' }) { map_collection_clone.add( [id: sample_id, feature_type: 'cmo' , cmo: empty_file, options:[:] ] ) } // return final map map_collection_clone