Context
GitHub Actions allows to use the outputs from dependent jobs. For single line strings, you can do the following
jobs:
job1:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.set-output.outputs.output1 }}
steps:
- id: set-output
run: |
echo "::set-output name=output1::'Hello World'"
job2:
runs-on: ubuntu-latest
needs: [job1]
steps:
- run: |
echo "Output1: ${{ needs.job1.outputs.output1 }}"
Unfortunately, the above solution doesn’t work for multiline strings. By default, using the :::set-output
command will cut the string to just the first line of content.
Implementation
We have 2 main options at this time when creating new multiline outputs to be used by downstream jobs:
- Escape the return characters to convert the multiline string into a single-line one. The runner will convert back appropriately when the output is used.
- Leverage the
actions/github-script
and thecore
context (from theactions/toolkit
package) to export the output through javascript
Escaping the characters through Bash string substitution
Let’s see how escaping would work in detail. The characters that need escaping are %
, \n
and \r
, based on this github community question.
Leveraging string substitution in bash, this looks as follows:
content="${content//'%'/'%25'}"
content="${content//$'\n'/'%0A'}"
content="${content//$'\r'/'%0D'}"
Then, the jobs would look like:
jobs:
job1:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.set-output.outputs.output1 }}
steps:
- id: set-output
run: |
# Assuming we have a $content variable already populated
content="${content//'%'/'%25'}"
content="${content//$'\n'/'%0A'}"
content="${content//$'\r'/'%0D'}"
echo "::set-output name=output1::$content"
job2:
runs-on: ubuntu-latest
needs: [job1]
steps:
- run: |
echo "Output1: ${{ needs.job1.outputs.output1 }}"
Using action/github-script and the core package in actions/toolkit
The github-script action provides access to different APIs accessible through different objects. The core
object referencees actions/core package, which allows setting the step’s output using the setOutput
function/
jobs:
job1:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.set-output.outputs.output1 }}
steps:
- uses: actions/github-script@v5
id: set-output
with:
script: |
# Assuming we have a `content` environment variable or output of previous step
core.setOutput("output1", `${{ env.context }}`);
job2:
runs-on: ubuntu-latest
needs: [job1]
steps:
- run: |
echo "Output1: ${{ needs.job1.outputs.output1 }}"
References
To learn more about it, check the following resources:
- https://github.community/t/set-output-truncates-multiline-strings
- https://github.com/actions/github-script
- https://github.com/actions/toolkit/tree/main/packages/core
- https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idoutputs
- https://github.community/t/what-is-the-correct-character-escaping-for-workflow-command-values-e-g-echo-xxxx/118465/4