From 0e220f642d3455b4999a663d83443d1b0e14f037 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Wed, 8 Apr 2026 15:19:47 -0500 Subject: [PATCH] Add option to disable printing workflow outputs Signed-off-by: Ben Sherman --- docs/reference/cli.md | 2 +- .../main/groovy/nextflow/cli/CmdRun.groovy | 6 +-- .../nextflow/config/ConfigBuilder.groovy | 6 --- .../groovy/nextflow/script/OutputDsl.groovy | 6 ++- .../nextflow/config/ConfigBuilderTest.groovy | 1 + .../nextflow/script/OutputDslTest.groovy | 40 +++++++++++-------- 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index d447f20f81..44ac9e31a6 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1614,7 +1614,7 @@ The `run` command is used to execute a local pipeline script or remote pipeline `-output-format` : :::{versionadded} 26.04.0 ::: -: Output format for printing workflow outputs. Options: `text` (default), `json`. +: Output format for printing workflow outputs. Options: `text` (default), `json`, `none`. `-params-file` : Load script parameters from a JSON/YAML file. diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 60e5c174a4..950e7cc4ed 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -116,7 +116,7 @@ class CmdRun extends CmdBase implements HubOptions { @Parameter(names=['-o', '-output-dir'], description = 'Directory where workflow outputs are stored') String outputDir - @Parameter(names=['-output-format'], description = 'Output format for printing workflow outputs. Options: `text` (default), `json`') + @Parameter(names=['-output-format'], description = 'Output format for printing workflow outputs. Options: `text` (default), `json`, `none`') String outputFormat = 'text' @Parameter(names=['-w', '-work-dir'], description = 'Directory where intermediate result files are stored') @@ -324,8 +324,8 @@ class CmdRun extends CmdBase implements HubOptions { if( offline && latest ) throw new AbortOperationException("Command line options `-latest` and `-offline` cannot be specified at the same time") - if( outputFormat !in ['json', 'text'] ) - throw new AbortOperationException("Command line option `-output-format` should be either `json` or `text`") + if( outputFormat !in ['text', 'json', 'none'] ) + throw new AbortOperationException("Command line option `-output-format` should be either `text`, `json`, or `none`") checkRunName() diff --git a/modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy b/modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy index 087c0efd39..8bae62923f 100644 --- a/modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy @@ -934,16 +934,10 @@ class ConfigBuilder { // strip secrets SecretHelper.hideSecrets(config) - // strip command line options - stripCliOptions(config) // compute config final result = toCanonicalString(config, false) // dump config for debugging log.trace "Resolved config:\n${result.indent('\t')}" return result } - - private static void stripCliOptions(Map config) { - config.remove('outputFormat') - } } diff --git a/modules/nextflow/src/main/groovy/nextflow/script/OutputDsl.groovy b/modules/nextflow/src/main/groovy/nextflow/script/OutputDsl.groovy index e8d7aeb9da..a731b4602d 100644 --- a/modules/nextflow/src/main/groovy/nextflow/script/OutputDsl.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/script/OutputDsl.groovy @@ -76,11 +76,13 @@ class OutputDsl { } // print workflow outputs on run completion - session.addIgniter { + session.workflowMetadata.onComplete { + if( !session.isSuccess() ) + return final output = getOutput() if( session.outputFormat == 'json' ) session.printConsole(DumpHelper.prettyPrintJson(output), true) - else + else if( session.outputFormat == 'text' ) printOutput(session, output) } } diff --git a/modules/nextflow/src/test/groovy/nextflow/config/ConfigBuilderTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/ConfigBuilderTest.groovy index 991dd9368f..64de44bad8 100644 --- a/modules/nextflow/src/test/groovy/nextflow/config/ConfigBuilderTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/config/ConfigBuilderTest.groovy @@ -2383,6 +2383,7 @@ class ConfigBuilderTest extends Specification { executor = { 'local' } } + outputFormat = 'text' workDir = 'work' tower { diff --git a/modules/nextflow/src/test/groovy/nextflow/script/OutputDslTest.groovy b/modules/nextflow/src/test/groovy/nextflow/script/OutputDslTest.groovy index 2f38b433cf..1d5a2dbae9 100644 --- a/modules/nextflow/src/test/groovy/nextflow/script/OutputDslTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/script/OutputDslTest.groovy @@ -26,14 +26,21 @@ import nextflow.exception.ScriptRuntimeException import nextflow.trace.event.FilePublishEvent import nextflow.trace.event.WorkflowOutputEvent import spock.lang.Specification - -import static test.ScriptHelper.runDataflow /** * * @author Ben Sherman */ class OutputDslTest extends Specification { + Session createSession(Map config) { + def session = new Session(config) { + @Override + void abort(Throwable cause) { throw cause } + } + session.init(null) + return session + } + def 'should publish workflow outputs'() { given: def root = Files.createTempDirectory('test') @@ -58,7 +65,7 @@ class OutputDslTest extends Specification { SysEnv.push(NXF_FILE_ROOT: root.toString()) when: - def session = Spy(new Session(config)) + def session = Spy(createSession(config)) session.outputs.put('foo', Channel.of(file1)) session.outputs.put('bar', Channel.of(file2)) @@ -77,6 +84,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: outputDir.resolve('foo/file1.txt').text == 'Hello' @@ -112,7 +120,7 @@ class OutputDslTest extends Specification { SysEnv.push(NXF_FILE_ROOT: root.toString()) when: - def session = Spy(new Session(config)) + def session = Spy(createSession(config)) session.outputs.put('foo', Channel.of(file1)) @@ -121,6 +129,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: outputDir.resolve('file1.txt').text == 'Hello' @@ -151,7 +160,7 @@ class OutputDslTest extends Specification { SysEnv.push(NXF_FILE_ROOT: root.toString()) when: - def session = Spy(new Session(config)) + def session = Spy(createSession(config)) session.outputs.put('foo', Channel.of(record)) @@ -160,6 +169,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: 0 * session.notifyFilePublish(_) @@ -220,9 +230,7 @@ class OutputDslTest extends Specification { def 'should report error for invalid path directive' () { when: - def session = new Session(outputDir: Path.of('results')) { - void abort(Throwable cause) { throw cause } - } + def session = createSession(outputDir: Path.of('results')) session.outputs.put('foo', Channel.of(1, 2, 3)) @@ -232,6 +240,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: def e = thrown(ScriptRuntimeException) @@ -241,9 +250,7 @@ class OutputDslTest extends Specification { def 'should report error for invalid publish target' () { when: - def session = new Session(outputDir: Path.of('results')) { - void abort(Throwable cause) { throw cause } - } + def session = createSession(outputDir: Path.of('results')) def file = Path.of('output.txt') session.outputs.put('foo', Channel.of([file, file, file])) @@ -254,6 +261,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: def e = thrown(ScriptRuntimeException) @@ -262,9 +270,7 @@ class OutputDslTest extends Specification { def 'should report error for invalid publish source' () { when: - def session = new Session(outputDir: Path.of('results')) { - void abort(Throwable cause) { throw cause } - } + def session = createSession(outputDir: Path.of('results')) session.outputs.put('foo', Channel.of(42)) @@ -274,6 +280,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: def e = thrown(ScriptRuntimeException) @@ -283,9 +290,7 @@ class OutputDslTest extends Specification { def 'should report error for invalid index file extension' () { when: - def session = new Session(outputDir: Path.of('results')) { - void abort(Throwable cause) { throw cause } - } + def session = createSession(outputDir: Path.of('results')) session.outputs.put('foo', Channel.empty()) @@ -297,6 +302,7 @@ class OutputDslTest extends Specification { } dsl.apply(session) session.fireDataflowNetwork() + dsl.getOutput() then: def e = thrown(ScriptRuntimeException)