Git SHA Badges for Deploys – Know Which Commit is Live

  • report
    Disclaimer
    Click for Disclaimer
    This Post is over a year old (first published about 3 years ago). As such, please keep in mind that some of the information may no longer be accurate, best practice, or a reflection of how I would approach the same thing today.
  • infoFull Post Details
    info_outlineClick for Full Post Details
    Date Posted:
    Dec. 11, 2020
    Last Updated:
    Dec. 14, 2020
  • classTags
    classClick for Tags

Demo Repo

I’ve taken the approaches covered in this post and created a fully-functional demo repo to demonstrate them in action:

Intro

When building a project that lives in a Git repository and auto-deploys elsewhere, a common concern is how to be 100% absolutely sure that the live “deployed” version is up-to-date. If I go to the live, production application, how can I trust that what I am seeing is the latest pushed code in my repository?

Just because my CI/CD pipeline passed does not necessarily mean that the latest commit is live yet. Furthermore, if something goes bad, I want to be able to know at a glance exactly what code is live in production. A simple “pass / fail” badge cannot tell me that.

Close, but Not Good Enough

Many automated deploy systems provide a build status badge, but this doesn’t tell us much:

Standard Netlify Status Badge:

Netlify Status

Many apps also display in the deployed UI the current version string of the app (e.g. 1.8.2-beta.1.10), but this actually still leaves a lot of room for ambiguity; what if a developer used --force to override the head of the main branch? What if there have been commits added to the main branch, but no new release authored? Etc.

Instead of a version string, a nice alternative (or better yet, an addition) is to use the actual SHA of the latest commit as a status indicator. This makes it very easy to compare against the latest commit in version control, and verify that the correct set of changes are live.

Furthermore, no two commits can have the same SHA, even if --force has been used, so if I know the SHA that is “live”, I know exactly what code is running, and can even quickly “checkout” that exact version of code in my local environment.

💡 Similar to how you can use git checkout {branch_name}, you can also use git checkout {SHA} to checkout your entire repo at that commit. It kind of feels like time travelling!

Methods for Creating SHA Badges

The general concept that we are going to employ is that our build step, which runs on every deploy, is now going to save the current SHA of the branch (aka the tip) in an accessible format that can be used for visual indicators.

Two easy approaches that build off this concept, and which I’ll explore here, are JSON files and raw SVG files.

JSON File

The basic idea with the JSON file approach is that, at build time, we stash the value of the latest SHA in a shared JSON file. This makes it easy to reuse across our codebase, possibly even at runtime, and in a widely compatible format.

The JSON file approach is my favorite, for a couple of reasons:

  • It is easier than dealing with SVG string building, but can be combined with that approach easily (more details further below)
  • The JSON file can be re-used, and even imported into runtime code (assuming they share access)!
  • Usable by APIs and 3rd party services (including Shields.io for badges)

JSON File – Getting and Saving the SHA

The implementation of this task could be accomplished in an endless number of ways, and the best option probably depends on the specifics of your build environment, project type, and operating system.

For a generic example, here is how I how I have integrated this approach into a NodeJS based project, within a package.json file:

{
  "scripts": {
    "build-badge": "$NETLIFY && git rev-parse --short HEAD | xargs -I % printf '{\"sha\":\"%\"}' > ./static/build-info.json",
    "build": "(yarn run build-badge || true) && gatsby build"
  }
}

The most important thing about the above command is that it modifies our build command, which used to be just gatsby build, to include a step that saves the current SHA to a JSON file (./static/build-info.json). If you wanted to, you could replace a lot of the bash stuff with a NodeJS or python script that does the same thing.

If you want a breakdown of the above command, you can view it below

Open Command Explanation

To break this down further, the build-badge command is composed of:

  • $NETLIFY is an environmental variable that only exists within my build environment (Netlify servers).
    • You could use any value that evaluates to true in your build system
    • Or, if you always want this file generated regardless of local vs production, you could omit the whole $VARIABLE && part
  • git rev-parse --short HEAD
    • This is a git command to get the short version of the last commit SHA
    • You could use the full SHA instead; just omit --short
    • If you are looking for a lot of nifty git commands like this one, here is a shameless plug to check out my Git Cheat Sheet (this command is on it!)
  • xargs -I % printf '{\"sha\":\"%\"}'
    • Not going to get too much into this, but it is a fancy way of passing the SHA from the last section into a stringified JSON representation
  • > ./static/build-info.json
    • Save the generated JSON string to a file. This could be any file, and it doesn’t even necessarily need the .json extension
    • You should make sure that whatever directory you save it to is going to be accessible after the build is complete, either internally and/or externally (hosted)

And I’ve changed my build command from gatsby build to (yarn run build-badge || true) && gatsby build:

  • This is an easy way to make sure that gatsby build, the true build command, always runs regardless of the success or failure of the build-badge command.

Again, as an alternative to a long bash command you could alternatively extract and save the SHA entirely in your favorite scripting language of choice (NodeJS, Python, etc.).

JSON File – Using the Stored SHA

As-is, once we have our JSON file, that alone is enough to be able to add badges or pull the SHA value into front-end code. For example, we can use the dynamic badge feature of Shields.io to easily get a SVG URL that displays our SHA. Something like:

<img src="https://img.shields.io/badge/dynamic/json?color=blue&label=Deployed%20SHA&query=sha&url=https%3A%2F%2Fgit-sha-badges.netlify.app%2Fbuild-info.json" alt="Deployed SHA" />

Notice the importance of:
 - query=sha
 - url=HOSTED_SITE/build-info.json
Deployed SHA

If you wanted to pull it into your code, in CommonJS, that is as easy as:

const buildInfo = require(`${BUILD_DIR}/build-info.json`);
console.log(buildInfo.sha);

SVG File

Rather than displaying your SHA in front-end code by pulling it in via AJAX or inside framework components (e.g. a React component), you could also pre-generate SVG badges at build time, with just a little bit of code.

The easiest approach is to have 99% of the SVG badge ready-to-go, stored as a string. You can use whatever method you prefer for designing your badge. The main thing to do is leave room for the SHA to be inserted at build time, as a <text></text> element inside the SVG.

At build time, you combine your SVG template string with the latest SHA (this is where it is handy to have that build-info.json already generated), and then write out the resulting string to an actual .svg file, which can be served.

You can checkout how I put this all together in build.js in my demo repo.

In pseudo code, this approach might look something like:

// Compose full SVG string
const svgStr = '<svg>' + svgTemplateStr + '<text>' + sha + '</text></svg>';
// Save to SVG
saveFile(svgStr, 'my-badge.svg');

And don’t forget, you can get as fancy as you want with your generated SVGs 😄

Bonus: Github Commit Badges

If all you want is a badge that shows the current HEAD of a particular branch in Github (i.e. the most recent pushed commit) – you can actually do this entirely with just Shields.io and the public Github API (no credentials required).

Caveat: this requires that your repo is published as public

First, you need get the API endpoint that returns HEAD info, for your specific repo and branch combo. The syntax follows:

https://api.github.com/repos/{USER}/{REPO}/git/refs/heads/{BRANCH}

So, for example, to get the currently pushed tip for my main branch in my demo repo for this post (joshuatz/git-sha-badges), I can use api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main.

If you try that URL, you will get a JSON response, including:

{
    // Other stuff
    "object": {
        "sha": "____",
        "type": "commit"
    }
}

Now, we can plug that endpoint directly into Shield’s amazing dynamic badge generator, which accepts a JSON endpoint and lets us control badge settings through query string parameters. By tweaking the parameters, we can get an orange badge that displays the object.sha value in the endpoint:

API Endpoint:
  https://api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main

Shield Badge Base Endpoint
  https://img.shields.io/badge/dynamic/json

Shield Badge URL (raw)
  https://img.shields.io/badge/dynamic/json?color=orange&label=Github SHA&query=object.sha&url=https://api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main

Same URL, encoded:
  https://img.shields.io/badge/dynamic/json?color=orange&label=Github%20SHA&query=object.sha&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain

---

Final badge code

---

Markdown:
  ![Github SHA](https://img.shields.io/badge/dynamic/json?color=orange&label=Github%20SHA&query=object.sha&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain)

HTML
  <img src="https://img.shields.io/badge/dynamic/json?color=orange&label=Github%20SHA&query=object.sha&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain" alt="Github SHA" />

And, here it is (this is a live badge!):

Github SHA

Wrap Up

I want to wrap up this post by pointing out that, although I gave some specific examples and code samples, the approaches throughout this post are fairly generic in nature and could be extended to solutions beyond just adding Git SHA badges.

For example, if you maintain several build and deploy targets (e.g. dev, test, staging-alpha, production, etc.), you could re-use most of the solutions through this post to add visual indicators to each environment that makes it clear what system you are looking at.

Hope this post helps someone out there! (And kudos to anyone that understands and appreciates what my fancy SVG example is referencing).

Leave a Reply

Your email address will not be published.