Capturing multiline output and exit status in a Github Action with bash
You can capture output from a Github Action by echoing a variable name to the
file specified by the "$GITHUB_OUTPUT"
variable:
echo "foobar='here is some data'" | tee -a "$GITHUB_OUTPUT"
This is a bit trickier with multiline output. You can use the bash EOF
syntax
for multiline strings:
FOOBAR<<EOF
here
is
some
multiline
data
EOF
The easiest way to use bash multiline strings in a Github Action is to use a bash command group with curly braces.
To be able to experiment with this locally, match the Github Action bash
settings and allow piping to a file specified in the "$GITHUB_OUTPUT"
bash
variable:
set -e -o pipefail # match Github Action bash settings
export GITHUB_OUTPUT=/dev/null # allow piping to "$GITHUB_OUTPUT"
{
echo 'FOOBAR<<EOF'
for i in {1..5}; do echo $i; done
echo 'EOF'
} | tee -a "$GITHUB_OUTPUT"
We’re using | tee -a
instead of >>
so that we can see the output in the
terminal history, which is useful for debugging.
Capturing multiline stderr output in bash in a Github Action
What about if you also want the stderr output from the command? The easiest way
to also capture stderr is to redirect it to stdout so you get both together.
Because Github Actions have set -e -o pipefail
by default, we need to run
set +e
in the Github Action so that it continues even after a non-zero exit
status.
set +e # Continue even after a non-zero exit status
For example if we want to capture the multiline stderr from the isort
Python
linter:
set +e
{
echo 'FOOBAR<<EOF'
isort . --check 2>&1
echo 'EOF'
} | tee -a "$GITHUB_OUTPUT"
Keeping exit status and multiline output in bash in a Github Action
What about if you also want to keep the exit status from the command? In the
example with isort
above, the exit status (accessible as $?
) is going to be
0
, because the exit status of the command group as a whole comes from the
final echo
:
set +e
{
false # exit status 1
echo "anything" # exit status 0
} | tee -a "$GITHUB_OUTPUT"
echo $? # 0
We can capture the exit status of the command into a local shell variable
first and then access it later. However, because it’s a local shell variable,
it would be local to the command group, which would make it inaccessible outside
the command group. We’ll have to run multiple echo commands to use EOF
for the
multiline output and still be able to access the local shell variable with the
exit status afterwards.
set +e
echo "COMMAND_OUT<<EOF" | tee "$GITHUB_OUTPUT"
isort . --check 2>&1 | tee -a "$GITHUB_OUTPUT" ; COMMAND_STATUS=$?
( echo ; echo "EOF" ) | tee -a "$GITHUB_OUTPUT"
echo $COMMAND_STATUS
This lets us capture the multiline stderr output from isort
as a Github Action
output called COMMAND_OUT
, and then use the exit status from isort
as
necessary. If you want the Github Action step to use the exit status from
isort
, then you can exit $COMMAND_STATUS
at the end of the bash script in
that step:
set +e
echo "COMMAND_OUT<<EOF" | tee "$GITHUB_OUTPUT"
isort . --check 2>&1 | tee -a "$GITHUB_OUTPUT" ; COMMAND_STATUS=$?
( echo ; echo "EOF" ) | tee -a "$GITHUB_OUTPUT"
echo $COMMAND_STATUS
exit $COMMAND_STATUS