From 5cd483ddfa61b40d577a26cb0089af2cfa3d090a Mon Sep 17 00:00:00 2001 From: delfiterradas Date: Tue, 8 Sep 2026 17:13:56 +0200 Subject: [PATCH 1/5] Implement review suggestions --- .github/workflows/awsfulltest.yml | 2 -- README.md | 4 ---- assets/samplesheet.csv | 2 +- assets/samplesheet_full.csv | 2 -- bin/create_test_files.sh | 8 -------- conf/igenomes.config | 0 conf/igenomes_ignored.config | 0 modules/local/create_filter_list/main.nf | 11 +++-------- nextflow_schema.json | 8 ++++---- .../local/utils_nfcore_datasync_pipeline/main.nf | 1 + 10 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 assets/samplesheet_full.csv delete mode 100644 bin/create_test_files.sh delete mode 100644 conf/igenomes.config delete mode 100644 conf/igenomes_ignored.config diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 4190c08..5a8075d 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -24,8 +24,6 @@ jobs: - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@51565b514bff1827cf34620de25d0055759f1fc9 # v2 - # Add full size test data (but still relatively small datasets for few samples) - # on the `test_full.config` test runs with only one set of parameters with: workspace_id: ${{ vars.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} diff --git a/README.md b/README.md index 0dcd130..3b88fa4 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,12 @@ [![GitHub Actions CI Status](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml) [![GitHub Actions Linting Status](https://github.com/nf-core/datasync/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/datasync/results)[![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/version-%E2%89%A525.10.4-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/) - [![nf-core template version](https://img.shields.io/badge/nf--core_template-4.0.3-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/4.0.3) - [![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/) [![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/datasync) - [![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23datasync-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/datasync)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) ## Introduction diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 6d48fd6..9abae2c 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,2 +1,2 @@ sample,input,output_path,checksum_md5 -sample,path/to/file,output_path,path/to/file +sample,s3://foo/path/to/files/,output_path/data,path/to/checksum.md5 diff --git a/assets/samplesheet_full.csv b/assets/samplesheet_full.csv deleted file mode 100644 index 9ed843e..0000000 --- a/assets/samplesheet_full.csv +++ /dev/null @@ -1,2 +0,0 @@ -sample,input,checksum_md5,checksum_sha -demultiplex,s3://nf-core-awsmegatests/demultiplex/results-fbec8e442f0599f8b74876e62263af05b9a41d33/,checksum_md5.tsv,checksum_sha.tsv diff --git a/bin/create_test_files.sh b/bin/create_test_files.sh deleted file mode 100644 index ddb0120..0000000 --- a/bin/create_test_files.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# This script expects a single folder $1 with several subfolders that then need to be synchronized and checksummed to another location $2 -mkdir -p $1 -mkdir -p $2 -touch $1/DemuxDone #Note this needs to be configured as a pattern to look for in the pipeline configuration, otherwise this trigger won't work. -touch $1/fake_file.fastq.gz -touch $1/fake_file2.fastq.gz -echo "Sync me to another place" > $1/SampleSheet.csv diff --git a/conf/igenomes.config b/conf/igenomes.config deleted file mode 100644 index e69de29..0000000 diff --git a/conf/igenomes_ignored.config b/conf/igenomes_ignored.config deleted file mode 100644 index e69de29..0000000 diff --git a/modules/local/create_filter_list/main.nf b/modules/local/create_filter_list/main.nf index b82caed..1da22b7 100644 --- a/modules/local/create_filter_list/main.nf +++ b/modules/local/create_filter_list/main.nf @@ -7,12 +7,7 @@ process CREATE_FILTER_LIST { output: tuple val(meta), path('files_to_copy.txt') - script: - def content = common.join('\n') - - """ - cat > files_to_copy.txt <<'EOF' -${content} -EOF - """ + exec: + def outFile = task.workDir.resolve('files_to_copy.txt') + outFile.text = common.join('\n') + '\n' } diff --git a/nextflow_schema.json b/nextflow_schema.json index 738538e..a4a5f43 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -47,24 +47,24 @@ "exists": true, "description": "Path to the rclone config file used for cloud storage authentication.", "help_text": "Provide an rclone config file to support cloud providers such as AWS S3 or Azure Blob Storage. This file is loaded by rclone when copying data.", - "fa_icon": "fas fa-file" + "fa_icon": "fas fa-cog" }, "rclone_dry_run": { "type": "boolean", "description": "Perform a dry run of the rclone copy command.", - "fa_icon": "fas fa-eye", + "fa_icon": "fas fa-forward", "help_text": "If set, the pipeline will not actually copy any files to the destination. Instead, it will print out what would have been copied. This is useful for testing and debugging." }, "copy_matching_only": { "type": "boolean", "description": "Only copy files that matched their provided input checksums.", - "fa_icon": "fas fa-eye", + "fa_icon": "fas fa-copy", "help_text": "If set, the pipeline will only copy files that were correctly validated and will skip any file that did not match their input checksum." }, "download": { "type": "boolean", "description": "Download remote files for sha256 checksum verification in `RCLONE_CHECKSUM`.", - "fa_icon": "fas fa-eye", + "fa_icon": "fas fa-cloud-download-alt", "help_text": "If set, `RCLONE_CHECKSUM` will download remote files for any sample for which a SHA256 file was provided." } } diff --git a/subworkflows/local/utils_nfcore_datasync_pipeline/main.nf b/subworkflows/local/utils_nfcore_datasync_pipeline/main.nf index dca6fc9..2f42376 100644 --- a/subworkflows/local/utils_nfcore_datasync_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_datasync_pipeline/main.nf @@ -11,6 +11,7 @@ include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' include { paramsSummaryMap } from 'plugin/nf-schema' include { samplesheetToList } from 'plugin/nf-schema' +include { paramsHelp } from 'plugin/nf-schema' include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' From bcfdc6abdc8a89bc41aa7e977e66058f0a787b0d Mon Sep 17 00:00:00 2001 From: delfiterradas Date: Tue, 8 Sep 2026 19:32:40 +0200 Subject: [PATCH 2/5] Update changelog for first release --- CHANGELOG.md | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc790b5..1bf1fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,28 +9,15 @@ Initial release of nf-core/datasync, created with the [nf-core](https://nf-co.re ### `Added` -- [[#87](https://github.com/nf-core/datasync/pull/87)] - Add local test profile ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#79](https://github.com/nf-core/datasync/pull/79)] - Add exit status for `RCLONE` modules in multiqc report ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#74](https://github.com/nf-core/datasync/pull/74)] - Implement warning and `--download` parameter when working with remote directories and SHA checksums ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila), [@apeltzer](https://github.com/apeltzer) and [@antoniasaracco](https://github.com/antoniasaracco)). -- [[#67](https://github.com/nf-core/datasync/pull/67)] - Copy only files which are successfully validated ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila) and [@antoniasaracco](https://github.com/antoniasaracco)). -- [[#57](https://github.com/nf-core/datasync/pull/57)] - Add nf-tests with edge cases([@atrigila](https://github.com/atrigila), review by [@delfiterradas](https://github.com/delfiterradas)). -- [[#55](https://github.com/nf-core/datasync/pull/55)] - Add subdirectories with sample id to results. Improvements to MultiQC report (width of columns, sections and sub-section headers)colors to multiqc results and display first errors in tables ([@atrigila](https://github.com/atrigila), review by [@delfiterradas](https://github.com/delfiterradas)). -- [[#54](https://github.com/nf-core/datasync/pull/54)] - Add colors to multiqc results and display first errors in tables ([@atrigila](https://github.com/atrigila), review by [@delfiterradas](https://github.com/delfiterradas)). -- [[#31](https://github.com/nf-core/datasync/pull/31)] - Compute checksum for each input file ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#35](https://github.com/nf-core/datasync/pull/35)] - Compare generated checksum to given checksum in input and update test profiles ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#46](https://github.com/nf-core/datasync/pull/46)] - Import Rclone module from nf-core([@antoniasaracco](https://github.com/antoniasaracco), review by [@delfiterradas](https://github.com/delfiterradas)). -- [[#41](https://github.com/nf-core/datasync/pull/41)] - Generate MultiQC Report with comparechecksum tables and input samplesheet ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#49](https://github.com/nf-core/datasync/pull/49)] - Install `RCLONE_CHECK` and `RCLONE_CHECKSUM` modules from nf-core ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila) and [@antoniasaracco](https://github.com/antoniasaracco)). -- [[#59](https://github.com/nf-core/datasync/pull/59)] - Create pipeline documentation ([@antoniasaracco](https://github.com/antoniasaracco), review by [@atrigila](https://github.com/atrigila) and [@delfiterradas](https://github.com/delfiterradas)). +- Samplesheet-driven copying of files and directories between local paths and rclone-supported object storage locations. +- Validation of source data against supplied MD5 and SHA-256 checksum manifests before transfer. +- The ability to copy only files that pass checksum validation, and to download remote files when SHA-256 verification is required. +- Post-transfer comparison of copied data against the source, with detailed rclone status files for each sample. +- A MultiQC report covering the input samplesheet, validation summary, checksum validation, and post-transfer checks. +- A local test profile to explore the pipeline and its outputs. ### `Fixed` -- [[#84](https://github.com/nf-core/datasync/pull/84)] - Update `RCLONE_` modules, enhance source and destination path handling, add local `create_filter_list` module and other small fixes ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#83](https://github.com/nf-core/datasync/pull/83)] - Add apptainer version to avoid error in CI test ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila)). -- [[#78](https://github.com/nf-core/datasync/pull/78)] - Fixed linting issues reported by nf-core and Nextflow, and updated `rclone` modules and nf-tests to sort generated report files for deterministic snapshots ([@atrigila](https://github.com/atrigila), review by [@delfiterradas](https://github.com/delfiterradas) and [@antoniasaracco](https://github.com/antoniasaracco)). -- [[#76](https://github.com/nf-core/datasync/pull/76)] - Remove `--one-way` from rclone/checksum confi ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila) and [@antoniasaracco](https://github.com/antoniasaracco)). -- [[#73](https://github.com/nf-core/datasync/pull/73)] - Create tmp `files_to_copy.tx` avoiding error with write permissions in work directory ([@delfiterradas](https://github.com/delfiterradas), review by [@atrigila](https://github.com/atrigila) and [@antoniasaracco](https://github.com/antoniasaracco)). - ### `Dependencies` ### `Deprecated` From 4cbf1464f628d9e62d21f41319ba09a1dabcc2f4 Mon Sep 17 00:00:00 2001 From: delfiterradas Date: Tue, 8 Sep 2026 20:53:34 +0200 Subject: [PATCH 3/5] Add nf-metro plot --- README.md | 2 +- docs/images/datasync-metromap.png | Bin 58840 -> 0 bytes docs/images/datasync-nf-metro.mmd | 33 ++++++++++ docs/images/datasync_nf-metro.svg | 98 ++++++++++++++++++++++++++++++ ro-crate-metadata.json | 2 +- 5 files changed, 133 insertions(+), 2 deletions(-) delete mode 100644 docs/images/datasync-metromap.png create mode 100644 docs/images/datasync-nf-metro.mmd create mode 100644 docs/images/datasync_nf-metro.svg diff --git a/README.md b/README.md index 7a63602..96f62ea 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The current tested use case for this pipeline is transfer between S3 buckets. Pass an `rclone` configuration with `--rclone_config` whenever a source or destination URI needs credentials or provider settings. Samplesheet paths use local paths or standard URIs such as `s3://bucket/path`, not rclone's `remote:path` syntax. For non-S3 layouts, design and validate the provider-specific configuration using the upstream [rclone documentation](https://rclone.org/docs/). -![nf-core/datasync metro map](docs/images/datasync-metromap.png) +![nf-core/datasync metro map](docs/images/datasync_nf-metro.svg) ## Quick start diff --git a/docs/images/datasync-metromap.png b/docs/images/datasync-metromap.png deleted file mode 100644 index 108bbc0d85c982f1fb48c6bff7b73099f2f236e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58840 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 diff --git a/docs/images/datasync-nf-metro.mmd b/docs/images/datasync-nf-metro.mmd new file mode 100644 index 0000000..c4558dd --- /dev/null +++ b/docs/images/datasync-nf-metro.mmd @@ -0,0 +1,33 @@ +%%metro title: nf-core/datasync +%%metro mode: dark +%%metro style: nfcore +%%metro diamond_style: symmetric +%%metro line: main | Pipeline | #4CAF50 + +%%metro file: ref_in | CSV | Input\nSamplesheet | banner +%%metro file: reads_in | Config | Rclone\nConfig | banner +%%metro file: report_out | HTML | Report | banner +%%metro grid: datasync | 0,0 + +graph LR + subgraph datasync [Datasync pipeline] + ref_in[Input] + reads_in[ ] + node1[Rclone\nChecksum] + node2[Create\nFilter List] + node3[Rclone\nCheck] + node4[Rclone\nCopy] + node5[MultiQC] + Rclone\nChecksum[Rclone\nChecksum] + ref_in -->|main| Rclone\nChecksum + reads_in -->|main| Rclone\nChecksum + report_out[Output] + ref_in -->|main| node1 + reads_in -->|main| node1 + node1 -->|main| node2 + node2 -->|main| node4 + node4 -->|main| node3 + node3 -->|main| node5 + node1 -->|main| node4 + node5 -->|main| report_out + end diff --git a/docs/images/datasync_nf-metro.svg b/docs/images/datasync_nf-metro.svg new file mode 100644 index 0000000..21982fe --- /dev/null +++ b/docs/images/datasync_nf-metro.svg @@ -0,0 +1,98 @@ + + + + +{"groups":[{"color":"#4CAF50","id":"main","label":"Pipeline"}],"height":419,"match":{"flags":"i","target":"fqProcessName","type":"regex"},"nodes":[{"groups":["main"],"h":10.0,"id":"ref_in","label":"Input","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":80.0,"y":133.6},{"groups":["main"],"h":10.0,"id":"reads_in","label":"reads_in","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":80.0,"y":267.2},{"groups":["main"],"h":10.0,"id":"node1","label":"Rclone\nChecksum","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":176.0,"y":200.4},{"groups":["main"],"h":10.0,"id":"node2","label":"Create\nFilter List","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":236.0,"y":167.0},{"groups":["main"],"h":10.0,"id":"node3","label":"Rclone\nCheck","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":356.0,"y":200.4},{"groups":["main"],"h":10.0,"id":"node4","label":"Rclone\nCopy","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":296.0,"y":200.4},{"groups":["main"],"h":10.0,"id":"node5","label":"MultiQC","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":416.0,"y":200.4},{"groups":["main"],"h":10.0,"id":"report_out","label":"Output","patterns":[],"r":5.0,"region":"datasync","rx":5.0,"w":10.0,"x":476.0,"y":200.4}],"regions":[{"id":"datasync","label":"Datasync pipeline"}],"title":"nf-core/datasync","version":"1.0","width":594} + + +nf-core/datasync + + +1 + + + + + + + + + + + + + + + + +CSV +InputSamplesheet + + + + + + + + + +Config +RcloneConfig + + + + + + + + + + + + + + + + + + + + + + + + +HTML +Report + + + +RcloneChecksum + +CreateFilter List + +RcloneCopy + +RcloneCheck + +MultiQC + + +Pipeline +created with nf-metro v2.0.0 + \ No newline at end of file diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index 8ca15e3..919556e 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -23,7 +23,7 @@ "@type": "Dataset", "creativeWorkStatus": "Stable", "datePublished": "2026-09-08T15:38:46+00:00", - "description": "

\n \n \n \"nf-core/datasync\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/datasync)\n[![GitHub Actions CI Status](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/datasync/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/datasync/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.10.4-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-4.1.0-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/4.1.0)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/datasync)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23datasync-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/datasync)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/datasync** is a Nextflow pipeline for copying files and directories between storage locations and documenting their integrity. For every row in an input samplesheet, the pipeline:\n\n1. validates the source against a supplied MD5 and/or SHA-256 checksum manifest using [`rclone checksum`](https://rclone.org/commands/rclone_checksum/);\n2. copies the source to the requested destination with [`rclone copy`](https://rclone.org/);\n3. compares the copied data with the source using [`rclone check`](https://rclone.org/commands/rclone_check/); and\n4. produces detailed `rclone` status files and a consolidated MultiQC report.\n\nSources and destinations may be local paths or object-storage URIs such as Amazon S3, S3-compatible storage, or Azure Blob Storage. HTTP(S) URLs are not currently supported for samplesheet `input` or `output_path` values.\n\nThe current tested use case for this pipeline is transfer between S3 buckets.\n\nPass an `rclone` configuration with `--rclone_config` whenever a source or destination URI needs credentials or provider settings. Samplesheet paths use local paths or standard URIs such as `s3://bucket/path`, not rclone's `remote:path` syntax. For non-S3 layouts, design and validate the provider-specific configuration using the upstream [rclone documentation](https://rclone.org/docs/).\n\n![nf-core/datasync metro map](docs/images/datasync-metromap.png)\n\n## Quick start\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, see the [nf-core environment setup guide](https://nf-co.re/docs/get_started/environment_setup/overview). Nextflow 25.10.4 or later is required.\n\nTo explore the pipeline outputs before preparing your own data, run the bundled `test` profile with a container profile:\n\n```bash\nnextflow run nf-core/datasync \\\n -profile test,docker \\\n --outdir results\n```\n\nThe `test` profile supplies a small samplesheet and `rclone` configuration automatically. It also enables `--rclone_dry_run`, so no files are actually transferred. This makes it useful for exploring the `rclone/` output folders and `multiqc/multiqc_report.html`; remember that post-copy comparison reports describe whatever is already present at the destination because the dry run does not write transfer data.\n\nTo run the pipeline on your own data, create a samplesheet containing one transfer per row:\n\n```csv\nsample,input,output_path,checksum_md5,checksum_sha\nrun_001,/data/run_001,s3://archive/runs,/data/manifests/run_001_md5.tsv\nreference,/data/reference.fa,/data/references,,/data/manifests/reference_sha256.tsv\n```\n\nThen launch the pipeline using:\n\n```bash\nnextflow run nf-core/datasync \\\n -r \\\n -profile docker \\\n --input samplesheet.csv \\\n --outdir results \\\n --rclone_config /path/to/rclone.conf\n```\n\n`--rclone_config` is optional only when every source and destination is accessible without a configured rclone remote. See the [`rclone` configuration section](docs/usage.md#configuring-rclone-remotes) for the tested S3-to-S3 use case and guidance on adapting rclone configuration files for other providers. To preview copy operations without transferring data, add `--rclone_dry_run`; note that subsequent comparison reports will then describe the unchanged destination.\n\nSee the [usage documentation](docs/usage.md) for samplesheet rules, destination semantics, remote configuration, and reproducible execution. The complete generated parameter reference is available on the [nf-core pipeline page](https://nf-co.re/datasync/parameters).\n\n## Pipeline output\n\nResults are written below `--outdir`. See the [output documentation](docs/output.md) for file names and status-code interpretation.\n\n## Credits\n\nnf-core/datasync was originally written by Alexander Peltzer.\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n- Julian Schwab\n- Gregor Sturm\n- Antonia Saracco\n- Delfina Terradas\n- Anabella Trigila\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](docs/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#datasync` channel](https://nfcore.slack.com/channels/datasync) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", + "description": "

\n \n \n \"nf-core/datasync\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/datasync)\n[![GitHub Actions CI Status](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/datasync/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/datasync/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/datasync/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.10.4-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-4.1.0-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/4.1.0)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/datasync)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23datasync-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/datasync)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/datasync** is a Nextflow pipeline for copying files and directories between storage locations and documenting their integrity. For every row in an input samplesheet, the pipeline:\n\n1. validates the source against a supplied MD5 and/or SHA-256 checksum manifest using [`rclone checksum`](https://rclone.org/commands/rclone_checksum/);\n2. copies the source to the requested destination with [`rclone copy`](https://rclone.org/);\n3. compares the copied data with the source using [`rclone check`](https://rclone.org/commands/rclone_check/); and\n4. produces detailed `rclone` status files and a consolidated MultiQC report.\n\nSources and destinations may be local paths or object-storage URIs such as Amazon S3, S3-compatible storage, or Azure Blob Storage. HTTP(S) URLs are not currently supported for samplesheet `input` or `output_path` values.\n\nThe current tested use case for this pipeline is transfer between S3 buckets.\n\nPass an `rclone` configuration with `--rclone_config` whenever a source or destination URI needs credentials or provider settings. Samplesheet paths use local paths or standard URIs such as `s3://bucket/path`, not rclone's `remote:path` syntax. For non-S3 layouts, design and validate the provider-specific configuration using the upstream [rclone documentation](https://rclone.org/docs/).\n\n![nf-core/datasync metro map](docs/images/datasync_nf-metro.svg)\n\n## Quick start\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, see the [nf-core environment setup guide](https://nf-co.re/docs/get_started/environment_setup/overview). Nextflow 25.10.4 or later is required.\n\nTo explore the pipeline outputs before preparing your own data, run the bundled `test` profile with a container profile:\n\n```bash\nnextflow run nf-core/datasync \\\n -profile test,docker \\\n --outdir results\n```\n\nThe `test` profile supplies a small samplesheet and `rclone` configuration automatically. It also enables `--rclone_dry_run`, so no files are actually transferred. This makes it useful for exploring the `rclone/` output folders and `multiqc/multiqc_report.html`; remember that post-copy comparison reports describe whatever is already present at the destination because the dry run does not write transfer data.\n\nTo run the pipeline on your own data, create a samplesheet containing one transfer per row:\n\n```csv\nsample,input,output_path,checksum_md5,checksum_sha\nrun_001,/data/run_001,s3://archive/runs,/data/manifests/run_001_md5.tsv\nreference,/data/reference.fa,/data/references,,/data/manifests/reference_sha256.tsv\n```\n\nThen launch the pipeline using:\n\n```bash\nnextflow run nf-core/datasync \\\n -r \\\n -profile docker \\\n --input samplesheet.csv \\\n --outdir results \\\n --rclone_config /path/to/rclone.conf\n```\n\n`--rclone_config` is optional only when every source and destination is accessible without a configured rclone remote. See the [`rclone` configuration section](docs/usage.md#configuring-rclone-remotes) for the tested S3-to-S3 use case and guidance on adapting rclone configuration files for other providers. To preview copy operations without transferring data, add `--rclone_dry_run`; note that subsequent comparison reports will then describe the unchanged destination.\n\nSee the [usage documentation](docs/usage.md) for samplesheet rules, destination semantics, remote configuration, and reproducible execution. The complete generated parameter reference is available on the [nf-core pipeline page](https://nf-co.re/datasync/parameters).\n\n## Pipeline output\n\nResults are written below `--outdir`. See the [output documentation](docs/output.md) for file names and status-code interpretation.\n\n## Credits\n\nnf-core/datasync was originally written by Alexander Peltzer.\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n- Julian Schwab\n- Gregor Sturm\n- Antonia Saracco\n- Delfina Terradas\n- Anabella Trigila\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](docs/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#datasync` channel](https://nfcore.slack.com/channels/datasync) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", "hasPart": [ { "@id": "main.nf" From c43ec123643414cdb698195501af632a7e6d35af Mon Sep 17 00:00:00 2001 From: Delfina Terradas <155591053+delfiterradas@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:02:11 +0000 Subject: [PATCH 4/5] Fix linting --- docs/images/datasync_nf-metro.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/images/datasync_nf-metro.svg b/docs/images/datasync_nf-metro.svg index 21982fe..62f95c3 100644 --- a/docs/images/datasync_nf-metro.svg +++ b/docs/images/datasync_nf-metro.svg @@ -95,4 +95,4 @@ Copy" data-section-id="datasync" data-section-name="Datasync pipeline" /> Pipeline created with nf-metro v2.0.0 - \ No newline at end of file + From 89905cbf7e3105340ea267a59a411548b4b1b943 Mon Sep 17 00:00:00 2001 From: Delfina Terradas <155591053+delfiterradas@users.noreply.github.com> Date: Wed, 9 Sep 2026 10:51:17 -0300 Subject: [PATCH 5/5] Clean up CHANGELOG by removing empty sections --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13174ac..70658fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,3 @@ Initial release of nf-core/datasync, created with the [nf-core](https://nf-co.re - Post-transfer comparison of copied data against the source, with detailed rclone status files for each sample. - A MultiQC report covering the input samplesheet, validation summary, checksum validation, and post-transfer checks. - A local test profile to explore the pipeline and its outputs. - -### `Fixed` - -### `Dependencies` - -### `Deprecated`