Github Actions and Heroku Pipelines
Lately I’ve started moving projects away from CircleCI and just to use Github Actions natively within our repos. There’s absolutely nothing wrong with CircleCI! But, it’s still one more service to configure and pay for when we’re already paying for Github Actions along with our organizational account. So in an effort to tidy up a bit, we’ve switched over our CI pipelines to use Actions instead.
The process was mostly smooth, but that’s a post for another day. One of the key areas that wasn’t smooth was showing the build status on our Heroku Pipeline. We use Heroku’s Pipeline feature to deploy quickly between environments and keep things in sync between our development, staging, and production servers. CircleCI automatically showed status updates in Heroku and they look something like this:
We rely on those green checks to make sure that we’re not accidentally deploying a build that didn’t pass all of our checks. In a normal pipeline, you may prefer to not allow builds to deploy until the checks pass. The first step of our pipeline is a development server, and I don’t care if the build doesn’t pass there. It also speeds up our deployment by deploying to dev while the tests run.
But with Github Actions by default, here’s what the deployed commits looked like:
It took some digging, and I didn’t find much out there in my searches to find out how this works. It turns out that these checks are called Commit Statuses in Github, and there’s an API for working with them.
Luckily for us, Github Actions provides easy access to the Github API with the Github CLI. Here are the steps needed within a Github Actions workflow to get those checks back:
On Begin Test Suite
When our test suite begins, we want to set up a “pending” commit status to let Github and Heroku Pipelines know that something is in progress. Here’s the workflow code to enable the first step:
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Announce Pending Status
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA} \
-f state='pending' \
-f target_url="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description='Build has started ...' \
-f context='Github Actions'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
And the resulting visual from Heroku:
A few bits about the step above:
- The
target_url
parameter allows us to put any URL into the status. When the Heroku Pipeline status is clicked, it will direct your browser to the run on Github Actions. This can easily be set to something else if you prefer. - Don’t forget to include the
env
forGH_TOKEN
. This allows theGITHUB_TOKEN
secret to be accessible within the api call. (This secret is set automatically by Github, and does not need to be configured.) - The
description
parameter is the string that will be visible in the Heroku UI. - The
context
parameter should be a unique key for each check you are running. We are just using one here, but you could easily create more than one context for multiple jobs. (Linting, test suite, etc could all be their own steps) When updating status later, you just need to pass the samecontext
again. - Finally, the
state
parameter is set to “pending” here to denote the yellow indicator and that something is “in progress”.
A note on permissions: If your Github organization or account doesn’t have admin permissions, you’ll probably want to add the following to the top of your job declaration:
build:
name: "My Job Name"
runs-on: ubuntu-latest
permissions:
actions: read
checks: write
contents: read
pull-requests: read
statuses: write
On Test Suite Passing
When the test suite completes without error, we want a green check on Heroku. The same Github CLI call can be modified to show success like so:
steps:
# Previous steps here, put this one at the end:
- name: Announce Success Status
if: ${{ success() }}
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA} \
-f state='success' \
-f target_url="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description='Github Actions Passed!' \
-f context='Github Actions'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Here is what that update looks like on Heroku now:
Notice in this step we included the line if: ${{ success() }}
. This ensures that the CLI call in this step only runs if the tests passed.
On Test Suite Failure
If the test suite fails with error, we want to note this as well. Here’s the step call to update the commit status with a failure:
steps:
- name: Announce Failure Status
if: ${{ failure() }}
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA} \
-f state='failure' \
-f target_url="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description='Github Actions Failed :(' \
-f context='Github Actions'
./bin/ci_notify --type=failure
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
And here is the resulting visual from Heroku for a failed suite:
Just like the passing check, we’re using if: ${{ failure() }}
to ensure this step is called only if a previous step fails.
That’s it, and we’re all set! Now each of our Heroku Pipeline steps have a proper build status indicator from Github Actions.
Last updated 3/9/2024. If you find this helpful, please let me know!.