Private Beta
.md

BountyHub Docs

Step Outputs

Declare what a step produces and pass it to later steps by reference instead of remembering file names.

Step Outputs

Every step declares what it produces. Consumers reference the step's result, not the producer's file name:

BHW

hosts.output is a file reference. The second step never needs to know that the file is called hosts.txt.

#output and outputs

A step result has:

FieldMeaning
run.outputthe single declared output (file or directory)
run.outputs.<name>a named declared output

run.output exists only when the step declares exactly one output. With zero or several, use run.outputs.<name>; using .output there is an error.

#Where outputs come from

The runner writes the file from stdout. stdout = path.file(...) exposes that file as run.output:

BHW

The tool writes the file itself. The runner only sees argv, so declare the file:

BHW

The step type owns the destination. step.transform publishes its destination; step.agent publishes its declared agent.output file:

BHW

#Multiple files

Use a name → path map. Each name becomes a field:

BHW

When a tool writes an unpredictable tree, publish a directory or a glob instead of listing every file:

BHW
BHW

#Passing outputs on

An output is a path-like resource. Use it anywhere a file reference is accepted:

BHW

#Modules

A module declares its own inputs and outputs. The caller sees only the declared outputs:

BHW

A module invocation is isolated: its declared file inputs are copied in, and only its declared outputs are copied back. It never reads the caller's other files, and it never writes into the caller's workspace.

#Loops

Inside step.for_each, each iteration runs the body as its own step sequence. Give each iteration a distinct output path, then consume the published files from a later step:

BHW

Per-iteration result collections (scans[i].outputs) are not available yet; the published files are the interface.

#Isolated steps

By default, script/exec/agent steps in an activity share one workspace. To give every data step its own private filesystem, opt in at the workflow level:

BHW

With isolation:

  • each script/exec/transform/agent/module/dataset/decision step gets a fresh directory;
  • declared outputs are the only data that leaves it;
  • a later step receives a referenced output by copy, and cannot read anything else the earlier step wrote;
  • files a step writes without declaring them are discarded.

Module steps are always isolated, regardless of the flag. for_each control steps run in the parent scope, but their body steps are isolated and their items (including glob and batch files) are materialized into each iteration. Artifact downloads are isolated too, and must declare outputs so the downloaded files leave the step.

References are the interface either way: run.output / run.outputs.<name>. Passing a bare path string that another step happened to write is not data flow; it only works while the shared workspace is enabled.

#Rules

  • A step's file names appear once: where the step is defined.
  • Consumers use run.output / run.outputs.<name>, never a producer's path.
  • .output only exists when exactly one output is declared.
  • outputs = {...} is the complete contract; it replaces the implicit stdout output.
  • Directories and globs are valid outputs for tools that write a tree.
  • Module steps are isolated; by default script/exec/agent share the activity workspace, and workflow(isolated_steps = True) gives each data step its own private scope.

#Migration

Old workflows shared a directory and passed file names between steps. Replace the path with the producer's result:

BHW