Skip to content

Commit 142f8dd

Browse files
committed
Add -print-output option to print workflow outputs JSON
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent a369d6f commit 142f8dd

8 files changed

Lines changed: 57 additions & 17 deletions

File tree

docs/reference/cli.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,11 @@ The `run` command is used to execute a local pipeline script or remote pipeline
13291329
:::
13301330
: Run the workflow script skipping the execution of all processes.
13311331

1332+
`-print-output`
1333+
: :::{versionadded} 26.04.0
1334+
:::
1335+
: Print JSON-formatted workflow outputs to standard output.
1336+
13321337
`-process.<key>=<value>`
13331338
: Set process config options.
13341339

modules/nextflow/src/main/groovy/nextflow/Session.groovy

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ class Session implements ISession {
149149
*/
150150
Path outputDir
151151

152+
/**
153+
* Print workflow outputs to standard output
154+
*/
155+
boolean printOutput
156+
152157
/**
153158
* The folder where tasks temporary files are stored
154159
*/
@@ -407,6 +412,7 @@ class Session implements ISession {
407412

408413
// -- init output dir
409414
this.outputDir = FileHelper.toCanonicalPath(config.outputDir ?: 'results')
415+
this.printOutput = config.printOutput
410416

411417
// -- init work dir
412418
this.workDir = FileHelper.toCanonicalPath(config.workDir ?: 'work')

modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ class CmdRun extends CmdBase implements HubOptions {
116116
@Parameter(names=['-o', '-output-dir'], description = 'Directory where workflow outputs are stored')
117117
String outputDir
118118

119+
@Parameter(names=['-print-output'], description = 'Print workflow outputs to standard output')
120+
String printOutput
121+
119122
@Parameter(names=['-w', '-work-dir'], description = 'Directory where intermediate result files are stored')
120123
String workDir
121124

modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,9 @@ class ConfigBuilder {
568568
if( cmdRun.outputDir )
569569
config.outputDir = cmdRun.outputDir
570570

571+
if( cmdRun.printOutput )
572+
config.printOutput = cmdRun.printOutput
573+
571574
if( cmdRun.preview )
572575
config.preview = cmdRun.preview
573576

modules/nextflow/src/main/groovy/nextflow/script/OutputDsl.groovy

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616

1717
package nextflow.script
1818

19+
import groovy.json.JsonBuilder
20+
import groovy.json.JsonOutput
1921
import groovy.transform.CompileStatic
2022
import groovy.util.logging.Slf4j
2123
import groovyx.gpars.dataflow.DataflowVariable
2224
import nextflow.Session
2325
import nextflow.exception.ScriptRuntimeException
2426
import nextflow.extension.CH
27+
import nextflow.extension.DumpHelper
2528
import nextflow.extension.PublishOp
2629
/**
2730
* Implements the DSL for publishing workflow outputs
@@ -34,7 +37,7 @@ class OutputDsl {
3437

3538
private Map<String,Map> declarations = [:]
3639

37-
private Map<String,DataflowVariable> output = [:]
40+
private Map<String,DataflowVariable> dataflowOutputs = [:]
3841

3942
void declare(String name, Closure closure) {
4043
if( declarations.containsKey(name) )
@@ -71,7 +74,15 @@ class OutputDsl {
7174
final opts = publishOptions(name, defaults, overrides)
7275

7376
if( opts.enabled == null || opts.enabled )
74-
output[name] = new PublishOp(session, name, CH.getReadChannel(source), opts).apply()
77+
dataflowOutputs[name] = new PublishOp(session, name, CH.getReadChannel(source), opts).apply()
78+
}
79+
80+
// retrieve workflow outputs in order to propagate any errors
81+
session.addIgniter {
82+
final output = getOutput()
83+
// write output JSON to stdout if specified
84+
if( session.printOutput )
85+
println DumpHelper.prettyPrintJson(output)
7586
}
7687
}
7788

@@ -94,7 +105,7 @@ class OutputDsl {
94105
}
95106

96107
Map<String,Object> getOutput() {
97-
output.collectEntries { name, dv -> [name, dv.get()] }
108+
dataflowOutputs.collectEntries { name, dv -> [name, dv.get()] }
98109
}
99110

100111
static class DeclareDsl {

modules/nextflow/src/test/groovy/nextflow/script/OutputDslTest.groovy

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ class OutputDslTest extends Specification {
6161
}
6262
dsl.apply(session)
6363
session.fireDataflowNetwork()
64-
dsl.getOutput()
6564

6665
then:
6766
outputDir.resolve('foo/file1.txt').text == 'Hello'
@@ -106,7 +105,6 @@ class OutputDslTest extends Specification {
106105
}
107106
dsl.apply(session)
108107
session.fireDataflowNetwork()
109-
dsl.getOutput()
110108

111109
then:
112110
outputDir.resolve('file1.txt').text == 'Hello'
@@ -146,7 +144,6 @@ class OutputDslTest extends Specification {
146144
}
147145
dsl.apply(session)
148146
session.fireDataflowNetwork()
149-
dsl.getOutput()
150147

151148
then:
152149
0 * session.notifyFilePublish(_)
@@ -207,7 +204,9 @@ class OutputDslTest extends Specification {
207204

208205
def 'should report error for invalid path directive' () {
209206
when:
210-
def session = new Session(outputDir: Path.of('results'))
207+
def session = new Session(outputDir: Path.of('results')) {
208+
void abort(Throwable cause) { throw cause }
209+
}
211210

212211
session.outputs.put('foo', Channel.of(1, 2, 3))
213212

@@ -217,7 +216,6 @@ class OutputDslTest extends Specification {
217216
}
218217
dsl.apply(session)
219218
session.fireDataflowNetwork()
220-
dsl.getOutput()
221219

222220
then:
223221
def e = thrown(ScriptRuntimeException)
@@ -227,7 +225,9 @@ class OutputDslTest extends Specification {
227225

228226
def 'should report error for invalid publish target' () {
229227
when:
230-
def session = new Session(outputDir: Path.of('results'))
228+
def session = new Session(outputDir: Path.of('results')) {
229+
void abort(Throwable cause) { throw cause }
230+
}
231231
def file = Path.of('output.txt')
232232

233233
session.outputs.put('foo', Channel.of([file, file, file]))
@@ -238,7 +238,6 @@ class OutputDslTest extends Specification {
238238
}
239239
dsl.apply(session)
240240
session.fireDataflowNetwork()
241-
dsl.getOutput()
242241

243242
then:
244243
def e = thrown(ScriptRuntimeException)
@@ -247,7 +246,9 @@ class OutputDslTest extends Specification {
247246

248247
def 'should report error for invalid publish source' () {
249248
when:
250-
def session = new Session(outputDir: Path.of('results'))
249+
def session = new Session(outputDir: Path.of('results')) {
250+
void abort(Throwable cause) { throw cause }
251+
}
251252

252253
session.outputs.put('foo', Channel.of(42))
253254

@@ -257,7 +258,6 @@ class OutputDslTest extends Specification {
257258
}
258259
dsl.apply(session)
259260
session.fireDataflowNetwork()
260-
dsl.getOutput()
261261

262262
then:
263263
def e = thrown(ScriptRuntimeException)
@@ -267,7 +267,9 @@ class OutputDslTest extends Specification {
267267

268268
def 'should report error for invalid index file extension' () {
269269
when:
270-
def session = new Session(outputDir: Path.of('results'))
270+
def session = new Session(outputDir: Path.of('results')) {
271+
void abort(Throwable cause) { throw cause }
272+
}
271273

272274
session.outputs.put('foo', Channel.empty())
273275

@@ -279,7 +281,6 @@ class OutputDslTest extends Specification {
279281
}
280282
dsl.apply(session)
281283
session.fireDataflowNetwork()
282-
dsl.getOutput()
283284

284285
then:
285286
def e = thrown(ScriptRuntimeException)
@@ -288,7 +289,9 @@ class OutputDslTest extends Specification {
288289

289290
def 'should report error for invalid published value' () {
290291
when:
291-
def session = new Session(outputDir: Path.of('results'))
292+
def session = new Session(outputDir: Path.of('results')) {
293+
void abort(Throwable cause) { throw cause }
294+
}
292295

293296
session.outputs.put('foo', Channel.of(42))
294297

@@ -297,7 +300,6 @@ class OutputDslTest extends Specification {
297300
}
298301
dsl.apply(session)
299302
session.fireDataflowNetwork()
300-
dsl.getOutput()
301303

302304
then:
303305
def e = thrown(ScriptRuntimeException)

tests/checks/output-dsl.nf/.checks

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11

22
echo "Initial run"
3-
$NXF_RUN --save_bam_bai | tee stdout
3+
$NXF_RUN -print-output --save_bam_bai | tee stdout
44

55
[[ $(grep INFO .nextflow.log | grep -c 'Submitted process > fastqc') == 3 ]] || false
66
[[ $(grep INFO .nextflow.log | grep -c 'Submitted process > align') == 3 ]] || false
77
[[ $(grep INFO .nextflow.log | grep -c 'Submitted process > quant') == 3 ]] || false
8+
sed "s|"$(dirname "$(realpath "$0")")"||g" stdout > stdout.norm
9+
cmp .expected stdout.norm || false
810

911
[[ -f results/log/alpha.fastqc.log ]] || false
1012
[[ -f results/log/beta.fastqc.log ]] || false
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"samples": "/results/samples.csv",
3+
"summary": [
4+
"/results/summary_report.html",
5+
"/results/summary_data/data.json",
6+
"/results/summary_data/fastqc.txt"
7+
]
8+
}

0 commit comments

Comments
 (0)