From c53cdc2a988ca83200e9ac628919fb2b85f9aec3 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Sun, 22 Sep 2024 09:31:04 -0500 Subject: [PATCH 1/5] Add `manifest.contributors` config option Signed-off-by: Ben Sherman --- docs/config.md | 17 ++- .../groovy/nextflow/config/Manifest.groovy | 57 +++++++-- .../nextflow/config/ManifestTest.groovy | 120 +++++++++++++----- 3 files changed, 153 insertions(+), 41 deletions(-) diff --git a/docs/config.md b/docs/config.md index 55de60428a..dc93122270 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1229,8 +1229,22 @@ The `manifest` scope allows you to define some meta-data information needed when The following settings are available: `manifest.author` +: :::{deprecated} 24.09.0-edge + Use `manifest.contributors` instead. + ::: : Project author name (use a comma to separate multiple names). +`manifest.contributors` +: :::{versionadded} 24.09.0-edge + ::: +: List of project contributors. Should be a list of maps. The following fields are supported in the contributor map: + - `name`: the contributor's name + - `affiliation`: the contributor's affiliated organization + - `email`: the contributor's email address + - `github`: the contributor's GitHub URL + - `contribution`: list of contributions, can be any of `'author'`, `'maintainer'` + - `orcid`: the contributor's [ORCID](https://orcid.org/) + `manifest.defaultBranch` : Git repository default branch (default: `master`). @@ -1255,9 +1269,6 @@ The following settings are available: `manifest.mainScript` : Project main script (default: `main.nf`). -`manifest.maintainer` -: Project maintainer name (use a comma to separate multiple names). - `manifest.name` : Project short name. diff --git a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy index f8592294e2..1aced2d2dc 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy @@ -16,9 +16,13 @@ package nextflow.config +import java.util.stream.Collectors import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode import groovy.util.logging.Slf4j +import nextflow.exception.AbortOperationException + import static nextflow.Const.DEFAULT_BRANCH import static nextflow.Const.DEFAULT_MAIN_FILE_NAME /** @@ -102,22 +106,33 @@ class Manifest { target.docsUrl } - String getIcon(){ + String getIcon() { target.icon } - String getMaintainer(){ - target.maintainer - } - - String getOrganisation(){ + String getOrganisation() { target.organisation } - String getLicense(){ + String getLicense() { target.license } + List getContributors() { + if( !target.contributors ) + return Collections.emptyList() + + try { + final contributors = target.contributors as List + return contributors.stream() + .map(opts -> new Contributor(opts)) + .collect(Collectors.toList()) + } + catch( ClassCastException | IllegalArgumentException e ){ + throw new AbortOperationException("Invalid config option `manifest.contributors` -- should be a list of maps") + } + } + Map toMap() { final result = new HashMap(15) result.author = getAuthor() @@ -131,9 +146,35 @@ class Manifest { result.doi = getDoi() result.docsUrl = getDocsUrl() result.icon = getIcon() - result.maintainer = getMaintainer() result.organisation = getOrganisation() result.license = getLicense() + result.contributors = getContributors() return result } + + @EqualsAndHashCode + static class Contributor { + String name + String affiliation + String email + String github + Set contribution + String orcid + + Contributor(Map opts) { + name = opts.name as String + affiliation = opts.affiliation as String + email = opts.email as String + github = opts.github as String + contribution = (opts.contribution as List).stream() + .map(c -> Contribution.valueOf(c.toUpperCase())) + .collect(Collectors.toSet()) + orcid = opts.orcid as String + } + } + + static enum Contribution { + AUTHOR, + MAINTAINER + } } diff --git a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy index 8fb398590b..07c4241d4f 100644 --- a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy @@ -16,6 +16,7 @@ package nextflow.config +import nextflow.exception.AbortOperationException import spock.lang.Specification /** * @@ -26,49 +27,108 @@ class ManifestTest extends Specification { def 'should check manifest object' () { given: - def MAN = [author: 'pablo', nextflowVersion: '1.2.3', name: 'foo', - maintainer: 'john', organisation: 'My Organisation', icon: 'icon.png', - docsUrl: 'https://docs.io', license: 'Apache v2'] + def MAN = [ + author: 'pablo', + contributors: [ + [ + name: 'Alice', + affiliation: 'University', + email: 'alice@university.edu', + contribution: ['author', 'maintainer'], + ], + [ + name: 'Bob', + affiliation: 'Company', + email: 'bob@company.com', + contribution: ['maintainer'], + ] + ], + nextflowVersion: '1.2.3', + name: 'foo', + organisation: 'My Organisation', + icon: 'icon.png', + docsUrl: 'https://docs.io', + license: 'Apache v2' + ] when: def manifest = new Manifest(MAN) then: - manifest.with { - author == 'pablo' - nextflowVersion == '1.2.3' - name == 'foo' - maintainer == 'john' - organisation == 'My Organisation' - icon == 'icon.png' - docsUrl == 'https://docs.io' - license == 'Apache v2' - } + manifest.author == 'pablo' + manifest.contributors == [ + new Manifest.Contributor([ + name: 'Alice', + affiliation: 'University', + email: 'alice@university.edu', + contribution: ['author', 'maintainer'], + ]), + new Manifest.Contributor([ + name: 'Bob', + affiliation: 'Company', + email: 'bob@company.com', + contribution: ['maintainer'], + ]) + ] + manifest.nextflowVersion == '1.2.3' + manifest.name == 'foo' + manifest.organisation == 'My Organisation' + manifest.icon == 'icon.png' + manifest.docsUrl == 'https://docs.io' + manifest.license == 'Apache v2' } def 'should check empty manifest' () { - // check empty manifest when: def manifest = new Manifest(new ConfigObject()) then: - manifest.with { - homePage == null - defaultBranch == 'master' - description == null - author == null - mainScript == 'main.nf' - gitmodules == null - nextflowVersion == null - version == null - name == null - maintainer == null - docsUrl == null - organisation == null - icon == null - license == null - } + manifest.homePage == null + manifest.defaultBranch == 'master' + manifest.description == null + manifest.author == null + manifest.contributors == [] + manifest.mainScript == 'main.nf' + manifest.gitmodules == null + manifest.nextflowVersion == null + manifest.version == null + manifest.name == null + manifest.docsUrl == null + manifest.organisation == null + manifest.icon == null + manifest.license == null } + def 'should throw error on invalid manifest' () { + when: + def manifest = new Manifest([ + contributors: [ 'Alice' ] + ]) + manifest.contributors + then: + thrown(AbortOperationException) + + when: + manifest = new Manifest([ + contributors: [[ + name: 'Alice', + contribution: 'author' + ]] + ]) + manifest.contributors + then: + thrown(AbortOperationException) + + when: + manifest = new Manifest([ + contributors: [[ + name: 'Alice', + contribution: [ 'owner' ] + ]] + ]) + manifest.contributors + then: + thrown(AbortOperationException) + } } From 01b9c529b091a961a6143f5bdb5320a8bd65b812 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Tue, 1 Oct 2024 01:06:33 -0500 Subject: [PATCH 2/5] Apply suggestions from review Signed-off-by: Ben Sherman --- docs/config.md | 4 +-- .../groovy/nextflow/config/Manifest.groovy | 9 +++--- .../nextflow/config/ManifestTest.groovy | 29 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/config.md b/docs/config.md index dc93122270..1b69c637fd 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1242,8 +1242,8 @@ The following settings are available: - `affiliation`: the contributor's affiliated organization - `email`: the contributor's email address - `github`: the contributor's GitHub URL - - `contribution`: list of contributions, can be any of `'author'`, `'maintainer'` - - `orcid`: the contributor's [ORCID](https://orcid.org/) + - `contribution`: list of contribution types, each element can be one of `'author'`, `'maintainer'`, or `'contributor'` + - `orcid`: the contributor's [ORCID](https://orcid.org/) URL `manifest.defaultBranch` : Git repository default branch (default: `master`). diff --git a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy index 1aced2d2dc..f7a0b76831 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy @@ -158,7 +158,7 @@ class Manifest { String affiliation String email String github - Set contribution + Set contribution String orcid Contributor(Map opts) { @@ -167,14 +167,15 @@ class Manifest { email = opts.email as String github = opts.github as String contribution = (opts.contribution as List).stream() - .map(c -> Contribution.valueOf(c.toUpperCase())) + .map(c -> ContributionType.valueOf(c.toUpperCase())) .collect(Collectors.toSet()) orcid = opts.orcid as String } } - static enum Contribution { + static enum ContributionType { AUTHOR, - MAINTAINER + MAINTAINER, + CONTRIBUTOR } } diff --git a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy index 07c4241d4f..854f674dad 100644 --- a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy @@ -18,6 +18,8 @@ package nextflow.config import nextflow.exception.AbortOperationException import spock.lang.Specification + +import static nextflow.config.Manifest.ContributionType /** * * @author Paolo Di Tommaso @@ -35,12 +37,13 @@ class ManifestTest extends Specification { affiliation: 'University', email: 'alice@university.edu', contribution: ['author', 'maintainer'], + orcid: 'https://orcid.org/0000-0000-0000-0000' ], [ name: 'Bob', affiliation: 'Company', email: 'bob@company.com', - contribution: ['maintainer'], + contribution: ['contributor'], ] ], nextflowVersion: '1.2.3', @@ -54,20 +57,16 @@ class ManifestTest extends Specification { def manifest = new Manifest(MAN) then: manifest.author == 'pablo' - manifest.contributors == [ - new Manifest.Contributor([ - name: 'Alice', - affiliation: 'University', - email: 'alice@university.edu', - contribution: ['author', 'maintainer'], - ]), - new Manifest.Contributor([ - name: 'Bob', - affiliation: 'Company', - email: 'bob@company.com', - contribution: ['maintainer'], - ]) - ] + manifest.contributors.size() == 2 + manifest.contributors[0].name == 'Alice' + manifest.contributors[0].affiliation == 'University' + manifest.contributors[0].email == 'alice@university.edu' + manifest.contributors[0].contribution == [ContributionType.AUTHOR, ContributionType.MAINTAINER] as Set + manifest.contributors[0].orcid == 'https://orcid.org/0000-0000-0000-0000' + manifest.contributors[1].name == 'Bob' + manifest.contributors[1].affiliation == 'Company' + manifest.contributors[1].email == 'bob@company.com' + manifest.contributors[1].contribution == [ContributionType.CONTRIBUTOR] as Set manifest.nextflowVersion == '1.2.3' manifest.name == 'foo' manifest.organisation == 'My Organisation' From dbd66f28b42736cdac5d42a09309987cce3ee515 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Tue, 1 Oct 2024 05:42:51 -0500 Subject: [PATCH 3/5] Serialize contributors to list of maps Signed-off-by: Ben Sherman --- .../groovy/nextflow/config/Manifest.groovy | 48 ++++++++++++------- .../nextflow/config/ManifestTest.groovy | 25 +++++++++- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy index f7a0b76831..8a2f50dca7 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy @@ -67,6 +67,21 @@ class Manifest { target.author } + List getContributors() { + if( !target.contributors ) + return Collections.emptyList() + + try { + final contributors = target.contributors as List + return contributors.stream() + .map(opts -> new Contributor(opts)) + .collect(Collectors.toList()) + } + catch( ClassCastException | IllegalArgumentException e ){ + throw new AbortOperationException("Invalid config option `manifest.contributors` -- should be a list of maps") + } + } + String getMainScript() { target.mainScript ?: DEFAULT_MAIN_FILE_NAME } @@ -118,24 +133,12 @@ class Manifest { target.license } - List getContributors() { - if( !target.contributors ) - return Collections.emptyList() - - try { - final contributors = target.contributors as List - return contributors.stream() - .map(opts -> new Contributor(opts)) - .collect(Collectors.toList()) - } - catch( ClassCastException | IllegalArgumentException e ){ - throw new AbortOperationException("Invalid config option `manifest.contributors` -- should be a list of maps") - } - } - Map toMap() { final result = new HashMap(15) result.author = getAuthor() + result.contributors = getContributors().stream() + .map(c -> c.toMap()) + .collect(Collectors.toList()) result.defaultBranch = getDefaultBranch() result.description = getDescription() result.homePage = homePage @@ -148,7 +151,6 @@ class Manifest { result.icon = getIcon() result.organisation = getOrganisation() result.license = getLicense() - result.contributors = getContributors() return result } @@ -171,6 +173,20 @@ class Manifest { .collect(Collectors.toSet()) orcid = opts.orcid as String } + + Map toMap() { + final result = new HashMap(6) + result.name = name + result.affiliation = affiliation + result.email = email + result.github = github + result.contribution = contribution.stream() + .map(c -> c.toString().toLowerCase()) + .sorted() + .collect(Collectors.toList()) + result.orcid = orcid + return result + } } static enum ContributionType { diff --git a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy index 854f674dad..0a128fe030 100644 --- a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy @@ -29,7 +29,7 @@ class ManifestTest extends Specification { def 'should check manifest object' () { given: - def MAN = [ + def MAP = [ author: 'pablo', contributors: [ [ @@ -54,7 +54,7 @@ class ManifestTest extends Specification { license: 'Apache v2' ] when: - def manifest = new Manifest(MAN) + def manifest = new Manifest(MAP) then: manifest.author == 'pablo' manifest.contributors.size() == 2 @@ -98,6 +98,27 @@ class ManifestTest extends Specification { } + def 'should convert manifest to map' () { + + when: + def MAP = [ + name: 'Alice', + affiliation: 'University', + email: 'alice@university.edu', + contribution: ['author', 'maintainer'], + orcid: 'https://orcid.org/0000-0000-0000-0000' + ] + then: + new Manifest.Contributor(MAP).toMap() == [ + name: 'Alice', + affiliation: 'University', + email: 'alice@university.edu', + github: null, + contribution: ['author', 'maintainer'], + orcid: 'https://orcid.org/0000-0000-0000-0000' + ] + } + def 'should throw error on invalid manifest' () { when: def manifest = new Manifest([ From c1e1e59ac17b8d534da0e8c00d2b41e7787ad34b Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Tue, 1 Oct 2024 09:37:30 -0500 Subject: [PATCH 4/5] Rename organisation -> organization Signed-off-by: Ben Sherman --- docs/reference/config.md | 6 +++--- .../src/main/groovy/nextflow/config/Manifest.groovy | 4 ++-- .../src/main/groovy/nextflow/scm/AssetManager.groovy | 2 +- .../src/test/groovy/nextflow/config/ManifestTest.groovy | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/reference/config.md b/docs/reference/config.md index 106c95e14e..41fbc289ea 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -614,7 +614,7 @@ The following settings are available: : :::{versionadded} 24.04.0 ::: : *Used only by the {ref}`slurm-executor`, {ref}`lsf-executor`, {ref}`pbs-executor` and {ref}`pbspro-executor` executors.* -: Allows specifying the project or organisation account that should be charged for running the pipeline jobs. +: Allows specifying the project or organization account that should be charged for running the pipeline jobs. `executor.cpus` : The maximum number of CPUs made available by the underlying system. Used only by the `local` executor. @@ -1200,8 +1200,8 @@ The following settings are available: manifest.nextflowVersion = '!>=1.2' // with ! prefix, stop execution if current version does not match required version. ``` -`manifest.organisation` -: Project organisation +`manifest.organization` +: Project organization `manifest.recurseSubmodules` : Pull submodules recursively from the Git repository. diff --git a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy index 8a2f50dca7..089f6cd212 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy @@ -126,7 +126,7 @@ class Manifest { } String getOrganisation() { - target.organisation + target.organization } String getLicense() { @@ -149,7 +149,7 @@ class Manifest { result.doi = getDoi() result.docsUrl = getDocsUrl() result.icon = getIcon() - result.organisation = getOrganisation() + result.organization = getOrganisation() result.license = getLicense() return result } diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/AssetManager.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/AssetManager.groovy index dd304ad59e..5f325620d8 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/AssetManager.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/AssetManager.groovy @@ -64,7 +64,7 @@ class AssetManager { /** * The pipeline name. It must be in the form {@code username/repo} where 'username' - * is a valid user name or organisation account, while 'repo' is the repository name + * is a valid user name or organization account, while 'repo' is the repository name * containing the pipeline code */ private String project diff --git a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy index 0a128fe030..6534450cc1 100644 --- a/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/config/ManifestTest.groovy @@ -48,7 +48,7 @@ class ManifestTest extends Specification { ], nextflowVersion: '1.2.3', name: 'foo', - organisation: 'My Organisation', + organization: 'My Organization', icon: 'icon.png', docsUrl: 'https://docs.io', license: 'Apache v2' @@ -69,7 +69,7 @@ class ManifestTest extends Specification { manifest.contributors[1].contribution == [ContributionType.CONTRIBUTOR] as Set manifest.nextflowVersion == '1.2.3' manifest.name == 'foo' - manifest.organisation == 'My Organisation' + manifest.organization == 'My Organization' manifest.icon == 'icon.png' manifest.docsUrl == 'https://docs.io' manifest.license == 'Apache v2' @@ -92,7 +92,7 @@ class ManifestTest extends Specification { manifest.version == null manifest.name == null manifest.docsUrl == null - manifest.organisation == null + manifest.organization == null manifest.icon == null manifest.license == null From 005624cf5abec63350a1bd5d60209d90859293ba Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Wed, 9 Oct 2024 09:47:42 -0500 Subject: [PATCH 5/5] Fix failing test Signed-off-by: Ben Sherman --- .../nextflow/src/main/groovy/nextflow/config/Manifest.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy index 089f6cd212..e2fb76a85b 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/Manifest.groovy @@ -125,7 +125,7 @@ class Manifest { target.icon } - String getOrganisation() { + String getOrganization() { target.organization } @@ -149,7 +149,7 @@ class Manifest { result.doi = getDoi() result.docsUrl = getDocsUrl() result.icon = getIcon() - result.organization = getOrganisation() + result.organization = getOrganization() result.license = getLicense() return result }