Development

Automatic artifact downloads inside PR comments

June 20, 2024
Automatic artifact downloads inside PR comments

In the pur­suit of per­fect automa­tion, hav­ing build arti­facts from Github pipelines be avail­able at your fin­ger­tips is an impres­sive qual­i­ty-of-life improve­ment for developers. 

If you’ve used Github Actions, you know that build arti­facts are eas­i­ly acces­si­ble to down­load after drilling down into an indi­vid­ual work­flow run. But when you’re review­ing code in a pull request (PR), this requires three clicks from a code review going to Actions → Build → Down­load Artifact:

I’ve always want­ed to get this click count down to one, so I’ll show you how I did it.

After set­ting up this Github Action, you’ll have a ful­ly auto­mat­ed com­ment on your PR with a down­load link to all of the indi­vid­ual arti­facts pro­duced by the workflow!

When this real­ly shines #

Some peo­ple believe that going from three clicks to one isn’t jus­ti­fi­able for the effort required. But real­ly, any automa­tion task is look­ing at the long-run. 

If you’re only deal­ing with a sin­gle arti­fact, then it may not be nec­es­sary. But on projects where you need mul­ti­ple arti­facts pro­duced and mul­ti­ple actions run­ning them, this con­sol­i­dates your arti­facts into a sin­gle PR comment.

For exam­ple, with React Native projects, in a pro­duc­tion envi­ron­ment we’re deal­ing with at least four artifacts:

  1. Sim­u­la­tor .app build for iOS
  2. Dis­tri­b­u­tion .ipa build for iOS
  3. Test­ing .apk build for Android
  4. Dis­tri­b­u­tion .aab build for Android

In my case, I use in-house run­ners to kick off iOS builds and Github cloud run­ners for Android. These can both run in par­al­lel, but the arti­facts end up in dif­fer­ent runs. 

Instead, through automa­tion, we can get all of these arti­facts into a sin­gle PR com­ment that self-updates with the asyn­chro­nous work­flow runs.

You can see an exam­ple of this in action in this PR.

How it works #

You may be think­ing that you could just use an exist­ing action from the Github mar­ket­place to do this. And there may be some that work, but I’ve had no such luck and don’t want to pay for anything. 

So start­ing with that assump­tion, let’s begin by inspect­ing what an arti­fact real­ly is. 

All we need is a link to the arti­fact, so let’s take a look at one:

https://​github​.com/​D​a​v​e​A​l​d​o​n​/​P​R​-​A​r​t​i​f​a​c​t​-​C​o​m​m​e​n​t​s​/​a​c​t​i​o​n​s​/​r​u​n​s​/​9454394757​/​a​r​t​i​f​a​c​t​s​/​1586896203

This link is bro­ken down into three parts:

  1. The repo ref­er­ence: Dav­eAl­don/PR-Arti­fact-Com­ments
  2. The work­flow run ID: 9454394757
  3. The arti­fact ID: 1586896203

Sounds easy enough, right? The repo ref­er­ence and work­flow run id is actu­al­ly real­ly easy to get dur­ing a giv­en action run, and are avail­able through the Github con­text API

For exam­ple, run id can be retrieved at any point like this:

steps:
  - name: Print Workflow Run ID
    run: echo "The workflow run ID is ${{ github.run_id }}"

But notice in the docs that get­ting any arti­fact ids is miss­ing? You can actu­al­ly retrieve them via the REST API, but the arti­fact link only lasts one minute and only works after the run has fin­ished. It’s not pos­si­ble using this method to pull the arti­fact ID dur­ing the run itself. Also, I’m expect­ing to kick off my runs and come back a while lat­er hop­ing to down­load what­ev­er I want, when­ev­er I want. Not with­in one minute.

So what I’ve done is use the Github API to its fullest, and inject code into the action process in order to get the id. Here’s how to do it:

  1. When you upload your arti­fact, bind it to an id:
    - name: Upload Artifact
            uses: actions/upload-artifact@v4.3.3
            id: sample-artifact
            with:
              name: sample-artifact
              if-no-files-found: error
              retention-days: 14
              path: ${{ github.workspace }}/sample.txt
    
  2. Use the id field to assign the arti­fact id to a tem­po­rary envi­ron­ment vari­able:
    - name: Output artifact ID
      run: echo "SAMPLE_ARTIFACT_ID=${{ steps.sample-artifact.outputs.artifact-id }}" >> $GITHUB_ENV
    
  3. Ref­er­ence the envi­ron­ment vari­able inside a script job to build the link and ini­ti­ate the PR com­ment:
    - name: Comment PR
      uses: actions/github-script@v7.0.1
      if: success()
      with:
        github-token: ${{secrets.GITHUB_TOKEN}}
        script: |
          const sample_artifact_id = process.env.SAMPLE_ARTIFACT_ID;
          const issue_number = context.issue.number;
          const run_id = context.runId;
          const repo = context.repo;
          // Merges to main branch do not have an associated issue number
          if(!issue_number) {
            return;
          }
          const development_link = `https://github.com/${repo.owner}/${repo.repo}/actions/runs/${run_id}/artifacts/${sample_artifact_id}`
          const comment = `
            ## ✅ Build Artifacts 😍
            | Build Name | Download Link |
            | --- | --- |
            | 🌎 Artifact Download | [Download](${development_link}) |
          `;
          // Create a new comment
          await github.rest.issues.createComment({
            owner: repo.owner,
            repo: repo.repo,
            issue_number: issue_number,
            body: comment
          });
    

And voila! When­ev­er you make a PR, the action will run, upload the arti­fact, and com­ment on the PR with a down­load link to the arti­fact. Anoth­er added bonus is that when an action com­ments on your PR, you’ll get an email with the con­tents, mean­ing that you can down­load your arti­facts from emails too!

You can take a look at the orig­i­nal code­base for this post in this repo which has even more automa­tion in the build script that handles:

  1. Cre­at­ing a com­ment tem­plate with in progress” sta­tus­es for oth­er asyn­chro­nous actions
  2. Search­es for a bot com­ment and replaces it with the new down­load link

This top­ic has been incred­i­bly help­ful for me to under­stand how Github’s Action API works, and to find out what’s real­ly pos­si­ble with PR automation.

David Crawford
David Crawford
Software Developer

Looking for more like this?

Sign up for our monthly newsletter to receive helpful articles, case studies, and stories from our team.

Advanced Tailwind: Container Queries
Development Web

Advanced Tailwind: Container Queries

July 28, 2023

Explore some advanced web layout techniques using Tailwind CSS framework

Read more
When to “shape up” versus be more “agile”
Business Process

When to “shape up” versus be more “agile”

May 30, 2024

Should you develop your new software using the Agile or Shape Up method? Here’s how to decide.

Read more
Application Architecture with SwiftUI
Development iOS

Application Architecture with SwiftUI

June 15, 2022

An overview of mobile application system architecture using SwiftUI

Read more
View more articles