1.1.3 Inputs and Outputs
Overview
This section is going to go over how to pass data between different steps in a job. We'll continue building on our hello-world.yml
pipeline.
In the previous section we learned that steps are where we tell Concourse what to run (i.e. run my tests, run this bash script, build this image, etc.). We are going to expand on the concept of steps and show you how to pass artifacts/files between tasks using inputs
and outputs
.
What are inputs and outputs?
The simple answer is that inputs and outputs are directories that get passed between steps. We'll refer to both inputs and outputs as artifacts.
Let's start exploring how artifacts work by adding a
to our hello-world-task
.
jobs:
- name: hello-world-job
plan:
- task: hello-world-task
config:
platform: linux
image_resource:
type: registry-image
source:
repository: busybox
tag: latest
# Adds a "the-artifact" directory to our task
outputs:
- name: the-artifact
run:
# Change the command to `ls -lF` to see
# what the task sees in its working directory
path: ls
args: ["-lF"]
Update the pipeline and trigger the job:
$ fly -t tutorial set-pipeline -p hello-world -c hello-world.yml
$ fly -t tutorial trigger-job --job hello-world/hello-world-job --watch
...
selected worker: 57d7419112ca
running ls -lF
total 4
drwxr-xr-x 2 root root 4096 Apr 8 16:42 the-artifact/
succeeded
We can see that in the task's current working directory there is now a folder called the-artifact
. Concourse makes output directories for you and will pass any contents inside the folder onto later steps. Let's see how that works next.
Passing outputs to another task
To pass artifacts from one task to another, the first task must declare an output. The second task must then declare an input with the exact same name. Let's update the pipeline to do the following:
Have the first task create a file inside
the-artifact
Create a second task to read the file inside
the-artifact
from the previous step
jobs:
- name: hello-world-job
plan:
- task: hello-world-task
config:
platform: linux
image_resource: &image # Declaring a YAML anchor
type: registry-image
source:
repository: busybox
tag: latest
outputs:
- name: the-artifact
run:
# This is a neat way of embedding a script into a task
path: sh
args:
- -cx
- |
ls -l .
echo "hello from another step!" > the-artifact/message
# Add a second task that reads the contents of the-artifact/message
- task: read-the-artifact
config:
platform: linux
image_resource: *image
# To receive "the-artifact", specify it as an input
inputs:
- name: the-artifact
run:
path: sh
args:
- -cx
- |
ls -l .
cat the-artifact/message
Update the pipeline and trigger the job:
$ fly -t tutorial set-pipeline -p hello-world -c hello-world.yml
$ fly -t tutorial trigger-job --job hello-world/hello-world-job --watch
initializing
selected worker: 57d7419112ca
running sh -cx ls -l .
echo "hello from another step!" > the-artifact/message
+ ls -l .
total 4
drwxr-xr-x 2 root root 4096 Feb 26 19:09 the-artifact
+ echo 'hello from another step!'
initializing
selected worker: 57d7419112ca
running sh -cx ls -l .
cat the-artifact/message
+ ls -l .
total 4
drwxr-xr-x 1 root root 4096 Feb 26 19:09 the-artifact
+ cat the-artifact/message
hello from another step!
succeeded
It's a bit hard to see when the first task stops and the second one starts in the terminal. Looking at the build from the web UI makes this clearer:

With the above pipeline we can see that the file made in the first step is made available in the second step via the the-artifact
.
How does Concourse track artifacts?
As Concourse is running the steps in your job, it is creating a list of named artifacts. Let's see what that looks like for the pipeline we just ran.
Concourse runs the task step
hello-world-task
with outputthe-artifact
Concourse creates an empty artifact, assigns it the name
the-artifact
, and mounts it inside the task container.Concourse runs the task step
read-the-artifact
with inputthe-artifact
Concourse looks up, in its list of artifacts for the build, for an artifact named
the-artifact
, and mounts it inside the task container. If no input with that name is found then the build would fail.
The next section will introduce you to the concept of Resources.
If you have any feedback for this tutorial please share it in this GitHub discussion