How To Pass Data Between Workflows in GitHub Actions?

Image is a featured image for the article about how to pass data between GitHub workflows and consist of GitHub logo in the foreground and a blurred photo of a skyscraper.

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:

Image presents a screenshot from GitHub actions workflow run with produced artifact.

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.

Share this:

Picture of Hi there! 👋

Hi there! 👋

My name is Piotr and I've created Codersee to share my knowledge about Kotlin, Spring Framework, and other related topics through practical, step-by-step guides. Always eager to chat and exchange knowledge.

Related content

2 Responses

  1. 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!

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Newsletter

Image presents 3 ebooks with Java, Spring and Kotlin interview questions.

Never miss any important updates from the Kotlin world and get 3 ebooks!

You may opt out any time. Terms of Use and Privacy Policy

Free tutorials and courses on Kotlin & backend

To make Codersee work, we log user data. By using our site, you agree to our Privacy Policy and Terms of Use.