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‚Äč/‚Äč9‚Äč4‚Äč5‚Äč4‚Äč3‚Äč9‚Äč4‚Äč7‚Äč5‚Äč7‚Äč/‚Äča‚Äčr‚Äčt‚Äči‚Äčf‚Äča‚Äčc‚Äčt‚Äčs‚Äč/‚Äč1‚Äč5‚Äč8‚Äč6‚Äč8‚Äč96203

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.

UX Writing Tips
Design Process

UX Writing Tips

February 3, 2023

Kai shares a few tips he's collected on how to write for user interfaces.

Read more
Three principles for creating user-friendly products
Business Design

Three principles for creating user-friendly products

January 25, 2023

Grayson discusses three guiding principles he utilizes when designing user experiences for products.

Read more
Between the brackets: MichiganLabs’ approach to software development
Development Team

Between the brackets: MichiganLabs’ approach to software development

February 12, 2024

Read more
View more articles