From c53cdc2a988ca83200e9ac628919fb2b85f9aec3 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Sun, 22 Sep 2024 09:31:04 -0500 Subject: [PATCH] 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) + } }