In this tutorial, we would like to see how to pass data between workflows in GitHub Actions. And to be even more specific- how can we pass the data from workflow A
to workflow B
, which is triggered using the workflow_run
event.
But before we start, just a quick reminder that this is my second post in the series about GitHub Actions workflow. And if you would like to see the introduction, then check out my previous article and learn how to create your first workflow.
Video Tutorial
If you prefer video content, then check out my video:
If you find this content useful, please leave a subscription 😉
Trigger Workflow On workflow_run
Let’s assume that we would like to add a new workflow to our GitHub Actions, workflow B
, which will trigger whenever workflow A
completes successfully. We go to the events documentation and figure out that the workflow_run will be the right choice.
So, for the following workflow A
:
name: Workflow A on: push: branches: [ "main" ] jobs: some_job: runs-on: ubuntu-latest steps: - name: Some Step run: echo "Hello!"
We pretty quickly came up with something, like this:
name: Workflow B on: workflow_run: workflows: ["Workflow A"] types: - completed jobs: run_on_workflow_a_success: if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - name: Run on workflow A success run: echo "Workflow A completes successfully"
Whenever the workflow A
completes successfully, we simply print out a message to the output.
Problem With Passing Data
But life is not usually that simple and we pretty quickly realize that we need to find a way to pass the data between our GitHub workflows.
If we learned from my previous article or spent some time with the official documentation, we finally land on the contexts page and try to find the necessary information.
If are lucky enough and the data we need are present, we can simply access them with one line of code.
Just like we did previously:
github.event.workflow_run.conclusion
As we can see, the above line accesses information about conclusion from the github
context.
But what if we would like to access some custom data produced by the workflow A
within the workflow B
?
Well- that’s the point where the story begins 🙂
Pass Data Between GitHub Workflows With Artifacts
Unfortunately, at the moment when I am writing this article, there’s no such thing as a custom context or something similar where we could put the data and access them inside another (dependent) workflow.
Fortunately, we can bypass this limitation by producing artifacts.
Let’s update our workflow A
first:
name: Workflow A on: push: branches: [ "main" ] jobs: some_job: runs-on: ubuntu-latest steps: - name: Some Step run: echo "Hello!" job_producing_data: runs-on: ubuntu-latest steps: - name: Some Step run: | printf '{ "prop_1": "Some value", "prop_2": true }' >> context.json - uses: actions/upload-artifact@v3 with: name: context.json path: ./
With this solution, we produce a JSON file called context.json, which then we publish as an artifact.
In simple words, GitHub artifacts are files (or collections of files), that we can produce during the workflow run and access later- from other workflows, through the API, or simply in the actions tab:
Following, let’s update the workflow B
to consume this file and access its data:
name: Workflow B on: workflow_run: workflows: ["Workflow A"] types: - completed jobs: run_on_workflow_a_success: if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - name: Run on workflow A success run: echo "Workflow A completes successfully" download_context_artifact: runs-on: ubuntu-latest steps: - name: 'Download artifact' uses: actions/github-script@v6 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.payload.workflow_run.id, }); let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { return artifact.name == "context.json" })[0]; let download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); let fs = require('fs'); fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/context.zip`, Buffer.from(download.data)); - name: 'Unzip artifact' run: unzip context.zip - name: 'Return Parsed JSON' uses: actions/github-script@v6 id: return-parsed-json with: script: | let fs = require('fs'); let data = fs.readFileSync('./context.json'); return JSON.parse(data); outputs: property_one: ${{fromJSON(steps.return-parsed-json.outputs.result).prop_1}} property_two: ${{fromJSON(steps.return-parsed-json.outputs.result).prop_2}} log_context_values: needs: - download_context_artifact runs-on: ubuntu-latest steps: - name: 'Log Context Values' run: | echo "${{ needs.download_context_artifact.outputs.property_one }}" echo "${{ needs.download_context_artifact.outputs.property_two }}"
As we can see, we added two new jobs: download_context_artifact
and log_context_values
.
The first one is responsible for downloading the artifact (unfortunately GitHub API supports only zip files at the moment). We list all articles from the triggering flow (workflow A
), match the one named context.json
and download it.
Then, we unzip the file and parse its JSON content.
As the last thing in the first job, we produce two outputs. The output is nothing else than a map accessible in all downstream jobs dependent on this job. And to put it simply, in all jobs which point to this one using needs.
The interesting thing here is that in order to access the parsed json- return JSON.parse(data)
– we need to use the <step id>.outputs.result.
The second job, log_context_values
, simply prints out our values.
Summary
And that’s all for this tutorial, in which we learned how to pass data between workflows in GitHub actions.
I hope you enjoyed this one and if you would like to share your feedback with me or ask about anything, then let me know in the comments section.
3 Responses
Hi Piotr!
Nice article.
Lately there is a download action v4, which would make the “Download artifact” step of download_context_artifact way more simple:
“`
– name: Download Artifact
uses: actions/download-artifact@v4
with:
# name: [artifact name] # If specified, files will be extracted to the root folder!
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
“`
I am new to Github actions – so I am not sure if this is a new-new setup – or if there is any reason NOT to use this setup though!
Hi man!
You’re right! The V4 version is quite new (mid of December) and introduced downloading artifacts from other workflow runs.
“Artifacts can be downloaded from other workflow runs and repositories when supplied with a PAT”
And I didn’t have too much time to update this setup.
Hi Piotr,
How to read a version from workflow A and use it to update version in workflow B?
Great article.
Fred